xref: /onnv-gate/usr/src/uts/common/io/pm.c (revision 3028)
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
5*3028Smh27603  * Common Development and Distribution License (the "License").
6*3028Smh27603  * 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*3028Smh27603  * Copyright 2006 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;
74*3028Smh27603 extern pm_cpupm_t cpupm;
75*3028Smh27603 extern int	pm_default_idle_threshold;
76*3028Smh27603 extern int	pm_system_idle_threshold;
77*3028Smh27603 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},
428*3028Smh27603 	{PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
429*3028Smh27603 	{PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
430*3028Smh27603 	{PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
431*3028Smh27603 	{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
432*3028Smh27603 	    0, 0, 0, SU},
433*3028Smh27603 	{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 {
467*3028Smh27603 	int cmd = *((int *)arg);
468*3028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
4710Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
4720Sstevel@tonic-gate 
473*3028Smh27603 	switch (cmd) {
474*3028Smh27603 	case PM_START_CPUPM:
475*3028Smh27603 		if (!PM_ISCPU(dip))
476*3028Smh27603 			return (DDI_WALK_CONTINUE);
477*3028Smh27603 		mutex_enter(&pm_scan_lock);
478*3028Smh27603 		if (!PM_CPUPM_DISABLED)
479*3028Smh27603 			pm_scan_init(dip);
480*3028Smh27603 		mutex_exit(&pm_scan_lock);
481*3028Smh27603 		break;
482*3028Smh27603 	case PM_START_PM:
483*3028Smh27603 		mutex_enter(&pm_scan_lock);
484*3028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_DISABLED) {
485*3028Smh27603 			mutex_exit(&pm_scan_lock);
486*3028Smh27603 			return (DDI_WALK_CONTINUE);
487*3028Smh27603 		}
488*3028Smh27603 		if (autopm_enabled)
489*3028Smh27603 			pm_scan_init(dip);
490*3028Smh27603 		mutex_exit(&pm_scan_lock);
491*3028Smh27603 		break;
492*3028Smh27603 	}
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	/*
4950Sstevel@tonic-gate 	 * Start doing pm on device: ensure pm_scan data structure initiated,
496*3028Smh27603 	 * no need to guarantee a successful scan run.
4970Sstevel@tonic-gate 	 */
4980Sstevel@tonic-gate 	PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
4990Sstevel@tonic-gate 	    PM_DEVICE(dip)))
5000Sstevel@tonic-gate 	pm_rescan(dip);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5030Sstevel@tonic-gate }
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate /*
5060Sstevel@tonic-gate  * Bring devices to full power level, then stop scan
5070Sstevel@tonic-gate  */
5080Sstevel@tonic-gate int
5090Sstevel@tonic-gate pm_stop_pm_walk(dev_info_t *dip, void *arg)
5100Sstevel@tonic-gate {
5110Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
512*3028Smh27603 	int cmd = *((int *)arg);
513*3028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	if (!info)
5160Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
517*3028Smh27603 
518*3028Smh27603 	switch (cmd) {
519*3028Smh27603 	case PM_STOP_PM:
520*3028Smh27603 		/*
521*3028Smh27603 		 * If CPU devices are being managed independently, then don't
522*3028Smh27603 		 * stop them as part of PM_STOP_PM. Only stop them as part of
523*3028Smh27603 		 * PM_STOP_CPUPM and PM_RESET_PM.
524*3028Smh27603 		 */
525*3028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_ENABLED)
526*3028Smh27603 			return (DDI_WALK_CONTINUE);
527*3028Smh27603 		break;
528*3028Smh27603 	case PM_STOP_CPUPM:
529*3028Smh27603 		/*
530*3028Smh27603 		 * If stopping CPU devices and this device is not marked
531*3028Smh27603 		 * as a CPU device, then skip.
532*3028Smh27603 		 */
533*3028Smh27603 		if (!PM_ISCPU(dip))
534*3028Smh27603 			return (DDI_WALK_CONTINUE);
535*3028Smh27603 		break;
536*3028Smh27603 	}
537*3028Smh27603 
5380Sstevel@tonic-gate 	/*
5390Sstevel@tonic-gate 	 * Stop the current scan, and then bring it back to normal power.
5400Sstevel@tonic-gate 	 */
5410Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
5420Sstevel@tonic-gate 		PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
5430Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
5440Sstevel@tonic-gate 		pm_scan_stop(dip);
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
5480Sstevel@tonic-gate 	    !pm_all_at_normal(dip)) {
5490Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
5500Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_DETACHING) {
5510Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
5520Sstevel@tonic-gate 			    "all_to_normal because %s@%s(%s#%d) is detaching\n",
5530Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
5540Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
5550Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
5560Sstevel@tonic-gate 			return (DDI_WALK_CONTINUE);
5570Sstevel@tonic-gate 		}
5580Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
5590Sstevel@tonic-gate 		if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
5600Sstevel@tonic-gate 			PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
5610Sstevel@tonic-gate 			    "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
5620Sstevel@tonic-gate 		}
5630Sstevel@tonic-gate 	}
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate static int
5690Sstevel@tonic-gate pm_start_idledown(dev_info_t *dip, void *arg)
5700Sstevel@tonic-gate {
5710Sstevel@tonic-gate 	int		flag = (int)(intptr_t)arg;
5720Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	if (!scanp)
5750Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5780Sstevel@tonic-gate 	scanp->ps_idle_down |= flag;
5790Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
5800Sstevel@tonic-gate 	pm_rescan(dip);
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate /*ARGSUSED*/
5860Sstevel@tonic-gate static int
5870Sstevel@tonic-gate pm_end_idledown(dev_info_t *dip, void *ignore)
5880Sstevel@tonic-gate {
5890Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	if (!scanp)
5920Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5950Sstevel@tonic-gate 	/*
5960Sstevel@tonic-gate 	 * The PMID_TIMERS bits are place holder till idledown expires.
5970Sstevel@tonic-gate 	 * The bits are also the base for regenerating PMID_SCANS bits.
5980Sstevel@tonic-gate 	 * While it's up to scan thread to clear up the PMID_SCANS bits
5990Sstevel@tonic-gate 	 * after each scan run, PMID_TIMERS ensure aggressive scan down
6000Sstevel@tonic-gate 	 * performance throughout the idledown period.
6010Sstevel@tonic-gate 	 */
6020Sstevel@tonic-gate 	scanp->ps_idle_down &= ~PMID_TIMERS;
6030Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate /*ARGSUSED*/
6090Sstevel@tonic-gate static void
6100Sstevel@tonic-gate pm_end_idledown_walk(void *ignore)
6110Sstevel@tonic-gate {
6120Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
6130Sstevel@tonic-gate 	    "off\n", (ulong_t)pmstp->pm_idledown_id));
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6160Sstevel@tonic-gate 	pmstp->pm_idledown_id = 0;
6170Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate /*
6230Sstevel@tonic-gate  * pm_timeout_idledown - keep idledown effect for 10 seconds.
6240Sstevel@tonic-gate  *
6250Sstevel@tonic-gate  * Return 0 if another competing caller scheduled idledown timeout,
6260Sstevel@tonic-gate  * otherwise, return idledown timeout_id.
6270Sstevel@tonic-gate  */
6280Sstevel@tonic-gate static timeout_id_t
6290Sstevel@tonic-gate pm_timeout_idledown(void)
6300Sstevel@tonic-gate {
6310Sstevel@tonic-gate 	timeout_id_t	to_id;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	/*
6340Sstevel@tonic-gate 	 * Keep idle-down in effect for either 10 seconds
6350Sstevel@tonic-gate 	 * or length of a scan interval, which ever is greater.
6360Sstevel@tonic-gate 	 */
6370Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6380Sstevel@tonic-gate 	if (pmstp->pm_idledown_id != 0) {
6390Sstevel@tonic-gate 		to_id = pmstp->pm_idledown_id;
6400Sstevel@tonic-gate 		pmstp->pm_idledown_id = 0;
6410Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6420Sstevel@tonic-gate 		(void) untimeout(to_id);
6430Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6440Sstevel@tonic-gate 		if (pmstp->pm_idledown_id != 0) {
6450Sstevel@tonic-gate 			PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
6460Sstevel@tonic-gate 			    "another caller got it, idledown_id(%lx)!\n",
6470Sstevel@tonic-gate 			    (ulong_t)pmstp->pm_idledown_id))
6480Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
6490Sstevel@tonic-gate 			return (0);
6500Sstevel@tonic-gate 		}
6510Sstevel@tonic-gate 	}
6520Sstevel@tonic-gate 	pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
6530Sstevel@tonic-gate 	    PM_IDLEDOWN_TIME * hz);
6540Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
6550Sstevel@tonic-gate 	    (ulong_t)pmstp->pm_idledown_id))
6560Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	return (pmstp->pm_idledown_id);
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate static int
6620Sstevel@tonic-gate pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
6630Sstevel@tonic-gate 	struct pollhead **phpp)
6640Sstevel@tonic-gate {
6650Sstevel@tonic-gate 	extern struct pollhead pm_pollhead;	/* common/os/sunpm.c */
6660Sstevel@tonic-gate 	int	clone;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
6690Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
6700Sstevel@tonic-gate 	if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
6710Sstevel@tonic-gate 		*reventsp |= (POLLIN | POLLRDNORM);
6720Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
6730Sstevel@tonic-gate 	} else {
6740Sstevel@tonic-gate 		*reventsp = 0;
6750Sstevel@tonic-gate 		if (!anyyet) {
6760Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
6770Sstevel@tonic-gate 			*phpp = &pm_pollhead;
6780Sstevel@tonic-gate 		}
6790Sstevel@tonic-gate #ifdef DEBUG
6800Sstevel@tonic-gate 		else {
6810Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
6820Sstevel@tonic-gate 		}
6830Sstevel@tonic-gate #endif
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 	return (0);
6860Sstevel@tonic-gate }
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate /*
6890Sstevel@tonic-gate  * called by pm_dicard_entries to free up the memory. It also decrements
6900Sstevel@tonic-gate  * pm_poll_cnt, if direct is non zero.
6910Sstevel@tonic-gate  */
6920Sstevel@tonic-gate static void
6930Sstevel@tonic-gate pm_free_entries(psce_t *pscep, int clone, int direct)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate 	pm_state_change_t	*p;
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 	if (pscep) {
6980Sstevel@tonic-gate 		p = pscep->psce_out;
6990Sstevel@tonic-gate 		while (p->size) {
7000Sstevel@tonic-gate 			if (direct) {
7010Sstevel@tonic-gate 				PMD(PMD_IOCTL, ("ioctl: discard: "
7020Sstevel@tonic-gate 				    "pm_poll_cnt[%d] is %d before "
7030Sstevel@tonic-gate 				    "ASSERT\n", clone,
7040Sstevel@tonic-gate 				    pm_poll_cnt[clone]))
7050Sstevel@tonic-gate 				ASSERT(pm_poll_cnt[clone]);
7060Sstevel@tonic-gate 				pm_poll_cnt[clone]--;
7070Sstevel@tonic-gate 			}
7080Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
7090Sstevel@tonic-gate 			p->size = 0;
7100Sstevel@tonic-gate 			if (p == pscep->psce_last)
7110Sstevel@tonic-gate 				p = pscep->psce_first;
7120Sstevel@tonic-gate 			else
7130Sstevel@tonic-gate 				p++;
7140Sstevel@tonic-gate 		}
7150Sstevel@tonic-gate 		pscep->psce_out = pscep->psce_first;
7160Sstevel@tonic-gate 		pscep->psce_in = pscep->psce_first;
7170Sstevel@tonic-gate 		mutex_exit(&pscep->psce_lock);
7180Sstevel@tonic-gate 	}
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate /*
7220Sstevel@tonic-gate  * Discard entries for this clone. Calls pm_free_entries to free up memory.
7230Sstevel@tonic-gate  */
7240Sstevel@tonic-gate static void
7250Sstevel@tonic-gate pm_discard_entries(int clone)
7260Sstevel@tonic-gate {
7270Sstevel@tonic-gate 	psce_t	*pscep;
7280Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_direct(int);
7290Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_interest(int);
7300Sstevel@tonic-gate 	int			direct = 0;
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
7330Sstevel@tonic-gate 	if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
7340Sstevel@tonic-gate 		direct = 1;
7350Sstevel@tonic-gate 	pm_free_entries(pscep, clone, direct);
7360Sstevel@tonic-gate 	pscep = pm_psc_clone_to_interest(clone);
7370Sstevel@tonic-gate 	pm_free_entries(pscep, clone, 0);
7380Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
7390Sstevel@tonic-gate }
7400Sstevel@tonic-gate 
741*3028Smh27603 
742*3028Smh27603 static void
743*3028Smh27603 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
7440Sstevel@tonic-gate {
7450Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
7460Sstevel@tonic-gate 		switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
7470Sstevel@tonic-gate 		case PMC_DEF_THRESH:
748*3028Smh27603 		case PMC_CPU_THRESH:
749*3028Smh27603 			PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
7500Sstevel@tonic-gate 			    "%s@%s(%s#%d) default thresh to 0t%d\n",
751*3028Smh27603 			    PM_DEVICE(dip), thresh))
752*3028Smh27603 			pm_set_device_threshold(dip, thresh, flag);
7530Sstevel@tonic-gate 			break;
7540Sstevel@tonic-gate 		default:
7550Sstevel@tonic-gate 			break;
7560Sstevel@tonic-gate 		}
7570Sstevel@tonic-gate 	}
758*3028Smh27603 }
7590Sstevel@tonic-gate 
760*3028Smh27603 static int
761*3028Smh27603 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
762*3028Smh27603 {
763*3028Smh27603 	int cmd = *((int *)arg);
764*3028Smh27603 
765*3028Smh27603 	if (!PM_GET_PM_INFO(dip))
766*3028Smh27603 		return (DDI_WALK_CONTINUE);
767*3028Smh27603 
768*3028Smh27603 	switch (cmd) {
769*3028Smh27603 	case PM_SET_SYSTEM_THRESHOLD:
770*3028Smh27603 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
771*3028Smh27603 			break;
772*3028Smh27603 		pm_set_idle_threshold(dip, pm_system_idle_threshold,
773*3028Smh27603 		    PMC_DEF_THRESH);
7740Sstevel@tonic-gate 		pm_rescan(dip);
775*3028Smh27603 		break;
776*3028Smh27603 	case PM_SET_CPU_THRESHOLD:
777*3028Smh27603 		if (!PM_ISCPU(dip))
778*3028Smh27603 			break;
779*3028Smh27603 		pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
780*3028Smh27603 		    PMC_CPU_THRESH);
781*3028Smh27603 		pm_rescan(dip);
782*3028Smh27603 		break;
783*3028Smh27603 	}
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
7860Sstevel@tonic-gate }
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate /*ARGSUSED*/
7890Sstevel@tonic-gate static int
7900Sstevel@tonic-gate pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
7910Sstevel@tonic-gate {
7920Sstevel@tonic-gate 	dev_t	dev;
7930Sstevel@tonic-gate 	int	instance;
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	switch (infocmd) {
7960Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
7970Sstevel@tonic-gate 		if (pmstp->pm_instance == -1)
7980Sstevel@tonic-gate 			return (DDI_FAILURE);
7990Sstevel@tonic-gate 		*result = pmstp->pm_dip;
8000Sstevel@tonic-gate 		return (DDI_SUCCESS);
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8030Sstevel@tonic-gate 		dev = (dev_t)arg;
8040Sstevel@tonic-gate 		instance = getminor(dev) >> 8;
8050Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8060Sstevel@tonic-gate 		return (DDI_SUCCESS);
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	default:
8090Sstevel@tonic-gate 		return (DDI_FAILURE);
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate }
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate /*ARGSUSED1*/
8150Sstevel@tonic-gate static int
8160Sstevel@tonic-gate pm_open(dev_t *devp, int flag, int otyp, cred_t *cr)
8170Sstevel@tonic-gate {
8180Sstevel@tonic-gate 	int		clone;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8210Sstevel@tonic-gate 		return (EINVAL);
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
8240Sstevel@tonic-gate 	for (clone = 1; clone < PM_MAX_CLONE; clone++)
8250Sstevel@tonic-gate 		if (!pmstp->pm_clones[clone])
8260Sstevel@tonic-gate 			break;
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	if (clone == PM_MAX_CLONE) {
8290Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
8300Sstevel@tonic-gate 		return (ENXIO);
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 	pmstp->pm_cred[clone] = cr;
8330Sstevel@tonic-gate 	crhold(cr);
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
8360Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 1;
8370Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	return (0);
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate /*ARGSUSED1*/
8430Sstevel@tonic-gate static int
8440Sstevel@tonic-gate pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
8450Sstevel@tonic-gate {
8460Sstevel@tonic-gate 	int clone;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8490Sstevel@tonic-gate 		return (EINVAL);
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
8520Sstevel@tonic-gate 	PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
8530Sstevel@tonic-gate 	    clone))
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	/*
8560Sstevel@tonic-gate 	 * Walk the entire device tree to find the corresponding
8570Sstevel@tonic-gate 	 * device and operate on it.
8580Sstevel@tonic-gate 	 */
8590Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
8600Sstevel@tonic-gate 	    (void *) &clone);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	crfree(pmstp->pm_cred[clone]);
8630Sstevel@tonic-gate 	pmstp->pm_cred[clone] = 0;
8640Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 0;
8650Sstevel@tonic-gate 	pm_discard_entries(clone);
8660Sstevel@tonic-gate 	ASSERT(pm_poll_cnt[clone] == 0);
8670Sstevel@tonic-gate 	pm_deregister_watcher(clone, NULL);
8680Sstevel@tonic-gate 	return (0);
8690Sstevel@tonic-gate }
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate /*ARGSUSED*/
8720Sstevel@tonic-gate static int
8730Sstevel@tonic-gate pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
8740Sstevel@tonic-gate {
8750Sstevel@tonic-gate 	struct pm_cmd_info *pc_info(int);
8760Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
8770Sstevel@tonic-gate 	pm_req_t	req;
8780Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
8790Sstevel@tonic-gate 	pm_info_t	*info = NULL;
8800Sstevel@tonic-gate 	int		clone;
8810Sstevel@tonic-gate 	char		*cmdstr = pm_decode_cmd(cmd);
8820Sstevel@tonic-gate 	/*
8830Sstevel@tonic-gate 	 * To keep devinfo nodes from going away while we're holding a
8840Sstevel@tonic-gate 	 * pointer to their dip, pm_name_to_dip() optionally holds
8850Sstevel@tonic-gate 	 * the devinfo node.  If we've done that, we set dipheld
8860Sstevel@tonic-gate 	 * so we know at the end of the ioctl processing to release the
8870Sstevel@tonic-gate 	 * node again.
8880Sstevel@tonic-gate 	 */
8890Sstevel@tonic-gate 	int		dipheld = 0;
8900Sstevel@tonic-gate 	int		icount = 0;
8910Sstevel@tonic-gate 	int		i;
8920Sstevel@tonic-gate 	int		comps;
8930Sstevel@tonic-gate 	size_t		lencopied;
8940Sstevel@tonic-gate 	int		ret = ENOTTY;
8950Sstevel@tonic-gate 	int		curpower;
8960Sstevel@tonic-gate 	char		who[MAXNAMELEN];
8970Sstevel@tonic-gate 	size_t		wholen;			/* copyinstr length */
8980Sstevel@tonic-gate 	size_t		deplen = MAXNAMELEN;
8990Sstevel@tonic-gate 	char		*dep, i_dep_buf[MAXNAMELEN];
9000Sstevel@tonic-gate 	char		*pathbuf;
9010Sstevel@tonic-gate 	struct pm_component *cp;
9020Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9030Sstevel@tonic-gate 	pm_state_change32_t		*pscp32;
9040Sstevel@tonic-gate 	pm_state_change32_t		psc32;
9050Sstevel@tonic-gate 	size_t				copysize32;
9060Sstevel@tonic-gate #endif
9070Sstevel@tonic-gate 	pm_state_change_t		*pscp;
9080Sstevel@tonic-gate 	pm_state_change_t		psc;
9090Sstevel@tonic-gate 	size_t		copysize;
9100Sstevel@tonic-gate 	extern void	pm_record_thresh(pm_thresh_rec_t *);
9110Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_direct(int);
9120Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_interest(int);
9130Sstevel@tonic-gate 	extern	void	pm_register_watcher(int, dev_info_t *);
9140Sstevel@tonic-gate 	extern	int	pm_get_current_power(dev_info_t *, int, int *);
9150Sstevel@tonic-gate 	extern	int	pm_interest_registered(int);
9160Sstevel@tonic-gate 	extern	void	pm_all_to_default_thresholds(void);
9170Sstevel@tonic-gate 	extern	int	pm_current_threshold(dev_info_t *, int, int *);
9180Sstevel@tonic-gate 	extern void	pm_deregister_watcher(int, dev_info_t *);
9190Sstevel@tonic-gate 	extern void	pm_unrecord_threshold(char *);
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate #ifdef DEBUG
9240Sstevel@tonic-gate 	if (cmd == 666) {
9250Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), print_info, NULL);
9260Sstevel@tonic-gate 		return (0);
9270Sstevel@tonic-gate 	}
9280Sstevel@tonic-gate 	ret = 0x0badcafe;			/* sanity checking */
9290Sstevel@tonic-gate 	pm_cmd = cmd;				/* for ASSERT debugging */
9300Sstevel@tonic-gate 	pm_cmd_string = cmdstr;	/* for ASSERT debugging */
9310Sstevel@tonic-gate #endif
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	if (pcip == NULL) {
9350Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
9360Sstevel@tonic-gate 		return (ENOTTY);
9370Sstevel@tonic-gate 	}
9380Sstevel@tonic-gate 	if (pcip == NULL || pcip->supported == 0) {
9390Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
9400Sstevel@tonic-gate 		    pcip->name))
9410Sstevel@tonic-gate 		return (ENOTTY);
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	wholen = 0;
9450Sstevel@tonic-gate 	dep = i_dep_buf;
9460Sstevel@tonic-gate 	i_dep_buf[0] = 0;
9470Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
9480Sstevel@tonic-gate 	if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
9490Sstevel@tonic-gate 		ret = EPERM;
9500Sstevel@tonic-gate 		return (ret);
9510Sstevel@tonic-gate 	}
9520Sstevel@tonic-gate 	switch (pcip->str_type) {
9530Sstevel@tonic-gate 	case PM_REQ:
9540Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9550Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9560Sstevel@tonic-gate 			pm_req32_t	req32;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &req32,
9590Sstevel@tonic-gate 			    sizeof (req32), mode) != 0) {
9600Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
9610Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
9620Sstevel@tonic-gate 				ret = EFAULT;
9630Sstevel@tonic-gate 				break;
9640Sstevel@tonic-gate 			}
9650Sstevel@tonic-gate 			req.component = req32.component;
9660Sstevel@tonic-gate 			req.value = req32.value;
9670Sstevel@tonic-gate 			req.datasize = req32.datasize;
9680Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
9690Sstevel@tonic-gate 				ret = copyinstr((char *)(uintptr_t)
9700Sstevel@tonic-gate 				    req32.physpath, who, MAXNAMELEN, &wholen);
9710Sstevel@tonic-gate 				if (ret) {
9720Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9730Sstevel@tonic-gate 					    "copyinstr fails returning %d\n",
9740Sstevel@tonic-gate 					    cmdstr, ret))
9750Sstevel@tonic-gate 					break;
9760Sstevel@tonic-gate 				}
9770Sstevel@tonic-gate 				req.physpath = who;
9780Sstevel@tonic-gate 			}
9790Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
9800Sstevel@tonic-gate 			    req.physpath))
9810Sstevel@tonic-gate 			if (pcip->inargs & INDATA) {
9820Sstevel@tonic-gate 				req.data = (void *)(uintptr_t)req32.data;
9830Sstevel@tonic-gate 				req.datasize = req32.datasize;
9840Sstevel@tonic-gate 			} else {
9850Sstevel@tonic-gate 				req.data = NULL;
9860Sstevel@tonic-gate 				req.datasize = 0;
9870Sstevel@tonic-gate 			}
9880Sstevel@tonic-gate 			switch (pcip->diptype) {
9890Sstevel@tonic-gate 			case DIP:
9900Sstevel@tonic-gate 				if (!(dip =
9910Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
9920Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9930Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
9940Sstevel@tonic-gate 					    cmdstr, req.physpath))
9950Sstevel@tonic-gate 					return (ENODEV);
9960Sstevel@tonic-gate 				}
9970Sstevel@tonic-gate 				ASSERT(!dipheld);
9980Sstevel@tonic-gate 				dipheld++;
9990Sstevel@tonic-gate 				break;
10000Sstevel@tonic-gate 			case NODIP:
10010Sstevel@tonic-gate 				break;
10020Sstevel@tonic-gate 			default:
10030Sstevel@tonic-gate 				/*
10040Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
10050Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
10060Sstevel@tonic-gate 				 */
10070Sstevel@tonic-gate #ifdef	DEBUG
10080Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
10090Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
10100Sstevel@tonic-gate #endif
10110Sstevel@tonic-gate 				ASSERT(0);
10120Sstevel@tonic-gate 				return (EIO);
10130Sstevel@tonic-gate 			}
10140Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
10150Sstevel@tonic-gate 				int32_t int32buf;
10160Sstevel@tonic-gate 				int32_t *i32p;
10170Sstevel@tonic-gate 				int *ip;
10180Sstevel@tonic-gate 				icount = req32.datasize / sizeof (int32_t);
10190Sstevel@tonic-gate 				if (icount <= 0) {
10200Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
10210Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
10220Sstevel@tonic-gate 					ret = EFAULT;
10230Sstevel@tonic-gate 					break;
10240Sstevel@tonic-gate 				}
10250Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
10260Sstevel@tonic-gate 				req.datasize = icount * sizeof (int);
10270Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
10280Sstevel@tonic-gate 				ip = req.data;
10290Sstevel@tonic-gate 				ret = 0;
10300Sstevel@tonic-gate 				for (i = 0,
10310Sstevel@tonic-gate 				    i32p = (int32_t *)(uintptr_t)req32.data;
10320Sstevel@tonic-gate 				    i < icount; i++, i32p++) {
10330Sstevel@tonic-gate 					if (ddi_copyin((void *)i32p, &int32buf,
10340Sstevel@tonic-gate 					    sizeof (int32_t), mode)) {
10350Sstevel@tonic-gate 						kmem_free(req.data,
10360Sstevel@tonic-gate 						    req.datasize);
10370Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10380Sstevel@tonic-gate 						    "entry %d EFAULT\n",
10390Sstevel@tonic-gate 						    cmdstr, i))
10400Sstevel@tonic-gate 						ret = EFAULT;
10410Sstevel@tonic-gate 						break;
10420Sstevel@tonic-gate 					}
10430Sstevel@tonic-gate 					*ip++ = (int)int32buf;
10440Sstevel@tonic-gate 				}
10450Sstevel@tonic-gate 				if (ret)
10460Sstevel@tonic-gate 					break;
10470Sstevel@tonic-gate 			}
10480Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
10490Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
10500Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
10510Sstevel@tonic-gate 				if (req32.data != NULL) {
10520Sstevel@tonic-gate 					size_t dummy;
10530Sstevel@tonic-gate 					if (copyinstr((void *)(uintptr_t)
10540Sstevel@tonic-gate 					    req32.data, dep, deplen, &dummy)) {
10550Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10560Sstevel@tonic-gate 						    "0x%p dep size %lx, EFAULT"
10570Sstevel@tonic-gate 						    "\n", cmdstr,
10580Sstevel@tonic-gate 						    (void *)req.data, deplen))
10590Sstevel@tonic-gate 						ret = EFAULT;
10600Sstevel@tonic-gate 						break;
10610Sstevel@tonic-gate 					}
10620Sstevel@tonic-gate #ifdef DEBUG
10630Sstevel@tonic-gate 					else {
10640Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
10650Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
10660Sstevel@tonic-gate 					}
10670Sstevel@tonic-gate #endif
10680Sstevel@tonic-gate 				} else {
10690Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
10700Sstevel@tonic-gate 					    "dependent\n", cmdstr))
10710Sstevel@tonic-gate 					ret = EINVAL;
10720Sstevel@tonic-gate 					break;
10730Sstevel@tonic-gate 				}
10740Sstevel@tonic-gate 			}
10750Sstevel@tonic-gate 		} else
10760Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
10770Sstevel@tonic-gate 		{
10780Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg,
10790Sstevel@tonic-gate 			    &req, sizeof (req), mode) != 0) {
10800Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
10810Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
10820Sstevel@tonic-gate 				ret = EFAULT;
10830Sstevel@tonic-gate 				break;
10840Sstevel@tonic-gate 			}
10850Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
10860Sstevel@tonic-gate 				ret = copyinstr((char *)req.physpath, who,
10870Sstevel@tonic-gate 				    MAXNAMELEN, &wholen);
10880Sstevel@tonic-gate 				if (ret) {
10890Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s copyinstr"
10900Sstevel@tonic-gate 					    " fails returning %d\n", cmdstr,
10910Sstevel@tonic-gate 					    ret))
10920Sstevel@tonic-gate 					break;
10930Sstevel@tonic-gate 				}
10940Sstevel@tonic-gate 				req.physpath = who;
10950Sstevel@tonic-gate 			}
10960Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
10970Sstevel@tonic-gate 			    req.physpath))
10980Sstevel@tonic-gate 			if (!(pcip->inargs & INDATA)) {
10990Sstevel@tonic-gate 				req.data = NULL;
11000Sstevel@tonic-gate 				req.datasize = 0;
11010Sstevel@tonic-gate 			}
11020Sstevel@tonic-gate 			switch (pcip->diptype) {
11030Sstevel@tonic-gate 			case DIP:
11040Sstevel@tonic-gate 				if (!(dip =
11050Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
11060Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
11070Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
11080Sstevel@tonic-gate 					    cmdstr, req.physpath))
11090Sstevel@tonic-gate 					return (ENODEV);
11100Sstevel@tonic-gate 				}
11110Sstevel@tonic-gate 				ASSERT(!dipheld);
11120Sstevel@tonic-gate 				dipheld++;
11130Sstevel@tonic-gate 				break;
11140Sstevel@tonic-gate 			case NODIP:
11150Sstevel@tonic-gate 				break;
11160Sstevel@tonic-gate 			default:
11170Sstevel@tonic-gate 				/*
11180Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
11190Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
11200Sstevel@tonic-gate 				 */
11210Sstevel@tonic-gate #ifdef	DEBUG
11220Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
11230Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
11240Sstevel@tonic-gate #endif
11250Sstevel@tonic-gate 				ASSERT(0);
11260Sstevel@tonic-gate 				return (EIO);
11270Sstevel@tonic-gate 			}
11280Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
11290Sstevel@tonic-gate 				int *ip;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
11320Sstevel@tonic-gate 				ip = req.data;
11330Sstevel@tonic-gate 				icount = req.datasize / sizeof (int);
11340Sstevel@tonic-gate 				if (icount <= 0) {
11350Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
11360Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
11370Sstevel@tonic-gate 					ret = EFAULT;
11380Sstevel@tonic-gate 					break;
11390Sstevel@tonic-gate 				}
11400Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
11410Sstevel@tonic-gate 				if (ddi_copyin((caddr_t)ip, req.data,
11420Sstevel@tonic-gate 				    req.datasize, mode) != 0) {
11430Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11440Sstevel@tonic-gate 					    "EFAULT\n\n", cmdstr))
11450Sstevel@tonic-gate 					ret = EFAULT;
11460Sstevel@tonic-gate 					break;
11470Sstevel@tonic-gate 				}
11480Sstevel@tonic-gate 			}
11490Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
11500Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
11510Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
11520Sstevel@tonic-gate 				if (req.data != NULL) {
11530Sstevel@tonic-gate 					size_t dummy;
11540Sstevel@tonic-gate 					if (copyinstr((caddr_t)req.data,
11550Sstevel@tonic-gate 					    dep, deplen, &dummy)) {
11560Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
11570Sstevel@tonic-gate 						    "0x%p dep size %lu, "
11580Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
11590Sstevel@tonic-gate 						    (void *)req.data, deplen))
11600Sstevel@tonic-gate 						ret = EFAULT;
11610Sstevel@tonic-gate 						break;
11620Sstevel@tonic-gate 					}
11630Sstevel@tonic-gate #ifdef DEBUG
11640Sstevel@tonic-gate 					else {
11650Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
11660Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
11670Sstevel@tonic-gate 					}
11680Sstevel@tonic-gate #endif
11690Sstevel@tonic-gate 				} else {
11700Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
11710Sstevel@tonic-gate 					    "dependent\n", cmdstr))
11720Sstevel@tonic-gate 					ret = EINVAL;
11730Sstevel@tonic-gate 					break;
11740Sstevel@tonic-gate 				}
11750Sstevel@tonic-gate 			}
11760Sstevel@tonic-gate 		}
11770Sstevel@tonic-gate 		/*
11780Sstevel@tonic-gate 		 * Now we've got all the args in for the commands that
11790Sstevel@tonic-gate 		 * use the new pm_req struct.
11800Sstevel@tonic-gate 		 */
11810Sstevel@tonic-gate 		switch (cmd) {
11820Sstevel@tonic-gate 		case PM_REPARSE_PM_PROPS:
11830Sstevel@tonic-gate 		{
11840Sstevel@tonic-gate 			struct dev_ops	*drv;
11850Sstevel@tonic-gate 			struct cb_ops	*cb;
11860Sstevel@tonic-gate 			void		*propval;
11870Sstevel@tonic-gate 			int length;
11880Sstevel@tonic-gate 			/*
11890Sstevel@tonic-gate 			 * This ioctl is provided only for the ddivs pm test.
11900Sstevel@tonic-gate 			 * We only do it to a driver which explicitly allows
11910Sstevel@tonic-gate 			 * us to do so by exporting a pm-reparse-ok property.
11920Sstevel@tonic-gate 			 * We only care whether the property exists or not.
11930Sstevel@tonic-gate 			 */
11940Sstevel@tonic-gate 			if ((drv = ddi_get_driver(dip)) == NULL) {
11950Sstevel@tonic-gate 				ret = EINVAL;
11960Sstevel@tonic-gate 				break;
11970Sstevel@tonic-gate 			}
11980Sstevel@tonic-gate 			if ((cb = drv->devo_cb_ops) != NULL) {
11990Sstevel@tonic-gate 				if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
12000Sstevel@tonic-gate 				    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12010Sstevel@tonic-gate 				    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12020Sstevel@tonic-gate 				    "pm-reparse-ok", (caddr_t)&propval,
12030Sstevel@tonic-gate 				    &length) != DDI_SUCCESS) {
12040Sstevel@tonic-gate 					ret = EINVAL;
12050Sstevel@tonic-gate 					break;
12060Sstevel@tonic-gate 				}
12070Sstevel@tonic-gate 			} else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
12080Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12090Sstevel@tonic-gate 			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12100Sstevel@tonic-gate 			    "pm-reparse-ok", (caddr_t)&propval,
12110Sstevel@tonic-gate 			    &length) != DDI_SUCCESS) {
12120Sstevel@tonic-gate 				ret = EINVAL;
12130Sstevel@tonic-gate 				break;
12140Sstevel@tonic-gate 			}
12150Sstevel@tonic-gate 			kmem_free(propval, length);
12160Sstevel@tonic-gate 			ret =  e_new_pm_props(dip);
12170Sstevel@tonic-gate 			break;
12180Sstevel@tonic-gate 		}
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD:
12210Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12220Sstevel@tonic-gate 			if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
12230Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12240Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
12250Sstevel@tonic-gate 				    cmdstr))
12260Sstevel@tonic-gate 				ret = ENODEV;
12270Sstevel@tonic-gate 				break;
12280Sstevel@tonic-gate 			}
12290Sstevel@tonic-gate 			*rval_p = DEVI(dip)->devi_pm_dev_thresh;
12300Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12310Sstevel@tonic-gate 			ret = 0;
12320Sstevel@tonic-gate 			break;
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 		case PM_DIRECT_PM:
12350Sstevel@tonic-gate 		{
12360Sstevel@tonic-gate 			int has_dep;
12370Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12380Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12390Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12400Sstevel@tonic-gate 				ret = ENODEV;
12410Sstevel@tonic-gate 				break;
12420Sstevel@tonic-gate 			}
12430Sstevel@tonic-gate 			/*
12440Sstevel@tonic-gate 			 * Check to see if we are there is a dependency on
12450Sstevel@tonic-gate 			 * this kept device, if so, return EBUSY.
12460Sstevel@tonic-gate 			 */
12470Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
12480Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
12490Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
12500Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
12510Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
12520Sstevel@tonic-gate 			if (has_dep) {
12530Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
12540Sstevel@tonic-gate 				    cmdstr))
12550Sstevel@tonic-gate 				ret = EBUSY;
12560Sstevel@tonic-gate 				break;
12570Sstevel@tonic-gate 			}
12580Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12590Sstevel@tonic-gate 			if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
12600Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12610Sstevel@tonic-gate 				    "%s@%s(%s#%d): EBUSY\n", cmdstr,
12620Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12630Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12640Sstevel@tonic-gate 				ret = EBUSY;
12650Sstevel@tonic-gate 				break;
12660Sstevel@tonic-gate 			}
12670Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_DIRECT;
12680Sstevel@tonic-gate 			info->pmi_clone = clone;
12690Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12700Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
12710Sstevel@tonic-gate 			    cmdstr, (void *)info, clone))
12720Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
12730Sstevel@tonic-gate 			pm_register_watcher(clone, dip);
12740Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
12750Sstevel@tonic-gate 			ret = 0;
12760Sstevel@tonic-gate 			break;
12770Sstevel@tonic-gate 		}
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate 		case PM_RELEASE_DIRECT_PM:
12800Sstevel@tonic-gate 		{
12810Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12820Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12830Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12840Sstevel@tonic-gate 				ret = ENODEV;
12850Sstevel@tonic-gate 				break;
12860Sstevel@tonic-gate 			}
12870Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12880Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
12890Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12900Sstevel@tonic-gate 				    "%s@%s(%s#%d) EINVAL\n", cmdstr,
12910Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12920Sstevel@tonic-gate 				ret = EINVAL;
12930Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12940Sstevel@tonic-gate 				break;
12950Sstevel@tonic-gate 			}
12960Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
12970Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_DIRECT;
12980Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12990Sstevel@tonic-gate 			/* Bring ourselves up if there is a keeper. */
13000Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
13010Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
13020Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
13030Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
13040Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
13050Sstevel@tonic-gate 			pm_discard_entries(clone);
13060Sstevel@tonic-gate 			pm_deregister_watcher(clone, dip);
13070Sstevel@tonic-gate 			/*
13080Sstevel@tonic-gate 			 * Now we could let the other threads that are
13090Sstevel@tonic-gate 			 * trying to do a DIRECT_PM thru
13100Sstevel@tonic-gate 			 */
13110Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13120Sstevel@tonic-gate 			info->pmi_clone = 0;
13130Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13140Sstevel@tonic-gate 			pm_proceed(dip, PMP_RELEASE, -1, -1);
13150Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13160Sstevel@tonic-gate 			    cmdstr))
13170Sstevel@tonic-gate 			pm_rescan(dip);
13180Sstevel@tonic-gate 			ret = 0;
13190Sstevel@tonic-gate 			break;
13200Sstevel@tonic-gate 		}
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 		case PM_SET_CURRENT_POWER:
13230Sstevel@tonic-gate 		{
13240Sstevel@tonic-gate 			int comp = req.component;
13250Sstevel@tonic-gate 			int  value = req.value;
13260Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
13270Sstevel@tonic-gate 			    "%d\n", cmdstr, req.physpath, comp, value))
13280Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, NULL) ||
13290Sstevel@tonic-gate 			    !e_pm_valid_power(dip, comp, value)) {
13300Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13310Sstevel@tonic-gate 				    "physpath=%s, comp=%d, level=%d, fails\n",
13320Sstevel@tonic-gate 				    cmdstr, req.physpath, comp, value))
13330Sstevel@tonic-gate 				ret = EINVAL;
13340Sstevel@tonic-gate 				break;
13350Sstevel@tonic-gate 			}
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13380Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13390Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13400Sstevel@tonic-gate 				ret = ENODEV;
13410Sstevel@tonic-gate 				break;
13420Sstevel@tonic-gate 			}
13430Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13440Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13450Sstevel@tonic-gate 				    "(not owner) %s fails; clone %d, owner %d"
13460Sstevel@tonic-gate 				    "\n", cmdstr, req.physpath, clone,
13470Sstevel@tonic-gate 				    info->pmi_clone))
13480Sstevel@tonic-gate 				ret = EINVAL;
13490Sstevel@tonic-gate 				break;
13500Sstevel@tonic-gate 			}
13510Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 			if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
13540Sstevel@tonic-gate 			    PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
13550Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13560Sstevel@tonic-gate 				    "pm_set_power for %s fails, errno=%d\n",
13570Sstevel@tonic-gate 				    cmdstr, req.physpath, ret))
13580Sstevel@tonic-gate 				break;
13590Sstevel@tonic-gate 			}
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 			pm_proceed(dip, PMP_SETPOWER, comp, value);
13620Sstevel@tonic-gate 
13630Sstevel@tonic-gate 			/*
13640Sstevel@tonic-gate 			 * Power down all idle components if console framebuffer
13650Sstevel@tonic-gate 			 * is powered off.
13660Sstevel@tonic-gate 			 */
13670Sstevel@tonic-gate 			if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
13680Sstevel@tonic-gate 			    pm_default_idle_threshold)) {
13690Sstevel@tonic-gate 				dev_info_t	*root = ddi_root_node();
13700Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
13710Sstevel@tonic-gate 					if (comp == 0 && value == 0 &&
13720Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13730Sstevel@tonic-gate 						ddi_walk_devs(root,
13740Sstevel@tonic-gate 						    pm_start_idledown,
13750Sstevel@tonic-gate 						    (void *)PMID_CFB);
13760Sstevel@tonic-gate 					}
13770Sstevel@tonic-gate 				} else {
13780Sstevel@tonic-gate 					int count = 0;
13790Sstevel@tonic-gate 					for (i = 0; i < PM_NUMCMPTS(dip); i++) {
13800Sstevel@tonic-gate 						ret = pm_get_current_power(dip,
13810Sstevel@tonic-gate 						    i, &curpower);
13820Sstevel@tonic-gate 						if (ret == DDI_SUCCESS &&
13830Sstevel@tonic-gate 						    curpower == 0)
13840Sstevel@tonic-gate 							count++;
13850Sstevel@tonic-gate 					}
13860Sstevel@tonic-gate 					if ((count == PM_NUMCMPTS(dip)) &&
13870Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13880Sstevel@tonic-gate 						ddi_walk_devs(root,
13890Sstevel@tonic-gate 						    pm_start_idledown,
13900Sstevel@tonic-gate 						    (void *)PMID_CFB);
13910Sstevel@tonic-gate 					}
13920Sstevel@tonic-gate 				}
13930Sstevel@tonic-gate 			}
13940Sstevel@tonic-gate 
13950Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13960Sstevel@tonic-gate 			    cmdstr))
13970Sstevel@tonic-gate 			pm_rescan(dip);
13980Sstevel@tonic-gate 			*rval_p = 0;
13990Sstevel@tonic-gate 			ret = 0;
14000Sstevel@tonic-gate 			break;
14010Sstevel@tonic-gate 		}
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 		case PM_GET_FULL_POWER:
14040Sstevel@tonic-gate 		{
14050Sstevel@tonic-gate 			int normal;
14060Sstevel@tonic-gate 			ASSERT(dip);
14070Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
14080Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component))
14090Sstevel@tonic-gate 			normal =  pm_get_normal_power(dip, req.component);
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate 			if (normal == DDI_FAILURE) {
14120Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
14130Sstevel@tonic-gate 				    "returns EINVAL\n", cmdstr))
14140Sstevel@tonic-gate 				ret = EINVAL;
14150Sstevel@tonic-gate 				break;
14160Sstevel@tonic-gate 			}
14170Sstevel@tonic-gate 			*rval_p = normal;
14180Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
14190Sstevel@tonic-gate 			    cmdstr, normal))
14200Sstevel@tonic-gate 			ret = 0;
14210Sstevel@tonic-gate 			break;
14220Sstevel@tonic-gate 		}
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 		case PM_GET_CURRENT_POWER:
14250Sstevel@tonic-gate 			if (pm_get_current_power(dip, req.component,
14260Sstevel@tonic-gate 			    rval_p) != DDI_SUCCESS) {
14270Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
14280Sstevel@tonic-gate 				    "EINVAL\n", cmdstr))
14290Sstevel@tonic-gate 				ret = EINVAL;
14300Sstevel@tonic-gate 				break;
14310Sstevel@tonic-gate 			}
14320Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
14330Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component, *rval_p))
14340Sstevel@tonic-gate 			if (*rval_p == PM_LEVEL_UNKNOWN)
14350Sstevel@tonic-gate 				ret = EAGAIN;
14360Sstevel@tonic-gate 			else
14370Sstevel@tonic-gate 				ret = 0;
14380Sstevel@tonic-gate 			break;
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 		case PM_GET_TIME_IDLE:
14410Sstevel@tonic-gate 		{
14420Sstevel@tonic-gate 			time_t timestamp;
14430Sstevel@tonic-gate 			int comp = req.component;
14440Sstevel@tonic-gate 			pm_component_t *cp;
14450Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, &cp)) {
14460Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
14470Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
14480Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), comp,
14490Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
14500Sstevel@tonic-gate 				ret = EINVAL;
14510Sstevel@tonic-gate 				break;
14520Sstevel@tonic-gate 			}
14530Sstevel@tonic-gate 			timestamp = cp->pmc_timestamp;
14540Sstevel@tonic-gate 			if (timestamp) {
14550Sstevel@tonic-gate 				time_t now;
14560Sstevel@tonic-gate 				(void) drv_getparm(TIME, &now);
14570Sstevel@tonic-gate 				*rval_p = (now - timestamp);
14580Sstevel@tonic-gate 			} else {
14590Sstevel@tonic-gate 				*rval_p = 0;
14600Sstevel@tonic-gate 			}
14610Sstevel@tonic-gate 			ret = 0;
14620Sstevel@tonic-gate 			break;
14630Sstevel@tonic-gate 		}
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 		case PM_ADD_DEPENDENT:
14660Sstevel@tonic-gate 		{
14670Sstevel@tonic-gate 			dev_info_t	*kept_dip;
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
14700Sstevel@tonic-gate 			    dep, req.physpath))
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 			/*
14730Sstevel@tonic-gate 			 * hold and install kept while processing dependency
14740Sstevel@tonic-gate 			 * keeper (in .physpath) has already been held.
14750Sstevel@tonic-gate 			 */
14760Sstevel@tonic-gate 			if (dep[0] == '\0') {
14770Sstevel@tonic-gate 				PMD(PMD_ERROR, ("kept NULL or null\n"))
14780Sstevel@tonic-gate 				ret = EINVAL;
14790Sstevel@tonic-gate 				break;
14800Sstevel@tonic-gate 			} else if ((kept_dip =
14810Sstevel@tonic-gate 			    pm_name_to_dip(dep, 1)) == NULL) {
14820Sstevel@tonic-gate 				PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
14830Sstevel@tonic-gate 				ret = ENODEV;
14840Sstevel@tonic-gate 				break;
14850Sstevel@tonic-gate 			} else if (kept_dip == dip) {
14860Sstevel@tonic-gate 				PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
14870Sstevel@tonic-gate 				    "self-dependency not allowed.\n",
14880Sstevel@tonic-gate 				    dep, (void *)kept_dip, req.physpath,
14890Sstevel@tonic-gate 				    (void *) dip))
14900Sstevel@tonic-gate 				PM_RELE(dip);	/* release "double" hold */
14910Sstevel@tonic-gate 				ret = EINVAL;
14920Sstevel@tonic-gate 				break;
14930Sstevel@tonic-gate 			}
14940Sstevel@tonic-gate 			ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 			/*
14970Sstevel@tonic-gate 			 * record dependency, then walk through device tree
14980Sstevel@tonic-gate 			 * independently on behalf of kept and keeper to
14990Sstevel@tonic-gate 			 * establish newly created dependency.
15000Sstevel@tonic-gate 			 */
15010Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
15020Sstevel@tonic-gate 			    req.physpath, dep, PM_DEP_WAIT, NULL, 0);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 			/*
15050Sstevel@tonic-gate 			 * release kept after establishing dependency, keeper
15060Sstevel@tonic-gate 			 * is released as part of ioctl exit processing.
15070Sstevel@tonic-gate 			 */
15080Sstevel@tonic-gate 			PM_RELE(kept_dip);
15090Sstevel@tonic-gate 			*rval_p = 0;
15100Sstevel@tonic-gate 			ret = 0;
15110Sstevel@tonic-gate 			break;
15120Sstevel@tonic-gate 		}
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 		case PM_ADD_DEPENDENT_PROPERTY:
15150Sstevel@tonic-gate 		{
15160Sstevel@tonic-gate 			char *keeper, *kept;
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 			if (dep[0] == '\0') {
15190Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
15200Sstevel@tonic-gate 				    "null\n", cmdstr))
15210Sstevel@tonic-gate 				ret = EINVAL;
15220Sstevel@tonic-gate 				break;
15230Sstevel@tonic-gate 			}
15240Sstevel@tonic-gate 			kept = dep;
15250Sstevel@tonic-gate 			keeper = req.physpath;
15260Sstevel@tonic-gate 			/*
15270Sstevel@tonic-gate 			 * record keeper - kept dependency, then walk through
15280Sstevel@tonic-gate 			 * device tree to find out all attached keeper, walk
15290Sstevel@tonic-gate 			 * through again to apply dependency to all the
15300Sstevel@tonic-gate 			 * potential kept.
15310Sstevel@tonic-gate 			 */
15320Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(
15330Sstevel@tonic-gate 			    PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
15340Sstevel@tonic-gate 			    PM_DEP_WAIT, NULL, 0);
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate 			*rval_p = 0;
15370Sstevel@tonic-gate 			ret = 0;
15380Sstevel@tonic-gate 			break;
15390Sstevel@tonic-gate 		}
15400Sstevel@tonic-gate 
15410Sstevel@tonic-gate 		case PM_SET_DEVICE_THRESHOLD:
15420Sstevel@tonic-gate 		{
15430Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
15440Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
15450Sstevel@tonic-gate 			int *tp;	/* threshold storage */
15460Sstevel@tonic-gate 			size_t size;
15470Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 			/*
15500Sstevel@tonic-gate 			 * The header struct plus one entry struct plus one
15510Sstevel@tonic-gate 			 * threshold plus the length of the string
15520Sstevel@tonic-gate 			 */
15530Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
15540Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * 1) +
15550Sstevel@tonic-gate 			    (1 * sizeof (int)) +
15560Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
15590Sstevel@tonic-gate 			rp->ptr_size = size;
15600Sstevel@tonic-gate 			rp->ptr_numcomps = 0;	/* means device threshold */
15610Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
15620Sstevel@tonic-gate 			rp->ptr_entries = ep;
15630Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
15640Sstevel@tonic-gate 			    (1 * sizeof (pm_pte_t)));
15650Sstevel@tonic-gate 			ep->pte_numthresh = 1;
15660Sstevel@tonic-gate 			ep->pte_thresh = tp;
15670Sstevel@tonic-gate 			*tp++ = req.value;
15680Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
15690Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
15700Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
15710Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
15720Sstevel@tonic-gate 			PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
15730Sstevel@tonic-gate 			    "%s\n", cmdstr, req.value, req.physpath))
15740Sstevel@tonic-gate 			pm_record_thresh(rp);
15750Sstevel@tonic-gate 			/*
15760Sstevel@tonic-gate 			 * Don't free rp, pm_record_thresh() keeps it.
15770Sstevel@tonic-gate 			 * We don't try to apply it ourselves because we'd need
15780Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
15790Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
15800Sstevel@tonic-gate 			 * we get here
15810Sstevel@tonic-gate 			 */
15820Sstevel@tonic-gate 			ASSERT(dip == NULL);
15830Sstevel@tonic-gate 			ret = 0;		/* can't fail now */
15840Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
15850Sstevel@tonic-gate 				break;
15860Sstevel@tonic-gate 			}
15870Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
15880Sstevel@tonic-gate 			PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
15890Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
15900Sstevel@tonic-gate 			PM_RELE(dip);
15910Sstevel@tonic-gate 			break;
15920Sstevel@tonic-gate 		}
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 		case PM_RESET_DEVICE_THRESHOLD:
15950Sstevel@tonic-gate 		{
15960Sstevel@tonic-gate 			/*
15970Sstevel@tonic-gate 			 * This only applies to a currently attached and power
15980Sstevel@tonic-gate 			 * managed node
15990Sstevel@tonic-gate 			 */
16000Sstevel@tonic-gate 			/*
16010Sstevel@tonic-gate 			 * We don't do this to old-style drivers
16020Sstevel@tonic-gate 			 */
16030Sstevel@tonic-gate 			info = PM_GET_PM_INFO(dip);
16040Sstevel@tonic-gate 			if (info == NULL) {
16050Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s not power "
16060Sstevel@tonic-gate 				    "managed\n", cmdstr, req.physpath))
16070Sstevel@tonic-gate 				ret = EINVAL;
16080Sstevel@tonic-gate 				break;
16090Sstevel@tonic-gate 			}
16100Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16110Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
16120Sstevel@tonic-gate 				    cmdstr, req.physpath))
16130Sstevel@tonic-gate 				ret = EINVAL;
16140Sstevel@tonic-gate 				break;
16150Sstevel@tonic-gate 			}
16160Sstevel@tonic-gate 			pm_unrecord_threshold(req.physpath);
1617*3028Smh27603 			if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
1618*3028Smh27603 				pm_set_device_threshold(dip,
1619*3028Smh27603 				    pm_cpu_idle_threshold, PMC_CPU_THRESH);
1620*3028Smh27603 			else
1621*3028Smh27603 				pm_set_device_threshold(dip,
1622*3028Smh27603 				    pm_system_idle_threshold, PMC_DEF_THRESH);
16230Sstevel@tonic-gate 			ret = 0;
16240Sstevel@tonic-gate 			break;
16250Sstevel@tonic-gate 		}
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 		case PM_GET_NUM_COMPONENTS:
16280Sstevel@tonic-gate 			ret = 0;
16290Sstevel@tonic-gate 			*rval_p = PM_NUMCMPTS(dip);
16300Sstevel@tonic-gate 			break;
16310Sstevel@tonic-gate 
16320Sstevel@tonic-gate 		case PM_GET_DEVICE_TYPE:
16330Sstevel@tonic-gate 			ret = 0;
16340Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
16350Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
16360Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
16370Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
16380Sstevel@tonic-gate 				break;
16390Sstevel@tonic-gate 			}
16400Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16410Sstevel@tonic-gate 				*rval_p = PM_CREATE_COMPONENTS;
16420Sstevel@tonic-gate 			} else {
16430Sstevel@tonic-gate 				*rval_p = PM_AUTOPM;
16440Sstevel@tonic-gate 			}
16450Sstevel@tonic-gate 			break;
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate 		case PM_SET_COMPONENT_THRESHOLDS:
16480Sstevel@tonic-gate 		{
16490Sstevel@tonic-gate 			int comps = 0;
16500Sstevel@tonic-gate 			int *end = (int *)req.data + icount;
16510Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
16520Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
16530Sstevel@tonic-gate 			int *tp;	/* threshold storage */
16540Sstevel@tonic-gate 			int *ip;
16550Sstevel@tonic-gate 			int j;
16560Sstevel@tonic-gate 			size_t size;
16570Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
16580Sstevel@tonic-gate 			extern int pm_valid_thresh(dev_info_t *,
16590Sstevel@tonic-gate 			    pm_thresh_rec_t *);
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 			for (ip = req.data; *ip; ip++) {
16620Sstevel@tonic-gate 				if (ip >= end) {
16630Sstevel@tonic-gate 					ret = EFAULT;
16640Sstevel@tonic-gate 					break;
16650Sstevel@tonic-gate 				}
16660Sstevel@tonic-gate 				comps++;
16670Sstevel@tonic-gate 				/* skip over indicated number of entries */
16680Sstevel@tonic-gate 				for (j = *ip; j; j--) {
16690Sstevel@tonic-gate 					if (++ip >= end) {
16700Sstevel@tonic-gate 						ret = EFAULT;
16710Sstevel@tonic-gate 						break;
16720Sstevel@tonic-gate 					}
16730Sstevel@tonic-gate 				}
16740Sstevel@tonic-gate 				if (ret)
16750Sstevel@tonic-gate 					break;
16760Sstevel@tonic-gate 			}
16770Sstevel@tonic-gate 			if (ret)
16780Sstevel@tonic-gate 				break;
16790Sstevel@tonic-gate 			if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
16800Sstevel@tonic-gate 				/* did not exactly fill buffer */
16810Sstevel@tonic-gate 				ret = EINVAL;
16820Sstevel@tonic-gate 				break;
16830Sstevel@tonic-gate 			}
16840Sstevel@tonic-gate 			if (comps == 0) {
16850Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
16860Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, req.physpath))
16870Sstevel@tonic-gate 				ret = EINVAL;
16880Sstevel@tonic-gate 				break;
16890Sstevel@tonic-gate 			}
16900Sstevel@tonic-gate 			/*
16910Sstevel@tonic-gate 			 * The header struct plus one entry struct per component
16920Sstevel@tonic-gate 			 * plus the size of the lists minus the counts
16930Sstevel@tonic-gate 			 * plus the length of the string
16940Sstevel@tonic-gate 			 */
16950Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
16960Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * comps) + req.datasize -
16970Sstevel@tonic-gate 			    ((comps + 1) * sizeof (int)) +
16980Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
17010Sstevel@tonic-gate 			rp->ptr_size = size;
17020Sstevel@tonic-gate 			rp->ptr_numcomps = comps;
17030Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
17040Sstevel@tonic-gate 			rp->ptr_entries = ep;
17050Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
17060Sstevel@tonic-gate 			    (comps * sizeof (pm_pte_t)));
17070Sstevel@tonic-gate 			for (ip = req.data; *ip; ep++) {
17080Sstevel@tonic-gate 				ep->pte_numthresh = *ip;
17090Sstevel@tonic-gate 				ep->pte_thresh = tp;
17100Sstevel@tonic-gate 				for (j = *ip++; j; j--) {
17110Sstevel@tonic-gate 					*tp++ = *ip++;
17120Sstevel@tonic-gate 				}
17130Sstevel@tonic-gate 			}
17140Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
17150Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
17160Sstevel@tonic-gate 			ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
17170Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
17180Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate 			ASSERT(dip == NULL);
17210Sstevel@tonic-gate 			/*
17220Sstevel@tonic-gate 			 * If this is not a currently power managed node,
17230Sstevel@tonic-gate 			 * then we can't check for validity of the thresholds
17240Sstevel@tonic-gate 			 */
17250Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
17260Sstevel@tonic-gate 				/* don't free rp, pm_record_thresh uses it */
17270Sstevel@tonic-gate 				pm_record_thresh(rp);
17280Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
17290Sstevel@tonic-gate 				    "for %s failed\n", cmdstr, req.physpath))
17300Sstevel@tonic-gate 				ret = 0;
17310Sstevel@tonic-gate 				break;
17320Sstevel@tonic-gate 			}
17330Sstevel@tonic-gate 			ASSERT(!dipheld);
17340Sstevel@tonic-gate 			dipheld++;
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 			if (!pm_valid_thresh(dip, rp)) {
17370Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
17380Sstevel@tonic-gate 				    "for %s@%s(%s#%d)\n", cmdstr,
17390Sstevel@tonic-gate 				    PM_DEVICE(dip)))
17400Sstevel@tonic-gate 				kmem_free(rp, size);
17410Sstevel@tonic-gate 				ret = EINVAL;
17420Sstevel@tonic-gate 				break;
17430Sstevel@tonic-gate 			}
17440Sstevel@tonic-gate 			/*
17450Sstevel@tonic-gate 			 * We don't just apply it ourselves because we'd need
17460Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
17470Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
17480Sstevel@tonic-gate 			 * we get here
17490Sstevel@tonic-gate 			 */
17500Sstevel@tonic-gate 			pm_record_thresh(rp);
17510Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
17520Sstevel@tonic-gate 			ret = 0;
17530Sstevel@tonic-gate 			break;
17540Sstevel@tonic-gate 		}
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 		case PM_GET_COMPONENT_THRESHOLDS:
17570Sstevel@tonic-gate 		{
17580Sstevel@tonic-gate 			int musthave;
17590Sstevel@tonic-gate 			int numthresholds = 0;
17600Sstevel@tonic-gate 			int wordsize;
17610Sstevel@tonic-gate 			int numcomps;
17620Sstevel@tonic-gate 			caddr_t uaddr = req.data;	/* user address */
17630Sstevel@tonic-gate 			int val;	/* int value to be copied out */
17640Sstevel@tonic-gate 			int32_t val32;	/* int32 value to be copied out */
17650Sstevel@tonic-gate 			caddr_t vaddr;	/* address to copyout from */
17660Sstevel@tonic-gate 			int j;
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
17690Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
17700Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
17710Sstevel@tonic-gate 			} else
17720Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
17730Sstevel@tonic-gate 			{
17740Sstevel@tonic-gate 				wordsize = sizeof (int);
17750Sstevel@tonic-gate 			}
17760Sstevel@tonic-gate 
17770Sstevel@tonic-gate 			ASSERT(dip);
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 			numcomps = PM_NUMCMPTS(dip);
17800Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17810Sstevel@tonic-gate 				cp = PM_CP(dip, i);
17820Sstevel@tonic-gate 				numthresholds += cp->pmc_comp.pmc_numlevels - 1;
17830Sstevel@tonic-gate 			}
17840Sstevel@tonic-gate 			musthave = (numthresholds + numcomps + 1) *  wordsize;
17850Sstevel@tonic-gate 			if (req.datasize < musthave) {
17860Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
17870Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
17880Sstevel@tonic-gate 				    musthave))
17890Sstevel@tonic-gate 				ret = EINVAL;
17900Sstevel@tonic-gate 				break;
17910Sstevel@tonic-gate 			}
17920Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
17930Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17940Sstevel@tonic-gate 				int *thp;
17950Sstevel@tonic-gate 				cp = PM_CP(dip, i);
17960Sstevel@tonic-gate 				thp = cp->pmc_comp.pmc_thresh;
17970Sstevel@tonic-gate 				/* first copyout the count */
17980Sstevel@tonic-gate 				if (wordsize == sizeof (int32_t)) {
17990Sstevel@tonic-gate 					val32 = cp->pmc_comp.pmc_numlevels - 1;
18000Sstevel@tonic-gate 					vaddr = (caddr_t)&val32;
18010Sstevel@tonic-gate 				} else {
18020Sstevel@tonic-gate 					val = cp->pmc_comp.pmc_numlevels - 1;
18030Sstevel@tonic-gate 					vaddr = (caddr_t)&val;
18040Sstevel@tonic-gate 				}
18050Sstevel@tonic-gate 				if (ddi_copyout(vaddr, (void *)uaddr,
18060Sstevel@tonic-gate 				    wordsize, mode) != 0) {
18070Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
18080Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
18090Sstevel@tonic-gate 					    "(%s#%d) vaddr %p EFAULT\n",
18100Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
18110Sstevel@tonic-gate 					    (void*)vaddr))
18120Sstevel@tonic-gate 					ret = EFAULT;
18130Sstevel@tonic-gate 					break;
18140Sstevel@tonic-gate 				}
18150Sstevel@tonic-gate 				vaddr = uaddr;
18160Sstevel@tonic-gate 				vaddr += wordsize;
18170Sstevel@tonic-gate 				uaddr = (caddr_t)vaddr;
18180Sstevel@tonic-gate 				/* then copyout each threshold value */
18190Sstevel@tonic-gate 				for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
18200Sstevel@tonic-gate 				    j++) {
18210Sstevel@tonic-gate 					if (wordsize == sizeof (int32_t)) {
18220Sstevel@tonic-gate 						val32 = thp[j + 1];
18230Sstevel@tonic-gate 						vaddr = (caddr_t)&val32;
18240Sstevel@tonic-gate 					} else {
18250Sstevel@tonic-gate 						val = thp[i + 1];
18260Sstevel@tonic-gate 						vaddr = (caddr_t)&val;
18270Sstevel@tonic-gate 					}
18280Sstevel@tonic-gate 					if (ddi_copyout(vaddr, (void *) uaddr,
18290Sstevel@tonic-gate 					    wordsize, mode) != 0) {
18300Sstevel@tonic-gate 						PM_UNLOCK_DIP(dip);
18310Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
18320Sstevel@tonic-gate 						    "%s@%s(%s#%d) uaddr %p "
18330Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
18340Sstevel@tonic-gate 						    PM_DEVICE(dip),
18350Sstevel@tonic-gate 						    (void *)uaddr))
18360Sstevel@tonic-gate 						ret = EFAULT;
18370Sstevel@tonic-gate 						break;
18380Sstevel@tonic-gate 					}
18390Sstevel@tonic-gate 					vaddr = uaddr;
18400Sstevel@tonic-gate 					vaddr += wordsize;
18410Sstevel@tonic-gate 					uaddr = (caddr_t)vaddr;
18420Sstevel@tonic-gate 				}
18430Sstevel@tonic-gate 			}
18440Sstevel@tonic-gate 			if (ret)
18450Sstevel@tonic-gate 				break;
18460Sstevel@tonic-gate 			/* last copyout a terminating 0 count */
18470Sstevel@tonic-gate 			if (wordsize == sizeof (int32_t)) {
18480Sstevel@tonic-gate 				val32 = 0;
18490Sstevel@tonic-gate 				vaddr = (caddr_t)&val32;
18500Sstevel@tonic-gate 			} else {
18510Sstevel@tonic-gate 				ASSERT(wordsize == sizeof (int));
18520Sstevel@tonic-gate 				val = 0;
18530Sstevel@tonic-gate 				vaddr = (caddr_t)&val;
18540Sstevel@tonic-gate 			}
18550Sstevel@tonic-gate 			if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
18560Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
18570Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
18580Sstevel@tonic-gate 				    "vaddr %p (0 count) EFAULT\n", cmdstr,
18590Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)vaddr))
18600Sstevel@tonic-gate 				ret = EFAULT;
18610Sstevel@tonic-gate 				break;
18620Sstevel@tonic-gate 			}
18630Sstevel@tonic-gate 			/* finished, so don't need to increment addresses */
18640Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
18650Sstevel@tonic-gate 			ret = 0;
18660Sstevel@tonic-gate 			break;
18670Sstevel@tonic-gate 		}
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 		case PM_GET_STATS:
18700Sstevel@tonic-gate 		{
18710Sstevel@tonic-gate 			time_t now;
18720Sstevel@tonic-gate 			time_t *timestamp;
18730Sstevel@tonic-gate 			extern int pm_cur_power(pm_component_t *cp);
18740Sstevel@tonic-gate 			int musthave;
18750Sstevel@tonic-gate 			int wordsize;
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
18780Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
18790Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
18800Sstevel@tonic-gate 			} else
18810Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
18820Sstevel@tonic-gate 			{
18830Sstevel@tonic-gate 				wordsize = sizeof (int);
18840Sstevel@tonic-gate 			}
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 			comps = PM_NUMCMPTS(dip);
18870Sstevel@tonic-gate 			if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
18880Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s no components"
18890Sstevel@tonic-gate 				    " or not power managed--EINVAL\n", cmdstr,
18900Sstevel@tonic-gate 				    req.physpath))
18910Sstevel@tonic-gate 				ret = EINVAL;
18920Sstevel@tonic-gate 				break;
18930Sstevel@tonic-gate 			}
18940Sstevel@tonic-gate 			musthave = comps * 2 * wordsize;
18950Sstevel@tonic-gate 			if (req.datasize < musthave) {
18960Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
18970Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
18980Sstevel@tonic-gate 				    musthave))
18990Sstevel@tonic-gate 				ret = EINVAL;
19000Sstevel@tonic-gate 				break;
19010Sstevel@tonic-gate 			}
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
19040Sstevel@tonic-gate 			(void) drv_getparm(TIME, &now);
19050Sstevel@tonic-gate 			timestamp = kmem_zalloc(comps * sizeof (time_t),
19060Sstevel@tonic-gate 			    KM_SLEEP);
19070Sstevel@tonic-gate 			pm_get_timestamps(dip, timestamp);
19080Sstevel@tonic-gate 			/*
19090Sstevel@tonic-gate 			 * First the current power levels
19100Sstevel@tonic-gate 			 */
19110Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19120Sstevel@tonic-gate 				int curpwr;
19130Sstevel@tonic-gate 				int32_t curpwr32;
19140Sstevel@tonic-gate 				caddr_t cpaddr;
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19170Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19180Sstevel@tonic-gate 					curpwr = pm_cur_power(cp);
19190Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr;
19200Sstevel@tonic-gate 				} else {
19210Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19220Sstevel@tonic-gate 					curpwr32 = pm_cur_power(cp);
19230Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr32;
19240Sstevel@tonic-gate 				}
19250Sstevel@tonic-gate 				if (ddi_copyout(cpaddr, (void *) req.data,
19260Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19270Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19280Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19290Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19300Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19310Sstevel@tonic-gate 					    (void *)req.data))
19320Sstevel@tonic-gate 					ASSERT(!dipheld);
19330Sstevel@tonic-gate 					return (EFAULT);
19340Sstevel@tonic-gate 				}
19350Sstevel@tonic-gate 				cpaddr = (caddr_t)req.data;
19360Sstevel@tonic-gate 				cpaddr += wordsize;
19370Sstevel@tonic-gate 				req.data = cpaddr;
19380Sstevel@tonic-gate 			}
19390Sstevel@tonic-gate 			/*
19400Sstevel@tonic-gate 			 * Then the times remaining
19410Sstevel@tonic-gate 			 */
19420Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19430Sstevel@tonic-gate 				int retval;
19440Sstevel@tonic-gate 				int32_t retval32;
19450Sstevel@tonic-gate 				caddr_t rvaddr;
19460Sstevel@tonic-gate 				int curpwr;
19470Sstevel@tonic-gate 
19480Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19490Sstevel@tonic-gate 				curpwr = cp->pmc_cur_pwr;
19500Sstevel@tonic-gate 				if (curpwr == 0 || timestamp[i] == 0) {
19510Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: "
19520Sstevel@tonic-gate 					    "cur_pwer %x, timestamp %lx\n",
19530Sstevel@tonic-gate 					    cmdstr, curpwr, timestamp[i]))
19540Sstevel@tonic-gate 					retval = INT_MAX;
19550Sstevel@tonic-gate 				} else {
19560Sstevel@tonic-gate 					int thresh;
19570Sstevel@tonic-gate 					(void) pm_current_threshold(dip, i,
19580Sstevel@tonic-gate 					    &thresh);
19590Sstevel@tonic-gate 					retval = thresh - (now - timestamp[i]);
19600Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: current "
19610Sstevel@tonic-gate 					    "thresh %x, now %lx, timestamp %lx,"
19620Sstevel@tonic-gate 					    " retval %x\n", cmdstr, thresh, now,
19630Sstevel@tonic-gate 					    timestamp[i], retval))
19640Sstevel@tonic-gate 				}
19650Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19660Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval;
19670Sstevel@tonic-gate 				} else {
19680Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19690Sstevel@tonic-gate 					retval32 = retval;
19700Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval32;
19710Sstevel@tonic-gate 				}
19720Sstevel@tonic-gate 				if (ddi_copyout(rvaddr, (void *) req.data,
19730Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19740Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19750Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19760Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19770Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19780Sstevel@tonic-gate 					    (void *)req.data))
19790Sstevel@tonic-gate 					ASSERT(!dipheld);
19800Sstevel@tonic-gate 					return (EFAULT);
19810Sstevel@tonic-gate 				}
19820Sstevel@tonic-gate 				rvaddr = (caddr_t)req.data;
19830Sstevel@tonic-gate 				rvaddr += wordsize;
19840Sstevel@tonic-gate 				req.data = (int *)rvaddr;
19850Sstevel@tonic-gate 			}
19860Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
19870Sstevel@tonic-gate 			*rval_p = comps;
19880Sstevel@tonic-gate 			ret = 0;
19890Sstevel@tonic-gate 			kmem_free(timestamp, comps * sizeof (time_t));
19900Sstevel@tonic-gate 			break;
19910Sstevel@tonic-gate 		}
19920Sstevel@tonic-gate 
19930Sstevel@tonic-gate 		case PM_GET_COMPONENT_NAME:
19940Sstevel@tonic-gate 			ASSERT(dip);
19950Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
19960Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
19970Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
19980Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
19990Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20000Sstevel@tonic-gate 				ret = EINVAL;
20010Sstevel@tonic-gate 				break;
20020Sstevel@tonic-gate 			}
20030Sstevel@tonic-gate 			if (ret = copyoutstr(cp->pmc_comp.pmc_name,
20040Sstevel@tonic-gate 			    (char *)req.data, req.datasize, &lencopied)) {
20050Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20060Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20070Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20080Sstevel@tonic-gate 				break;
20090Sstevel@tonic-gate 			}
20100Sstevel@tonic-gate 			*rval_p = lencopied;
20110Sstevel@tonic-gate 			ret = 0;
20120Sstevel@tonic-gate 			break;
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 		case PM_GET_POWER_NAME:
20150Sstevel@tonic-gate 		{
20160Sstevel@tonic-gate 			int i;
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 			ASSERT(dip);
20190Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20200Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20210Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20220Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20230Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20240Sstevel@tonic-gate 				ret = EINVAL;
20250Sstevel@tonic-gate 				break;
20260Sstevel@tonic-gate 			}
20270Sstevel@tonic-gate 			if ((i = req.value) < 0 ||
20280Sstevel@tonic-gate 			    i > cp->pmc_comp.pmc_numlevels - 1) {
20290Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20300Sstevel@tonic-gate 				    "value %d > num_levels - 1 %d--EINVAL\n",
20310Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.value,
20320Sstevel@tonic-gate 				    cp->pmc_comp.pmc_numlevels - 1))
20330Sstevel@tonic-gate 				ret = EINVAL;
20340Sstevel@tonic-gate 				break;
20350Sstevel@tonic-gate 			}
20360Sstevel@tonic-gate 			dep = cp->pmc_comp.pmc_lnames[req.value];
20370Sstevel@tonic-gate 			if (ret = copyoutstr(dep,
20380Sstevel@tonic-gate 			    req.data, req.datasize, &lencopied)) {
20390Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20400Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20410Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20420Sstevel@tonic-gate 				break;
20430Sstevel@tonic-gate 			}
20440Sstevel@tonic-gate 			*rval_p = lencopied;
20450Sstevel@tonic-gate 			ret = 0;
20460Sstevel@tonic-gate 			break;
20470Sstevel@tonic-gate 		}
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 		case PM_GET_POWER_LEVELS:
20500Sstevel@tonic-gate 		{
20510Sstevel@tonic-gate 			int musthave;
20520Sstevel@tonic-gate 			int numlevels;
20530Sstevel@tonic-gate 			int wordsize;
20540Sstevel@tonic-gate 
20550Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
20560Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
20570Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
20580Sstevel@tonic-gate 			} else
20590Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
20600Sstevel@tonic-gate 			{
20610Sstevel@tonic-gate 				wordsize = sizeof (int);
20620Sstevel@tonic-gate 			}
20630Sstevel@tonic-gate 			ASSERT(dip);
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20660Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20670Sstevel@tonic-gate 				    "has %d components, component %d requested"
20680Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, PM_DEVICE(dip),
20690Sstevel@tonic-gate 				    PM_NUMCMPTS(dip), req.component))
20700Sstevel@tonic-gate 				ret = EINVAL;
20710Sstevel@tonic-gate 				break;
20720Sstevel@tonic-gate 			}
20730Sstevel@tonic-gate 			numlevels = cp->pmc_comp.pmc_numlevels;
20740Sstevel@tonic-gate 			musthave = numlevels *  wordsize;
20750Sstevel@tonic-gate 			if (req.datasize < musthave) {
20760Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
20770Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
20780Sstevel@tonic-gate 				    musthave))
20790Sstevel@tonic-gate 				ret = EINVAL;
20800Sstevel@tonic-gate 				break;
20810Sstevel@tonic-gate 			}
20820Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
20830Sstevel@tonic-gate 			for (i = 0; i < numlevels; i++) {
20840Sstevel@tonic-gate 				int level;
20850Sstevel@tonic-gate 				int32_t level32;
20860Sstevel@tonic-gate 				caddr_t laddr;
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
20890Sstevel@tonic-gate 					level = cp->pmc_comp.pmc_lvals[i];
20900Sstevel@tonic-gate 					laddr = (caddr_t)&level;
20910Sstevel@tonic-gate 				} else {
20920Sstevel@tonic-gate 					level32 = cp->pmc_comp.pmc_lvals[i];
20930Sstevel@tonic-gate 					laddr = (caddr_t)&level32;
20940Sstevel@tonic-gate 				}
20950Sstevel@tonic-gate 				if (ddi_copyout(laddr, (void *) req.data,
20960Sstevel@tonic-gate 				    wordsize, mode) != 0) {
20970Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
20980Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
20990Sstevel@tonic-gate 					    "(%s#%d) laddr %p EFAULT\n",
21000Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
21010Sstevel@tonic-gate 					    (void *)laddr))
21020Sstevel@tonic-gate 					ASSERT(!dipheld);
21030Sstevel@tonic-gate 					return (EFAULT);
21040Sstevel@tonic-gate 				}
21050Sstevel@tonic-gate 				laddr = (caddr_t)req.data;
21060Sstevel@tonic-gate 				laddr += wordsize;
21070Sstevel@tonic-gate 				req.data = (int *)laddr;
21080Sstevel@tonic-gate 			}
21090Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21100Sstevel@tonic-gate 			*rval_p = numlevels;
21110Sstevel@tonic-gate 			ret = 0;
21120Sstevel@tonic-gate 			break;
21130Sstevel@tonic-gate 		}
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 		case PM_GET_NUM_POWER_LEVELS:
21170Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21180Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21190Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
21200Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
21210Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
21220Sstevel@tonic-gate 				ret = EINVAL;
21230Sstevel@tonic-gate 				break;
21240Sstevel@tonic-gate 			}
21250Sstevel@tonic-gate 			*rval_p = cp->pmc_comp.pmc_numlevels;
21260Sstevel@tonic-gate 			ret = 0;
21270Sstevel@tonic-gate 			break;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD_BASIS:
21300Sstevel@tonic-gate 			ret = 0;
21310Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21320Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
21330Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21340Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
21350Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
21360Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
21370Sstevel@tonic-gate 				break;
21380Sstevel@tonic-gate 			}
21390Sstevel@tonic-gate 			if (PM_ISDIRECT(dip)) {
21400Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21410Sstevel@tonic-gate 				*rval_p = PM_DIRECTLY_MANAGED;
21420Sstevel@tonic-gate 				break;
21430Sstevel@tonic-gate 			}
21440Sstevel@tonic-gate 			switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
21450Sstevel@tonic-gate 			case PMC_DEF_THRESH:
21460Sstevel@tonic-gate 			case PMC_NEXDEF_THRESH:
21470Sstevel@tonic-gate 				*rval_p = PM_DEFAULT_THRESHOLD;
21480Sstevel@tonic-gate 				break;
21490Sstevel@tonic-gate 			case PMC_DEV_THRESH:
21500Sstevel@tonic-gate 				*rval_p = PM_DEVICE_THRESHOLD;
21510Sstevel@tonic-gate 				break;
21520Sstevel@tonic-gate 			case PMC_COMP_THRESH:
21530Sstevel@tonic-gate 				*rval_p = PM_COMPONENT_THRESHOLD;
21540Sstevel@tonic-gate 				break;
2155*3028Smh27603 			case PMC_CPU_THRESH:
2156*3028Smh27603 				*rval_p = PM_CPU_THRESHOLD;
2157*3028Smh27603 				break;
21580Sstevel@tonic-gate 			default:
21590Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
21600Sstevel@tonic-gate 					*rval_p = PM_OLD_THRESHOLD;
21610Sstevel@tonic-gate 					break;
21620Sstevel@tonic-gate 				}
21630Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: default, not "
21640Sstevel@tonic-gate 				    "BC--EINVAL", cmdstr))
21650Sstevel@tonic-gate 				ret = EINVAL;
21660Sstevel@tonic-gate 				break;
21670Sstevel@tonic-gate 			}
21680Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21690Sstevel@tonic-gate 			break;
21700Sstevel@tonic-gate 		}
21710Sstevel@tonic-gate 		break;
21720Sstevel@tonic-gate 
21730Sstevel@tonic-gate 	case PM_PSC:
21740Sstevel@tonic-gate 		/*
21750Sstevel@tonic-gate 		 * Commands that require pm_state_change_t as arg
21760Sstevel@tonic-gate 		 */
21770Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
21780Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
21790Sstevel@tonic-gate 			pscp32 = (pm_state_change32_t *)arg;
21800Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc32,
21810Sstevel@tonic-gate 			    sizeof (psc32), mode) != 0) {
21820Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
21830Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
21840Sstevel@tonic-gate 				ASSERT(!dipheld);
21850Sstevel@tonic-gate 				return (EFAULT);
21860Sstevel@tonic-gate 			}
21870Sstevel@tonic-gate 			psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
21880Sstevel@tonic-gate 			psc.size = psc32.size;
21890Sstevel@tonic-gate 		} else
21900Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
21910Sstevel@tonic-gate 		{
21920Sstevel@tonic-gate 			pscp = (pm_state_change_t *)arg;
21930Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc,
21940Sstevel@tonic-gate 			    sizeof (psc), mode) != 0) {
21950Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
21960Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
21970Sstevel@tonic-gate 				ASSERT(!dipheld);
21980Sstevel@tonic-gate 				return (EFAULT);
21990Sstevel@tonic-gate 			}
22000Sstevel@tonic-gate 		}
22010Sstevel@tonic-gate 		switch (cmd) {
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE:
22040Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE_WAIT:
22050Sstevel@tonic-gate 		{
22060Sstevel@tonic-gate 			psce_t			*pscep;
22070Sstevel@tonic-gate 			pm_state_change_t	*p;
22080Sstevel@tonic-gate 			caddr_t			physpath;
22090Sstevel@tonic-gate 			size_t			physlen;
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 			/*
22120Sstevel@tonic-gate 			 * We want to know if any device has changed state.
22130Sstevel@tonic-gate 			 * We look up by clone.  In case we have another thread
22140Sstevel@tonic-gate 			 * from the same process, we loop.
22150Sstevel@tonic-gate 			 * pm_psc_clone_to_interest() returns a locked entry.
22160Sstevel@tonic-gate 			 * We create an internal copy of the event entry prior
22170Sstevel@tonic-gate 			 * to copyout to user space because we don't want to
22180Sstevel@tonic-gate 			 * hold the psce_lock while doing copyout as we might
22190Sstevel@tonic-gate 			 * hit page fault  which eventually brings us back
22200Sstevel@tonic-gate 			 * here requesting the same lock.
22210Sstevel@tonic-gate 			 */
22220Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
22230Sstevel@tonic-gate 			if (!pm_interest_registered(clone))
22240Sstevel@tonic-gate 				pm_register_watcher(clone, NULL);
22250Sstevel@tonic-gate 			while ((pscep =
22260Sstevel@tonic-gate 			    pm_psc_clone_to_interest(clone)) == NULL) {
22270Sstevel@tonic-gate 				if (cmd == PM_GET_STATE_CHANGE) {
22280Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
22290Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
22300Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
22310Sstevel@tonic-gate 					ASSERT(!dipheld);
22320Sstevel@tonic-gate 					return (EWOULDBLOCK);
22330Sstevel@tonic-gate 				} else {
22340Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
22350Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
22360Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
22370Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s "
22380Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
22390Sstevel@tonic-gate 						ASSERT(!dipheld);
22400Sstevel@tonic-gate 						return (EINTR);
22410Sstevel@tonic-gate 					}
22420Sstevel@tonic-gate 				}
22430Sstevel@tonic-gate 			}
22440Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
22450Sstevel@tonic-gate 
22460Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
22470Sstevel@tonic-gate 			physpath = NULL;
22480Sstevel@tonic-gate 			/*
22490Sstevel@tonic-gate 			 * If we were unable to store the path while bringing
22500Sstevel@tonic-gate 			 * up the console fb upon entering the prom, we give
22510Sstevel@tonic-gate 			 * a "" name with the overrun event set
22520Sstevel@tonic-gate 			 */
22530Sstevel@tonic-gate 			if (physlen == (size_t)-1) {	/* kmemalloc failed */
22540Sstevel@tonic-gate 				physpath = kmem_zalloc(1, KM_SLEEP);
22550Sstevel@tonic-gate 				physlen = 1;
22560Sstevel@tonic-gate 			}
22570Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
22580Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
22590Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
22600Sstevel@tonic-gate 				ret = EFAULT;
22610Sstevel@tonic-gate 				break;
22620Sstevel@tonic-gate 			}
22630Sstevel@tonic-gate 			if (physpath == NULL) {
22640Sstevel@tonic-gate 				physpath = kmem_zalloc(physlen, KM_SLEEP);
22650Sstevel@tonic-gate 				bcopy((const void *) pscep->psce_out->physpath,
22660Sstevel@tonic-gate 				    (void *) physpath, physlen);
22670Sstevel@tonic-gate 			}
22680Sstevel@tonic-gate 
22690Sstevel@tonic-gate 			p = pscep->psce_out;
22700Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
22710Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
22720Sstevel@tonic-gate #ifdef DEBUG
22730Sstevel@tonic-gate 				size_t usrcopysize;
22740Sstevel@tonic-gate #endif
22750Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
22760Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
22770Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
22780Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
22790Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
22800Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
22810Sstevel@tonic-gate 				copysize32 = ((intptr_t)&psc32.size -
22820Sstevel@tonic-gate 				    (intptr_t)&psc32.component);
22830Sstevel@tonic-gate #ifdef DEBUG
22840Sstevel@tonic-gate 				usrcopysize = ((intptr_t)&pscp32->size -
22850Sstevel@tonic-gate 				    (intptr_t)&pscp32->component);
22860Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
22870Sstevel@tonic-gate #endif
22880Sstevel@tonic-gate 			} else
22890Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22900Sstevel@tonic-gate 			{
22910Sstevel@tonic-gate 				psc.flags = p->flags;
22920Sstevel@tonic-gate 				psc.event = p->event;
22930Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
22940Sstevel@tonic-gate 				psc.component = p->component;
22950Sstevel@tonic-gate 				psc.old_level = p->old_level;
22960Sstevel@tonic-gate 				psc.new_level = p->new_level;
22970Sstevel@tonic-gate 				copysize = ((long)&p->size -
22980Sstevel@tonic-gate 				    (long)&p->component);
22990Sstevel@tonic-gate 			}
23000Sstevel@tonic-gate 			if (p->size != (size_t)-1)
23010Sstevel@tonic-gate 				kmem_free(p->physpath, p->size);
23020Sstevel@tonic-gate 			p->size = 0;
23030Sstevel@tonic-gate 			p->physpath = NULL;
23040Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
23050Sstevel@tonic-gate 				p = pscep->psce_first;
23060Sstevel@tonic-gate 			else
23070Sstevel@tonic-gate 				p++;
23080Sstevel@tonic-gate 			pscep->psce_out = p;
23090Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
23100Sstevel@tonic-gate 
23110Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
23120Sstevel@tonic-gate 			    physlen, &lencopied);
23130Sstevel@tonic-gate 			kmem_free(physpath, physlen);
23140Sstevel@tonic-gate 			if (ret) {
23150Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
23160Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
23170Sstevel@tonic-gate 				    (void *)psc.physpath))
23180Sstevel@tonic-gate 				break;
23190Sstevel@tonic-gate 			}
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23220Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23230Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
23240Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
23250Sstevel@tonic-gate 				    != 0) {
23260Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23270Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23280Sstevel@tonic-gate 					ret = EFAULT;
23290Sstevel@tonic-gate 					break;
23300Sstevel@tonic-gate 				}
23310Sstevel@tonic-gate 			} else
23320Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
23330Sstevel@tonic-gate 			{
23340Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
23350Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
23360Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23370Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23380Sstevel@tonic-gate 					ret = EFAULT;
23390Sstevel@tonic-gate 					break;
23400Sstevel@tonic-gate 				}
23410Sstevel@tonic-gate 			}
23420Sstevel@tonic-gate 			ret = 0;
23430Sstevel@tonic-gate 			break;
23440Sstevel@tonic-gate 		}
23450Sstevel@tonic-gate 
23460Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY:
23470Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY_WAIT:
23480Sstevel@tonic-gate 		{
23490Sstevel@tonic-gate 			psce_t			*pscep;
23500Sstevel@tonic-gate 			pm_state_change_t	*p;
23510Sstevel@tonic-gate 			caddr_t			physpath;
23520Sstevel@tonic-gate 			size_t			physlen;
23530Sstevel@tonic-gate 			/*
23540Sstevel@tonic-gate 			 * We want to know if any direct device of ours has
23550Sstevel@tonic-gate 			 * something we should know about.  We look up by clone.
23560Sstevel@tonic-gate 			 * In case we have another thread from the same process,
23570Sstevel@tonic-gate 			 * we loop.
23580Sstevel@tonic-gate 			 * pm_psc_clone_to_direct() returns a locked entry.
23590Sstevel@tonic-gate 			 */
23600Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
23610Sstevel@tonic-gate 			while (pm_poll_cnt[clone] == 0 ||
23620Sstevel@tonic-gate 			    (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
23630Sstevel@tonic-gate 				if (cmd == PM_DIRECT_NOTIFY) {
23640Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
23650Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
23660Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
23670Sstevel@tonic-gate 					ASSERT(!dipheld);
23680Sstevel@tonic-gate 					return (EWOULDBLOCK);
23690Sstevel@tonic-gate 				} else {
23700Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
23710Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
23720Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
23730Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
23740Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
23750Sstevel@tonic-gate 						ASSERT(!dipheld);
23760Sstevel@tonic-gate 						return (EINTR);
23770Sstevel@tonic-gate 					}
23780Sstevel@tonic-gate 				}
23790Sstevel@tonic-gate 			}
23800Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
23810Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
23820Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
23830Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
23840Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
23850Sstevel@tonic-gate 				    cmdstr))
23860Sstevel@tonic-gate 				ret = EFAULT;
23870Sstevel@tonic-gate 				break;
23880Sstevel@tonic-gate 			}
23890Sstevel@tonic-gate 			physpath = kmem_zalloc(physlen, KM_SLEEP);
23900Sstevel@tonic-gate 			bcopy((const void *) pscep->psce_out->physpath,
23910Sstevel@tonic-gate 			    (void *) physpath, physlen);
23920Sstevel@tonic-gate 
23930Sstevel@tonic-gate 			p = pscep->psce_out;
23940Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23950Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23960Sstevel@tonic-gate #ifdef DEBUG
23970Sstevel@tonic-gate 				size_t usrcopysize;
23980Sstevel@tonic-gate #endif
23990Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
24000Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
24010Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
24020Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
24030Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
24040Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
24050Sstevel@tonic-gate 				copysize32 = (intptr_t)&psc32.size -
24060Sstevel@tonic-gate 				    (intptr_t)&psc32.component;
24070Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
24080Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24090Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24100Sstevel@tonic-gate #ifdef DEBUG
24110Sstevel@tonic-gate 				usrcopysize = (intptr_t)&pscp32->size -
24120Sstevel@tonic-gate 				    (intptr_t)&pscp32->component;
24130Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
24140Sstevel@tonic-gate #endif
24150Sstevel@tonic-gate 			} else
24160Sstevel@tonic-gate #endif
24170Sstevel@tonic-gate 			{
24180Sstevel@tonic-gate 				psc.component = p->component;
24190Sstevel@tonic-gate 				psc.flags = p->flags;
24200Sstevel@tonic-gate 				psc.event = p->event;
24210Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
24220Sstevel@tonic-gate 				psc.old_level = p->old_level;
24230Sstevel@tonic-gate 				psc.new_level = p->new_level;
24240Sstevel@tonic-gate 				copysize = (intptr_t)&p->size -
24250Sstevel@tonic-gate 				    (intptr_t)&p->component;
24260Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
24270Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24280Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24290Sstevel@tonic-gate 			}
24300Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24310Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
24320Sstevel@tonic-gate 			    "before decrement\n", cmdstr, clone,
24330Sstevel@tonic-gate 			    pm_poll_cnt[clone]))
24340Sstevel@tonic-gate 			pm_poll_cnt[clone]--;
24350Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24360Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
24370Sstevel@tonic-gate 			p->size = 0;
24380Sstevel@tonic-gate 			p->physpath = NULL;
24390Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
24400Sstevel@tonic-gate 				p = pscep->psce_first;
24410Sstevel@tonic-gate 			else
24420Sstevel@tonic-gate 				p++;
24430Sstevel@tonic-gate 			pscep->psce_out = p;
24440Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
24450Sstevel@tonic-gate 
24460Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
24470Sstevel@tonic-gate 			    physlen, &lencopied);
24480Sstevel@tonic-gate 			kmem_free(physpath, physlen);
24490Sstevel@tonic-gate 			if (ret) {
24500Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
24510Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
24520Sstevel@tonic-gate 				    (void *)psc.physpath))
24530Sstevel@tonic-gate 				break;
24540Sstevel@tonic-gate 			}
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
24570Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24580Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
24590Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
24600Sstevel@tonic-gate 					!= 0) {
24610Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24620Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24630Sstevel@tonic-gate 					ret = EFAULT;
24640Sstevel@tonic-gate 					break;
24650Sstevel@tonic-gate 				}
24660Sstevel@tonic-gate 			} else
24670Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
24680Sstevel@tonic-gate 			{
24690Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
24700Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
24710Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24720Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24730Sstevel@tonic-gate 					ret = EFAULT;
24740Sstevel@tonic-gate 					break;
24750Sstevel@tonic-gate 				}
24760Sstevel@tonic-gate 			}
24770Sstevel@tonic-gate 			ret = 0;
24780Sstevel@tonic-gate 			break;
24790Sstevel@tonic-gate 		}
24800Sstevel@tonic-gate 		default:
24810Sstevel@tonic-gate 			ASSERT(0);
24820Sstevel@tonic-gate 		}
24830Sstevel@tonic-gate 		break;
24840Sstevel@tonic-gate 
24850Sstevel@tonic-gate 	case NOSTRUCT:
24860Sstevel@tonic-gate 		switch (cmd) {
24870Sstevel@tonic-gate 		case PM_START_PM:
2488*3028Smh27603 		case PM_START_CPUPM:
24890Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
2490*3028Smh27603 			if ((cmd == PM_START_PM && autopm_enabled) ||
2491*3028Smh27603 			    (cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) {
24920Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
24930Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
24940Sstevel@tonic-gate 				    cmdstr))
24950Sstevel@tonic-gate 				ret = EBUSY;
24960Sstevel@tonic-gate 				break;
24970Sstevel@tonic-gate 			}
2498*3028Smh27603 			if (cmd == PM_START_PM)
2499*3028Smh27603 			    autopm_enabled = 1;
2500*3028Smh27603 			else
2501*3028Smh27603 			    cpupm = PM_CPUPM_ENABLE;
25020Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
25030Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd);
25040Sstevel@tonic-gate 			ret = 0;
25050Sstevel@tonic-gate 			break;
25060Sstevel@tonic-gate 
25070Sstevel@tonic-gate 		case PM_RESET_PM:
25080Sstevel@tonic-gate 		case PM_STOP_PM:
2509*3028Smh27603 		case PM_STOP_CPUPM:
25100Sstevel@tonic-gate 		{
25110Sstevel@tonic-gate 			extern void pm_discard_thresholds(void);
25120Sstevel@tonic-gate 
25130Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
2514*3028Smh27603 			if ((cmd == PM_STOP_PM && !autopm_enabled) ||
2515*3028Smh27603 			    (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
25160Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
25170Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
25180Sstevel@tonic-gate 				    cmdstr))
25190Sstevel@tonic-gate 				ret = EINVAL;
25200Sstevel@tonic-gate 				break;
25210Sstevel@tonic-gate 			}
2522*3028Smh27603 			if (cmd == PM_STOP_PM)
2523*3028Smh27603 			    autopm_enabled = 0;
2524*3028Smh27603 			else if (cmd == PM_STOP_CPUPM)
2525*3028Smh27603 			    cpupm = PM_CPUPM_DISABLE;
2526*3028Smh27603 			else {
2527*3028Smh27603 			    autopm_enabled = 0;
2528*3028Smh27603 			    cpupm = PM_CPUPM_NOTSET;
2529*3028Smh27603 			}
25300Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
2531*3028Smh27603 
25320Sstevel@tonic-gate 			/*
25330Sstevel@tonic-gate 			 * bring devices to full power level, stop scan
25340Sstevel@tonic-gate 			 */
25350Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
25360Sstevel@tonic-gate 			ret = 0;
2537*3028Smh27603 			if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
25380Sstevel@tonic-gate 				break;
25390Sstevel@tonic-gate 			/*
25400Sstevel@tonic-gate 			 * Now do only PM_RESET_PM stuff.
25410Sstevel@tonic-gate 			 */
25420Sstevel@tonic-gate 			pm_system_idle_threshold = pm_default_idle_threshold;
2543*3028Smh27603 			pm_cpu_idle_threshold = 0;
25440Sstevel@tonic-gate 			pm_discard_thresholds();
25450Sstevel@tonic-gate 			pm_all_to_default_thresholds();
25460Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
25470Sstevel@tonic-gate 			    NULL, NULL, PM_DEP_WAIT, NULL, 0);
25480Sstevel@tonic-gate 			break;
25490Sstevel@tonic-gate 		}
25500Sstevel@tonic-gate 
25510Sstevel@tonic-gate 		case PM_GET_SYSTEM_THRESHOLD:
25520Sstevel@tonic-gate 			*rval_p = pm_system_idle_threshold;
25530Sstevel@tonic-gate 			ret = 0;
25540Sstevel@tonic-gate 			break;
25550Sstevel@tonic-gate 
25560Sstevel@tonic-gate 		case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
25570Sstevel@tonic-gate 			*rval_p = pm_default_idle_threshold;
25580Sstevel@tonic-gate 			ret = 0;
25590Sstevel@tonic-gate 			break;
25600Sstevel@tonic-gate 
2561*3028Smh27603 		case PM_GET_CPU_THRESHOLD:
2562*3028Smh27603 			*rval_p = pm_cpu_idle_threshold;
2563*3028Smh27603 			ret = 0;
2564*3028Smh27603 			break;
2565*3028Smh27603 
25660Sstevel@tonic-gate 		case PM_SET_SYSTEM_THRESHOLD:
2567*3028Smh27603 		case PM_SET_CPU_THRESHOLD:
25680Sstevel@tonic-gate 			if ((int)arg < 0) {
25690Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
25700Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, (int)arg))
25710Sstevel@tonic-gate 				ret = EINVAL;
25720Sstevel@tonic-gate 				break;
25730Sstevel@tonic-gate 			}
25740Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
25750Sstevel@tonic-gate 			    (int)arg, (int)arg))
2576*3028Smh27603 			if (cmd == PM_SET_SYSTEM_THRESHOLD)
2577*3028Smh27603 				pm_system_idle_threshold = (int)arg;
2578*3028Smh27603 			else {
2579*3028Smh27603 				pm_cpu_idle_threshold = (int)arg;
2580*3028Smh27603 			}
2581*3028Smh27603 			ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
2582*3028Smh27603 				    (void *) &cmd);
2583*3028Smh27603 
25840Sstevel@tonic-gate 			ret = 0;
25850Sstevel@tonic-gate 			break;
25860Sstevel@tonic-gate 
25870Sstevel@tonic-gate 		case PM_IDLE_DOWN:
25880Sstevel@tonic-gate 			if (pm_timeout_idledown() != 0) {
25890Sstevel@tonic-gate 				ddi_walk_devs(ddi_root_node(),
25900Sstevel@tonic-gate 				    pm_start_idledown, (void *)PMID_IOC);
25910Sstevel@tonic-gate 			}
25920Sstevel@tonic-gate 			ret = 0;
25930Sstevel@tonic-gate 			break;
25940Sstevel@tonic-gate 
25950Sstevel@tonic-gate 		case PM_GET_PM_STATE:
25960Sstevel@tonic-gate 			if (autopm_enabled) {
25970Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_ENABLED;
25980Sstevel@tonic-gate 			} else {
25990Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_DISABLED;
26000Sstevel@tonic-gate 			}
26010Sstevel@tonic-gate 			ret = 0;
26020Sstevel@tonic-gate 			break;
2603*3028Smh27603 
2604*3028Smh27603 		case PM_GET_CPUPM_STATE:
2605*3028Smh27603 			if (PM_CPUPM_ENABLED)
2606*3028Smh27603 				*rval_p = PM_CPU_PM_ENABLED;
2607*3028Smh27603 			else if (PM_CPUPM_DISABLED)
2608*3028Smh27603 				*rval_p = PM_CPU_PM_DISABLED;
2609*3028Smh27603 			else
2610*3028Smh27603 				*rval_p = PM_CPU_PM_NOTSET;
2611*3028Smh27603 			ret = 0;
2612*3028Smh27603 			break;
26130Sstevel@tonic-gate 		}
26140Sstevel@tonic-gate 		break;
26150Sstevel@tonic-gate 
26160Sstevel@tonic-gate 	default:
26170Sstevel@tonic-gate 		/*
26180Sstevel@tonic-gate 		 * Internal error, invalid ioctl description
26190Sstevel@tonic-gate 		 * force debug entry even if pm_debug not set
26200Sstevel@tonic-gate 		 */
26210Sstevel@tonic-gate #ifdef	DEBUG
26220Sstevel@tonic-gate 		pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
26230Sstevel@tonic-gate 		    pcip->str_type, cmd, pcip->name);
26240Sstevel@tonic-gate #endif
26250Sstevel@tonic-gate 		ASSERT(0);
26260Sstevel@tonic-gate 		return (EIO);
26270Sstevel@tonic-gate 	}
26280Sstevel@tonic-gate 	ASSERT(ret != 0x0badcafe);	/* some cmd in wrong case! */
26290Sstevel@tonic-gate 	if (dipheld) {
26300Sstevel@tonic-gate 		ASSERT(dip);
26310Sstevel@tonic-gate 		PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
26320Sstevel@tonic-gate 		    "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
26330Sstevel@tonic-gate 		PM_RELE(dip);
26340Sstevel@tonic-gate 	}
26350Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
26360Sstevel@tonic-gate 	return (ret);
26370Sstevel@tonic-gate }
2638