xref: /onnv-gate/usr/src/uts/common/io/pm.c (revision 7656)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53028Smh27603  * Common Development and Distribution License (the "License").
63028Smh27603  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * pm	This driver now only handles the ioctl interface.  The scanning
290Sstevel@tonic-gate  *	and policy stuff now lives in common/os/sunpm.c.
300Sstevel@tonic-gate  *	Not DDI compliant
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/errno.h>
350Sstevel@tonic-gate #include <sys/modctl.h>
360Sstevel@tonic-gate #include <sys/conf.h>		/* driver flags and functions */
370Sstevel@tonic-gate #include <sys/open.h>		/* OTYP_CHR definition */
380Sstevel@tonic-gate #include <sys/stat.h>		/* S_IFCHR definition */
390Sstevel@tonic-gate #include <sys/pathname.h>	/* name -> dev_info xlation */
400Sstevel@tonic-gate #include <sys/kmem.h>		/* memory alloc stuff */
410Sstevel@tonic-gate #include <sys/debug.h>
420Sstevel@tonic-gate #include <sys/pm.h>
430Sstevel@tonic-gate #include <sys/ddi.h>
440Sstevel@tonic-gate #include <sys/sunddi.h>
450Sstevel@tonic-gate #include <sys/epm.h>
460Sstevel@tonic-gate #include <sys/vfs.h>
470Sstevel@tonic-gate #include <sys/mode.h>
480Sstevel@tonic-gate #include <sys/mkdev.h>
490Sstevel@tonic-gate #include <sys/promif.h>
500Sstevel@tonic-gate #include <sys/consdev.h>
510Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
520Sstevel@tonic-gate #include <sys/poll.h>
530Sstevel@tonic-gate #include <sys/note.h>
540Sstevel@tonic-gate #include <sys/taskq.h>
550Sstevel@tonic-gate #include <sys/policy.h>
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
585295Srandyf  * Minor number is instance<<8 + clone minor from range 1-254; (0 reserved
595295Srandyf  * for "original")
600Sstevel@tonic-gate  */
615295Srandyf #define	PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE -1))
620Sstevel@tonic-gate 
630Sstevel@tonic-gate #define	PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
640Sstevel@tonic-gate #define	PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
650Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_driver_major(dip)
660Sstevel@tonic-gate #define	PM_RELE(dip) ddi_release_devi(dip)
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #define	PM_IDLEDOWN_TIME	10
695295Srandyf #define	MAXSMBIOSSTRLEN 64	/* from SMBIOS spec */
705295Srandyf #define	MAXCOPYBUF 	(MAXSMBIOSSTRLEN + 1)
710Sstevel@tonic-gate 
720Sstevel@tonic-gate extern kmutex_t	pm_scan_lock;	/* protects autopm_enable, pm_scans_disabled */
730Sstevel@tonic-gate extern kmutex_t	pm_clone_lock;	/* protects pm_clones array */
740Sstevel@tonic-gate extern int	autopm_enabled;
753028Smh27603 extern pm_cpupm_t cpupm;
763028Smh27603 extern int	pm_default_idle_threshold;
773028Smh27603 extern int	pm_system_idle_threshold;
783028Smh27603 extern int	pm_cpu_idle_threshold;
790Sstevel@tonic-gate extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
800Sstevel@tonic-gate extern uint_t	pm_poll_cnt[PM_MAX_CLONE];
815295Srandyf extern int	autoS3_enabled;
825295Srandyf extern void	pm_record_thresh(pm_thresh_rec_t *);
835295Srandyf extern void	pm_register_watcher(int, dev_info_t *);
845295Srandyf extern int	pm_get_current_power(dev_info_t *, int, int *);
855295Srandyf extern int	pm_interest_registered(int);
865295Srandyf extern void	pm_all_to_default_thresholds(void);
875295Srandyf extern int	pm_current_threshold(dev_info_t *, int, int *);
885295Srandyf extern void	pm_deregister_watcher(int, dev_info_t *);
895295Srandyf extern void	pm_unrecord_threshold(char *);
905295Srandyf extern int	pm_S3_enabled;
915295Srandyf extern int	pm_ppm_searchlist(pm_searchargs_t *);
925295Srandyf extern psce_t	*pm_psc_clone_to_direct(int);
935295Srandyf extern psce_t	*pm_psc_clone_to_interest(int);
940Sstevel@tonic-gate 
950Sstevel@tonic-gate /*
960Sstevel@tonic-gate  * The soft state of the power manager.  Since there will only
970Sstevel@tonic-gate  * one of these, just reference it through a static pointer.
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate static struct pmstate {
1000Sstevel@tonic-gate 	dev_info_t	*pm_dip;		/* ptr to our dev_info node */
1010Sstevel@tonic-gate 	int		pm_instance;		/* for ddi_get_instance() */
1020Sstevel@tonic-gate 	timeout_id_t	pm_idledown_id;		/* pm idledown timeout id */
1030Sstevel@tonic-gate 	uchar_t		pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */
1040Sstevel@tonic-gate 	struct cred	*pm_cred[PM_MAX_CLONE];	/* cred for each unique open */
1050Sstevel@tonic-gate } pm_state = { NULL, -1, (timeout_id_t)0 };
1060Sstevel@tonic-gate typedef struct pmstate *pm_state_t;
1070Sstevel@tonic-gate static pm_state_t pmstp = &pm_state;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate static int	pm_open(dev_t *, int, int, cred_t *);
1100Sstevel@tonic-gate static int	pm_close(dev_t, int, int, cred_t *);
1110Sstevel@tonic-gate static int	pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1120Sstevel@tonic-gate static int	pm_chpoll(dev_t, short, int, short *, struct pollhead **);
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static struct cb_ops pm_cb_ops = {
1150Sstevel@tonic-gate 	pm_open,	/* open */
1160Sstevel@tonic-gate 	pm_close,	/* close */
1170Sstevel@tonic-gate 	nodev,		/* strategy */
1180Sstevel@tonic-gate 	nodev,		/* print */
1190Sstevel@tonic-gate 	nodev,		/* dump */
1200Sstevel@tonic-gate 	nodev,		/* read */
1210Sstevel@tonic-gate 	nodev,		/* write */
1220Sstevel@tonic-gate 	pm_ioctl,	/* ioctl */
1230Sstevel@tonic-gate 	nodev,		/* devmap */
1240Sstevel@tonic-gate 	nodev,		/* mmap */
1250Sstevel@tonic-gate 	nodev,		/* segmap */
1260Sstevel@tonic-gate 	pm_chpoll,	/* poll */
1270Sstevel@tonic-gate 	ddi_prop_op,	/* prop_op */
1280Sstevel@tonic-gate 	NULL,		/* streamtab */
1290Sstevel@tonic-gate 	D_NEW | D_MP	/* driver compatibility flag */
1300Sstevel@tonic-gate };
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1330Sstevel@tonic-gate     void **result);
1340Sstevel@tonic-gate static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1350Sstevel@tonic-gate static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate static struct dev_ops pm_ops = {
1380Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1390Sstevel@tonic-gate 	0,			/* refcnt */
1400Sstevel@tonic-gate 	pm_getinfo,		/* info */
1410Sstevel@tonic-gate 	nulldev,		/* identify */
1420Sstevel@tonic-gate 	nulldev,		/* probe */
1430Sstevel@tonic-gate 	pm_attach,		/* attach */
1440Sstevel@tonic-gate 	pm_detach,		/* detach */
1450Sstevel@tonic-gate 	nodev,			/* reset */
1460Sstevel@tonic-gate 	&pm_cb_ops,		/* driver operations */
1470Sstevel@tonic-gate 	NULL,			/* bus operations */
148*7656SSherry.Moore@Sun.COM 	NULL,			/* power */
149*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1500Sstevel@tonic-gate };
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate static struct modldrv modldrv = {
1530Sstevel@tonic-gate 	&mod_driverops,
154*7656SSherry.Moore@Sun.COM 	"power management driver",
1550Sstevel@tonic-gate 	&pm_ops
1560Sstevel@tonic-gate };
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate static struct modlinkage modlinkage = {
1590Sstevel@tonic-gate 	MODREV_1, &modldrv, 0
1600Sstevel@tonic-gate };
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /* Local functions */
1630Sstevel@tonic-gate #ifdef DEBUG
1640Sstevel@tonic-gate static int	print_info(dev_info_t *, void *);
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate #endif
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate int
1690Sstevel@tonic-gate _init(void)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate int
1750Sstevel@tonic-gate _fini(void)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate int
1810Sstevel@tonic-gate _info(struct modinfo *modinfop)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate static int
1870Sstevel@tonic-gate pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate 	int		i;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	switch (cmd) {
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	case DDI_ATTACH:
1940Sstevel@tonic-gate 		if (pmstp->pm_instance != -1)	/* Only allow one instance */
1950Sstevel@tonic-gate 			return (DDI_FAILURE);
1960Sstevel@tonic-gate 		pmstp->pm_instance = ddi_get_instance(dip);
1970Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "pm", S_IFCHR,
1980Sstevel@tonic-gate 		    (pmstp->pm_instance << 8) + 0,
1995295Srandyf 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
2000Sstevel@tonic-gate 			return (DDI_FAILURE);
2010Sstevel@tonic-gate 		}
2020Sstevel@tonic-gate 		pmstp->pm_dip = dip;	/* pm_init and getinfo depend on it */
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2050Sstevel@tonic-gate 			cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL);
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 		ddi_report_dev(dip);
2080Sstevel@tonic-gate 		return (DDI_SUCCESS);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	default:
2110Sstevel@tonic-gate 		return (DDI_FAILURE);
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate /* ARGSUSED */
2160Sstevel@tonic-gate static int
2170Sstevel@tonic-gate pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate 	int i;
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	switch (cmd) {
2220Sstevel@tonic-gate 	case DDI_DETACH:
2230Sstevel@tonic-gate 		/*
2240Sstevel@tonic-gate 		 * Don't detach while idledown timeout is pending.  Note that
2250Sstevel@tonic-gate 		 * we already know we're not in pm_ioctl() due to framework
2260Sstevel@tonic-gate 		 * synchronization, so this is a sufficient test
2270Sstevel@tonic-gate 		 */
2280Sstevel@tonic-gate 		if (pmstp->pm_idledown_id)
2290Sstevel@tonic-gate 			return (DDI_FAILURE);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2320Sstevel@tonic-gate 			cv_destroy(&pm_clones_cv[i]);
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
2350Sstevel@tonic-gate 		pmstp->pm_instance = -1;
2360Sstevel@tonic-gate 		return (DDI_SUCCESS);
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	default:
2390Sstevel@tonic-gate 		return (DDI_FAILURE);
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate static int
2440Sstevel@tonic-gate pm_close_direct_pm_device(dev_info_t *dip, void *arg)
2450Sstevel@tonic-gate {
2460Sstevel@tonic-gate 	int clone;
2470Sstevel@tonic-gate 	char *pathbuf;
2480Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	clone = *((int *)arg);
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	if (!info)
2530Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2560Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
2570Sstevel@tonic-gate 	if (clone == info->pmi_clone) {
2580Sstevel@tonic-gate 		PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n",
2590Sstevel@tonic-gate 		    PM_DEVICE(dip)))
2600Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip));
2610Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
2620Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2630Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
2640Sstevel@tonic-gate 		/* Bring ourselves up if there is a keeper that is up */
2650Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
2660Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL,
2670Sstevel@tonic-gate 		    pathbuf, PM_DEP_NOWAIT, NULL, 0);
2680Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
2690Sstevel@tonic-gate 		info->pmi_clone = 0;
2700Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2710Sstevel@tonic-gate 	} else {
2720Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2730Sstevel@tonic-gate 	}
2740Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	/* restart autopm on device released from direct pm */
2770Sstevel@tonic-gate 	pm_rescan(dip);
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate #define	PM_REQ		1
2830Sstevel@tonic-gate #define	NOSTRUCT	2
2840Sstevel@tonic-gate #define	DIP		3
2850Sstevel@tonic-gate #define	NODIP		4
2860Sstevel@tonic-gate #define	NODEP		5
2870Sstevel@tonic-gate #define	DEP		6
2880Sstevel@tonic-gate #define	PM_PSC		7
2895295Srandyf #define	PM_SRCH		8
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate #define	CHECKPERMS	0x001
2920Sstevel@tonic-gate #define	SU		0x002
2930Sstevel@tonic-gate #define	SG		0x004
2940Sstevel@tonic-gate #define	OWNER		0x008
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate #define	INWHO		0x001
2970Sstevel@tonic-gate #define	INDATAINT	0x002
2980Sstevel@tonic-gate #define	INDATASTRING	0x004
2990Sstevel@tonic-gate #define	INDEP		0x008
3000Sstevel@tonic-gate #define	INDATAOUT	0x010
3010Sstevel@tonic-gate #define	INDATA	(INDATAOUT | INDATAINT | INDATASTRING | INDEP)
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate struct pm_cmd_info {
3040Sstevel@tonic-gate 	int cmd;		/* command code */
3050Sstevel@tonic-gate 	char *name;		/* printable string */
3060Sstevel@tonic-gate 	int supported;		/* true if still supported */
3070Sstevel@tonic-gate 	int str_type;		/* PM_REQ or NOSTRUCT */
3080Sstevel@tonic-gate 	int inargs;		/* INWHO, INDATAINT, INDATASTRING, INDEP, */
3090Sstevel@tonic-gate 				/* INDATAOUT */
3100Sstevel@tonic-gate 	int diptype;		/* DIP or NODIP */
3110Sstevel@tonic-gate 	int deptype;		/* DEP or NODEP */
3120Sstevel@tonic-gate 	int permission;		/* SU, GU, or CHECKPERMS */
3130Sstevel@tonic-gate };
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate #ifdef DEBUG
3160Sstevel@tonic-gate char *pm_cmd_string;
3170Sstevel@tonic-gate int pm_cmd;
3180Sstevel@tonic-gate #endif
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate  * Returns true if permission granted by credentials
3220Sstevel@tonic-gate  */
3230Sstevel@tonic-gate static int
3240Sstevel@tonic-gate pm_perms(int perm, cred_t *cr)
3250Sstevel@tonic-gate {
3260Sstevel@tonic-gate 	if (perm == 0)			/* no restrictions */
3270Sstevel@tonic-gate 		return (1);
3280Sstevel@tonic-gate 	if (perm == CHECKPERMS)		/* ok for now (is checked later) */
3290Sstevel@tonic-gate 		return (1);
3300Sstevel@tonic-gate 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
3310Sstevel@tonic-gate 		return (1);
3320Sstevel@tonic-gate 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
3330Sstevel@tonic-gate 		return (1);
3340Sstevel@tonic-gate 	return (0);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate #ifdef DEBUG
3380Sstevel@tonic-gate static int
3390Sstevel@tonic-gate print_info(dev_info_t *dip, void *arg)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
3420Sstevel@tonic-gate 	pm_info_t	*info;
3430Sstevel@tonic-gate 	int		i, j;
3440Sstevel@tonic-gate 	struct pm_component *cp;
3450Sstevel@tonic-gate 	extern int pm_cur_power(pm_component_t *cp);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
3480Sstevel@tonic-gate 	if (!info)
3490Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
3500Sstevel@tonic-gate 	cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip));
3510Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
3520Sstevel@tonic-gate 		cp = PM_CP(dip, i);
3530Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tThresholds[%d] =",  i);
3540Sstevel@tonic-gate 		for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++)
3550Sstevel@tonic-gate 			cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]);
3560Sstevel@tonic-gate 		cmn_err(CE_CONT, "\n");
3570Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i,
3580Sstevel@tonic-gate 		    pm_cur_power(cp));
3590Sstevel@tonic-gate 	}
3600Sstevel@tonic-gate 	if (PM_ISDIRECT(dip))
3610Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tDirect power management\n");
3620Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate #endif
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate /*
3670Sstevel@tonic-gate  * command, name, supported, str_type, inargs, diptype, deptype, permission
3680Sstevel@tonic-gate  */
3690Sstevel@tonic-gate static struct pm_cmd_info pmci[] = {
3700Sstevel@tonic-gate 	{PM_SCHEDULE, "PM_SCHEDULE", 0},
3710Sstevel@tonic-gate 	{PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
3720Sstevel@tonic-gate 	{PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
3730Sstevel@tonic-gate 	{PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
3740Sstevel@tonic-gate 	{PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
3750Sstevel@tonic-gate 	{PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
3760Sstevel@tonic-gate 	{PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
3770Sstevel@tonic-gate 	{PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
3780Sstevel@tonic-gate 	{PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
3790Sstevel@tonic-gate 	{PM_GET_DEP, "PM_GET_DEP", 0},
3800Sstevel@tonic-gate 	{PM_ADD_DEP, "PM_ADD_DEP", 0},
3810Sstevel@tonic-gate 	{PM_REM_DEP, "PM_REM_DEP", 0},
3820Sstevel@tonic-gate 	{PM_REM_DEVICE, "PM_REM_DEVICE", 0},
3830Sstevel@tonic-gate 	{PM_REM_DEVICES, "PM_REM_DEVICES", 0},
3840Sstevel@tonic-gate 	{PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP,
3850Sstevel@tonic-gate 	    NODEP},
3860Sstevel@tonic-gate 	{PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
3870Sstevel@tonic-gate 	{PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
3880Sstevel@tonic-gate 	{PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
3890Sstevel@tonic-gate 	{PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ,
3900Sstevel@tonic-gate 	    INWHO, NODIP, NODEP, SU},
3910Sstevel@tonic-gate 	{PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT},
3920Sstevel@tonic-gate 	{PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
3930Sstevel@tonic-gate 	    1, NOSTRUCT},
3940Sstevel@tonic-gate 	{PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT,
3950Sstevel@tonic-gate 	    0, 0, 0, SU},
3960Sstevel@tonic-gate 	{PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3970Sstevel@tonic-gate 	{PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3980Sstevel@tonic-gate 	{PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3990Sstevel@tonic-gate 	{PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT,
4000Sstevel@tonic-gate 	    DIP, NODEP},
4010Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO,
4020Sstevel@tonic-gate 	    DIP, NODEP},
4030Sstevel@tonic-gate 	{PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT,
4040Sstevel@tonic-gate 	    DIP, NODEP},
4050Sstevel@tonic-gate 	{PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ,
4060Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4070Sstevel@tonic-gate 	{PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO,
4080Sstevel@tonic-gate 	    DIP, NODEP},
4090Sstevel@tonic-gate 	{PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ,
4100Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4110Sstevel@tonic-gate 	{PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO,
4120Sstevel@tonic-gate 	    DIP, NODEP},
4130Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC},
4140Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC},
4150Sstevel@tonic-gate 	{PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP,
4160Sstevel@tonic-gate 	    (SU | SG)},
4170Sstevel@tonic-gate 	{PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO,
4180Sstevel@tonic-gate 	    DIP, NODEP},
4190Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC},
4200Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC},
4210Sstevel@tonic-gate 	{PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
4220Sstevel@tonic-gate 	    INWHO, DIP, NODEP, SU},
4230Sstevel@tonic-gate 	{PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
4245295Srandyf 	{PM_GET_AUTOS3_STATE, "PM_GET_AUTOS3_STATE", 1, NOSTRUCT},
4255295Srandyf 	{PM_GET_S3_SUPPORT_STATE, "PM_GET_S3_SUPPORT_STATE", 1, NOSTRUCT},
4260Sstevel@tonic-gate 	{PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
4270Sstevel@tonic-gate 	    DIP, NODEP},
4280Sstevel@tonic-gate 	{PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4290Sstevel@tonic-gate 	    INWHO | INDATAINT, NODIP, NODEP, SU},
4300Sstevel@tonic-gate 	{PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4310Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4320Sstevel@tonic-gate 	{PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU},
4330Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1,
4340Sstevel@tonic-gate 	    PM_REQ, INWHO, DIP, NODEP},
4350Sstevel@tonic-gate 	{PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4360Sstevel@tonic-gate 	    NODEP},
4370Sstevel@tonic-gate 	{PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4380Sstevel@tonic-gate 	    NODEP},
4390Sstevel@tonic-gate 	{PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP,
4400Sstevel@tonic-gate 	    NODEP},
4410Sstevel@tonic-gate 	{PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING,
4420Sstevel@tonic-gate 	    DIP, DEP, SU},
4430Sstevel@tonic-gate 	{PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP},
4440Sstevel@tonic-gate 	{PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ,
4450Sstevel@tonic-gate 	    INWHO | INDATASTRING, NODIP, DEP, SU},
4463028Smh27603 	{PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
4473028Smh27603 	{PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
4483028Smh27603 	{PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
4493028Smh27603 	{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
4503028Smh27603 	    0, 0, 0, SU},
4513028Smh27603 	{PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
4525295Srandyf 	{PM_START_AUTOS3, "PM_START_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
4535295Srandyf 	{PM_STOP_AUTOS3, "PM_STOP_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
4545295Srandyf 	{PM_ENABLE_S3, "PM_ENABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4555295Srandyf 	{PM_DISABLE_S3, "PM_DISABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4565295Srandyf 	{PM_ENTER_S3, "PM_ENTER_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4575295Srandyf 	{PM_SEARCH_LIST, "PM_SEARCH_LIST", 1, PM_SRCH, 0, 0, 0, SU},
4585295Srandyf 	{PM_GET_CMD_NAME, "PM_GET_CMD_NAME", 1, PM_REQ, INDATAOUT, NODIP,
4595295Srandyf 	    NODEP, 0},
4600Sstevel@tonic-gate 	{0, NULL}
4610Sstevel@tonic-gate };
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate struct pm_cmd_info *
4640Sstevel@tonic-gate pc_info(int cmd)
4650Sstevel@tonic-gate {
4660Sstevel@tonic-gate 	struct pm_cmd_info *pcip;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	for (pcip = pmci; pcip->name; pcip++) {
4690Sstevel@tonic-gate 		if (cmd == pcip->cmd)
4700Sstevel@tonic-gate 			return (pcip);
4710Sstevel@tonic-gate 	}
4720Sstevel@tonic-gate 	return (NULL);
4730Sstevel@tonic-gate }
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate static char *
4760Sstevel@tonic-gate pm_decode_cmd(int cmd)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	static char invbuf[64];
4790Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
4800Sstevel@tonic-gate 	if (pcip != NULL)
4810Sstevel@tonic-gate 		return (pcip->name);
4820Sstevel@tonic-gate 	(void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd);
4830Sstevel@tonic-gate 	return (invbuf);
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate /*
4870Sstevel@tonic-gate  * Allocate scan resource, create taskq, then dispatch scan,
4880Sstevel@tonic-gate  * called only if autopm is enabled.
4890Sstevel@tonic-gate  */
4900Sstevel@tonic-gate int
4910Sstevel@tonic-gate pm_start_pm_walk(dev_info_t *dip, void *arg)
4920Sstevel@tonic-gate {
4933028Smh27603 	int cmd = *((int *)arg);
4943839Skchow #ifdef PMDDEBUG
4953028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
4963839Skchow #endif
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
4990Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5000Sstevel@tonic-gate 
5013028Smh27603 	switch (cmd) {
5023028Smh27603 	case PM_START_CPUPM:
5033028Smh27603 		if (!PM_ISCPU(dip))
5043028Smh27603 			return (DDI_WALK_CONTINUE);
5053028Smh27603 		mutex_enter(&pm_scan_lock);
5063028Smh27603 		if (!PM_CPUPM_DISABLED)
5073028Smh27603 			pm_scan_init(dip);
5083028Smh27603 		mutex_exit(&pm_scan_lock);
5093028Smh27603 		break;
5103028Smh27603 	case PM_START_PM:
5113028Smh27603 		mutex_enter(&pm_scan_lock);
5123028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_DISABLED) {
5133028Smh27603 			mutex_exit(&pm_scan_lock);
5143028Smh27603 			return (DDI_WALK_CONTINUE);
5153028Smh27603 		}
5163028Smh27603 		if (autopm_enabled)
5173028Smh27603 			pm_scan_init(dip);
5183028Smh27603 		mutex_exit(&pm_scan_lock);
5193028Smh27603 		break;
5203028Smh27603 	}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	/*
5230Sstevel@tonic-gate 	 * Start doing pm on device: ensure pm_scan data structure initiated,
5243028Smh27603 	 * no need to guarantee a successful scan run.
5250Sstevel@tonic-gate 	 */
5260Sstevel@tonic-gate 	PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
5270Sstevel@tonic-gate 	    PM_DEVICE(dip)))
5280Sstevel@tonic-gate 	pm_rescan(dip);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5310Sstevel@tonic-gate }
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate /*
5340Sstevel@tonic-gate  * Bring devices to full power level, then stop scan
5350Sstevel@tonic-gate  */
5360Sstevel@tonic-gate int
5370Sstevel@tonic-gate pm_stop_pm_walk(dev_info_t *dip, void *arg)
5380Sstevel@tonic-gate {
5390Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
5403028Smh27603 	int cmd = *((int *)arg);
5413839Skchow #ifdef PMDDEBUG
5423028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
5433839Skchow #endif
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	if (!info)
5460Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5473028Smh27603 
5483028Smh27603 	switch (cmd) {
5493028Smh27603 	case PM_STOP_PM:
5503028Smh27603 		/*
5513028Smh27603 		 * If CPU devices are being managed independently, then don't
5523028Smh27603 		 * stop them as part of PM_STOP_PM. Only stop them as part of
5533028Smh27603 		 * PM_STOP_CPUPM and PM_RESET_PM.
5543028Smh27603 		 */
5553028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_ENABLED)
5563028Smh27603 			return (DDI_WALK_CONTINUE);
5573028Smh27603 		break;
5583028Smh27603 	case PM_STOP_CPUPM:
5593028Smh27603 		/*
5603028Smh27603 		 * If stopping CPU devices and this device is not marked
5613028Smh27603 		 * as a CPU device, then skip.
5623028Smh27603 		 */
5633028Smh27603 		if (!PM_ISCPU(dip))
5643028Smh27603 			return (DDI_WALK_CONTINUE);
5653028Smh27603 		break;
5663028Smh27603 	}
5673028Smh27603 
5680Sstevel@tonic-gate 	/*
5690Sstevel@tonic-gate 	 * Stop the current scan, and then bring it back to normal power.
5700Sstevel@tonic-gate 	 */
5710Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
5720Sstevel@tonic-gate 		PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
5730Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
5740Sstevel@tonic-gate 		pm_scan_stop(dip);
5750Sstevel@tonic-gate 	}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
5780Sstevel@tonic-gate 	    !pm_all_at_normal(dip)) {
5790Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
5800Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_DETACHING) {
5810Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
5820Sstevel@tonic-gate 			    "all_to_normal because %s@%s(%s#%d) is detaching\n",
5830Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
5840Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
5850Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
5860Sstevel@tonic-gate 			return (DDI_WALK_CONTINUE);
5870Sstevel@tonic-gate 		}
5880Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
5890Sstevel@tonic-gate 		if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
5900Sstevel@tonic-gate 			PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
5910Sstevel@tonic-gate 			    "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
5920Sstevel@tonic-gate 		}
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5960Sstevel@tonic-gate }
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate static int
5990Sstevel@tonic-gate pm_start_idledown(dev_info_t *dip, void *arg)
6000Sstevel@tonic-gate {
6010Sstevel@tonic-gate 	int		flag = (int)(intptr_t)arg;
6020Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	if (!scanp)
6050Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6080Sstevel@tonic-gate 	scanp->ps_idle_down |= flag;
6090Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6100Sstevel@tonic-gate 	pm_rescan(dip);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate /*ARGSUSED*/
6160Sstevel@tonic-gate static int
6170Sstevel@tonic-gate pm_end_idledown(dev_info_t *dip, void *ignore)
6180Sstevel@tonic-gate {
6190Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	if (!scanp)
6220Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6250Sstevel@tonic-gate 	/*
6260Sstevel@tonic-gate 	 * The PMID_TIMERS bits are place holder till idledown expires.
6270Sstevel@tonic-gate 	 * The bits are also the base for regenerating PMID_SCANS bits.
6280Sstevel@tonic-gate 	 * While it's up to scan thread to clear up the PMID_SCANS bits
6290Sstevel@tonic-gate 	 * after each scan run, PMID_TIMERS ensure aggressive scan down
6300Sstevel@tonic-gate 	 * performance throughout the idledown period.
6310Sstevel@tonic-gate 	 */
6320Sstevel@tonic-gate 	scanp->ps_idle_down &= ~PMID_TIMERS;
6330Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate /*ARGSUSED*/
6390Sstevel@tonic-gate static void
6400Sstevel@tonic-gate pm_end_idledown_walk(void *ignore)
6410Sstevel@tonic-gate {
6420Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
6430Sstevel@tonic-gate 	    "off\n", (ulong_t)pmstp->pm_idledown_id));
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6460Sstevel@tonic-gate 	pmstp->pm_idledown_id = 0;
6470Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate /*
6530Sstevel@tonic-gate  * pm_timeout_idledown - keep idledown effect for 10 seconds.
6540Sstevel@tonic-gate  *
6550Sstevel@tonic-gate  * Return 0 if another competing caller scheduled idledown timeout,
6560Sstevel@tonic-gate  * otherwise, return idledown timeout_id.
6570Sstevel@tonic-gate  */
6580Sstevel@tonic-gate static timeout_id_t
6590Sstevel@tonic-gate pm_timeout_idledown(void)
6600Sstevel@tonic-gate {
6610Sstevel@tonic-gate 	timeout_id_t	to_id;
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	/*
6640Sstevel@tonic-gate 	 * Keep idle-down in effect for either 10 seconds
6650Sstevel@tonic-gate 	 * or length of a scan interval, which ever is greater.
6660Sstevel@tonic-gate 	 */
6670Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6680Sstevel@tonic-gate 	if (pmstp->pm_idledown_id != 0) {
6690Sstevel@tonic-gate 		to_id = pmstp->pm_idledown_id;
6700Sstevel@tonic-gate 		pmstp->pm_idledown_id = 0;
6710Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6720Sstevel@tonic-gate 		(void) untimeout(to_id);
6730Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6740Sstevel@tonic-gate 		if (pmstp->pm_idledown_id != 0) {
6750Sstevel@tonic-gate 			PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
6760Sstevel@tonic-gate 			    "another caller got it, idledown_id(%lx)!\n",
6770Sstevel@tonic-gate 			    (ulong_t)pmstp->pm_idledown_id))
6780Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
6790Sstevel@tonic-gate 			return (0);
6800Sstevel@tonic-gate 		}
6810Sstevel@tonic-gate 	}
6820Sstevel@tonic-gate 	pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
6830Sstevel@tonic-gate 	    PM_IDLEDOWN_TIME * hz);
6840Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
6850Sstevel@tonic-gate 	    (ulong_t)pmstp->pm_idledown_id))
6860Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	return (pmstp->pm_idledown_id);
6890Sstevel@tonic-gate }
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate static int
6920Sstevel@tonic-gate pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
6930Sstevel@tonic-gate 	struct pollhead **phpp)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate 	extern struct pollhead pm_pollhead;	/* common/os/sunpm.c */
6960Sstevel@tonic-gate 	int	clone;
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
6990Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
7000Sstevel@tonic-gate 	if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
7010Sstevel@tonic-gate 		*reventsp |= (POLLIN | POLLRDNORM);
7020Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
7030Sstevel@tonic-gate 	} else {
7040Sstevel@tonic-gate 		*reventsp = 0;
7050Sstevel@tonic-gate 		if (!anyyet) {
7060Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
7070Sstevel@tonic-gate 			*phpp = &pm_pollhead;
7080Sstevel@tonic-gate 		}
7090Sstevel@tonic-gate #ifdef DEBUG
7100Sstevel@tonic-gate 		else {
7110Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate #endif
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 	return (0);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate /*
7190Sstevel@tonic-gate  * called by pm_dicard_entries to free up the memory. It also decrements
7200Sstevel@tonic-gate  * pm_poll_cnt, if direct is non zero.
7210Sstevel@tonic-gate  */
7220Sstevel@tonic-gate static void
7230Sstevel@tonic-gate pm_free_entries(psce_t *pscep, int clone, int direct)
7240Sstevel@tonic-gate {
7250Sstevel@tonic-gate 	pm_state_change_t	*p;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	if (pscep) {
7280Sstevel@tonic-gate 		p = pscep->psce_out;
7290Sstevel@tonic-gate 		while (p->size) {
7300Sstevel@tonic-gate 			if (direct) {
7310Sstevel@tonic-gate 				PMD(PMD_IOCTL, ("ioctl: discard: "
7320Sstevel@tonic-gate 				    "pm_poll_cnt[%d] is %d before "
7330Sstevel@tonic-gate 				    "ASSERT\n", clone,
7340Sstevel@tonic-gate 				    pm_poll_cnt[clone]))
7350Sstevel@tonic-gate 				ASSERT(pm_poll_cnt[clone]);
7360Sstevel@tonic-gate 				pm_poll_cnt[clone]--;
7370Sstevel@tonic-gate 			}
7380Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
7390Sstevel@tonic-gate 			p->size = 0;
7400Sstevel@tonic-gate 			if (p == pscep->psce_last)
7410Sstevel@tonic-gate 				p = pscep->psce_first;
7420Sstevel@tonic-gate 			else
7430Sstevel@tonic-gate 				p++;
7440Sstevel@tonic-gate 		}
7450Sstevel@tonic-gate 		pscep->psce_out = pscep->psce_first;
7460Sstevel@tonic-gate 		pscep->psce_in = pscep->psce_first;
7470Sstevel@tonic-gate 		mutex_exit(&pscep->psce_lock);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate /*
7520Sstevel@tonic-gate  * Discard entries for this clone. Calls pm_free_entries to free up memory.
7530Sstevel@tonic-gate  */
7540Sstevel@tonic-gate static void
7550Sstevel@tonic-gate pm_discard_entries(int clone)
7560Sstevel@tonic-gate {
7570Sstevel@tonic-gate 	psce_t	*pscep;
7580Sstevel@tonic-gate 	int			direct = 0;
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
7610Sstevel@tonic-gate 	if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
7620Sstevel@tonic-gate 		direct = 1;
7630Sstevel@tonic-gate 	pm_free_entries(pscep, clone, direct);
7640Sstevel@tonic-gate 	pscep = pm_psc_clone_to_interest(clone);
7650Sstevel@tonic-gate 	pm_free_entries(pscep, clone, 0);
7660Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate 
7693028Smh27603 
7703028Smh27603 static void
7713028Smh27603 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
7720Sstevel@tonic-gate {
7730Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
7740Sstevel@tonic-gate 		switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
7750Sstevel@tonic-gate 		case PMC_DEF_THRESH:
7763028Smh27603 		case PMC_CPU_THRESH:
7773028Smh27603 			PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
7780Sstevel@tonic-gate 			    "%s@%s(%s#%d) default thresh to 0t%d\n",
7793028Smh27603 			    PM_DEVICE(dip), thresh))
7803028Smh27603 			pm_set_device_threshold(dip, thresh, flag);
7810Sstevel@tonic-gate 			break;
7820Sstevel@tonic-gate 		default:
7830Sstevel@tonic-gate 			break;
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 	}
7863028Smh27603 }
7870Sstevel@tonic-gate 
7883028Smh27603 static int
7893028Smh27603 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
7903028Smh27603 {
7913028Smh27603 	int cmd = *((int *)arg);
7923028Smh27603 
7933028Smh27603 	if (!PM_GET_PM_INFO(dip))
7943028Smh27603 		return (DDI_WALK_CONTINUE);
7953028Smh27603 
7963028Smh27603 	switch (cmd) {
7973028Smh27603 	case PM_SET_SYSTEM_THRESHOLD:
7983028Smh27603 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
7993028Smh27603 			break;
8003028Smh27603 		pm_set_idle_threshold(dip, pm_system_idle_threshold,
8013028Smh27603 		    PMC_DEF_THRESH);
8020Sstevel@tonic-gate 		pm_rescan(dip);
8033028Smh27603 		break;
8043028Smh27603 	case PM_SET_CPU_THRESHOLD:
8053028Smh27603 		if (!PM_ISCPU(dip))
8063028Smh27603 			break;
8073028Smh27603 		pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
8083028Smh27603 		    PMC_CPU_THRESH);
8093028Smh27603 		pm_rescan(dip);
8103028Smh27603 		break;
8113028Smh27603 	}
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
8140Sstevel@tonic-gate }
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate /*ARGSUSED*/
8170Sstevel@tonic-gate static int
8180Sstevel@tonic-gate pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	dev_t	dev;
8210Sstevel@tonic-gate 	int	instance;
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	switch (infocmd) {
8240Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
8250Sstevel@tonic-gate 		if (pmstp->pm_instance == -1)
8260Sstevel@tonic-gate 			return (DDI_FAILURE);
8270Sstevel@tonic-gate 		*result = pmstp->pm_dip;
8280Sstevel@tonic-gate 		return (DDI_SUCCESS);
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8310Sstevel@tonic-gate 		dev = (dev_t)arg;
8320Sstevel@tonic-gate 		instance = getminor(dev) >> 8;
8330Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8340Sstevel@tonic-gate 		return (DDI_SUCCESS);
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	default:
8370Sstevel@tonic-gate 		return (DDI_FAILURE);
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate /*ARGSUSED1*/
8430Sstevel@tonic-gate static int
8440Sstevel@tonic-gate pm_open(dev_t *devp, 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 	mutex_enter(&pm_clone_lock);
8520Sstevel@tonic-gate 	for (clone = 1; clone < PM_MAX_CLONE; clone++)
8530Sstevel@tonic-gate 		if (!pmstp->pm_clones[clone])
8540Sstevel@tonic-gate 			break;
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	if (clone == PM_MAX_CLONE) {
8570Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
8580Sstevel@tonic-gate 		return (ENXIO);
8590Sstevel@tonic-gate 	}
8600Sstevel@tonic-gate 	pmstp->pm_cred[clone] = cr;
8610Sstevel@tonic-gate 	crhold(cr);
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
8640Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 1;
8650Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 	return (0);
8680Sstevel@tonic-gate }
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate /*ARGSUSED1*/
8710Sstevel@tonic-gate static int
8720Sstevel@tonic-gate pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
8730Sstevel@tonic-gate {
8740Sstevel@tonic-gate 	int clone;
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8770Sstevel@tonic-gate 		return (EINVAL);
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
8800Sstevel@tonic-gate 	PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
8810Sstevel@tonic-gate 	    clone))
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	/*
8840Sstevel@tonic-gate 	 * Walk the entire device tree to find the corresponding
8850Sstevel@tonic-gate 	 * device and operate on it.
8860Sstevel@tonic-gate 	 */
8870Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
8880Sstevel@tonic-gate 	    (void *) &clone);
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	crfree(pmstp->pm_cred[clone]);
8910Sstevel@tonic-gate 	pmstp->pm_cred[clone] = 0;
8920Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 0;
8930Sstevel@tonic-gate 	pm_discard_entries(clone);
8940Sstevel@tonic-gate 	ASSERT(pm_poll_cnt[clone] == 0);
8950Sstevel@tonic-gate 	pm_deregister_watcher(clone, NULL);
8960Sstevel@tonic-gate 	return (0);
8970Sstevel@tonic-gate }
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate /*ARGSUSED*/
9000Sstevel@tonic-gate static int
9010Sstevel@tonic-gate pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
9020Sstevel@tonic-gate {
9030Sstevel@tonic-gate 	struct pm_cmd_info *pc_info(int);
9040Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
9050Sstevel@tonic-gate 	pm_req_t	req;
9060Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
9070Sstevel@tonic-gate 	pm_info_t	*info = NULL;
9080Sstevel@tonic-gate 	int		clone;
9090Sstevel@tonic-gate 	char		*cmdstr = pm_decode_cmd(cmd);
9100Sstevel@tonic-gate 	/*
9110Sstevel@tonic-gate 	 * To keep devinfo nodes from going away while we're holding a
9120Sstevel@tonic-gate 	 * pointer to their dip, pm_name_to_dip() optionally holds
9130Sstevel@tonic-gate 	 * the devinfo node.  If we've done that, we set dipheld
9140Sstevel@tonic-gate 	 * so we know at the end of the ioctl processing to release the
9150Sstevel@tonic-gate 	 * node again.
9160Sstevel@tonic-gate 	 */
9170Sstevel@tonic-gate 	int		dipheld = 0;
9180Sstevel@tonic-gate 	int		icount = 0;
9190Sstevel@tonic-gate 	int		i;
9200Sstevel@tonic-gate 	int		comps;
9210Sstevel@tonic-gate 	size_t		lencopied;
9220Sstevel@tonic-gate 	int		ret = ENOTTY;
9230Sstevel@tonic-gate 	int		curpower;
9240Sstevel@tonic-gate 	char		who[MAXNAMELEN];
9250Sstevel@tonic-gate 	size_t		wholen;			/* copyinstr length */
9260Sstevel@tonic-gate 	size_t		deplen = MAXNAMELEN;
9270Sstevel@tonic-gate 	char		*dep, i_dep_buf[MAXNAMELEN];
9285295Srandyf 	char		pathbuf[MAXNAMELEN];
9290Sstevel@tonic-gate 	struct pm_component *cp;
9300Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9310Sstevel@tonic-gate 	pm_state_change32_t		*pscp32;
9320Sstevel@tonic-gate 	pm_state_change32_t		psc32;
9335295Srandyf 	pm_searchargs32_t		psa32;
9340Sstevel@tonic-gate 	size_t				copysize32;
9350Sstevel@tonic-gate #endif
9360Sstevel@tonic-gate 	pm_state_change_t		*pscp;
9370Sstevel@tonic-gate 	pm_state_change_t		psc;
9385295Srandyf 	pm_searchargs_t		psa;
9395295Srandyf 	char		listname[MAXCOPYBUF];
9405295Srandyf 	char		manufacturer[MAXCOPYBUF];
9415295Srandyf 	char		product[MAXCOPYBUF];
9420Sstevel@tonic-gate 	size_t		copysize;
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate #ifdef DEBUG
9470Sstevel@tonic-gate 	if (cmd == 666) {
9480Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), print_info, NULL);
9490Sstevel@tonic-gate 		return (0);
9500Sstevel@tonic-gate 	}
9510Sstevel@tonic-gate 	ret = 0x0badcafe;			/* sanity checking */
9520Sstevel@tonic-gate 	pm_cmd = cmd;				/* for ASSERT debugging */
9530Sstevel@tonic-gate 	pm_cmd_string = cmdstr;	/* for ASSERT debugging */
9540Sstevel@tonic-gate #endif
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	if (pcip == NULL) {
9580Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
9590Sstevel@tonic-gate 		return (ENOTTY);
9600Sstevel@tonic-gate 	}
9610Sstevel@tonic-gate 	if (pcip == NULL || pcip->supported == 0) {
9620Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
9630Sstevel@tonic-gate 		    pcip->name))
9640Sstevel@tonic-gate 		return (ENOTTY);
9650Sstevel@tonic-gate 	}
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	wholen = 0;
9680Sstevel@tonic-gate 	dep = i_dep_buf;
9690Sstevel@tonic-gate 	i_dep_buf[0] = 0;
9700Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
9710Sstevel@tonic-gate 	if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
9720Sstevel@tonic-gate 		ret = EPERM;
9730Sstevel@tonic-gate 		return (ret);
9740Sstevel@tonic-gate 	}
9750Sstevel@tonic-gate 	switch (pcip->str_type) {
9760Sstevel@tonic-gate 	case PM_REQ:
9775295Srandyf 	{
9780Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9790Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9800Sstevel@tonic-gate 			pm_req32_t	req32;
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &req32,
9830Sstevel@tonic-gate 			    sizeof (req32), mode) != 0) {
9840Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
9850Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
9860Sstevel@tonic-gate 				ret = EFAULT;
9870Sstevel@tonic-gate 				break;
9880Sstevel@tonic-gate 			}
9890Sstevel@tonic-gate 			req.component = req32.component;
9900Sstevel@tonic-gate 			req.value = req32.value;
9910Sstevel@tonic-gate 			req.datasize = req32.datasize;
9920Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
9930Sstevel@tonic-gate 				ret = copyinstr((char *)(uintptr_t)
9940Sstevel@tonic-gate 				    req32.physpath, who, MAXNAMELEN, &wholen);
9950Sstevel@tonic-gate 				if (ret) {
9960Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9970Sstevel@tonic-gate 					    "copyinstr fails returning %d\n",
9980Sstevel@tonic-gate 					    cmdstr, ret))
9990Sstevel@tonic-gate 					break;
10000Sstevel@tonic-gate 				}
10010Sstevel@tonic-gate 				req.physpath = who;
10025295Srandyf 				PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
10035295Srandyf 				    cmdstr, req.physpath))
10040Sstevel@tonic-gate 			}
10050Sstevel@tonic-gate 			if (pcip->inargs & INDATA) {
10060Sstevel@tonic-gate 				req.data = (void *)(uintptr_t)req32.data;
10070Sstevel@tonic-gate 				req.datasize = req32.datasize;
10080Sstevel@tonic-gate 			} else {
10090Sstevel@tonic-gate 				req.data = NULL;
10100Sstevel@tonic-gate 				req.datasize = 0;
10110Sstevel@tonic-gate 			}
10120Sstevel@tonic-gate 			switch (pcip->diptype) {
10130Sstevel@tonic-gate 			case DIP:
10140Sstevel@tonic-gate 				if (!(dip =
10150Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
10160Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
10170Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
10180Sstevel@tonic-gate 					    cmdstr, req.physpath))
10190Sstevel@tonic-gate 					return (ENODEV);
10200Sstevel@tonic-gate 				}
10210Sstevel@tonic-gate 				ASSERT(!dipheld);
10220Sstevel@tonic-gate 				dipheld++;
10230Sstevel@tonic-gate 				break;
10240Sstevel@tonic-gate 			case NODIP:
10250Sstevel@tonic-gate 				break;
10260Sstevel@tonic-gate 			default:
10270Sstevel@tonic-gate 				/*
10280Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
10290Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
10300Sstevel@tonic-gate 				 */
10310Sstevel@tonic-gate #ifdef	DEBUG
10320Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
10330Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
10340Sstevel@tonic-gate #endif
10350Sstevel@tonic-gate 				ASSERT(0);
10360Sstevel@tonic-gate 				return (EIO);
10370Sstevel@tonic-gate 			}
10380Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
10390Sstevel@tonic-gate 				int32_t int32buf;
10400Sstevel@tonic-gate 				int32_t *i32p;
10410Sstevel@tonic-gate 				int *ip;
10420Sstevel@tonic-gate 				icount = req32.datasize / sizeof (int32_t);
10430Sstevel@tonic-gate 				if (icount <= 0) {
10440Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
10450Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
10460Sstevel@tonic-gate 					ret = EFAULT;
10470Sstevel@tonic-gate 					break;
10480Sstevel@tonic-gate 				}
10490Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
10500Sstevel@tonic-gate 				req.datasize = icount * sizeof (int);
10510Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
10520Sstevel@tonic-gate 				ip = req.data;
10530Sstevel@tonic-gate 				ret = 0;
10540Sstevel@tonic-gate 				for (i = 0,
10550Sstevel@tonic-gate 				    i32p = (int32_t *)(uintptr_t)req32.data;
10560Sstevel@tonic-gate 				    i < icount; i++, i32p++) {
10570Sstevel@tonic-gate 					if (ddi_copyin((void *)i32p, &int32buf,
10580Sstevel@tonic-gate 					    sizeof (int32_t), mode)) {
10590Sstevel@tonic-gate 						kmem_free(req.data,
10600Sstevel@tonic-gate 						    req.datasize);
10610Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10620Sstevel@tonic-gate 						    "entry %d EFAULT\n",
10630Sstevel@tonic-gate 						    cmdstr, i))
10640Sstevel@tonic-gate 						ret = EFAULT;
10650Sstevel@tonic-gate 						break;
10660Sstevel@tonic-gate 					}
10670Sstevel@tonic-gate 					*ip++ = (int)int32buf;
10680Sstevel@tonic-gate 				}
10690Sstevel@tonic-gate 				if (ret)
10700Sstevel@tonic-gate 					break;
10710Sstevel@tonic-gate 			}
10720Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
10730Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
10740Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
10750Sstevel@tonic-gate 				if (req32.data != NULL) {
10760Sstevel@tonic-gate 					if (copyinstr((void *)(uintptr_t)
10775295Srandyf 					    req32.data, dep, deplen, NULL)) {
10780Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10790Sstevel@tonic-gate 						    "0x%p dep size %lx, EFAULT"
10800Sstevel@tonic-gate 						    "\n", cmdstr,
10810Sstevel@tonic-gate 						    (void *)req.data, deplen))
10820Sstevel@tonic-gate 						ret = EFAULT;
10830Sstevel@tonic-gate 						break;
10840Sstevel@tonic-gate 					}
10850Sstevel@tonic-gate #ifdef DEBUG
10860Sstevel@tonic-gate 					else {
10870Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
10880Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
10890Sstevel@tonic-gate 					}
10900Sstevel@tonic-gate #endif
10910Sstevel@tonic-gate 				} else {
10920Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
10930Sstevel@tonic-gate 					    "dependent\n", cmdstr))
10940Sstevel@tonic-gate 					ret = EINVAL;
10950Sstevel@tonic-gate 					break;
10960Sstevel@tonic-gate 				}
10970Sstevel@tonic-gate 			}
10980Sstevel@tonic-gate 		} else
10990Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
11000Sstevel@tonic-gate 		{
11010Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg,
11020Sstevel@tonic-gate 			    &req, sizeof (req), mode) != 0) {
11030Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11040Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
11050Sstevel@tonic-gate 				ret = EFAULT;
11060Sstevel@tonic-gate 				break;
11070Sstevel@tonic-gate 			}
11080Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
11090Sstevel@tonic-gate 				ret = copyinstr((char *)req.physpath, who,
11100Sstevel@tonic-gate 				    MAXNAMELEN, &wholen);
11110Sstevel@tonic-gate 				if (ret) {
11120Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s copyinstr"
11130Sstevel@tonic-gate 					    " fails returning %d\n", cmdstr,
11140Sstevel@tonic-gate 					    ret))
11150Sstevel@tonic-gate 					break;
11160Sstevel@tonic-gate 				}
11170Sstevel@tonic-gate 				req.physpath = who;
11185295Srandyf 				PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
11195295Srandyf 				    cmdstr, req.physpath))
11200Sstevel@tonic-gate 			}
11210Sstevel@tonic-gate 			if (!(pcip->inargs & INDATA)) {
11220Sstevel@tonic-gate 				req.data = NULL;
11230Sstevel@tonic-gate 				req.datasize = 0;
11240Sstevel@tonic-gate 			}
11250Sstevel@tonic-gate 			switch (pcip->diptype) {
11260Sstevel@tonic-gate 			case DIP:
11270Sstevel@tonic-gate 				if (!(dip =
11280Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
11290Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
11300Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
11310Sstevel@tonic-gate 					    cmdstr, req.physpath))
11320Sstevel@tonic-gate 					return (ENODEV);
11330Sstevel@tonic-gate 				}
11340Sstevel@tonic-gate 				ASSERT(!dipheld);
11350Sstevel@tonic-gate 				dipheld++;
11360Sstevel@tonic-gate 				break;
11370Sstevel@tonic-gate 			case NODIP:
11380Sstevel@tonic-gate 				break;
11390Sstevel@tonic-gate 			default:
11400Sstevel@tonic-gate 				/*
11410Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
11420Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
11430Sstevel@tonic-gate 				 */
11440Sstevel@tonic-gate #ifdef	DEBUG
11450Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
11460Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
11470Sstevel@tonic-gate #endif
11480Sstevel@tonic-gate 				ASSERT(0);
11490Sstevel@tonic-gate 				return (EIO);
11500Sstevel@tonic-gate 			}
11510Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
11520Sstevel@tonic-gate 				int *ip;
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
11550Sstevel@tonic-gate 				ip = req.data;
11560Sstevel@tonic-gate 				icount = req.datasize / sizeof (int);
11570Sstevel@tonic-gate 				if (icount <= 0) {
11580Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
11590Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
11600Sstevel@tonic-gate 					ret = EFAULT;
11610Sstevel@tonic-gate 					break;
11620Sstevel@tonic-gate 				}
11630Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
11640Sstevel@tonic-gate 				if (ddi_copyin((caddr_t)ip, req.data,
11650Sstevel@tonic-gate 				    req.datasize, mode) != 0) {
11660Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11670Sstevel@tonic-gate 					    "EFAULT\n\n", cmdstr))
11680Sstevel@tonic-gate 					ret = EFAULT;
11690Sstevel@tonic-gate 					break;
11700Sstevel@tonic-gate 				}
11710Sstevel@tonic-gate 			}
11720Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
11730Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
11740Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
11750Sstevel@tonic-gate 				if (req.data != NULL) {
11760Sstevel@tonic-gate 					if (copyinstr((caddr_t)req.data,
11775295Srandyf 					    dep, deplen, NULL)) {
11780Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
11790Sstevel@tonic-gate 						    "0x%p dep size %lu, "
11800Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
11810Sstevel@tonic-gate 						    (void *)req.data, deplen))
11820Sstevel@tonic-gate 						ret = EFAULT;
11830Sstevel@tonic-gate 						break;
11840Sstevel@tonic-gate 					}
11850Sstevel@tonic-gate #ifdef DEBUG
11860Sstevel@tonic-gate 					else {
11870Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
11880Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
11890Sstevel@tonic-gate 					}
11900Sstevel@tonic-gate #endif
11910Sstevel@tonic-gate 				} else {
11920Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
11930Sstevel@tonic-gate 					    "dependent\n", cmdstr))
11940Sstevel@tonic-gate 					ret = EINVAL;
11950Sstevel@tonic-gate 					break;
11960Sstevel@tonic-gate 				}
11970Sstevel@tonic-gate 			}
11980Sstevel@tonic-gate 		}
11990Sstevel@tonic-gate 		/*
12000Sstevel@tonic-gate 		 * Now we've got all the args in for the commands that
12010Sstevel@tonic-gate 		 * use the new pm_req struct.
12020Sstevel@tonic-gate 		 */
12030Sstevel@tonic-gate 		switch (cmd) {
12040Sstevel@tonic-gate 		case PM_REPARSE_PM_PROPS:
12050Sstevel@tonic-gate 		{
12060Sstevel@tonic-gate 			struct dev_ops	*drv;
12070Sstevel@tonic-gate 			struct cb_ops	*cb;
12080Sstevel@tonic-gate 			void		*propval;
12090Sstevel@tonic-gate 			int length;
12100Sstevel@tonic-gate 			/*
12110Sstevel@tonic-gate 			 * This ioctl is provided only for the ddivs pm test.
12120Sstevel@tonic-gate 			 * We only do it to a driver which explicitly allows
12130Sstevel@tonic-gate 			 * us to do so by exporting a pm-reparse-ok property.
12140Sstevel@tonic-gate 			 * We only care whether the property exists or not.
12150Sstevel@tonic-gate 			 */
12160Sstevel@tonic-gate 			if ((drv = ddi_get_driver(dip)) == NULL) {
12170Sstevel@tonic-gate 				ret = EINVAL;
12180Sstevel@tonic-gate 				break;
12190Sstevel@tonic-gate 			}
12200Sstevel@tonic-gate 			if ((cb = drv->devo_cb_ops) != NULL) {
12210Sstevel@tonic-gate 				if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
12220Sstevel@tonic-gate 				    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12230Sstevel@tonic-gate 				    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12240Sstevel@tonic-gate 				    "pm-reparse-ok", (caddr_t)&propval,
12250Sstevel@tonic-gate 				    &length) != DDI_SUCCESS) {
12260Sstevel@tonic-gate 					ret = EINVAL;
12270Sstevel@tonic-gate 					break;
12280Sstevel@tonic-gate 				}
12290Sstevel@tonic-gate 			} else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
12300Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12310Sstevel@tonic-gate 			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12320Sstevel@tonic-gate 			    "pm-reparse-ok", (caddr_t)&propval,
12330Sstevel@tonic-gate 			    &length) != DDI_SUCCESS) {
12340Sstevel@tonic-gate 				ret = EINVAL;
12350Sstevel@tonic-gate 				break;
12360Sstevel@tonic-gate 			}
12370Sstevel@tonic-gate 			kmem_free(propval, length);
12380Sstevel@tonic-gate 			ret =  e_new_pm_props(dip);
12390Sstevel@tonic-gate 			break;
12400Sstevel@tonic-gate 		}
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD:
12435295Srandyf 		{
12440Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12450Sstevel@tonic-gate 			if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
12460Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12470Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
12480Sstevel@tonic-gate 				    cmdstr))
12490Sstevel@tonic-gate 				ret = ENODEV;
12500Sstevel@tonic-gate 				break;
12510Sstevel@tonic-gate 			}
12520Sstevel@tonic-gate 			*rval_p = DEVI(dip)->devi_pm_dev_thresh;
12530Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12540Sstevel@tonic-gate 			ret = 0;
12550Sstevel@tonic-gate 			break;
12565295Srandyf 		}
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 		case PM_DIRECT_PM:
12590Sstevel@tonic-gate 		{
12600Sstevel@tonic-gate 			int has_dep;
12610Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12620Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12630Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12640Sstevel@tonic-gate 				ret = ENODEV;
12650Sstevel@tonic-gate 				break;
12660Sstevel@tonic-gate 			}
12670Sstevel@tonic-gate 			/*
12680Sstevel@tonic-gate 			 * Check to see if we are there is a dependency on
12690Sstevel@tonic-gate 			 * this kept device, if so, return EBUSY.
12700Sstevel@tonic-gate 			 */
12710Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
12720Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
12730Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
12740Sstevel@tonic-gate 			if (has_dep) {
12750Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
12760Sstevel@tonic-gate 				    cmdstr))
12770Sstevel@tonic-gate 				ret = EBUSY;
12780Sstevel@tonic-gate 				break;
12790Sstevel@tonic-gate 			}
12800Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12810Sstevel@tonic-gate 			if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
12820Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12830Sstevel@tonic-gate 				    "%s@%s(%s#%d): EBUSY\n", cmdstr,
12840Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12850Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12860Sstevel@tonic-gate 				ret = EBUSY;
12870Sstevel@tonic-gate 				break;
12880Sstevel@tonic-gate 			}
12890Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_DIRECT;
12900Sstevel@tonic-gate 			info->pmi_clone = clone;
12910Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12920Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
12930Sstevel@tonic-gate 			    cmdstr, (void *)info, clone))
12940Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
12950Sstevel@tonic-gate 			pm_register_watcher(clone, dip);
12960Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
12970Sstevel@tonic-gate 			ret = 0;
12980Sstevel@tonic-gate 			break;
12990Sstevel@tonic-gate 		}
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 		case PM_RELEASE_DIRECT_PM:
13020Sstevel@tonic-gate 		{
13030Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13040Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13050Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13060Sstevel@tonic-gate 				ret = ENODEV;
13070Sstevel@tonic-gate 				break;
13080Sstevel@tonic-gate 			}
13090Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13100Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13110Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13120Sstevel@tonic-gate 				    "%s@%s(%s#%d) EINVAL\n", cmdstr,
13130Sstevel@tonic-gate 				    PM_DEVICE(dip)))
13140Sstevel@tonic-gate 				ret = EINVAL;
13150Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
13160Sstevel@tonic-gate 				break;
13170Sstevel@tonic-gate 			}
13180Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13190Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_DIRECT;
13200Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13210Sstevel@tonic-gate 			/* Bring ourselves up if there is a keeper. */
13220Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
13230Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
13240Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
13250Sstevel@tonic-gate 			pm_discard_entries(clone);
13260Sstevel@tonic-gate 			pm_deregister_watcher(clone, dip);
13270Sstevel@tonic-gate 			/*
13280Sstevel@tonic-gate 			 * Now we could let the other threads that are
13290Sstevel@tonic-gate 			 * trying to do a DIRECT_PM thru
13300Sstevel@tonic-gate 			 */
13310Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13320Sstevel@tonic-gate 			info->pmi_clone = 0;
13330Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13340Sstevel@tonic-gate 			pm_proceed(dip, PMP_RELEASE, -1, -1);
13350Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13360Sstevel@tonic-gate 			    cmdstr))
13370Sstevel@tonic-gate 			pm_rescan(dip);
13380Sstevel@tonic-gate 			ret = 0;
13390Sstevel@tonic-gate 			break;
13400Sstevel@tonic-gate 		}
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate 		case PM_SET_CURRENT_POWER:
13430Sstevel@tonic-gate 		{
13440Sstevel@tonic-gate 			int comp = req.component;
13450Sstevel@tonic-gate 			int  value = req.value;
13460Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
13470Sstevel@tonic-gate 			    "%d\n", cmdstr, req.physpath, comp, value))
13480Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, NULL) ||
13490Sstevel@tonic-gate 			    !e_pm_valid_power(dip, comp, value)) {
13500Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13510Sstevel@tonic-gate 				    "physpath=%s, comp=%d, level=%d, fails\n",
13520Sstevel@tonic-gate 				    cmdstr, req.physpath, comp, value))
13530Sstevel@tonic-gate 				ret = EINVAL;
13540Sstevel@tonic-gate 				break;
13550Sstevel@tonic-gate 			}
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13580Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13590Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13600Sstevel@tonic-gate 				ret = ENODEV;
13610Sstevel@tonic-gate 				break;
13620Sstevel@tonic-gate 			}
13630Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13640Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13650Sstevel@tonic-gate 				    "(not owner) %s fails; clone %d, owner %d"
13660Sstevel@tonic-gate 				    "\n", cmdstr, req.physpath, clone,
13670Sstevel@tonic-gate 				    info->pmi_clone))
13680Sstevel@tonic-gate 				ret = EINVAL;
13690Sstevel@tonic-gate 				break;
13700Sstevel@tonic-gate 			}
13710Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 			if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
13740Sstevel@tonic-gate 			    PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
13750Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13760Sstevel@tonic-gate 				    "pm_set_power for %s fails, errno=%d\n",
13770Sstevel@tonic-gate 				    cmdstr, req.physpath, ret))
13780Sstevel@tonic-gate 				break;
13790Sstevel@tonic-gate 			}
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 			pm_proceed(dip, PMP_SETPOWER, comp, value);
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 			/*
13840Sstevel@tonic-gate 			 * Power down all idle components if console framebuffer
13850Sstevel@tonic-gate 			 * is powered off.
13860Sstevel@tonic-gate 			 */
13870Sstevel@tonic-gate 			if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
13880Sstevel@tonic-gate 			    pm_default_idle_threshold)) {
13890Sstevel@tonic-gate 				dev_info_t	*root = ddi_root_node();
13900Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
13910Sstevel@tonic-gate 					if (comp == 0 && value == 0 &&
13920Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13930Sstevel@tonic-gate 						ddi_walk_devs(root,
13940Sstevel@tonic-gate 						    pm_start_idledown,
13950Sstevel@tonic-gate 						    (void *)PMID_CFB);
13960Sstevel@tonic-gate 					}
13970Sstevel@tonic-gate 				} else {
13980Sstevel@tonic-gate 					int count = 0;
13990Sstevel@tonic-gate 					for (i = 0; i < PM_NUMCMPTS(dip); i++) {
14000Sstevel@tonic-gate 						ret = pm_get_current_power(dip,
14010Sstevel@tonic-gate 						    i, &curpower);
14020Sstevel@tonic-gate 						if (ret == DDI_SUCCESS &&
14030Sstevel@tonic-gate 						    curpower == 0)
14040Sstevel@tonic-gate 							count++;
14050Sstevel@tonic-gate 					}
14060Sstevel@tonic-gate 					if ((count == PM_NUMCMPTS(dip)) &&
14070Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
14080Sstevel@tonic-gate 						ddi_walk_devs(root,
14090Sstevel@tonic-gate 						    pm_start_idledown,
14100Sstevel@tonic-gate 						    (void *)PMID_CFB);
14110Sstevel@tonic-gate 					}
14120Sstevel@tonic-gate 				}
14130Sstevel@tonic-gate 			}
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
14160Sstevel@tonic-gate 			    cmdstr))
14170Sstevel@tonic-gate 			pm_rescan(dip);
14180Sstevel@tonic-gate 			*rval_p = 0;
14190Sstevel@tonic-gate 			ret = 0;
14200Sstevel@tonic-gate 			break;
14210Sstevel@tonic-gate 		}
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 		case PM_GET_FULL_POWER:
14240Sstevel@tonic-gate 		{
14250Sstevel@tonic-gate 			int normal;
14260Sstevel@tonic-gate 			ASSERT(dip);
14270Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
14280Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component))
14290Sstevel@tonic-gate 			normal =  pm_get_normal_power(dip, req.component);
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 			if (normal == DDI_FAILURE) {
14320Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
14330Sstevel@tonic-gate 				    "returns EINVAL\n", cmdstr))
14340Sstevel@tonic-gate 				ret = EINVAL;
14350Sstevel@tonic-gate 				break;
14360Sstevel@tonic-gate 			}
14370Sstevel@tonic-gate 			*rval_p = normal;
14380Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
14390Sstevel@tonic-gate 			    cmdstr, normal))
14400Sstevel@tonic-gate 			ret = 0;
14410Sstevel@tonic-gate 			break;
14420Sstevel@tonic-gate 		}
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 		case PM_GET_CURRENT_POWER:
14455295Srandyf 		{
14460Sstevel@tonic-gate 			if (pm_get_current_power(dip, req.component,
14470Sstevel@tonic-gate 			    rval_p) != DDI_SUCCESS) {
14480Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
14490Sstevel@tonic-gate 				    "EINVAL\n", cmdstr))
14500Sstevel@tonic-gate 				ret = EINVAL;
14510Sstevel@tonic-gate 				break;
14520Sstevel@tonic-gate 			}
14530Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
14540Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component, *rval_p))
14550Sstevel@tonic-gate 			if (*rval_p == PM_LEVEL_UNKNOWN)
14560Sstevel@tonic-gate 				ret = EAGAIN;
14570Sstevel@tonic-gate 			else
14580Sstevel@tonic-gate 				ret = 0;
14590Sstevel@tonic-gate 			break;
14605295Srandyf 		}
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 		case PM_GET_TIME_IDLE:
14630Sstevel@tonic-gate 		{
14640Sstevel@tonic-gate 			time_t timestamp;
14650Sstevel@tonic-gate 			int comp = req.component;
14660Sstevel@tonic-gate 			pm_component_t *cp;
14670Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, &cp)) {
14680Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
14690Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
14700Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), comp,
14710Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
14720Sstevel@tonic-gate 				ret = EINVAL;
14730Sstevel@tonic-gate 				break;
14740Sstevel@tonic-gate 			}
14750Sstevel@tonic-gate 			timestamp = cp->pmc_timestamp;
14760Sstevel@tonic-gate 			if (timestamp) {
14770Sstevel@tonic-gate 				time_t now;
14780Sstevel@tonic-gate 				(void) drv_getparm(TIME, &now);
14790Sstevel@tonic-gate 				*rval_p = (now - timestamp);
14800Sstevel@tonic-gate 			} else {
14810Sstevel@tonic-gate 				*rval_p = 0;
14820Sstevel@tonic-gate 			}
14830Sstevel@tonic-gate 			ret = 0;
14840Sstevel@tonic-gate 			break;
14850Sstevel@tonic-gate 		}
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 		case PM_ADD_DEPENDENT:
14880Sstevel@tonic-gate 		{
14890Sstevel@tonic-gate 			dev_info_t	*kept_dip;
14900Sstevel@tonic-gate 
14910Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
14920Sstevel@tonic-gate 			    dep, req.physpath))
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 			/*
14950Sstevel@tonic-gate 			 * hold and install kept while processing dependency
14960Sstevel@tonic-gate 			 * keeper (in .physpath) has already been held.
14970Sstevel@tonic-gate 			 */
14980Sstevel@tonic-gate 			if (dep[0] == '\0') {
14990Sstevel@tonic-gate 				PMD(PMD_ERROR, ("kept NULL or null\n"))
15000Sstevel@tonic-gate 				ret = EINVAL;
15010Sstevel@tonic-gate 				break;
15020Sstevel@tonic-gate 			} else if ((kept_dip =
15030Sstevel@tonic-gate 			    pm_name_to_dip(dep, 1)) == NULL) {
15040Sstevel@tonic-gate 				PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
15050Sstevel@tonic-gate 				ret = ENODEV;
15060Sstevel@tonic-gate 				break;
15070Sstevel@tonic-gate 			} else if (kept_dip == dip) {
15080Sstevel@tonic-gate 				PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
15090Sstevel@tonic-gate 				    "self-dependency not allowed.\n",
15100Sstevel@tonic-gate 				    dep, (void *)kept_dip, req.physpath,
15110Sstevel@tonic-gate 				    (void *) dip))
15120Sstevel@tonic-gate 				PM_RELE(dip);	/* release "double" hold */
15130Sstevel@tonic-gate 				ret = EINVAL;
15140Sstevel@tonic-gate 				break;
15150Sstevel@tonic-gate 			}
15160Sstevel@tonic-gate 			ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 			/*
15190Sstevel@tonic-gate 			 * record dependency, then walk through device tree
15200Sstevel@tonic-gate 			 * independently on behalf of kept and keeper to
15210Sstevel@tonic-gate 			 * establish newly created dependency.
15220Sstevel@tonic-gate 			 */
15230Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
15240Sstevel@tonic-gate 			    req.physpath, dep, PM_DEP_WAIT, NULL, 0);
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 			/*
15270Sstevel@tonic-gate 			 * release kept after establishing dependency, keeper
15280Sstevel@tonic-gate 			 * is released as part of ioctl exit processing.
15290Sstevel@tonic-gate 			 */
15300Sstevel@tonic-gate 			PM_RELE(kept_dip);
15310Sstevel@tonic-gate 			*rval_p = 0;
15320Sstevel@tonic-gate 			ret = 0;
15330Sstevel@tonic-gate 			break;
15340Sstevel@tonic-gate 		}
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate 		case PM_ADD_DEPENDENT_PROPERTY:
15370Sstevel@tonic-gate 		{
15380Sstevel@tonic-gate 			char *keeper, *kept;
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 			if (dep[0] == '\0') {
15410Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
15420Sstevel@tonic-gate 				    "null\n", cmdstr))
15430Sstevel@tonic-gate 				ret = EINVAL;
15440Sstevel@tonic-gate 				break;
15450Sstevel@tonic-gate 			}
15460Sstevel@tonic-gate 			kept = dep;
15470Sstevel@tonic-gate 			keeper = req.physpath;
15480Sstevel@tonic-gate 			/*
15490Sstevel@tonic-gate 			 * record keeper - kept dependency, then walk through
15500Sstevel@tonic-gate 			 * device tree to find out all attached keeper, walk
15510Sstevel@tonic-gate 			 * through again to apply dependency to all the
15520Sstevel@tonic-gate 			 * potential kept.
15530Sstevel@tonic-gate 			 */
15540Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(
15550Sstevel@tonic-gate 			    PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
15560Sstevel@tonic-gate 			    PM_DEP_WAIT, NULL, 0);
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 			*rval_p = 0;
15590Sstevel@tonic-gate 			ret = 0;
15600Sstevel@tonic-gate 			break;
15610Sstevel@tonic-gate 		}
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 		case PM_SET_DEVICE_THRESHOLD:
15640Sstevel@tonic-gate 		{
15650Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
15660Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
15670Sstevel@tonic-gate 			int *tp;	/* threshold storage */
15680Sstevel@tonic-gate 			size_t size;
15690Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 			/*
15720Sstevel@tonic-gate 			 * The header struct plus one entry struct plus one
15730Sstevel@tonic-gate 			 * threshold plus the length of the string
15740Sstevel@tonic-gate 			 */
15750Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
15760Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * 1) +
15770Sstevel@tonic-gate 			    (1 * sizeof (int)) +
15780Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
15810Sstevel@tonic-gate 			rp->ptr_size = size;
15820Sstevel@tonic-gate 			rp->ptr_numcomps = 0;	/* means device threshold */
15830Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
15840Sstevel@tonic-gate 			rp->ptr_entries = ep;
15850Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
15860Sstevel@tonic-gate 			    (1 * sizeof (pm_pte_t)));
15870Sstevel@tonic-gate 			ep->pte_numthresh = 1;
15880Sstevel@tonic-gate 			ep->pte_thresh = tp;
15890Sstevel@tonic-gate 			*tp++ = req.value;
15900Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
15910Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
15920Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
15930Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
15940Sstevel@tonic-gate 			PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
15950Sstevel@tonic-gate 			    "%s\n", cmdstr, req.value, req.physpath))
15960Sstevel@tonic-gate 			pm_record_thresh(rp);
15970Sstevel@tonic-gate 			/*
15980Sstevel@tonic-gate 			 * Don't free rp, pm_record_thresh() keeps it.
15990Sstevel@tonic-gate 			 * We don't try to apply it ourselves because we'd need
16000Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
16010Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
16020Sstevel@tonic-gate 			 * we get here
16030Sstevel@tonic-gate 			 */
16040Sstevel@tonic-gate 			ASSERT(dip == NULL);
16050Sstevel@tonic-gate 			ret = 0;		/* can't fail now */
16060Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
16070Sstevel@tonic-gate 				break;
16080Sstevel@tonic-gate 			}
16090Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
16100Sstevel@tonic-gate 			PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
16110Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
16120Sstevel@tonic-gate 			PM_RELE(dip);
16130Sstevel@tonic-gate 			break;
16140Sstevel@tonic-gate 		}
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 		case PM_RESET_DEVICE_THRESHOLD:
16170Sstevel@tonic-gate 		{
16180Sstevel@tonic-gate 			/*
16190Sstevel@tonic-gate 			 * This only applies to a currently attached and power
16200Sstevel@tonic-gate 			 * managed node
16210Sstevel@tonic-gate 			 */
16220Sstevel@tonic-gate 			/*
16230Sstevel@tonic-gate 			 * We don't do this to old-style drivers
16240Sstevel@tonic-gate 			 */
16250Sstevel@tonic-gate 			info = PM_GET_PM_INFO(dip);
16260Sstevel@tonic-gate 			if (info == NULL) {
16270Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s not power "
16280Sstevel@tonic-gate 				    "managed\n", cmdstr, req.physpath))
16290Sstevel@tonic-gate 				ret = EINVAL;
16300Sstevel@tonic-gate 				break;
16310Sstevel@tonic-gate 			}
16320Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16330Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
16340Sstevel@tonic-gate 				    cmdstr, req.physpath))
16350Sstevel@tonic-gate 				ret = EINVAL;
16360Sstevel@tonic-gate 				break;
16370Sstevel@tonic-gate 			}
16380Sstevel@tonic-gate 			pm_unrecord_threshold(req.physpath);
16393028Smh27603 			if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
16403028Smh27603 				pm_set_device_threshold(dip,
16413028Smh27603 				    pm_cpu_idle_threshold, PMC_CPU_THRESH);
16423028Smh27603 			else
16433028Smh27603 				pm_set_device_threshold(dip,
16443028Smh27603 				    pm_system_idle_threshold, PMC_DEF_THRESH);
16450Sstevel@tonic-gate 			ret = 0;
16460Sstevel@tonic-gate 			break;
16470Sstevel@tonic-gate 		}
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 		case PM_GET_NUM_COMPONENTS:
16505295Srandyf 		{
16510Sstevel@tonic-gate 			ret = 0;
16520Sstevel@tonic-gate 			*rval_p = PM_NUMCMPTS(dip);
16530Sstevel@tonic-gate 			break;
16545295Srandyf 		}
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 		case PM_GET_DEVICE_TYPE:
16575295Srandyf 		{
16580Sstevel@tonic-gate 			ret = 0;
16590Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
16600Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
16610Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
16620Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
16630Sstevel@tonic-gate 				break;
16640Sstevel@tonic-gate 			}
16650Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16660Sstevel@tonic-gate 				*rval_p = PM_CREATE_COMPONENTS;
16670Sstevel@tonic-gate 			} else {
16680Sstevel@tonic-gate 				*rval_p = PM_AUTOPM;
16690Sstevel@tonic-gate 			}
16700Sstevel@tonic-gate 			break;
16715295Srandyf 		}
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 		case PM_SET_COMPONENT_THRESHOLDS:
16740Sstevel@tonic-gate 		{
16750Sstevel@tonic-gate 			int comps = 0;
16760Sstevel@tonic-gate 			int *end = (int *)req.data + icount;
16770Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
16780Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
16790Sstevel@tonic-gate 			int *tp;	/* threshold storage */
16800Sstevel@tonic-gate 			int *ip;
16810Sstevel@tonic-gate 			int j;
16820Sstevel@tonic-gate 			size_t size;
16830Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
16840Sstevel@tonic-gate 			extern int pm_valid_thresh(dev_info_t *,
16850Sstevel@tonic-gate 			    pm_thresh_rec_t *);
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 			for (ip = req.data; *ip; ip++) {
16880Sstevel@tonic-gate 				if (ip >= end) {
16890Sstevel@tonic-gate 					ret = EFAULT;
16900Sstevel@tonic-gate 					break;
16910Sstevel@tonic-gate 				}
16920Sstevel@tonic-gate 				comps++;
16930Sstevel@tonic-gate 				/* skip over indicated number of entries */
16940Sstevel@tonic-gate 				for (j = *ip; j; j--) {
16950Sstevel@tonic-gate 					if (++ip >= end) {
16960Sstevel@tonic-gate 						ret = EFAULT;
16970Sstevel@tonic-gate 						break;
16980Sstevel@tonic-gate 					}
16990Sstevel@tonic-gate 				}
17000Sstevel@tonic-gate 				if (ret)
17010Sstevel@tonic-gate 					break;
17020Sstevel@tonic-gate 			}
17030Sstevel@tonic-gate 			if (ret)
17040Sstevel@tonic-gate 				break;
17050Sstevel@tonic-gate 			if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
17060Sstevel@tonic-gate 				/* did not exactly fill buffer */
17070Sstevel@tonic-gate 				ret = EINVAL;
17080Sstevel@tonic-gate 				break;
17090Sstevel@tonic-gate 			}
17100Sstevel@tonic-gate 			if (comps == 0) {
17110Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
17120Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, req.physpath))
17130Sstevel@tonic-gate 				ret = EINVAL;
17140Sstevel@tonic-gate 				break;
17150Sstevel@tonic-gate 			}
17160Sstevel@tonic-gate 			/*
17170Sstevel@tonic-gate 			 * The header struct plus one entry struct per component
17180Sstevel@tonic-gate 			 * plus the size of the lists minus the counts
17190Sstevel@tonic-gate 			 * plus the length of the string
17200Sstevel@tonic-gate 			 */
17210Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
17220Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * comps) + req.datasize -
17230Sstevel@tonic-gate 			    ((comps + 1) * sizeof (int)) +
17240Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
17270Sstevel@tonic-gate 			rp->ptr_size = size;
17280Sstevel@tonic-gate 			rp->ptr_numcomps = comps;
17290Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
17300Sstevel@tonic-gate 			rp->ptr_entries = ep;
17310Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
17320Sstevel@tonic-gate 			    (comps * sizeof (pm_pte_t)));
17330Sstevel@tonic-gate 			for (ip = req.data; *ip; ep++) {
17340Sstevel@tonic-gate 				ep->pte_numthresh = *ip;
17350Sstevel@tonic-gate 				ep->pte_thresh = tp;
17360Sstevel@tonic-gate 				for (j = *ip++; j; j--) {
17370Sstevel@tonic-gate 					*tp++ = *ip++;
17380Sstevel@tonic-gate 				}
17390Sstevel@tonic-gate 			}
17400Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
17410Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
17420Sstevel@tonic-gate 			ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
17430Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
17440Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 			ASSERT(dip == NULL);
17470Sstevel@tonic-gate 			/*
17480Sstevel@tonic-gate 			 * If this is not a currently power managed node,
17490Sstevel@tonic-gate 			 * then we can't check for validity of the thresholds
17500Sstevel@tonic-gate 			 */
17510Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
17520Sstevel@tonic-gate 				/* don't free rp, pm_record_thresh uses it */
17530Sstevel@tonic-gate 				pm_record_thresh(rp);
17540Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
17550Sstevel@tonic-gate 				    "for %s failed\n", cmdstr, req.physpath))
17560Sstevel@tonic-gate 				ret = 0;
17570Sstevel@tonic-gate 				break;
17580Sstevel@tonic-gate 			}
17590Sstevel@tonic-gate 			ASSERT(!dipheld);
17600Sstevel@tonic-gate 			dipheld++;
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 			if (!pm_valid_thresh(dip, rp)) {
17630Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
17640Sstevel@tonic-gate 				    "for %s@%s(%s#%d)\n", cmdstr,
17650Sstevel@tonic-gate 				    PM_DEVICE(dip)))
17660Sstevel@tonic-gate 				kmem_free(rp, size);
17670Sstevel@tonic-gate 				ret = EINVAL;
17680Sstevel@tonic-gate 				break;
17690Sstevel@tonic-gate 			}
17700Sstevel@tonic-gate 			/*
17710Sstevel@tonic-gate 			 * We don't just apply it ourselves because we'd need
17720Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
17730Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
17740Sstevel@tonic-gate 			 * we get here
17750Sstevel@tonic-gate 			 */
17760Sstevel@tonic-gate 			pm_record_thresh(rp);
17770Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
17780Sstevel@tonic-gate 			ret = 0;
17790Sstevel@tonic-gate 			break;
17800Sstevel@tonic-gate 		}
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 		case PM_GET_COMPONENT_THRESHOLDS:
17830Sstevel@tonic-gate 		{
17840Sstevel@tonic-gate 			int musthave;
17850Sstevel@tonic-gate 			int numthresholds = 0;
17860Sstevel@tonic-gate 			int wordsize;
17870Sstevel@tonic-gate 			int numcomps;
17880Sstevel@tonic-gate 			caddr_t uaddr = req.data;	/* user address */
17890Sstevel@tonic-gate 			int val;	/* int value to be copied out */
17900Sstevel@tonic-gate 			int32_t val32;	/* int32 value to be copied out */
17910Sstevel@tonic-gate 			caddr_t vaddr;	/* address to copyout from */
17920Sstevel@tonic-gate 			int j;
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
17950Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
17960Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
17970Sstevel@tonic-gate 			} else
17980Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
17990Sstevel@tonic-gate 			{
18000Sstevel@tonic-gate 				wordsize = sizeof (int);
18010Sstevel@tonic-gate 			}
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 			ASSERT(dip);
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 			numcomps = PM_NUMCMPTS(dip);
18060Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
18070Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18080Sstevel@tonic-gate 				numthresholds += cp->pmc_comp.pmc_numlevels - 1;
18090Sstevel@tonic-gate 			}
18100Sstevel@tonic-gate 			musthave = (numthresholds + numcomps + 1) *  wordsize;
18110Sstevel@tonic-gate 			if (req.datasize < musthave) {
18120Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
18130Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
18140Sstevel@tonic-gate 				    musthave))
18150Sstevel@tonic-gate 				ret = EINVAL;
18160Sstevel@tonic-gate 				break;
18170Sstevel@tonic-gate 			}
18180Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
18190Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
18200Sstevel@tonic-gate 				int *thp;
18210Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18220Sstevel@tonic-gate 				thp = cp->pmc_comp.pmc_thresh;
18230Sstevel@tonic-gate 				/* first copyout the count */
18240Sstevel@tonic-gate 				if (wordsize == sizeof (int32_t)) {
18250Sstevel@tonic-gate 					val32 = cp->pmc_comp.pmc_numlevels - 1;
18260Sstevel@tonic-gate 					vaddr = (caddr_t)&val32;
18270Sstevel@tonic-gate 				} else {
18280Sstevel@tonic-gate 					val = cp->pmc_comp.pmc_numlevels - 1;
18290Sstevel@tonic-gate 					vaddr = (caddr_t)&val;
18300Sstevel@tonic-gate 				}
18310Sstevel@tonic-gate 				if (ddi_copyout(vaddr, (void *)uaddr,
18320Sstevel@tonic-gate 				    wordsize, mode) != 0) {
18330Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
18340Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
18350Sstevel@tonic-gate 					    "(%s#%d) vaddr %p EFAULT\n",
18360Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
18370Sstevel@tonic-gate 					    (void*)vaddr))
18380Sstevel@tonic-gate 					ret = EFAULT;
18390Sstevel@tonic-gate 					break;
18400Sstevel@tonic-gate 				}
18410Sstevel@tonic-gate 				vaddr = uaddr;
18420Sstevel@tonic-gate 				vaddr += wordsize;
18430Sstevel@tonic-gate 				uaddr = (caddr_t)vaddr;
18440Sstevel@tonic-gate 				/* then copyout each threshold value */
18450Sstevel@tonic-gate 				for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
18460Sstevel@tonic-gate 				    j++) {
18470Sstevel@tonic-gate 					if (wordsize == sizeof (int32_t)) {
18480Sstevel@tonic-gate 						val32 = thp[j + 1];
18490Sstevel@tonic-gate 						vaddr = (caddr_t)&val32;
18500Sstevel@tonic-gate 					} else {
18510Sstevel@tonic-gate 						val = thp[i + 1];
18520Sstevel@tonic-gate 						vaddr = (caddr_t)&val;
18530Sstevel@tonic-gate 					}
18540Sstevel@tonic-gate 					if (ddi_copyout(vaddr, (void *) uaddr,
18550Sstevel@tonic-gate 					    wordsize, mode) != 0) {
18560Sstevel@tonic-gate 						PM_UNLOCK_DIP(dip);
18570Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
18580Sstevel@tonic-gate 						    "%s@%s(%s#%d) uaddr %p "
18590Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
18600Sstevel@tonic-gate 						    PM_DEVICE(dip),
18610Sstevel@tonic-gate 						    (void *)uaddr))
18620Sstevel@tonic-gate 						ret = EFAULT;
18630Sstevel@tonic-gate 						break;
18640Sstevel@tonic-gate 					}
18650Sstevel@tonic-gate 					vaddr = uaddr;
18660Sstevel@tonic-gate 					vaddr += wordsize;
18670Sstevel@tonic-gate 					uaddr = (caddr_t)vaddr;
18680Sstevel@tonic-gate 				}
18690Sstevel@tonic-gate 			}
18700Sstevel@tonic-gate 			if (ret)
18710Sstevel@tonic-gate 				break;
18720Sstevel@tonic-gate 			/* last copyout a terminating 0 count */
18730Sstevel@tonic-gate 			if (wordsize == sizeof (int32_t)) {
18740Sstevel@tonic-gate 				val32 = 0;
18750Sstevel@tonic-gate 				vaddr = (caddr_t)&val32;
18760Sstevel@tonic-gate 			} else {
18770Sstevel@tonic-gate 				ASSERT(wordsize == sizeof (int));
18780Sstevel@tonic-gate 				val = 0;
18790Sstevel@tonic-gate 				vaddr = (caddr_t)&val;
18800Sstevel@tonic-gate 			}
18810Sstevel@tonic-gate 			if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
18820Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
18830Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
18840Sstevel@tonic-gate 				    "vaddr %p (0 count) EFAULT\n", cmdstr,
18850Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)vaddr))
18860Sstevel@tonic-gate 				ret = EFAULT;
18870Sstevel@tonic-gate 				break;
18880Sstevel@tonic-gate 			}
18890Sstevel@tonic-gate 			/* finished, so don't need to increment addresses */
18900Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
18910Sstevel@tonic-gate 			ret = 0;
18920Sstevel@tonic-gate 			break;
18930Sstevel@tonic-gate 		}
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 		case PM_GET_STATS:
18960Sstevel@tonic-gate 		{
18970Sstevel@tonic-gate 			time_t now;
18980Sstevel@tonic-gate 			time_t *timestamp;
18990Sstevel@tonic-gate 			extern int pm_cur_power(pm_component_t *cp);
19000Sstevel@tonic-gate 			int musthave;
19010Sstevel@tonic-gate 			int wordsize;
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
19040Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
19050Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
19060Sstevel@tonic-gate 			} else
19070Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
19080Sstevel@tonic-gate 			{
19090Sstevel@tonic-gate 				wordsize = sizeof (int);
19100Sstevel@tonic-gate 			}
19110Sstevel@tonic-gate 
19120Sstevel@tonic-gate 			comps = PM_NUMCMPTS(dip);
19130Sstevel@tonic-gate 			if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
19140Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s no components"
19150Sstevel@tonic-gate 				    " or not power managed--EINVAL\n", cmdstr,
19160Sstevel@tonic-gate 				    req.physpath))
19170Sstevel@tonic-gate 				ret = EINVAL;
19180Sstevel@tonic-gate 				break;
19190Sstevel@tonic-gate 			}
19200Sstevel@tonic-gate 			musthave = comps * 2 * wordsize;
19210Sstevel@tonic-gate 			if (req.datasize < musthave) {
19220Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
19230Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
19240Sstevel@tonic-gate 				    musthave))
19250Sstevel@tonic-gate 				ret = EINVAL;
19260Sstevel@tonic-gate 				break;
19270Sstevel@tonic-gate 			}
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
19300Sstevel@tonic-gate 			(void) drv_getparm(TIME, &now);
19310Sstevel@tonic-gate 			timestamp = kmem_zalloc(comps * sizeof (time_t),
19320Sstevel@tonic-gate 			    KM_SLEEP);
19330Sstevel@tonic-gate 			pm_get_timestamps(dip, timestamp);
19340Sstevel@tonic-gate 			/*
19350Sstevel@tonic-gate 			 * First the current power levels
19360Sstevel@tonic-gate 			 */
19370Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19380Sstevel@tonic-gate 				int curpwr;
19390Sstevel@tonic-gate 				int32_t curpwr32;
19400Sstevel@tonic-gate 				caddr_t cpaddr;
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19430Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19440Sstevel@tonic-gate 					curpwr = pm_cur_power(cp);
19450Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr;
19460Sstevel@tonic-gate 				} else {
19470Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19480Sstevel@tonic-gate 					curpwr32 = pm_cur_power(cp);
19490Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr32;
19500Sstevel@tonic-gate 				}
19510Sstevel@tonic-gate 				if (ddi_copyout(cpaddr, (void *) req.data,
19520Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19530Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19540Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19550Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19560Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19570Sstevel@tonic-gate 					    (void *)req.data))
19580Sstevel@tonic-gate 					ASSERT(!dipheld);
19590Sstevel@tonic-gate 					return (EFAULT);
19600Sstevel@tonic-gate 				}
19610Sstevel@tonic-gate 				cpaddr = (caddr_t)req.data;
19620Sstevel@tonic-gate 				cpaddr += wordsize;
19630Sstevel@tonic-gate 				req.data = cpaddr;
19640Sstevel@tonic-gate 			}
19650Sstevel@tonic-gate 			/*
19660Sstevel@tonic-gate 			 * Then the times remaining
19670Sstevel@tonic-gate 			 */
19680Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19690Sstevel@tonic-gate 				int retval;
19700Sstevel@tonic-gate 				int32_t retval32;
19710Sstevel@tonic-gate 				caddr_t rvaddr;
19720Sstevel@tonic-gate 				int curpwr;
19730Sstevel@tonic-gate 
19740Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19750Sstevel@tonic-gate 				curpwr = cp->pmc_cur_pwr;
19760Sstevel@tonic-gate 				if (curpwr == 0 || timestamp[i] == 0) {
19770Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: "
19780Sstevel@tonic-gate 					    "cur_pwer %x, timestamp %lx\n",
19790Sstevel@tonic-gate 					    cmdstr, curpwr, timestamp[i]))
19800Sstevel@tonic-gate 					retval = INT_MAX;
19810Sstevel@tonic-gate 				} else {
19820Sstevel@tonic-gate 					int thresh;
19830Sstevel@tonic-gate 					(void) pm_current_threshold(dip, i,
19840Sstevel@tonic-gate 					    &thresh);
19850Sstevel@tonic-gate 					retval = thresh - (now - timestamp[i]);
19860Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: current "
19870Sstevel@tonic-gate 					    "thresh %x, now %lx, timestamp %lx,"
19880Sstevel@tonic-gate 					    " retval %x\n", cmdstr, thresh, now,
19890Sstevel@tonic-gate 					    timestamp[i], retval))
19900Sstevel@tonic-gate 				}
19910Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19920Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval;
19930Sstevel@tonic-gate 				} else {
19940Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19950Sstevel@tonic-gate 					retval32 = retval;
19960Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval32;
19970Sstevel@tonic-gate 				}
19980Sstevel@tonic-gate 				if (ddi_copyout(rvaddr, (void *) req.data,
19990Sstevel@tonic-gate 				    wordsize, mode) != 0) {
20000Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
20010Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
20020Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
20030Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
20040Sstevel@tonic-gate 					    (void *)req.data))
20050Sstevel@tonic-gate 					ASSERT(!dipheld);
20065295Srandyf 					kmem_free(timestamp,
20075295Srandyf 					    comps * sizeof (time_t));
20080Sstevel@tonic-gate 					return (EFAULT);
20090Sstevel@tonic-gate 				}
20100Sstevel@tonic-gate 				rvaddr = (caddr_t)req.data;
20110Sstevel@tonic-gate 				rvaddr += wordsize;
20120Sstevel@tonic-gate 				req.data = (int *)rvaddr;
20130Sstevel@tonic-gate 			}
20140Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
20150Sstevel@tonic-gate 			*rval_p = comps;
20160Sstevel@tonic-gate 			ret = 0;
20170Sstevel@tonic-gate 			kmem_free(timestamp, comps * sizeof (time_t));
20180Sstevel@tonic-gate 			break;
20190Sstevel@tonic-gate 		}
20200Sstevel@tonic-gate 
20215295Srandyf 		case PM_GET_CMD_NAME:
20225295Srandyf 		{
20235295Srandyf 			PMD(PMD_IOCTL, ("%s: %s\n", cmdstr,
20245295Srandyf 			    pm_decode_cmd(req.value)))
20255295Srandyf 			if (ret = copyoutstr(pm_decode_cmd(req.value),
20265295Srandyf 			    (char *)req.data, req.datasize, &lencopied)) {
20275295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20285295Srandyf 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20295295Srandyf 				    PM_DEVICE(dip), (void *)req.data))
20305295Srandyf 				break;
20315295Srandyf 			}
20325295Srandyf 			*rval_p = lencopied;
20335295Srandyf 			ret = 0;
20345295Srandyf 			break;
20355295Srandyf 		}
20365295Srandyf 
20370Sstevel@tonic-gate 		case PM_GET_COMPONENT_NAME:
20385295Srandyf 		{
20390Sstevel@tonic-gate 			ASSERT(dip);
20400Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20410Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20420Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20430Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20440Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20450Sstevel@tonic-gate 				ret = EINVAL;
20460Sstevel@tonic-gate 				break;
20470Sstevel@tonic-gate 			}
20480Sstevel@tonic-gate 			if (ret = copyoutstr(cp->pmc_comp.pmc_name,
20490Sstevel@tonic-gate 			    (char *)req.data, req.datasize, &lencopied)) {
20500Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20510Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20520Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20530Sstevel@tonic-gate 				break;
20540Sstevel@tonic-gate 			}
20550Sstevel@tonic-gate 			*rval_p = lencopied;
20560Sstevel@tonic-gate 			ret = 0;
20570Sstevel@tonic-gate 			break;
20585295Srandyf 		}
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate 		case PM_GET_POWER_NAME:
20610Sstevel@tonic-gate 		{
20620Sstevel@tonic-gate 			int i;
20630Sstevel@tonic-gate 
20640Sstevel@tonic-gate 			ASSERT(dip);
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 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20680Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20690Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20700Sstevel@tonic-gate 				ret = EINVAL;
20710Sstevel@tonic-gate 				break;
20720Sstevel@tonic-gate 			}
20730Sstevel@tonic-gate 			if ((i = req.value) < 0 ||
20740Sstevel@tonic-gate 			    i > cp->pmc_comp.pmc_numlevels - 1) {
20750Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20760Sstevel@tonic-gate 				    "value %d > num_levels - 1 %d--EINVAL\n",
20770Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.value,
20780Sstevel@tonic-gate 				    cp->pmc_comp.pmc_numlevels - 1))
20790Sstevel@tonic-gate 				ret = EINVAL;
20800Sstevel@tonic-gate 				break;
20810Sstevel@tonic-gate 			}
20820Sstevel@tonic-gate 			dep = cp->pmc_comp.pmc_lnames[req.value];
20830Sstevel@tonic-gate 			if (ret = copyoutstr(dep,
20840Sstevel@tonic-gate 			    req.data, req.datasize, &lencopied)) {
20850Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20860Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20870Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20880Sstevel@tonic-gate 				break;
20890Sstevel@tonic-gate 			}
20900Sstevel@tonic-gate 			*rval_p = lencopied;
20910Sstevel@tonic-gate 			ret = 0;
20920Sstevel@tonic-gate 			break;
20930Sstevel@tonic-gate 		}
20940Sstevel@tonic-gate 
20950Sstevel@tonic-gate 		case PM_GET_POWER_LEVELS:
20960Sstevel@tonic-gate 		{
20970Sstevel@tonic-gate 			int musthave;
20980Sstevel@tonic-gate 			int numlevels;
20990Sstevel@tonic-gate 			int wordsize;
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
21020Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
21030Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
21040Sstevel@tonic-gate 			} else
21050Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
21060Sstevel@tonic-gate 			{
21070Sstevel@tonic-gate 				wordsize = sizeof (int);
21080Sstevel@tonic-gate 			}
21090Sstevel@tonic-gate 			ASSERT(dip);
21100Sstevel@tonic-gate 
21110Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21120Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21130Sstevel@tonic-gate 				    "has %d components, component %d requested"
21140Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, PM_DEVICE(dip),
21150Sstevel@tonic-gate 				    PM_NUMCMPTS(dip), req.component))
21160Sstevel@tonic-gate 				ret = EINVAL;
21170Sstevel@tonic-gate 				break;
21180Sstevel@tonic-gate 			}
21190Sstevel@tonic-gate 			numlevels = cp->pmc_comp.pmc_numlevels;
21200Sstevel@tonic-gate 			musthave = numlevels *  wordsize;
21210Sstevel@tonic-gate 			if (req.datasize < musthave) {
21220Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
21230Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
21240Sstevel@tonic-gate 				    musthave))
21250Sstevel@tonic-gate 				ret = EINVAL;
21260Sstevel@tonic-gate 				break;
21270Sstevel@tonic-gate 			}
21280Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21290Sstevel@tonic-gate 			for (i = 0; i < numlevels; i++) {
21300Sstevel@tonic-gate 				int level;
21310Sstevel@tonic-gate 				int32_t level32;
21320Sstevel@tonic-gate 				caddr_t laddr;
21330Sstevel@tonic-gate 
21340Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
21350Sstevel@tonic-gate 					level = cp->pmc_comp.pmc_lvals[i];
21360Sstevel@tonic-gate 					laddr = (caddr_t)&level;
21370Sstevel@tonic-gate 				} else {
21380Sstevel@tonic-gate 					level32 = cp->pmc_comp.pmc_lvals[i];
21390Sstevel@tonic-gate 					laddr = (caddr_t)&level32;
21400Sstevel@tonic-gate 				}
21410Sstevel@tonic-gate 				if (ddi_copyout(laddr, (void *) req.data,
21420Sstevel@tonic-gate 				    wordsize, mode) != 0) {
21430Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
21440Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
21450Sstevel@tonic-gate 					    "(%s#%d) laddr %p EFAULT\n",
21460Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
21470Sstevel@tonic-gate 					    (void *)laddr))
21480Sstevel@tonic-gate 					ASSERT(!dipheld);
21490Sstevel@tonic-gate 					return (EFAULT);
21500Sstevel@tonic-gate 				}
21510Sstevel@tonic-gate 				laddr = (caddr_t)req.data;
21520Sstevel@tonic-gate 				laddr += wordsize;
21530Sstevel@tonic-gate 				req.data = (int *)laddr;
21540Sstevel@tonic-gate 			}
21550Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21560Sstevel@tonic-gate 			*rval_p = numlevels;
21570Sstevel@tonic-gate 			ret = 0;
21580Sstevel@tonic-gate 			break;
21590Sstevel@tonic-gate 		}
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 
21620Sstevel@tonic-gate 		case PM_GET_NUM_POWER_LEVELS:
21635295Srandyf 		{
21640Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21650Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21660Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
21670Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
21680Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
21690Sstevel@tonic-gate 				ret = EINVAL;
21700Sstevel@tonic-gate 				break;
21710Sstevel@tonic-gate 			}
21720Sstevel@tonic-gate 			*rval_p = cp->pmc_comp.pmc_numlevels;
21730Sstevel@tonic-gate 			ret = 0;
21740Sstevel@tonic-gate 			break;
21755295Srandyf 		}
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD_BASIS:
21785295Srandyf 		{
21790Sstevel@tonic-gate 			ret = 0;
21800Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21810Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
21820Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21830Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
21840Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
21850Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
21860Sstevel@tonic-gate 				break;
21870Sstevel@tonic-gate 			}
21880Sstevel@tonic-gate 			if (PM_ISDIRECT(dip)) {
21890Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21900Sstevel@tonic-gate 				*rval_p = PM_DIRECTLY_MANAGED;
21910Sstevel@tonic-gate 				break;
21920Sstevel@tonic-gate 			}
21930Sstevel@tonic-gate 			switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
21940Sstevel@tonic-gate 			case PMC_DEF_THRESH:
21950Sstevel@tonic-gate 			case PMC_NEXDEF_THRESH:
21960Sstevel@tonic-gate 				*rval_p = PM_DEFAULT_THRESHOLD;
21970Sstevel@tonic-gate 				break;
21980Sstevel@tonic-gate 			case PMC_DEV_THRESH:
21990Sstevel@tonic-gate 				*rval_p = PM_DEVICE_THRESHOLD;
22000Sstevel@tonic-gate 				break;
22010Sstevel@tonic-gate 			case PMC_COMP_THRESH:
22020Sstevel@tonic-gate 				*rval_p = PM_COMPONENT_THRESHOLD;
22030Sstevel@tonic-gate 				break;
22043028Smh27603 			case PMC_CPU_THRESH:
22053028Smh27603 				*rval_p = PM_CPU_THRESHOLD;
22063028Smh27603 				break;
22070Sstevel@tonic-gate 			default:
22080Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
22090Sstevel@tonic-gate 					*rval_p = PM_OLD_THRESHOLD;
22100Sstevel@tonic-gate 					break;
22110Sstevel@tonic-gate 				}
22120Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: default, not "
22130Sstevel@tonic-gate 				    "BC--EINVAL", cmdstr))
22140Sstevel@tonic-gate 				ret = EINVAL;
22150Sstevel@tonic-gate 				break;
22160Sstevel@tonic-gate 			}
22170Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
22180Sstevel@tonic-gate 			break;
22190Sstevel@tonic-gate 		}
22205295Srandyf 		default:
22215295Srandyf 			/*
22225295Srandyf 			 * Internal error, invalid ioctl description
22235295Srandyf 			 * force debug entry even if pm_debug not set
22245295Srandyf 			 */
22255295Srandyf #ifdef	DEBUG
22265295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
22275295Srandyf 			    pcip->diptype, cmd, pcip->name);
22285295Srandyf #endif
22295295Srandyf 			ASSERT(0);
22305295Srandyf 			return (EIO);
22315295Srandyf 		}
22320Sstevel@tonic-gate 		break;
22335295Srandyf 	}
22340Sstevel@tonic-gate 
22350Sstevel@tonic-gate 	case PM_PSC:
22365295Srandyf 	{
22370Sstevel@tonic-gate 		/*
22380Sstevel@tonic-gate 		 * Commands that require pm_state_change_t as arg
22390Sstevel@tonic-gate 		 */
22400Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
22410Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
22420Sstevel@tonic-gate 			pscp32 = (pm_state_change32_t *)arg;
22430Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc32,
22440Sstevel@tonic-gate 			    sizeof (psc32), mode) != 0) {
22450Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
22460Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
22470Sstevel@tonic-gate 				ASSERT(!dipheld);
22480Sstevel@tonic-gate 				return (EFAULT);
22490Sstevel@tonic-gate 			}
22500Sstevel@tonic-gate 			psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
22510Sstevel@tonic-gate 			psc.size = psc32.size;
22520Sstevel@tonic-gate 		} else
22530Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22540Sstevel@tonic-gate 		{
22550Sstevel@tonic-gate 			pscp = (pm_state_change_t *)arg;
22560Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc,
22570Sstevel@tonic-gate 			    sizeof (psc), mode) != 0) {
22580Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
22590Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
22600Sstevel@tonic-gate 				ASSERT(!dipheld);
22610Sstevel@tonic-gate 				return (EFAULT);
22620Sstevel@tonic-gate 			}
22630Sstevel@tonic-gate 		}
22640Sstevel@tonic-gate 		switch (cmd) {
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE:
22670Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE_WAIT:
22680Sstevel@tonic-gate 		{
22690Sstevel@tonic-gate 			psce_t			*pscep;
22700Sstevel@tonic-gate 			pm_state_change_t	*p;
22710Sstevel@tonic-gate 			caddr_t			physpath;
22720Sstevel@tonic-gate 			size_t			physlen;
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate 			/*
22750Sstevel@tonic-gate 			 * We want to know if any device has changed state.
22760Sstevel@tonic-gate 			 * We look up by clone.  In case we have another thread
22770Sstevel@tonic-gate 			 * from the same process, we loop.
22780Sstevel@tonic-gate 			 * pm_psc_clone_to_interest() returns a locked entry.
22790Sstevel@tonic-gate 			 * We create an internal copy of the event entry prior
22800Sstevel@tonic-gate 			 * to copyout to user space because we don't want to
22810Sstevel@tonic-gate 			 * hold the psce_lock while doing copyout as we might
22820Sstevel@tonic-gate 			 * hit page fault  which eventually brings us back
22830Sstevel@tonic-gate 			 * here requesting the same lock.
22840Sstevel@tonic-gate 			 */
22850Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
22860Sstevel@tonic-gate 			if (!pm_interest_registered(clone))
22870Sstevel@tonic-gate 				pm_register_watcher(clone, NULL);
22880Sstevel@tonic-gate 			while ((pscep =
22890Sstevel@tonic-gate 			    pm_psc_clone_to_interest(clone)) == NULL) {
22900Sstevel@tonic-gate 				if (cmd == PM_GET_STATE_CHANGE) {
22910Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
22920Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
22930Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
22940Sstevel@tonic-gate 					ASSERT(!dipheld);
22950Sstevel@tonic-gate 					return (EWOULDBLOCK);
22960Sstevel@tonic-gate 				} else {
22970Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
22980Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
22990Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
23000Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s "
23010Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
23020Sstevel@tonic-gate 						ASSERT(!dipheld);
23030Sstevel@tonic-gate 						return (EINTR);
23040Sstevel@tonic-gate 					}
23050Sstevel@tonic-gate 				}
23060Sstevel@tonic-gate 			}
23070Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
23080Sstevel@tonic-gate 
23090Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
23100Sstevel@tonic-gate 			physpath = NULL;
23110Sstevel@tonic-gate 			/*
23120Sstevel@tonic-gate 			 * If we were unable to store the path while bringing
23130Sstevel@tonic-gate 			 * up the console fb upon entering the prom, we give
23140Sstevel@tonic-gate 			 * a "" name with the overrun event set
23150Sstevel@tonic-gate 			 */
23160Sstevel@tonic-gate 			if (physlen == (size_t)-1) {	/* kmemalloc failed */
23170Sstevel@tonic-gate 				physpath = kmem_zalloc(1, KM_SLEEP);
23180Sstevel@tonic-gate 				physlen = 1;
23190Sstevel@tonic-gate 			}
23200Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
23210Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
23220Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
23230Sstevel@tonic-gate 				ret = EFAULT;
23240Sstevel@tonic-gate 				break;
23250Sstevel@tonic-gate 			}
23260Sstevel@tonic-gate 			if (physpath == NULL) {
23270Sstevel@tonic-gate 				physpath = kmem_zalloc(physlen, KM_SLEEP);
23280Sstevel@tonic-gate 				bcopy((const void *) pscep->psce_out->physpath,
23290Sstevel@tonic-gate 				    (void *) physpath, physlen);
23300Sstevel@tonic-gate 			}
23310Sstevel@tonic-gate 
23320Sstevel@tonic-gate 			p = pscep->psce_out;
23330Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23340Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23350Sstevel@tonic-gate #ifdef DEBUG
23360Sstevel@tonic-gate 				size_t usrcopysize;
23370Sstevel@tonic-gate #endif
23380Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
23390Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
23400Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
23410Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
23420Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
23430Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
23440Sstevel@tonic-gate 				copysize32 = ((intptr_t)&psc32.size -
23450Sstevel@tonic-gate 				    (intptr_t)&psc32.component);
23460Sstevel@tonic-gate #ifdef DEBUG
23470Sstevel@tonic-gate 				usrcopysize = ((intptr_t)&pscp32->size -
23480Sstevel@tonic-gate 				    (intptr_t)&pscp32->component);
23490Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
23500Sstevel@tonic-gate #endif
23510Sstevel@tonic-gate 			} else
23520Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
23530Sstevel@tonic-gate 			{
23540Sstevel@tonic-gate 				psc.flags = p->flags;
23550Sstevel@tonic-gate 				psc.event = p->event;
23560Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
23570Sstevel@tonic-gate 				psc.component = p->component;
23580Sstevel@tonic-gate 				psc.old_level = p->old_level;
23590Sstevel@tonic-gate 				psc.new_level = p->new_level;
23600Sstevel@tonic-gate 				copysize = ((long)&p->size -
23610Sstevel@tonic-gate 				    (long)&p->component);
23620Sstevel@tonic-gate 			}
23630Sstevel@tonic-gate 			if (p->size != (size_t)-1)
23640Sstevel@tonic-gate 				kmem_free(p->physpath, p->size);
23650Sstevel@tonic-gate 			p->size = 0;
23660Sstevel@tonic-gate 			p->physpath = NULL;
23670Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
23680Sstevel@tonic-gate 				p = pscep->psce_first;
23690Sstevel@tonic-gate 			else
23700Sstevel@tonic-gate 				p++;
23710Sstevel@tonic-gate 			pscep->psce_out = p;
23720Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
23750Sstevel@tonic-gate 			    physlen, &lencopied);
23760Sstevel@tonic-gate 			kmem_free(physpath, physlen);
23770Sstevel@tonic-gate 			if (ret) {
23780Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
23790Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
23800Sstevel@tonic-gate 				    (void *)psc.physpath))
23810Sstevel@tonic-gate 				break;
23820Sstevel@tonic-gate 			}
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23850Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23860Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
23870Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
23880Sstevel@tonic-gate 				    != 0) {
23890Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23900Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23910Sstevel@tonic-gate 					ret = EFAULT;
23920Sstevel@tonic-gate 					break;
23930Sstevel@tonic-gate 				}
23940Sstevel@tonic-gate 			} else
23950Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
23960Sstevel@tonic-gate 			{
23970Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
23980Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
23990Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24000Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24010Sstevel@tonic-gate 					ret = EFAULT;
24020Sstevel@tonic-gate 					break;
24030Sstevel@tonic-gate 				}
24040Sstevel@tonic-gate 			}
24050Sstevel@tonic-gate 			ret = 0;
24060Sstevel@tonic-gate 			break;
24070Sstevel@tonic-gate 		}
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY:
24100Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY_WAIT:
24110Sstevel@tonic-gate 		{
24120Sstevel@tonic-gate 			psce_t			*pscep;
24130Sstevel@tonic-gate 			pm_state_change_t	*p;
24140Sstevel@tonic-gate 			caddr_t			physpath;
24150Sstevel@tonic-gate 			size_t			physlen;
24160Sstevel@tonic-gate 			/*
24170Sstevel@tonic-gate 			 * We want to know if any direct device of ours has
24180Sstevel@tonic-gate 			 * something we should know about.  We look up by clone.
24190Sstevel@tonic-gate 			 * In case we have another thread from the same process,
24200Sstevel@tonic-gate 			 * we loop.
24210Sstevel@tonic-gate 			 * pm_psc_clone_to_direct() returns a locked entry.
24220Sstevel@tonic-gate 			 */
24230Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24240Sstevel@tonic-gate 			while (pm_poll_cnt[clone] == 0 ||
24250Sstevel@tonic-gate 			    (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
24260Sstevel@tonic-gate 				if (cmd == PM_DIRECT_NOTIFY) {
24270Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
24280Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
24290Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
24300Sstevel@tonic-gate 					ASSERT(!dipheld);
24310Sstevel@tonic-gate 					return (EWOULDBLOCK);
24320Sstevel@tonic-gate 				} else {
24330Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
24340Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
24350Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
24360Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
24370Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
24380Sstevel@tonic-gate 						ASSERT(!dipheld);
24390Sstevel@tonic-gate 						return (EINTR);
24400Sstevel@tonic-gate 					}
24410Sstevel@tonic-gate 				}
24420Sstevel@tonic-gate 			}
24430Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24440Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
24450Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
24460Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
24470Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
24480Sstevel@tonic-gate 				    cmdstr))
24490Sstevel@tonic-gate 				ret = EFAULT;
24500Sstevel@tonic-gate 				break;
24510Sstevel@tonic-gate 			}
24520Sstevel@tonic-gate 			physpath = kmem_zalloc(physlen, KM_SLEEP);
24530Sstevel@tonic-gate 			bcopy((const void *) pscep->psce_out->physpath,
24540Sstevel@tonic-gate 			    (void *) physpath, physlen);
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate 			p = pscep->psce_out;
24570Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
24580Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24590Sstevel@tonic-gate #ifdef DEBUG
24600Sstevel@tonic-gate 				size_t usrcopysize;
24610Sstevel@tonic-gate #endif
24620Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
24630Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
24640Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
24650Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
24660Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
24670Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
24680Sstevel@tonic-gate 				copysize32 = (intptr_t)&psc32.size -
24690Sstevel@tonic-gate 				    (intptr_t)&psc32.component;
24700Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
24710Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24720Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24730Sstevel@tonic-gate #ifdef DEBUG
24740Sstevel@tonic-gate 				usrcopysize = (intptr_t)&pscp32->size -
24750Sstevel@tonic-gate 				    (intptr_t)&pscp32->component;
24760Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
24770Sstevel@tonic-gate #endif
24780Sstevel@tonic-gate 			} else
24790Sstevel@tonic-gate #endif
24800Sstevel@tonic-gate 			{
24810Sstevel@tonic-gate 				psc.component = p->component;
24820Sstevel@tonic-gate 				psc.flags = p->flags;
24830Sstevel@tonic-gate 				psc.event = p->event;
24840Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
24850Sstevel@tonic-gate 				psc.old_level = p->old_level;
24860Sstevel@tonic-gate 				psc.new_level = p->new_level;
24870Sstevel@tonic-gate 				copysize = (intptr_t)&p->size -
24880Sstevel@tonic-gate 				    (intptr_t)&p->component;
24890Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
24900Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24910Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24920Sstevel@tonic-gate 			}
24930Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24940Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
24950Sstevel@tonic-gate 			    "before decrement\n", cmdstr, clone,
24960Sstevel@tonic-gate 			    pm_poll_cnt[clone]))
24970Sstevel@tonic-gate 			pm_poll_cnt[clone]--;
24980Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24990Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
25000Sstevel@tonic-gate 			p->size = 0;
25010Sstevel@tonic-gate 			p->physpath = NULL;
25020Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
25030Sstevel@tonic-gate 				p = pscep->psce_first;
25040Sstevel@tonic-gate 			else
25050Sstevel@tonic-gate 				p++;
25060Sstevel@tonic-gate 			pscep->psce_out = p;
25070Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
25080Sstevel@tonic-gate 
25090Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
25100Sstevel@tonic-gate 			    physlen, &lencopied);
25110Sstevel@tonic-gate 			kmem_free(physpath, physlen);
25120Sstevel@tonic-gate 			if (ret) {
25130Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
25140Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
25150Sstevel@tonic-gate 				    (void *)psc.physpath))
25160Sstevel@tonic-gate 				break;
25170Sstevel@tonic-gate 			}
25180Sstevel@tonic-gate 
25190Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
25200Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
25210Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
25220Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
25235295Srandyf 				    != 0) {
25240Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
25250Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
25260Sstevel@tonic-gate 					ret = EFAULT;
25270Sstevel@tonic-gate 					break;
25280Sstevel@tonic-gate 				}
25290Sstevel@tonic-gate 			} else
25300Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
25310Sstevel@tonic-gate 			{
25320Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
25330Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
25340Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
25350Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
25360Sstevel@tonic-gate 					ret = EFAULT;
25370Sstevel@tonic-gate 					break;
25380Sstevel@tonic-gate 				}
25390Sstevel@tonic-gate 			}
25400Sstevel@tonic-gate 			ret = 0;
25410Sstevel@tonic-gate 			break;
25420Sstevel@tonic-gate 		}
25430Sstevel@tonic-gate 		default:
25445295Srandyf 			/*
25455295Srandyf 			 * Internal error, invalid ioctl description
25465295Srandyf 			 * force debug entry even if pm_debug not set
25475295Srandyf 			 */
25485295Srandyf #ifdef	DEBUG
25495295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
25505295Srandyf 			    pcip->diptype, cmd, pcip->name);
25515295Srandyf #endif
25520Sstevel@tonic-gate 			ASSERT(0);
25535295Srandyf 			return (EIO);
25540Sstevel@tonic-gate 		}
25550Sstevel@tonic-gate 		break;
25565295Srandyf 	}
25575295Srandyf 
25585295Srandyf 	case PM_SRCH:		/* command that takes a pm_searchargs_t arg */
25595295Srandyf 	{
25605295Srandyf 		/*
25615295Srandyf 		 * If no ppm, then there is nothing to search.
25625295Srandyf 		 */
25635295Srandyf 		if (DEVI(ddi_root_node())->devi_pm_ppm == NULL) {
25645295Srandyf 			ret = ENODEV;
25655295Srandyf 			break;
25665295Srandyf 		}
25675295Srandyf 
25685295Srandyf #ifdef	_MULTI_DATAMODEL
25695295Srandyf 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
25705295Srandyf 			if (ddi_copyin((caddr_t)arg, &psa32,
25715295Srandyf 			    sizeof (psa32), mode) != 0) {
25725295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
25735295Srandyf 				    "EFAULT\n\n", cmdstr))
25745295Srandyf 				return (EFAULT);
25755295Srandyf 			}
25765295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_listname,
25775295Srandyf 			    listname, MAXCOPYBUF, NULL)) {
25785295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
25795295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
25805295Srandyf 				    (void *)(uintptr_t)psa32.pms_listname,
25815295Srandyf 				    MAXCOPYBUF))
25825295Srandyf 				ret = EFAULT;
25835295Srandyf 				break;
25845295Srandyf 			}
25855295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_manufacturer,
25865295Srandyf 			    manufacturer, MAXCOPYBUF, NULL)) {
25875295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
25885295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
25895295Srandyf 				    (void *)(uintptr_t)psa32.pms_manufacturer,
25905295Srandyf 				    MAXCOPYBUF))
25915295Srandyf 				ret = EFAULT;
25925295Srandyf 				break;
25935295Srandyf 			}
25945295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_product,
25955295Srandyf 			    product, MAXCOPYBUF, NULL)) {
25965295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
25975295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
25985295Srandyf 				    (void *)(uintptr_t)psa32.pms_product,
25995295Srandyf 				    MAXCOPYBUF))
26005295Srandyf 				ret = EFAULT;
26015295Srandyf 				break;
26025295Srandyf 			}
26035295Srandyf 		} else
26045295Srandyf #endif /* _MULTI_DATAMODEL */
26055295Srandyf 		{
26065295Srandyf 			if (ddi_copyin((caddr_t)arg, &psa,
26075295Srandyf 			    sizeof (psa), mode) != 0) {
26085295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
26095295Srandyf 				    "EFAULT\n\n", cmdstr))
26105295Srandyf 				return (EFAULT);
26115295Srandyf 			}
26125295Srandyf 			if (copyinstr(psa.pms_listname,
26135295Srandyf 			    listname, MAXCOPYBUF, NULL)) {
26145295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26155295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26165295Srandyf 				    (void *)psa.pms_listname, MAXCOPYBUF))
26175295Srandyf 				ret = EFAULT;
26185295Srandyf 				break;
26195295Srandyf 			}
26205295Srandyf 			if (copyinstr(psa.pms_manufacturer,
26215295Srandyf 			    manufacturer, MAXCOPYBUF, NULL)) {
26225295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26235295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26245295Srandyf 				    (void *)psa.pms_manufacturer, MAXCOPYBUF))
26255295Srandyf 				ret = EFAULT;
26265295Srandyf 				break;
26275295Srandyf 			}
26285295Srandyf 			if (copyinstr(psa.pms_product,
26295295Srandyf 			    product, MAXCOPYBUF, NULL)) {
26305295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26315295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26325295Srandyf 				    (void *)psa.pms_product, MAXCOPYBUF))
26335295Srandyf 				ret = EFAULT;
26345295Srandyf 				break;
26355295Srandyf 			}
26365295Srandyf 		}
26375295Srandyf 		psa.pms_listname = listname;
26385295Srandyf 		psa.pms_manufacturer = manufacturer;
26395295Srandyf 		psa.pms_product = product;
26405295Srandyf 		switch (cmd) {
26415295Srandyf 		case PM_SEARCH_LIST:
26425295Srandyf 			ret = pm_ppm_searchlist(&psa);
26435295Srandyf 			break;
26445295Srandyf 
26455295Srandyf 		default:
26465295Srandyf 			/*
26475295Srandyf 			 * Internal error, invalid ioctl description
26485295Srandyf 			 * force debug entry even if pm_debug not set
26495295Srandyf 			 */
26505295Srandyf #ifdef	DEBUG
26515295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
26525295Srandyf 			    pcip->diptype, cmd, pcip->name);
26535295Srandyf #endif
26545295Srandyf 			ASSERT(0);
26555295Srandyf 			return (EIO);
26565295Srandyf 		}
26575295Srandyf 		break;
26585295Srandyf 	}
26590Sstevel@tonic-gate 
26600Sstevel@tonic-gate 	case NOSTRUCT:
26615295Srandyf 	{
26620Sstevel@tonic-gate 		switch (cmd) {
26630Sstevel@tonic-gate 		case PM_START_PM:
26643028Smh27603 		case PM_START_CPUPM:
26655295Srandyf 		{
26660Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
26673028Smh27603 			if ((cmd == PM_START_PM && autopm_enabled) ||
26683028Smh27603 			    (cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) {
26690Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
26700Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
26710Sstevel@tonic-gate 				    cmdstr))
26720Sstevel@tonic-gate 				ret = EBUSY;
26730Sstevel@tonic-gate 				break;
26740Sstevel@tonic-gate 			}
26753028Smh27603 			if (cmd == PM_START_PM)
26765295Srandyf 				autopm_enabled = 1;
26773028Smh27603 			else
26785295Srandyf 				cpupm = PM_CPUPM_ENABLE;
26790Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
26800Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd);
26810Sstevel@tonic-gate 			ret = 0;
26820Sstevel@tonic-gate 			break;
26835295Srandyf 		}
26840Sstevel@tonic-gate 
26850Sstevel@tonic-gate 		case PM_RESET_PM:
26860Sstevel@tonic-gate 		case PM_STOP_PM:
26873028Smh27603 		case PM_STOP_CPUPM:
26880Sstevel@tonic-gate 		{
26890Sstevel@tonic-gate 			extern void pm_discard_thresholds(void);
26900Sstevel@tonic-gate 
26910Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
26923028Smh27603 			if ((cmd == PM_STOP_PM && !autopm_enabled) ||
26933028Smh27603 			    (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
26940Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
26950Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
26960Sstevel@tonic-gate 				    cmdstr))
26970Sstevel@tonic-gate 				ret = EINVAL;
26980Sstevel@tonic-gate 				break;
26990Sstevel@tonic-gate 			}
27005295Srandyf 			if (cmd == PM_STOP_PM) {
27015295Srandyf 				autopm_enabled = 0;
27025295Srandyf 				pm_S3_enabled = 0;
27035295Srandyf 				autoS3_enabled = 0;
27045295Srandyf 			} else if (cmd == PM_STOP_CPUPM) {
27055295Srandyf 				cpupm = PM_CPUPM_DISABLE;
27065295Srandyf 			} else {
27075295Srandyf 				autopm_enabled = 0;
27085295Srandyf 				autoS3_enabled = 0;
27095295Srandyf 				cpupm = PM_CPUPM_NOTSET;
27103028Smh27603 			}
27110Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
27123028Smh27603 
27130Sstevel@tonic-gate 			/*
27140Sstevel@tonic-gate 			 * bring devices to full power level, stop scan
27150Sstevel@tonic-gate 			 */
27160Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
27170Sstevel@tonic-gate 			ret = 0;
27183028Smh27603 			if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
27190Sstevel@tonic-gate 				break;
27200Sstevel@tonic-gate 			/*
27210Sstevel@tonic-gate 			 * Now do only PM_RESET_PM stuff.
27220Sstevel@tonic-gate 			 */
27230Sstevel@tonic-gate 			pm_system_idle_threshold = pm_default_idle_threshold;
27243028Smh27603 			pm_cpu_idle_threshold = 0;
27250Sstevel@tonic-gate 			pm_discard_thresholds();
27260Sstevel@tonic-gate 			pm_all_to_default_thresholds();
27270Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
27280Sstevel@tonic-gate 			    NULL, NULL, PM_DEP_WAIT, NULL, 0);
27290Sstevel@tonic-gate 			break;
27300Sstevel@tonic-gate 		}
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 		case PM_GET_SYSTEM_THRESHOLD:
27335295Srandyf 		{
27340Sstevel@tonic-gate 			*rval_p = pm_system_idle_threshold;
27350Sstevel@tonic-gate 			ret = 0;
27360Sstevel@tonic-gate 			break;
27375295Srandyf 		}
27380Sstevel@tonic-gate 
27390Sstevel@tonic-gate 		case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
27405295Srandyf 		{
27410Sstevel@tonic-gate 			*rval_p = pm_default_idle_threshold;
27420Sstevel@tonic-gate 			ret = 0;
27430Sstevel@tonic-gate 			break;
27445295Srandyf 		}
27450Sstevel@tonic-gate 
27463028Smh27603 		case PM_GET_CPU_THRESHOLD:
27475295Srandyf 		{
27483028Smh27603 			*rval_p = pm_cpu_idle_threshold;
27493028Smh27603 			ret = 0;
27503028Smh27603 			break;
27515295Srandyf 		}
27523028Smh27603 
27530Sstevel@tonic-gate 		case PM_SET_SYSTEM_THRESHOLD:
27543028Smh27603 		case PM_SET_CPU_THRESHOLD:
27555295Srandyf 		{
27560Sstevel@tonic-gate 			if ((int)arg < 0) {
27570Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
27580Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, (int)arg))
27590Sstevel@tonic-gate 				ret = EINVAL;
27600Sstevel@tonic-gate 				break;
27610Sstevel@tonic-gate 			}
27620Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
27630Sstevel@tonic-gate 			    (int)arg, (int)arg))
27643028Smh27603 			if (cmd == PM_SET_SYSTEM_THRESHOLD)
27653028Smh27603 				pm_system_idle_threshold = (int)arg;
27663028Smh27603 			else {
27673028Smh27603 				pm_cpu_idle_threshold = (int)arg;
27683028Smh27603 			}
27693028Smh27603 			ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
27705295Srandyf 			    (void *) &cmd);
27713028Smh27603 
27720Sstevel@tonic-gate 			ret = 0;
27730Sstevel@tonic-gate 			break;
27745295Srandyf 		}
27750Sstevel@tonic-gate 
27760Sstevel@tonic-gate 		case PM_IDLE_DOWN:
27775295Srandyf 		{
27780Sstevel@tonic-gate 			if (pm_timeout_idledown() != 0) {
27790Sstevel@tonic-gate 				ddi_walk_devs(ddi_root_node(),
27800Sstevel@tonic-gate 				    pm_start_idledown, (void *)PMID_IOC);
27810Sstevel@tonic-gate 			}
27820Sstevel@tonic-gate 			ret = 0;
27830Sstevel@tonic-gate 			break;
27845295Srandyf 		}
27850Sstevel@tonic-gate 
27860Sstevel@tonic-gate 		case PM_GET_PM_STATE:
27875295Srandyf 		{
27880Sstevel@tonic-gate 			if (autopm_enabled) {
27890Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_ENABLED;
27900Sstevel@tonic-gate 			} else {
27910Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_DISABLED;
27920Sstevel@tonic-gate 			}
27930Sstevel@tonic-gate 			ret = 0;
27940Sstevel@tonic-gate 			break;
27955295Srandyf 		}
27963028Smh27603 
27973028Smh27603 		case PM_GET_CPUPM_STATE:
27985295Srandyf 		{
27993028Smh27603 			if (PM_CPUPM_ENABLED)
28003028Smh27603 				*rval_p = PM_CPU_PM_ENABLED;
28013028Smh27603 			else if (PM_CPUPM_DISABLED)
28023028Smh27603 				*rval_p = PM_CPU_PM_DISABLED;
28033028Smh27603 			else
28043028Smh27603 				*rval_p = PM_CPU_PM_NOTSET;
28053028Smh27603 			ret = 0;
28063028Smh27603 			break;
28070Sstevel@tonic-gate 		}
28085295Srandyf 
28095295Srandyf 		case PM_GET_AUTOS3_STATE:
28105295Srandyf 		{
28115295Srandyf 			if (autoS3_enabled) {
28125295Srandyf 				*rval_p = PM_AUTOS3_ENABLED;
28135295Srandyf 			} else {
28145295Srandyf 				*rval_p = PM_AUTOS3_DISABLED;
28155295Srandyf 			}
28165295Srandyf 			ret = 0;
28175295Srandyf 			break;
28185295Srandyf 		}
28195295Srandyf 
28205295Srandyf 		case PM_GET_S3_SUPPORT_STATE:
28215295Srandyf 		{
28225295Srandyf 			if (pm_S3_enabled) {
28235295Srandyf 				*rval_p = PM_S3_SUPPORT_ENABLED;
28245295Srandyf 			} else {
28255295Srandyf 				*rval_p = PM_S3_SUPPORT_DISABLED;
28265295Srandyf 			}
28275295Srandyf 			ret = 0;
28285295Srandyf 			break;
28295295Srandyf 		}
28305295Srandyf 
28315295Srandyf 		/*
28325295Srandyf 		 * pmconfig tells us if the platform supports S3
28335295Srandyf 		 */
28345295Srandyf 		case PM_ENABLE_S3:
28355295Srandyf 		{
28365295Srandyf 			mutex_enter(&pm_scan_lock);
28375295Srandyf 			if (pm_S3_enabled) {
28385295Srandyf 				mutex_exit(&pm_scan_lock);
28395295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
28405295Srandyf 				    cmdstr))
28415295Srandyf 				ret = EBUSY;
28425295Srandyf 				break;
28435295Srandyf 			}
28445295Srandyf 			pm_S3_enabled = 1;
28455295Srandyf 			mutex_exit(&pm_scan_lock);
28465295Srandyf 			ret = 0;
28475295Srandyf 			break;
28485295Srandyf 		}
28495295Srandyf 
28505295Srandyf 		case PM_DISABLE_S3:
28515295Srandyf 		{
28525295Srandyf 			mutex_enter(&pm_scan_lock);
28535295Srandyf 			pm_S3_enabled = 0;
28545295Srandyf 			mutex_exit(&pm_scan_lock);
28555295Srandyf 			ret = 0;
28565295Srandyf 			break;
28575295Srandyf 		}
28585295Srandyf 
28595295Srandyf 		case PM_START_AUTOS3:
28605295Srandyf 		{
28615295Srandyf 			mutex_enter(&pm_scan_lock);
28625295Srandyf 			if (autoS3_enabled) {
28635295Srandyf 				mutex_exit(&pm_scan_lock);
28645295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
28655295Srandyf 				    cmdstr))
28665295Srandyf 				ret = EBUSY;
28675295Srandyf 				break;
28685295Srandyf 			}
28695295Srandyf 			autoS3_enabled = 1;
28705295Srandyf 			mutex_exit(&pm_scan_lock);
28715295Srandyf 			ret = 0;
28725295Srandyf 			break;
28735295Srandyf 		}
28745295Srandyf 
28755295Srandyf 		case PM_STOP_AUTOS3:
28765295Srandyf 		{
28775295Srandyf 			mutex_enter(&pm_scan_lock);
28785295Srandyf 			autoS3_enabled = 0;
28795295Srandyf 			mutex_exit(&pm_scan_lock);
28805295Srandyf 			ret = 0;
28815295Srandyf 			break;
28825295Srandyf 		}
28835295Srandyf 
28845295Srandyf 		default:
28855295Srandyf 			/*
28865295Srandyf 			 * Internal error, invalid ioctl description
28875295Srandyf 			 * force debug entry even if pm_debug not set
28885295Srandyf 			 */
28895295Srandyf #ifdef	DEBUG
28905295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
28915295Srandyf 			    pcip->diptype, cmd, pcip->name);
28925295Srandyf #endif
28935295Srandyf 			ASSERT(0);
28945295Srandyf 			return (EIO);
28955295Srandyf 		}
28960Sstevel@tonic-gate 		break;
28975295Srandyf 	}
28980Sstevel@tonic-gate 
28990Sstevel@tonic-gate 	default:
29000Sstevel@tonic-gate 		/*
29010Sstevel@tonic-gate 		 * Internal error, invalid ioctl description
29020Sstevel@tonic-gate 		 * force debug entry even if pm_debug not set
29030Sstevel@tonic-gate 		 */
29040Sstevel@tonic-gate #ifdef	DEBUG
29050Sstevel@tonic-gate 		pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
29060Sstevel@tonic-gate 		    pcip->str_type, cmd, pcip->name);
29070Sstevel@tonic-gate #endif
29080Sstevel@tonic-gate 		ASSERT(0);
29090Sstevel@tonic-gate 		return (EIO);
29100Sstevel@tonic-gate 	}
29110Sstevel@tonic-gate 	ASSERT(ret != 0x0badcafe);	/* some cmd in wrong case! */
29120Sstevel@tonic-gate 	if (dipheld) {
29130Sstevel@tonic-gate 		ASSERT(dip);
29140Sstevel@tonic-gate 		PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
29150Sstevel@tonic-gate 		    "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
29160Sstevel@tonic-gate 		PM_RELE(dip);
29170Sstevel@tonic-gate 	}
29180Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
29190Sstevel@tonic-gate 	return (ret);
29200Sstevel@tonic-gate }
2921