xref: /onnv-gate/usr/src/uts/common/io/pm.c (revision 8906:e559381f1e2b)
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*8906SEric.Saxe@Sun.COM  * Copyright 2009 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  * pm	This driver now only handles the ioctl interface.  The scanning
280Sstevel@tonic-gate  *	and policy stuff now lives in common/os/sunpm.c.
290Sstevel@tonic-gate  *	Not DDI compliant
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/errno.h>
340Sstevel@tonic-gate #include <sys/modctl.h>
35*8906SEric.Saxe@Sun.COM #include <sys/callb.h>		/* callback registration for cpu_deep_idle */
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>
56*8906SEric.Saxe@Sun.COM #include <sys/cpu_pm.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
595295Srandyf  * Minor number is instance<<8 + clone minor from range 1-254; (0 reserved
605295Srandyf  * for "original")
610Sstevel@tonic-gate  */
625295Srandyf #define	PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE -1))
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
650Sstevel@tonic-gate #define	PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
660Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_driver_major(dip)
670Sstevel@tonic-gate #define	PM_RELE(dip) ddi_release_devi(dip)
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	PM_IDLEDOWN_TIME	10
705295Srandyf #define	MAXSMBIOSSTRLEN 64	/* from SMBIOS spec */
715295Srandyf #define	MAXCOPYBUF 	(MAXSMBIOSSTRLEN + 1)
720Sstevel@tonic-gate 
730Sstevel@tonic-gate extern kmutex_t	pm_scan_lock;	/* protects autopm_enable, pm_scans_disabled */
740Sstevel@tonic-gate extern kmutex_t	pm_clone_lock;	/* protects pm_clones array */
750Sstevel@tonic-gate extern int	autopm_enabled;
763028Smh27603 extern pm_cpupm_t cpupm;
77*8906SEric.Saxe@Sun.COM extern pm_cpupm_t cpupm_default_mode;
783028Smh27603 extern int	pm_default_idle_threshold;
793028Smh27603 extern int	pm_system_idle_threshold;
803028Smh27603 extern int	pm_cpu_idle_threshold;
810Sstevel@tonic-gate extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
820Sstevel@tonic-gate extern uint_t	pm_poll_cnt[PM_MAX_CLONE];
835295Srandyf extern int	autoS3_enabled;
845295Srandyf extern void	pm_record_thresh(pm_thresh_rec_t *);
855295Srandyf extern void	pm_register_watcher(int, dev_info_t *);
865295Srandyf extern int	pm_get_current_power(dev_info_t *, int, int *);
875295Srandyf extern int	pm_interest_registered(int);
885295Srandyf extern void	pm_all_to_default_thresholds(void);
895295Srandyf extern int	pm_current_threshold(dev_info_t *, int, int *);
905295Srandyf extern void	pm_deregister_watcher(int, dev_info_t *);
915295Srandyf extern void	pm_unrecord_threshold(char *);
925295Srandyf extern int	pm_S3_enabled;
935295Srandyf extern int	pm_ppm_searchlist(pm_searchargs_t *);
945295Srandyf extern psce_t	*pm_psc_clone_to_direct(int);
955295Srandyf extern psce_t	*pm_psc_clone_to_interest(int);
960Sstevel@tonic-gate 
970Sstevel@tonic-gate /*
980Sstevel@tonic-gate  * The soft state of the power manager.  Since there will only
990Sstevel@tonic-gate  * one of these, just reference it through a static pointer.
1000Sstevel@tonic-gate  */
1010Sstevel@tonic-gate static struct pmstate {
1020Sstevel@tonic-gate 	dev_info_t	*pm_dip;		/* ptr to our dev_info node */
1030Sstevel@tonic-gate 	int		pm_instance;		/* for ddi_get_instance() */
1040Sstevel@tonic-gate 	timeout_id_t	pm_idledown_id;		/* pm idledown timeout id */
1050Sstevel@tonic-gate 	uchar_t		pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */
1060Sstevel@tonic-gate 	struct cred	*pm_cred[PM_MAX_CLONE];	/* cred for each unique open */
1070Sstevel@tonic-gate } pm_state = { NULL, -1, (timeout_id_t)0 };
1080Sstevel@tonic-gate typedef struct pmstate *pm_state_t;
1090Sstevel@tonic-gate static pm_state_t pmstp = &pm_state;
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate static int	pm_open(dev_t *, int, int, cred_t *);
1120Sstevel@tonic-gate static int	pm_close(dev_t, int, int, cred_t *);
1130Sstevel@tonic-gate static int	pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1140Sstevel@tonic-gate static int	pm_chpoll(dev_t, short, int, short *, struct pollhead **);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate static struct cb_ops pm_cb_ops = {
1170Sstevel@tonic-gate 	pm_open,	/* open */
1180Sstevel@tonic-gate 	pm_close,	/* close */
1190Sstevel@tonic-gate 	nodev,		/* strategy */
1200Sstevel@tonic-gate 	nodev,		/* print */
1210Sstevel@tonic-gate 	nodev,		/* dump */
1220Sstevel@tonic-gate 	nodev,		/* read */
1230Sstevel@tonic-gate 	nodev,		/* write */
1240Sstevel@tonic-gate 	pm_ioctl,	/* ioctl */
1250Sstevel@tonic-gate 	nodev,		/* devmap */
1260Sstevel@tonic-gate 	nodev,		/* mmap */
1270Sstevel@tonic-gate 	nodev,		/* segmap */
1280Sstevel@tonic-gate 	pm_chpoll,	/* poll */
1290Sstevel@tonic-gate 	ddi_prop_op,	/* prop_op */
1300Sstevel@tonic-gate 	NULL,		/* streamtab */
1310Sstevel@tonic-gate 	D_NEW | D_MP	/* driver compatibility flag */
1320Sstevel@tonic-gate };
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1350Sstevel@tonic-gate     void **result);
1360Sstevel@tonic-gate static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1370Sstevel@tonic-gate static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate static struct dev_ops pm_ops = {
1400Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1410Sstevel@tonic-gate 	0,			/* refcnt */
1420Sstevel@tonic-gate 	pm_getinfo,		/* info */
1430Sstevel@tonic-gate 	nulldev,		/* identify */
1440Sstevel@tonic-gate 	nulldev,		/* probe */
1450Sstevel@tonic-gate 	pm_attach,		/* attach */
1460Sstevel@tonic-gate 	pm_detach,		/* detach */
1470Sstevel@tonic-gate 	nodev,			/* reset */
1480Sstevel@tonic-gate 	&pm_cb_ops,		/* driver operations */
1490Sstevel@tonic-gate 	NULL,			/* bus operations */
1507656SSherry.Moore@Sun.COM 	NULL,			/* power */
1517656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate static struct modldrv modldrv = {
1550Sstevel@tonic-gate 	&mod_driverops,
1567656SSherry.Moore@Sun.COM 	"power management driver",
1570Sstevel@tonic-gate 	&pm_ops
1580Sstevel@tonic-gate };
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate static struct modlinkage modlinkage = {
1610Sstevel@tonic-gate 	MODREV_1, &modldrv, 0
1620Sstevel@tonic-gate };
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate /* Local functions */
1650Sstevel@tonic-gate #ifdef DEBUG
1660Sstevel@tonic-gate static int	print_info(dev_info_t *, void *);
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate #endif
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate int
_init(void)1710Sstevel@tonic-gate _init(void)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate int
_fini(void)1770Sstevel@tonic-gate _fini(void)
1780Sstevel@tonic-gate {
1790Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1830Sstevel@tonic-gate _info(struct modinfo *modinfop)
1840Sstevel@tonic-gate {
1850Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate static int
pm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1890Sstevel@tonic-gate pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1900Sstevel@tonic-gate {
1910Sstevel@tonic-gate 	int		i;
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	switch (cmd) {
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	case DDI_ATTACH:
1960Sstevel@tonic-gate 		if (pmstp->pm_instance != -1)	/* Only allow one instance */
1970Sstevel@tonic-gate 			return (DDI_FAILURE);
1980Sstevel@tonic-gate 		pmstp->pm_instance = ddi_get_instance(dip);
1990Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "pm", S_IFCHR,
2000Sstevel@tonic-gate 		    (pmstp->pm_instance << 8) + 0,
2015295Srandyf 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
2020Sstevel@tonic-gate 			return (DDI_FAILURE);
2030Sstevel@tonic-gate 		}
2040Sstevel@tonic-gate 		pmstp->pm_dip = dip;	/* pm_init and getinfo depend on it */
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2070Sstevel@tonic-gate 			cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL);
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 		ddi_report_dev(dip);
2100Sstevel@tonic-gate 		return (DDI_SUCCESS);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	default:
2130Sstevel@tonic-gate 		return (DDI_FAILURE);
2140Sstevel@tonic-gate 	}
2150Sstevel@tonic-gate }
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate /* ARGSUSED */
2180Sstevel@tonic-gate static int
pm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2190Sstevel@tonic-gate pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate 	int i;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	switch (cmd) {
2240Sstevel@tonic-gate 	case DDI_DETACH:
2250Sstevel@tonic-gate 		/*
2260Sstevel@tonic-gate 		 * Don't detach while idledown timeout is pending.  Note that
2270Sstevel@tonic-gate 		 * we already know we're not in pm_ioctl() due to framework
2280Sstevel@tonic-gate 		 * synchronization, so this is a sufficient test
2290Sstevel@tonic-gate 		 */
2300Sstevel@tonic-gate 		if (pmstp->pm_idledown_id)
2310Sstevel@tonic-gate 			return (DDI_FAILURE);
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2340Sstevel@tonic-gate 			cv_destroy(&pm_clones_cv[i]);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
2370Sstevel@tonic-gate 		pmstp->pm_instance = -1;
2380Sstevel@tonic-gate 		return (DDI_SUCCESS);
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	default:
2410Sstevel@tonic-gate 		return (DDI_FAILURE);
2420Sstevel@tonic-gate 	}
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate static int
pm_close_direct_pm_device(dev_info_t * dip,void * arg)2460Sstevel@tonic-gate pm_close_direct_pm_device(dev_info_t *dip, void *arg)
2470Sstevel@tonic-gate {
2480Sstevel@tonic-gate 	int clone;
2490Sstevel@tonic-gate 	char *pathbuf;
2500Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	clone = *((int *)arg);
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	if (!info)
2550Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2580Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
2590Sstevel@tonic-gate 	if (clone == info->pmi_clone) {
2600Sstevel@tonic-gate 		PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n",
2610Sstevel@tonic-gate 		    PM_DEVICE(dip)))
2620Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip));
2630Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
2640Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2650Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
2660Sstevel@tonic-gate 		/* Bring ourselves up if there is a keeper that is up */
2670Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
2680Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL,
2690Sstevel@tonic-gate 		    pathbuf, PM_DEP_NOWAIT, NULL, 0);
2700Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
2710Sstevel@tonic-gate 		info->pmi_clone = 0;
2720Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2730Sstevel@tonic-gate 	} else {
2740Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	/* restart autopm on device released from direct pm */
2790Sstevel@tonic-gate 	pm_rescan(dip);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate #define	PM_REQ		1
2850Sstevel@tonic-gate #define	NOSTRUCT	2
2860Sstevel@tonic-gate #define	DIP		3
2870Sstevel@tonic-gate #define	NODIP		4
2880Sstevel@tonic-gate #define	NODEP		5
2890Sstevel@tonic-gate #define	DEP		6
2900Sstevel@tonic-gate #define	PM_PSC		7
2915295Srandyf #define	PM_SRCH		8
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate #define	CHECKPERMS	0x001
2940Sstevel@tonic-gate #define	SU		0x002
2950Sstevel@tonic-gate #define	SG		0x004
2960Sstevel@tonic-gate #define	OWNER		0x008
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate #define	INWHO		0x001
2990Sstevel@tonic-gate #define	INDATAINT	0x002
3000Sstevel@tonic-gate #define	INDATASTRING	0x004
3010Sstevel@tonic-gate #define	INDEP		0x008
3020Sstevel@tonic-gate #define	INDATAOUT	0x010
3030Sstevel@tonic-gate #define	INDATA	(INDATAOUT | INDATAINT | INDATASTRING | INDEP)
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate struct pm_cmd_info {
3060Sstevel@tonic-gate 	int cmd;		/* command code */
3070Sstevel@tonic-gate 	char *name;		/* printable string */
3080Sstevel@tonic-gate 	int supported;		/* true if still supported */
3090Sstevel@tonic-gate 	int str_type;		/* PM_REQ or NOSTRUCT */
3100Sstevel@tonic-gate 	int inargs;		/* INWHO, INDATAINT, INDATASTRING, INDEP, */
3110Sstevel@tonic-gate 				/* INDATAOUT */
3120Sstevel@tonic-gate 	int diptype;		/* DIP or NODIP */
3130Sstevel@tonic-gate 	int deptype;		/* DEP or NODEP */
3140Sstevel@tonic-gate 	int permission;		/* SU, GU, or CHECKPERMS */
3150Sstevel@tonic-gate };
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate #ifdef DEBUG
3180Sstevel@tonic-gate char *pm_cmd_string;
3190Sstevel@tonic-gate int pm_cmd;
3200Sstevel@tonic-gate #endif
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate /*
3230Sstevel@tonic-gate  * Returns true if permission granted by credentials
3240Sstevel@tonic-gate  */
3250Sstevel@tonic-gate static int
pm_perms(int perm,cred_t * cr)3260Sstevel@tonic-gate pm_perms(int perm, cred_t *cr)
3270Sstevel@tonic-gate {
3280Sstevel@tonic-gate 	if (perm == 0)			/* no restrictions */
3290Sstevel@tonic-gate 		return (1);
3300Sstevel@tonic-gate 	if (perm == CHECKPERMS)		/* ok for now (is checked later) */
3310Sstevel@tonic-gate 		return (1);
3320Sstevel@tonic-gate 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
3330Sstevel@tonic-gate 		return (1);
3340Sstevel@tonic-gate 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
3350Sstevel@tonic-gate 		return (1);
3360Sstevel@tonic-gate 	return (0);
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate #ifdef DEBUG
3400Sstevel@tonic-gate static int
print_info(dev_info_t * dip,void * arg)3410Sstevel@tonic-gate print_info(dev_info_t *dip, void *arg)
3420Sstevel@tonic-gate {
3430Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
3440Sstevel@tonic-gate 	pm_info_t	*info;
3450Sstevel@tonic-gate 	int		i, j;
3460Sstevel@tonic-gate 	struct pm_component *cp;
3470Sstevel@tonic-gate 	extern int pm_cur_power(pm_component_t *cp);
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
3500Sstevel@tonic-gate 	if (!info)
3510Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
3520Sstevel@tonic-gate 	cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip));
3530Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
3540Sstevel@tonic-gate 		cp = PM_CP(dip, i);
3550Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tThresholds[%d] =",  i);
3560Sstevel@tonic-gate 		for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++)
3570Sstevel@tonic-gate 			cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]);
3580Sstevel@tonic-gate 		cmn_err(CE_CONT, "\n");
3590Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i,
3600Sstevel@tonic-gate 		    pm_cur_power(cp));
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 	if (PM_ISDIRECT(dip))
3630Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tDirect power management\n");
3640Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3650Sstevel@tonic-gate }
3660Sstevel@tonic-gate #endif
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate /*
3690Sstevel@tonic-gate  * command, name, supported, str_type, inargs, diptype, deptype, permission
3700Sstevel@tonic-gate  */
3710Sstevel@tonic-gate static struct pm_cmd_info pmci[] = {
3720Sstevel@tonic-gate 	{PM_SCHEDULE, "PM_SCHEDULE", 0},
3730Sstevel@tonic-gate 	{PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
3740Sstevel@tonic-gate 	{PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
3750Sstevel@tonic-gate 	{PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
3760Sstevel@tonic-gate 	{PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
3770Sstevel@tonic-gate 	{PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
3780Sstevel@tonic-gate 	{PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
3790Sstevel@tonic-gate 	{PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
3800Sstevel@tonic-gate 	{PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
3810Sstevel@tonic-gate 	{PM_GET_DEP, "PM_GET_DEP", 0},
3820Sstevel@tonic-gate 	{PM_ADD_DEP, "PM_ADD_DEP", 0},
3830Sstevel@tonic-gate 	{PM_REM_DEP, "PM_REM_DEP", 0},
3840Sstevel@tonic-gate 	{PM_REM_DEVICE, "PM_REM_DEVICE", 0},
3850Sstevel@tonic-gate 	{PM_REM_DEVICES, "PM_REM_DEVICES", 0},
3860Sstevel@tonic-gate 	{PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP,
3870Sstevel@tonic-gate 	    NODEP},
3880Sstevel@tonic-gate 	{PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
3890Sstevel@tonic-gate 	{PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
3900Sstevel@tonic-gate 	{PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
3910Sstevel@tonic-gate 	{PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ,
3920Sstevel@tonic-gate 	    INWHO, NODIP, NODEP, SU},
3930Sstevel@tonic-gate 	{PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT},
3940Sstevel@tonic-gate 	{PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
3950Sstevel@tonic-gate 	    1, NOSTRUCT},
3960Sstevel@tonic-gate 	{PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT,
3970Sstevel@tonic-gate 	    0, 0, 0, SU},
3980Sstevel@tonic-gate 	{PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3990Sstevel@tonic-gate 	{PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU},
4000Sstevel@tonic-gate 	{PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU},
4010Sstevel@tonic-gate 	{PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT,
4020Sstevel@tonic-gate 	    DIP, NODEP},
4030Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO,
4040Sstevel@tonic-gate 	    DIP, NODEP},
4050Sstevel@tonic-gate 	{PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT,
4060Sstevel@tonic-gate 	    DIP, NODEP},
4070Sstevel@tonic-gate 	{PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ,
4080Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4090Sstevel@tonic-gate 	{PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO,
4100Sstevel@tonic-gate 	    DIP, NODEP},
4110Sstevel@tonic-gate 	{PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ,
4120Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4130Sstevel@tonic-gate 	{PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO,
4140Sstevel@tonic-gate 	    DIP, NODEP},
4150Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC},
4160Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC},
4170Sstevel@tonic-gate 	{PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP,
4180Sstevel@tonic-gate 	    (SU | SG)},
4190Sstevel@tonic-gate 	{PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO,
4200Sstevel@tonic-gate 	    DIP, NODEP},
4210Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC},
4220Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC},
4230Sstevel@tonic-gate 	{PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
4240Sstevel@tonic-gate 	    INWHO, DIP, NODEP, SU},
4250Sstevel@tonic-gate 	{PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
4265295Srandyf 	{PM_GET_AUTOS3_STATE, "PM_GET_AUTOS3_STATE", 1, NOSTRUCT},
4275295Srandyf 	{PM_GET_S3_SUPPORT_STATE, "PM_GET_S3_SUPPORT_STATE", 1, NOSTRUCT},
4280Sstevel@tonic-gate 	{PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
4290Sstevel@tonic-gate 	    DIP, NODEP},
4300Sstevel@tonic-gate 	{PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4310Sstevel@tonic-gate 	    INWHO | INDATAINT, NODIP, NODEP, SU},
4320Sstevel@tonic-gate 	{PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4330Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4340Sstevel@tonic-gate 	{PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU},
4350Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1,
4360Sstevel@tonic-gate 	    PM_REQ, INWHO, DIP, NODEP},
4370Sstevel@tonic-gate 	{PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4380Sstevel@tonic-gate 	    NODEP},
4390Sstevel@tonic-gate 	{PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4400Sstevel@tonic-gate 	    NODEP},
4410Sstevel@tonic-gate 	{PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP,
4420Sstevel@tonic-gate 	    NODEP},
4430Sstevel@tonic-gate 	{PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING,
4440Sstevel@tonic-gate 	    DIP, DEP, SU},
4450Sstevel@tonic-gate 	{PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP},
4460Sstevel@tonic-gate 	{PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ,
4470Sstevel@tonic-gate 	    INWHO | INDATASTRING, NODIP, DEP, SU},
4483028Smh27603 	{PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
449*8906SEric.Saxe@Sun.COM 	{PM_START_CPUPM_EV, "PM_START_CPUPM_EV", 1, NOSTRUCT, 0,
450*8906SEric.Saxe@Sun.COM 	    0, 0, SU},
451*8906SEric.Saxe@Sun.COM 	{PM_START_CPUPM_POLL, "PM_START_CPUPM_POLL", 1, NOSTRUCT, 0,
452*8906SEric.Saxe@Sun.COM 	    0, 0, SU},
4533028Smh27603 	{PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
4543028Smh27603 	{PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
4553028Smh27603 	{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
4563028Smh27603 	    0, 0, 0, SU},
4573028Smh27603 	{PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
4585295Srandyf 	{PM_START_AUTOS3, "PM_START_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
4595295Srandyf 	{PM_STOP_AUTOS3, "PM_STOP_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
4605295Srandyf 	{PM_ENABLE_S3, "PM_ENABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4615295Srandyf 	{PM_DISABLE_S3, "PM_DISABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4625295Srandyf 	{PM_ENTER_S3, "PM_ENTER_S3", 1, NOSTRUCT, 0, 0, 0, SU},
4635295Srandyf 	{PM_SEARCH_LIST, "PM_SEARCH_LIST", 1, PM_SRCH, 0, 0, 0, SU},
4645295Srandyf 	{PM_GET_CMD_NAME, "PM_GET_CMD_NAME", 1, PM_REQ, INDATAOUT, NODIP,
4655295Srandyf 	    NODEP, 0},
466*8906SEric.Saxe@Sun.COM 	{PM_DISABLE_CPU_DEEP_IDLE, "PM_DISABLE_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
467*8906SEric.Saxe@Sun.COM 	    0, 0, SU},
468*8906SEric.Saxe@Sun.COM 	{PM_ENABLE_CPU_DEEP_IDLE, "PM_START_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
469*8906SEric.Saxe@Sun.COM 	    0, 0, SU},
470*8906SEric.Saxe@Sun.COM 	{PM_DEFAULT_CPU_DEEP_IDLE, "PM_DFLT_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
471*8906SEric.Saxe@Sun.COM 	    0, 0, SU},
4720Sstevel@tonic-gate 	{0, NULL}
4730Sstevel@tonic-gate };
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate struct pm_cmd_info *
pc_info(int cmd)4760Sstevel@tonic-gate pc_info(int cmd)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	struct pm_cmd_info *pcip;
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	for (pcip = pmci; pcip->name; pcip++) {
4810Sstevel@tonic-gate 		if (cmd == pcip->cmd)
4820Sstevel@tonic-gate 			return (pcip);
4830Sstevel@tonic-gate 	}
4840Sstevel@tonic-gate 	return (NULL);
4850Sstevel@tonic-gate }
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate static char *
pm_decode_cmd(int cmd)4880Sstevel@tonic-gate pm_decode_cmd(int cmd)
4890Sstevel@tonic-gate {
4900Sstevel@tonic-gate 	static char invbuf[64];
4910Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
4920Sstevel@tonic-gate 	if (pcip != NULL)
4930Sstevel@tonic-gate 		return (pcip->name);
4940Sstevel@tonic-gate 	(void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd);
4950Sstevel@tonic-gate 	return (invbuf);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate /*
4990Sstevel@tonic-gate  * Allocate scan resource, create taskq, then dispatch scan,
5000Sstevel@tonic-gate  * called only if autopm is enabled.
5010Sstevel@tonic-gate  */
5020Sstevel@tonic-gate int
pm_start_pm_walk(dev_info_t * dip,void * arg)5030Sstevel@tonic-gate pm_start_pm_walk(dev_info_t *dip, void *arg)
5040Sstevel@tonic-gate {
5053028Smh27603 	int cmd = *((int *)arg);
5063839Skchow #ifdef PMDDEBUG
5073028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
5083839Skchow #endif
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
5110Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5120Sstevel@tonic-gate 
5133028Smh27603 	switch (cmd) {
5143028Smh27603 	case PM_START_CPUPM:
515*8906SEric.Saxe@Sun.COM 	case PM_START_CPUPM_POLL:
5163028Smh27603 		if (!PM_ISCPU(dip))
5173028Smh27603 			return (DDI_WALK_CONTINUE);
5183028Smh27603 		mutex_enter(&pm_scan_lock);
519*8906SEric.Saxe@Sun.COM 		if (!PM_CPUPM_DISABLED && !PM_EVENT_CPUPM)
5203028Smh27603 			pm_scan_init(dip);
5213028Smh27603 		mutex_exit(&pm_scan_lock);
5223028Smh27603 		break;
5233028Smh27603 	case PM_START_PM:
5243028Smh27603 		mutex_enter(&pm_scan_lock);
525*8906SEric.Saxe@Sun.COM 		if (PM_ISCPU(dip) && (PM_CPUPM_DISABLED || PM_EVENT_CPUPM)) {
5263028Smh27603 			mutex_exit(&pm_scan_lock);
5273028Smh27603 			return (DDI_WALK_CONTINUE);
5283028Smh27603 		}
5293028Smh27603 		if (autopm_enabled)
5303028Smh27603 			pm_scan_init(dip);
5313028Smh27603 		mutex_exit(&pm_scan_lock);
5323028Smh27603 		break;
5333028Smh27603 	}
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	/*
5360Sstevel@tonic-gate 	 * Start doing pm on device: ensure pm_scan data structure initiated,
5373028Smh27603 	 * no need to guarantee a successful scan run.
5380Sstevel@tonic-gate 	 */
5390Sstevel@tonic-gate 	PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
5400Sstevel@tonic-gate 	    PM_DEVICE(dip)))
5410Sstevel@tonic-gate 	pm_rescan(dip);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate /*
5470Sstevel@tonic-gate  * Bring devices to full power level, then stop scan
5480Sstevel@tonic-gate  */
5490Sstevel@tonic-gate int
pm_stop_pm_walk(dev_info_t * dip,void * arg)5500Sstevel@tonic-gate pm_stop_pm_walk(dev_info_t *dip, void *arg)
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
5533028Smh27603 	int cmd = *((int *)arg);
5543839Skchow #ifdef PMDDEBUG
5553028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
5563839Skchow #endif
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	if (!info)
5590Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5603028Smh27603 
5613028Smh27603 	switch (cmd) {
5623028Smh27603 	case PM_STOP_PM:
5633028Smh27603 		/*
5643028Smh27603 		 * If CPU devices are being managed independently, then don't
5653028Smh27603 		 * stop them as part of PM_STOP_PM. Only stop them as part of
5663028Smh27603 		 * PM_STOP_CPUPM and PM_RESET_PM.
5673028Smh27603 		 */
568*8906SEric.Saxe@Sun.COM 		if (PM_ISCPU(dip) && PM_POLLING_CPUPM)
5693028Smh27603 			return (DDI_WALK_CONTINUE);
5703028Smh27603 		break;
5713028Smh27603 	case PM_STOP_CPUPM:
5723028Smh27603 		/*
5733028Smh27603 		 * If stopping CPU devices and this device is not marked
5743028Smh27603 		 * as a CPU device, then skip.
5753028Smh27603 		 */
5763028Smh27603 		if (!PM_ISCPU(dip))
5773028Smh27603 			return (DDI_WALK_CONTINUE);
5783028Smh27603 		break;
5793028Smh27603 	}
5803028Smh27603 
5810Sstevel@tonic-gate 	/*
5820Sstevel@tonic-gate 	 * Stop the current scan, and then bring it back to normal power.
5830Sstevel@tonic-gate 	 */
5840Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
5850Sstevel@tonic-gate 		PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
5860Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
5870Sstevel@tonic-gate 		pm_scan_stop(dip);
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
5910Sstevel@tonic-gate 	    !pm_all_at_normal(dip)) {
5920Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
5930Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_DETACHING) {
5940Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
5950Sstevel@tonic-gate 			    "all_to_normal because %s@%s(%s#%d) is detaching\n",
5960Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
5970Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
5980Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
5990Sstevel@tonic-gate 			return (DDI_WALK_CONTINUE);
6000Sstevel@tonic-gate 		}
6010Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
6020Sstevel@tonic-gate 		if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
6030Sstevel@tonic-gate 			PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
6040Sstevel@tonic-gate 			    "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
6050Sstevel@tonic-gate 		}
6060Sstevel@tonic-gate 	}
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6090Sstevel@tonic-gate }
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate static int
pm_start_idledown(dev_info_t * dip,void * arg)6120Sstevel@tonic-gate pm_start_idledown(dev_info_t *dip, void *arg)
6130Sstevel@tonic-gate {
6140Sstevel@tonic-gate 	int		flag = (int)(intptr_t)arg;
6150Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	if (!scanp)
6180Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6210Sstevel@tonic-gate 	scanp->ps_idle_down |= flag;
6220Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6230Sstevel@tonic-gate 	pm_rescan(dip);
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6260Sstevel@tonic-gate }
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate /*ARGSUSED*/
6290Sstevel@tonic-gate static int
pm_end_idledown(dev_info_t * dip,void * ignore)6300Sstevel@tonic-gate pm_end_idledown(dev_info_t *dip, void *ignore)
6310Sstevel@tonic-gate {
6320Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	if (!scanp)
6350Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6380Sstevel@tonic-gate 	/*
6390Sstevel@tonic-gate 	 * The PMID_TIMERS bits are place holder till idledown expires.
6400Sstevel@tonic-gate 	 * The bits are also the base for regenerating PMID_SCANS bits.
6410Sstevel@tonic-gate 	 * While it's up to scan thread to clear up the PMID_SCANS bits
6420Sstevel@tonic-gate 	 * after each scan run, PMID_TIMERS ensure aggressive scan down
6430Sstevel@tonic-gate 	 * performance throughout the idledown period.
6440Sstevel@tonic-gate 	 */
6450Sstevel@tonic-gate 	scanp->ps_idle_down &= ~PMID_TIMERS;
6460Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6490Sstevel@tonic-gate }
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate /*ARGSUSED*/
6520Sstevel@tonic-gate static void
pm_end_idledown_walk(void * ignore)6530Sstevel@tonic-gate pm_end_idledown_walk(void *ignore)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
6560Sstevel@tonic-gate 	    "off\n", (ulong_t)pmstp->pm_idledown_id));
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6590Sstevel@tonic-gate 	pmstp->pm_idledown_id = 0;
6600Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
6630Sstevel@tonic-gate }
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate /*
6660Sstevel@tonic-gate  * pm_timeout_idledown - keep idledown effect for 10 seconds.
6670Sstevel@tonic-gate  *
6680Sstevel@tonic-gate  * Return 0 if another competing caller scheduled idledown timeout,
6690Sstevel@tonic-gate  * otherwise, return idledown timeout_id.
6700Sstevel@tonic-gate  */
6710Sstevel@tonic-gate static timeout_id_t
pm_timeout_idledown(void)6720Sstevel@tonic-gate pm_timeout_idledown(void)
6730Sstevel@tonic-gate {
6740Sstevel@tonic-gate 	timeout_id_t	to_id;
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	/*
6770Sstevel@tonic-gate 	 * Keep idle-down in effect for either 10 seconds
6780Sstevel@tonic-gate 	 * or length of a scan interval, which ever is greater.
6790Sstevel@tonic-gate 	 */
6800Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6810Sstevel@tonic-gate 	if (pmstp->pm_idledown_id != 0) {
6820Sstevel@tonic-gate 		to_id = pmstp->pm_idledown_id;
6830Sstevel@tonic-gate 		pmstp->pm_idledown_id = 0;
6840Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6850Sstevel@tonic-gate 		(void) untimeout(to_id);
6860Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6870Sstevel@tonic-gate 		if (pmstp->pm_idledown_id != 0) {
6880Sstevel@tonic-gate 			PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
6890Sstevel@tonic-gate 			    "another caller got it, idledown_id(%lx)!\n",
6900Sstevel@tonic-gate 			    (ulong_t)pmstp->pm_idledown_id))
6910Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
6920Sstevel@tonic-gate 			return (0);
6930Sstevel@tonic-gate 		}
6940Sstevel@tonic-gate 	}
6950Sstevel@tonic-gate 	pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
6960Sstevel@tonic-gate 	    PM_IDLEDOWN_TIME * hz);
6970Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
6980Sstevel@tonic-gate 	    (ulong_t)pmstp->pm_idledown_id))
6990Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	return (pmstp->pm_idledown_id);
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate static int
pm_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)7050Sstevel@tonic-gate pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
7060Sstevel@tonic-gate 	struct pollhead **phpp)
7070Sstevel@tonic-gate {
7080Sstevel@tonic-gate 	extern struct pollhead pm_pollhead;	/* common/os/sunpm.c */
7090Sstevel@tonic-gate 	int	clone;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
7120Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
7130Sstevel@tonic-gate 	if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
7140Sstevel@tonic-gate 		*reventsp |= (POLLIN | POLLRDNORM);
7150Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
7160Sstevel@tonic-gate 	} else {
7170Sstevel@tonic-gate 		*reventsp = 0;
7180Sstevel@tonic-gate 		if (!anyyet) {
7190Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
7200Sstevel@tonic-gate 			*phpp = &pm_pollhead;
7210Sstevel@tonic-gate 		}
7220Sstevel@tonic-gate #ifdef DEBUG
7230Sstevel@tonic-gate 		else {
7240Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
7250Sstevel@tonic-gate 		}
7260Sstevel@tonic-gate #endif
7270Sstevel@tonic-gate 	}
7280Sstevel@tonic-gate 	return (0);
7290Sstevel@tonic-gate }
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate /*
7320Sstevel@tonic-gate  * called by pm_dicard_entries to free up the memory. It also decrements
7330Sstevel@tonic-gate  * pm_poll_cnt, if direct is non zero.
7340Sstevel@tonic-gate  */
7350Sstevel@tonic-gate static void
pm_free_entries(psce_t * pscep,int clone,int direct)7360Sstevel@tonic-gate pm_free_entries(psce_t *pscep, int clone, int direct)
7370Sstevel@tonic-gate {
7380Sstevel@tonic-gate 	pm_state_change_t	*p;
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	if (pscep) {
7410Sstevel@tonic-gate 		p = pscep->psce_out;
7420Sstevel@tonic-gate 		while (p->size) {
7430Sstevel@tonic-gate 			if (direct) {
7440Sstevel@tonic-gate 				PMD(PMD_IOCTL, ("ioctl: discard: "
7450Sstevel@tonic-gate 				    "pm_poll_cnt[%d] is %d before "
7460Sstevel@tonic-gate 				    "ASSERT\n", clone,
7470Sstevel@tonic-gate 				    pm_poll_cnt[clone]))
7480Sstevel@tonic-gate 				ASSERT(pm_poll_cnt[clone]);
7490Sstevel@tonic-gate 				pm_poll_cnt[clone]--;
7500Sstevel@tonic-gate 			}
7510Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
7520Sstevel@tonic-gate 			p->size = 0;
7530Sstevel@tonic-gate 			if (p == pscep->psce_last)
7540Sstevel@tonic-gate 				p = pscep->psce_first;
7550Sstevel@tonic-gate 			else
7560Sstevel@tonic-gate 				p++;
7570Sstevel@tonic-gate 		}
7580Sstevel@tonic-gate 		pscep->psce_out = pscep->psce_first;
7590Sstevel@tonic-gate 		pscep->psce_in = pscep->psce_first;
7600Sstevel@tonic-gate 		mutex_exit(&pscep->psce_lock);
7610Sstevel@tonic-gate 	}
7620Sstevel@tonic-gate }
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate /*
7650Sstevel@tonic-gate  * Discard entries for this clone. Calls pm_free_entries to free up memory.
7660Sstevel@tonic-gate  */
7670Sstevel@tonic-gate static void
pm_discard_entries(int clone)7680Sstevel@tonic-gate pm_discard_entries(int clone)
7690Sstevel@tonic-gate {
7700Sstevel@tonic-gate 	psce_t	*pscep;
7710Sstevel@tonic-gate 	int			direct = 0;
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
7740Sstevel@tonic-gate 	if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
7750Sstevel@tonic-gate 		direct = 1;
7760Sstevel@tonic-gate 	pm_free_entries(pscep, clone, direct);
7770Sstevel@tonic-gate 	pscep = pm_psc_clone_to_interest(clone);
7780Sstevel@tonic-gate 	pm_free_entries(pscep, clone, 0);
7790Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
7800Sstevel@tonic-gate }
7810Sstevel@tonic-gate 
7823028Smh27603 
7833028Smh27603 static void
pm_set_idle_threshold(dev_info_t * dip,int thresh,int flag)7843028Smh27603 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
7850Sstevel@tonic-gate {
7860Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
7870Sstevel@tonic-gate 		switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
7880Sstevel@tonic-gate 		case PMC_DEF_THRESH:
7893028Smh27603 		case PMC_CPU_THRESH:
7903028Smh27603 			PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
7910Sstevel@tonic-gate 			    "%s@%s(%s#%d) default thresh to 0t%d\n",
7923028Smh27603 			    PM_DEVICE(dip), thresh))
7933028Smh27603 			pm_set_device_threshold(dip, thresh, flag);
7940Sstevel@tonic-gate 			break;
7950Sstevel@tonic-gate 		default:
7960Sstevel@tonic-gate 			break;
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 	}
7993028Smh27603 }
8000Sstevel@tonic-gate 
8013028Smh27603 static int
pm_set_idle_thresh_walk(dev_info_t * dip,void * arg)8023028Smh27603 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
8033028Smh27603 {
8043028Smh27603 	int cmd = *((int *)arg);
8053028Smh27603 
8063028Smh27603 	if (!PM_GET_PM_INFO(dip))
8073028Smh27603 		return (DDI_WALK_CONTINUE);
8083028Smh27603 
8093028Smh27603 	switch (cmd) {
8103028Smh27603 	case PM_SET_SYSTEM_THRESHOLD:
8113028Smh27603 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
8123028Smh27603 			break;
8133028Smh27603 		pm_set_idle_threshold(dip, pm_system_idle_threshold,
8143028Smh27603 		    PMC_DEF_THRESH);
8150Sstevel@tonic-gate 		pm_rescan(dip);
8163028Smh27603 		break;
8173028Smh27603 	case PM_SET_CPU_THRESHOLD:
8183028Smh27603 		if (!PM_ISCPU(dip))
8193028Smh27603 			break;
8203028Smh27603 		pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
8213028Smh27603 		    PMC_CPU_THRESH);
8223028Smh27603 		pm_rescan(dip);
8233028Smh27603 		break;
8243028Smh27603 	}
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
8270Sstevel@tonic-gate }
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate /*ARGSUSED*/
8300Sstevel@tonic-gate static int
pm_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8310Sstevel@tonic-gate pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
8320Sstevel@tonic-gate {
8330Sstevel@tonic-gate 	dev_t	dev;
8340Sstevel@tonic-gate 	int	instance;
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	switch (infocmd) {
8370Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
8380Sstevel@tonic-gate 		if (pmstp->pm_instance == -1)
8390Sstevel@tonic-gate 			return (DDI_FAILURE);
8400Sstevel@tonic-gate 		*result = pmstp->pm_dip;
8410Sstevel@tonic-gate 		return (DDI_SUCCESS);
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8440Sstevel@tonic-gate 		dev = (dev_t)arg;
8450Sstevel@tonic-gate 		instance = getminor(dev) >> 8;
8460Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8470Sstevel@tonic-gate 		return (DDI_SUCCESS);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	default:
8500Sstevel@tonic-gate 		return (DDI_FAILURE);
8510Sstevel@tonic-gate 	}
8520Sstevel@tonic-gate }
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate /*ARGSUSED1*/
8560Sstevel@tonic-gate static int
pm_open(dev_t * devp,int flag,int otyp,cred_t * cr)8570Sstevel@tonic-gate pm_open(dev_t *devp, int flag, int otyp, cred_t *cr)
8580Sstevel@tonic-gate {
8590Sstevel@tonic-gate 	int		clone;
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8620Sstevel@tonic-gate 		return (EINVAL);
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
8650Sstevel@tonic-gate 	for (clone = 1; clone < PM_MAX_CLONE; clone++)
8660Sstevel@tonic-gate 		if (!pmstp->pm_clones[clone])
8670Sstevel@tonic-gate 			break;
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	if (clone == PM_MAX_CLONE) {
8700Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
8710Sstevel@tonic-gate 		return (ENXIO);
8720Sstevel@tonic-gate 	}
8730Sstevel@tonic-gate 	pmstp->pm_cred[clone] = cr;
8740Sstevel@tonic-gate 	crhold(cr);
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
8770Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 1;
8780Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	return (0);
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate /*ARGSUSED1*/
8840Sstevel@tonic-gate static int
pm_close(dev_t dev,int flag,int otyp,cred_t * cr)8850Sstevel@tonic-gate pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
8860Sstevel@tonic-gate {
8870Sstevel@tonic-gate 	int clone;
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8900Sstevel@tonic-gate 		return (EINVAL);
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
8930Sstevel@tonic-gate 	PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
8940Sstevel@tonic-gate 	    clone))
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 	/*
8970Sstevel@tonic-gate 	 * Walk the entire device tree to find the corresponding
8980Sstevel@tonic-gate 	 * device and operate on it.
8990Sstevel@tonic-gate 	 */
9000Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
9010Sstevel@tonic-gate 	    (void *) &clone);
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	crfree(pmstp->pm_cred[clone]);
9040Sstevel@tonic-gate 	pmstp->pm_cred[clone] = 0;
9050Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 0;
9060Sstevel@tonic-gate 	pm_discard_entries(clone);
9070Sstevel@tonic-gate 	ASSERT(pm_poll_cnt[clone] == 0);
9080Sstevel@tonic-gate 	pm_deregister_watcher(clone, NULL);
9090Sstevel@tonic-gate 	return (0);
9100Sstevel@tonic-gate }
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate /*ARGSUSED*/
9130Sstevel@tonic-gate static int
pm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval_p)9140Sstevel@tonic-gate pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
9150Sstevel@tonic-gate {
9160Sstevel@tonic-gate 	struct pm_cmd_info *pc_info(int);
9170Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
9180Sstevel@tonic-gate 	pm_req_t	req;
9190Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
9200Sstevel@tonic-gate 	pm_info_t	*info = NULL;
9210Sstevel@tonic-gate 	int		clone;
9220Sstevel@tonic-gate 	char		*cmdstr = pm_decode_cmd(cmd);
9230Sstevel@tonic-gate 	/*
9240Sstevel@tonic-gate 	 * To keep devinfo nodes from going away while we're holding a
9250Sstevel@tonic-gate 	 * pointer to their dip, pm_name_to_dip() optionally holds
9260Sstevel@tonic-gate 	 * the devinfo node.  If we've done that, we set dipheld
9270Sstevel@tonic-gate 	 * so we know at the end of the ioctl processing to release the
9280Sstevel@tonic-gate 	 * node again.
9290Sstevel@tonic-gate 	 */
9300Sstevel@tonic-gate 	int		dipheld = 0;
9310Sstevel@tonic-gate 	int		icount = 0;
9320Sstevel@tonic-gate 	int		i;
9330Sstevel@tonic-gate 	int		comps;
9340Sstevel@tonic-gate 	size_t		lencopied;
9350Sstevel@tonic-gate 	int		ret = ENOTTY;
9360Sstevel@tonic-gate 	int		curpower;
9370Sstevel@tonic-gate 	char		who[MAXNAMELEN];
9380Sstevel@tonic-gate 	size_t		wholen;			/* copyinstr length */
9390Sstevel@tonic-gate 	size_t		deplen = MAXNAMELEN;
9400Sstevel@tonic-gate 	char		*dep, i_dep_buf[MAXNAMELEN];
9415295Srandyf 	char		pathbuf[MAXNAMELEN];
9420Sstevel@tonic-gate 	struct pm_component *cp;
9430Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9440Sstevel@tonic-gate 	pm_state_change32_t		*pscp32;
9450Sstevel@tonic-gate 	pm_state_change32_t		psc32;
9465295Srandyf 	pm_searchargs32_t		psa32;
9470Sstevel@tonic-gate 	size_t				copysize32;
9480Sstevel@tonic-gate #endif
9490Sstevel@tonic-gate 	pm_state_change_t		*pscp;
9500Sstevel@tonic-gate 	pm_state_change_t		psc;
9515295Srandyf 	pm_searchargs_t		psa;
9525295Srandyf 	char		listname[MAXCOPYBUF];
9535295Srandyf 	char		manufacturer[MAXCOPYBUF];
9545295Srandyf 	char		product[MAXCOPYBUF];
9550Sstevel@tonic-gate 	size_t		copysize;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate #ifdef DEBUG
9600Sstevel@tonic-gate 	if (cmd == 666) {
9610Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), print_info, NULL);
9620Sstevel@tonic-gate 		return (0);
9630Sstevel@tonic-gate 	}
9640Sstevel@tonic-gate 	ret = 0x0badcafe;			/* sanity checking */
9650Sstevel@tonic-gate 	pm_cmd = cmd;				/* for ASSERT debugging */
9660Sstevel@tonic-gate 	pm_cmd_string = cmdstr;	/* for ASSERT debugging */
9670Sstevel@tonic-gate #endif
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	if (pcip == NULL) {
9710Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
9720Sstevel@tonic-gate 		return (ENOTTY);
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 	if (pcip == NULL || pcip->supported == 0) {
9750Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
9760Sstevel@tonic-gate 		    pcip->name))
9770Sstevel@tonic-gate 		return (ENOTTY);
9780Sstevel@tonic-gate 	}
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	wholen = 0;
9810Sstevel@tonic-gate 	dep = i_dep_buf;
9820Sstevel@tonic-gate 	i_dep_buf[0] = 0;
9830Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
9840Sstevel@tonic-gate 	if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
9850Sstevel@tonic-gate 		ret = EPERM;
9860Sstevel@tonic-gate 		return (ret);
9870Sstevel@tonic-gate 	}
9880Sstevel@tonic-gate 	switch (pcip->str_type) {
9890Sstevel@tonic-gate 	case PM_REQ:
9905295Srandyf 	{
9910Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9920Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9930Sstevel@tonic-gate 			pm_req32_t	req32;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &req32,
9960Sstevel@tonic-gate 			    sizeof (req32), mode) != 0) {
9970Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
9980Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
9990Sstevel@tonic-gate 				ret = EFAULT;
10000Sstevel@tonic-gate 				break;
10010Sstevel@tonic-gate 			}
10020Sstevel@tonic-gate 			req.component = req32.component;
10030Sstevel@tonic-gate 			req.value = req32.value;
10040Sstevel@tonic-gate 			req.datasize = req32.datasize;
10050Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
10060Sstevel@tonic-gate 				ret = copyinstr((char *)(uintptr_t)
10070Sstevel@tonic-gate 				    req32.physpath, who, MAXNAMELEN, &wholen);
10080Sstevel@tonic-gate 				if (ret) {
10090Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
10100Sstevel@tonic-gate 					    "copyinstr fails returning %d\n",
10110Sstevel@tonic-gate 					    cmdstr, ret))
10120Sstevel@tonic-gate 					break;
10130Sstevel@tonic-gate 				}
10140Sstevel@tonic-gate 				req.physpath = who;
10155295Srandyf 				PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
10165295Srandyf 				    cmdstr, req.physpath))
10170Sstevel@tonic-gate 			}
10180Sstevel@tonic-gate 			if (pcip->inargs & INDATA) {
10190Sstevel@tonic-gate 				req.data = (void *)(uintptr_t)req32.data;
10200Sstevel@tonic-gate 				req.datasize = req32.datasize;
10210Sstevel@tonic-gate 			} else {
10220Sstevel@tonic-gate 				req.data = NULL;
10230Sstevel@tonic-gate 				req.datasize = 0;
10240Sstevel@tonic-gate 			}
10250Sstevel@tonic-gate 			switch (pcip->diptype) {
10260Sstevel@tonic-gate 			case DIP:
10270Sstevel@tonic-gate 				if (!(dip =
10280Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
10290Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
10300Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
10310Sstevel@tonic-gate 					    cmdstr, req.physpath))
10320Sstevel@tonic-gate 					return (ENODEV);
10330Sstevel@tonic-gate 				}
10340Sstevel@tonic-gate 				ASSERT(!dipheld);
10350Sstevel@tonic-gate 				dipheld++;
10360Sstevel@tonic-gate 				break;
10370Sstevel@tonic-gate 			case NODIP:
10380Sstevel@tonic-gate 				break;
10390Sstevel@tonic-gate 			default:
10400Sstevel@tonic-gate 				/*
10410Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
10420Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
10430Sstevel@tonic-gate 				 */
10440Sstevel@tonic-gate #ifdef	DEBUG
10450Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
10460Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
10470Sstevel@tonic-gate #endif
10480Sstevel@tonic-gate 				ASSERT(0);
10490Sstevel@tonic-gate 				return (EIO);
10500Sstevel@tonic-gate 			}
10510Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
10520Sstevel@tonic-gate 				int32_t int32buf;
10530Sstevel@tonic-gate 				int32_t *i32p;
10540Sstevel@tonic-gate 				int *ip;
10550Sstevel@tonic-gate 				icount = req32.datasize / sizeof (int32_t);
10560Sstevel@tonic-gate 				if (icount <= 0) {
10570Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
10580Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
10590Sstevel@tonic-gate 					ret = EFAULT;
10600Sstevel@tonic-gate 					break;
10610Sstevel@tonic-gate 				}
10620Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
10630Sstevel@tonic-gate 				req.datasize = icount * sizeof (int);
10640Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
10650Sstevel@tonic-gate 				ip = req.data;
10660Sstevel@tonic-gate 				ret = 0;
10670Sstevel@tonic-gate 				for (i = 0,
10680Sstevel@tonic-gate 				    i32p = (int32_t *)(uintptr_t)req32.data;
10690Sstevel@tonic-gate 				    i < icount; i++, i32p++) {
10700Sstevel@tonic-gate 					if (ddi_copyin((void *)i32p, &int32buf,
10710Sstevel@tonic-gate 					    sizeof (int32_t), mode)) {
10720Sstevel@tonic-gate 						kmem_free(req.data,
10730Sstevel@tonic-gate 						    req.datasize);
10740Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10750Sstevel@tonic-gate 						    "entry %d EFAULT\n",
10760Sstevel@tonic-gate 						    cmdstr, i))
10770Sstevel@tonic-gate 						ret = EFAULT;
10780Sstevel@tonic-gate 						break;
10790Sstevel@tonic-gate 					}
10800Sstevel@tonic-gate 					*ip++ = (int)int32buf;
10810Sstevel@tonic-gate 				}
10820Sstevel@tonic-gate 				if (ret)
10830Sstevel@tonic-gate 					break;
10840Sstevel@tonic-gate 			}
10850Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
10860Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
10870Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
10880Sstevel@tonic-gate 				if (req32.data != NULL) {
10890Sstevel@tonic-gate 					if (copyinstr((void *)(uintptr_t)
10905295Srandyf 					    req32.data, dep, deplen, NULL)) {
10910Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10920Sstevel@tonic-gate 						    "0x%p dep size %lx, EFAULT"
10930Sstevel@tonic-gate 						    "\n", cmdstr,
10940Sstevel@tonic-gate 						    (void *)req.data, deplen))
10950Sstevel@tonic-gate 						ret = EFAULT;
10960Sstevel@tonic-gate 						break;
10970Sstevel@tonic-gate 					}
10980Sstevel@tonic-gate #ifdef DEBUG
10990Sstevel@tonic-gate 					else {
11000Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
11010Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
11020Sstevel@tonic-gate 					}
11030Sstevel@tonic-gate #endif
11040Sstevel@tonic-gate 				} else {
11050Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
11060Sstevel@tonic-gate 					    "dependent\n", cmdstr))
11070Sstevel@tonic-gate 					ret = EINVAL;
11080Sstevel@tonic-gate 					break;
11090Sstevel@tonic-gate 				}
11100Sstevel@tonic-gate 			}
11110Sstevel@tonic-gate 		} else
11120Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
11130Sstevel@tonic-gate 		{
11140Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg,
11150Sstevel@tonic-gate 			    &req, sizeof (req), mode) != 0) {
11160Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11170Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
11180Sstevel@tonic-gate 				ret = EFAULT;
11190Sstevel@tonic-gate 				break;
11200Sstevel@tonic-gate 			}
11210Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
11220Sstevel@tonic-gate 				ret = copyinstr((char *)req.physpath, who,
11230Sstevel@tonic-gate 				    MAXNAMELEN, &wholen);
11240Sstevel@tonic-gate 				if (ret) {
11250Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s copyinstr"
11260Sstevel@tonic-gate 					    " fails returning %d\n", cmdstr,
11270Sstevel@tonic-gate 					    ret))
11280Sstevel@tonic-gate 					break;
11290Sstevel@tonic-gate 				}
11300Sstevel@tonic-gate 				req.physpath = who;
11315295Srandyf 				PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
11325295Srandyf 				    cmdstr, req.physpath))
11330Sstevel@tonic-gate 			}
11340Sstevel@tonic-gate 			if (!(pcip->inargs & INDATA)) {
11350Sstevel@tonic-gate 				req.data = NULL;
11360Sstevel@tonic-gate 				req.datasize = 0;
11370Sstevel@tonic-gate 			}
11380Sstevel@tonic-gate 			switch (pcip->diptype) {
11390Sstevel@tonic-gate 			case DIP:
11400Sstevel@tonic-gate 				if (!(dip =
11410Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
11420Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
11430Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
11440Sstevel@tonic-gate 					    cmdstr, req.physpath))
11450Sstevel@tonic-gate 					return (ENODEV);
11460Sstevel@tonic-gate 				}
11470Sstevel@tonic-gate 				ASSERT(!dipheld);
11480Sstevel@tonic-gate 				dipheld++;
11490Sstevel@tonic-gate 				break;
11500Sstevel@tonic-gate 			case NODIP:
11510Sstevel@tonic-gate 				break;
11520Sstevel@tonic-gate 			default:
11530Sstevel@tonic-gate 				/*
11540Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
11550Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
11560Sstevel@tonic-gate 				 */
11570Sstevel@tonic-gate #ifdef	DEBUG
11580Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
11590Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
11600Sstevel@tonic-gate #endif
11610Sstevel@tonic-gate 				ASSERT(0);
11620Sstevel@tonic-gate 				return (EIO);
11630Sstevel@tonic-gate 			}
11640Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
11650Sstevel@tonic-gate 				int *ip;
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
11680Sstevel@tonic-gate 				ip = req.data;
11690Sstevel@tonic-gate 				icount = req.datasize / sizeof (int);
11700Sstevel@tonic-gate 				if (icount <= 0) {
11710Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
11720Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
11730Sstevel@tonic-gate 					ret = EFAULT;
11740Sstevel@tonic-gate 					break;
11750Sstevel@tonic-gate 				}
11760Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
11770Sstevel@tonic-gate 				if (ddi_copyin((caddr_t)ip, req.data,
11780Sstevel@tonic-gate 				    req.datasize, mode) != 0) {
11790Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11800Sstevel@tonic-gate 					    "EFAULT\n\n", cmdstr))
11810Sstevel@tonic-gate 					ret = EFAULT;
11820Sstevel@tonic-gate 					break;
11830Sstevel@tonic-gate 				}
11840Sstevel@tonic-gate 			}
11850Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
11860Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
11870Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
11880Sstevel@tonic-gate 				if (req.data != NULL) {
11890Sstevel@tonic-gate 					if (copyinstr((caddr_t)req.data,
11905295Srandyf 					    dep, deplen, NULL)) {
11910Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
11920Sstevel@tonic-gate 						    "0x%p dep size %lu, "
11930Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
11940Sstevel@tonic-gate 						    (void *)req.data, deplen))
11950Sstevel@tonic-gate 						ret = EFAULT;
11960Sstevel@tonic-gate 						break;
11970Sstevel@tonic-gate 					}
11980Sstevel@tonic-gate #ifdef DEBUG
11990Sstevel@tonic-gate 					else {
12000Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
12010Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
12020Sstevel@tonic-gate 					}
12030Sstevel@tonic-gate #endif
12040Sstevel@tonic-gate 				} else {
12050Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
12060Sstevel@tonic-gate 					    "dependent\n", cmdstr))
12070Sstevel@tonic-gate 					ret = EINVAL;
12080Sstevel@tonic-gate 					break;
12090Sstevel@tonic-gate 				}
12100Sstevel@tonic-gate 			}
12110Sstevel@tonic-gate 		}
12120Sstevel@tonic-gate 		/*
12130Sstevel@tonic-gate 		 * Now we've got all the args in for the commands that
12140Sstevel@tonic-gate 		 * use the new pm_req struct.
12150Sstevel@tonic-gate 		 */
12160Sstevel@tonic-gate 		switch (cmd) {
12170Sstevel@tonic-gate 		case PM_REPARSE_PM_PROPS:
12180Sstevel@tonic-gate 		{
12190Sstevel@tonic-gate 			struct dev_ops	*drv;
12200Sstevel@tonic-gate 			struct cb_ops	*cb;
12210Sstevel@tonic-gate 			void		*propval;
12220Sstevel@tonic-gate 			int length;
12230Sstevel@tonic-gate 			/*
12240Sstevel@tonic-gate 			 * This ioctl is provided only for the ddivs pm test.
12250Sstevel@tonic-gate 			 * We only do it to a driver which explicitly allows
12260Sstevel@tonic-gate 			 * us to do so by exporting a pm-reparse-ok property.
12270Sstevel@tonic-gate 			 * We only care whether the property exists or not.
12280Sstevel@tonic-gate 			 */
12290Sstevel@tonic-gate 			if ((drv = ddi_get_driver(dip)) == NULL) {
12300Sstevel@tonic-gate 				ret = EINVAL;
12310Sstevel@tonic-gate 				break;
12320Sstevel@tonic-gate 			}
12330Sstevel@tonic-gate 			if ((cb = drv->devo_cb_ops) != NULL) {
12340Sstevel@tonic-gate 				if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
12350Sstevel@tonic-gate 				    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12360Sstevel@tonic-gate 				    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12370Sstevel@tonic-gate 				    "pm-reparse-ok", (caddr_t)&propval,
12380Sstevel@tonic-gate 				    &length) != DDI_SUCCESS) {
12390Sstevel@tonic-gate 					ret = EINVAL;
12400Sstevel@tonic-gate 					break;
12410Sstevel@tonic-gate 				}
12420Sstevel@tonic-gate 			} else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
12430Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12440Sstevel@tonic-gate 			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12450Sstevel@tonic-gate 			    "pm-reparse-ok", (caddr_t)&propval,
12460Sstevel@tonic-gate 			    &length) != DDI_SUCCESS) {
12470Sstevel@tonic-gate 				ret = EINVAL;
12480Sstevel@tonic-gate 				break;
12490Sstevel@tonic-gate 			}
12500Sstevel@tonic-gate 			kmem_free(propval, length);
12510Sstevel@tonic-gate 			ret =  e_new_pm_props(dip);
12520Sstevel@tonic-gate 			break;
12530Sstevel@tonic-gate 		}
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD:
12565295Srandyf 		{
12570Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12580Sstevel@tonic-gate 			if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
12590Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12600Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
12610Sstevel@tonic-gate 				    cmdstr))
12620Sstevel@tonic-gate 				ret = ENODEV;
12630Sstevel@tonic-gate 				break;
12640Sstevel@tonic-gate 			}
12650Sstevel@tonic-gate 			*rval_p = DEVI(dip)->devi_pm_dev_thresh;
12660Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12670Sstevel@tonic-gate 			ret = 0;
12680Sstevel@tonic-gate 			break;
12695295Srandyf 		}
12700Sstevel@tonic-gate 
12710Sstevel@tonic-gate 		case PM_DIRECT_PM:
12720Sstevel@tonic-gate 		{
12730Sstevel@tonic-gate 			int has_dep;
12740Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12750Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12760Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12770Sstevel@tonic-gate 				ret = ENODEV;
12780Sstevel@tonic-gate 				break;
12790Sstevel@tonic-gate 			}
12800Sstevel@tonic-gate 			/*
12810Sstevel@tonic-gate 			 * Check to see if we are there is a dependency on
12820Sstevel@tonic-gate 			 * this kept device, if so, return EBUSY.
12830Sstevel@tonic-gate 			 */
12840Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
12850Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
12860Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
12870Sstevel@tonic-gate 			if (has_dep) {
12880Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
12890Sstevel@tonic-gate 				    cmdstr))
12900Sstevel@tonic-gate 				ret = EBUSY;
12910Sstevel@tonic-gate 				break;
12920Sstevel@tonic-gate 			}
12930Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12940Sstevel@tonic-gate 			if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
12950Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12960Sstevel@tonic-gate 				    "%s@%s(%s#%d): EBUSY\n", cmdstr,
12970Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12980Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12990Sstevel@tonic-gate 				ret = EBUSY;
13000Sstevel@tonic-gate 				break;
13010Sstevel@tonic-gate 			}
13020Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_DIRECT;
13030Sstevel@tonic-gate 			info->pmi_clone = clone;
13040Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13050Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
13060Sstevel@tonic-gate 			    cmdstr, (void *)info, clone))
13070Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
13080Sstevel@tonic-gate 			pm_register_watcher(clone, dip);
13090Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
13100Sstevel@tonic-gate 			ret = 0;
13110Sstevel@tonic-gate 			break;
13120Sstevel@tonic-gate 		}
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 		case PM_RELEASE_DIRECT_PM:
13150Sstevel@tonic-gate 		{
13160Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13170Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13180Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13190Sstevel@tonic-gate 				ret = ENODEV;
13200Sstevel@tonic-gate 				break;
13210Sstevel@tonic-gate 			}
13220Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13230Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13240Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13250Sstevel@tonic-gate 				    "%s@%s(%s#%d) EINVAL\n", cmdstr,
13260Sstevel@tonic-gate 				    PM_DEVICE(dip)))
13270Sstevel@tonic-gate 				ret = EINVAL;
13280Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
13290Sstevel@tonic-gate 				break;
13300Sstevel@tonic-gate 			}
13310Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13320Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_DIRECT;
13330Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13340Sstevel@tonic-gate 			/* Bring ourselves up if there is a keeper. */
13350Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
13360Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
13370Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
13380Sstevel@tonic-gate 			pm_discard_entries(clone);
13390Sstevel@tonic-gate 			pm_deregister_watcher(clone, dip);
13400Sstevel@tonic-gate 			/*
13410Sstevel@tonic-gate 			 * Now we could let the other threads that are
13420Sstevel@tonic-gate 			 * trying to do a DIRECT_PM thru
13430Sstevel@tonic-gate 			 */
13440Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13450Sstevel@tonic-gate 			info->pmi_clone = 0;
13460Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13470Sstevel@tonic-gate 			pm_proceed(dip, PMP_RELEASE, -1, -1);
13480Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13490Sstevel@tonic-gate 			    cmdstr))
13500Sstevel@tonic-gate 			pm_rescan(dip);
13510Sstevel@tonic-gate 			ret = 0;
13520Sstevel@tonic-gate 			break;
13530Sstevel@tonic-gate 		}
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 		case PM_SET_CURRENT_POWER:
13560Sstevel@tonic-gate 		{
13570Sstevel@tonic-gate 			int comp = req.component;
13580Sstevel@tonic-gate 			int  value = req.value;
13590Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
13600Sstevel@tonic-gate 			    "%d\n", cmdstr, req.physpath, comp, value))
13610Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, NULL) ||
13620Sstevel@tonic-gate 			    !e_pm_valid_power(dip, comp, value)) {
13630Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13640Sstevel@tonic-gate 				    "physpath=%s, comp=%d, level=%d, fails\n",
13650Sstevel@tonic-gate 				    cmdstr, req.physpath, comp, value))
13660Sstevel@tonic-gate 				ret = EINVAL;
13670Sstevel@tonic-gate 				break;
13680Sstevel@tonic-gate 			}
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13710Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13720Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13730Sstevel@tonic-gate 				ret = ENODEV;
13740Sstevel@tonic-gate 				break;
13750Sstevel@tonic-gate 			}
13760Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13770Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13780Sstevel@tonic-gate 				    "(not owner) %s fails; clone %d, owner %d"
13790Sstevel@tonic-gate 				    "\n", cmdstr, req.physpath, clone,
13800Sstevel@tonic-gate 				    info->pmi_clone))
13810Sstevel@tonic-gate 				ret = EINVAL;
13820Sstevel@tonic-gate 				break;
13830Sstevel@tonic-gate 			}
13840Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 			if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
13870Sstevel@tonic-gate 			    PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
13880Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13890Sstevel@tonic-gate 				    "pm_set_power for %s fails, errno=%d\n",
13900Sstevel@tonic-gate 				    cmdstr, req.physpath, ret))
13910Sstevel@tonic-gate 				break;
13920Sstevel@tonic-gate 			}
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate 			pm_proceed(dip, PMP_SETPOWER, comp, value);
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 			/*
13970Sstevel@tonic-gate 			 * Power down all idle components if console framebuffer
13980Sstevel@tonic-gate 			 * is powered off.
13990Sstevel@tonic-gate 			 */
14000Sstevel@tonic-gate 			if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
14010Sstevel@tonic-gate 			    pm_default_idle_threshold)) {
14020Sstevel@tonic-gate 				dev_info_t	*root = ddi_root_node();
14030Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
14040Sstevel@tonic-gate 					if (comp == 0 && value == 0 &&
14050Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
14060Sstevel@tonic-gate 						ddi_walk_devs(root,
14070Sstevel@tonic-gate 						    pm_start_idledown,
14080Sstevel@tonic-gate 						    (void *)PMID_CFB);
14090Sstevel@tonic-gate 					}
14100Sstevel@tonic-gate 				} else {
14110Sstevel@tonic-gate 					int count = 0;
14120Sstevel@tonic-gate 					for (i = 0; i < PM_NUMCMPTS(dip); i++) {
14130Sstevel@tonic-gate 						ret = pm_get_current_power(dip,
14140Sstevel@tonic-gate 						    i, &curpower);
14150Sstevel@tonic-gate 						if (ret == DDI_SUCCESS &&
14160Sstevel@tonic-gate 						    curpower == 0)
14170Sstevel@tonic-gate 							count++;
14180Sstevel@tonic-gate 					}
14190Sstevel@tonic-gate 					if ((count == PM_NUMCMPTS(dip)) &&
14200Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
14210Sstevel@tonic-gate 						ddi_walk_devs(root,
14220Sstevel@tonic-gate 						    pm_start_idledown,
14230Sstevel@tonic-gate 						    (void *)PMID_CFB);
14240Sstevel@tonic-gate 					}
14250Sstevel@tonic-gate 				}
14260Sstevel@tonic-gate 			}
14270Sstevel@tonic-gate 
14280Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
14290Sstevel@tonic-gate 			    cmdstr))
14300Sstevel@tonic-gate 			pm_rescan(dip);
14310Sstevel@tonic-gate 			*rval_p = 0;
14320Sstevel@tonic-gate 			ret = 0;
14330Sstevel@tonic-gate 			break;
14340Sstevel@tonic-gate 		}
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate 		case PM_GET_FULL_POWER:
14370Sstevel@tonic-gate 		{
14380Sstevel@tonic-gate 			int normal;
14390Sstevel@tonic-gate 			ASSERT(dip);
14400Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
14410Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component))
14420Sstevel@tonic-gate 			normal =  pm_get_normal_power(dip, req.component);
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 			if (normal == DDI_FAILURE) {
14450Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
14460Sstevel@tonic-gate 				    "returns EINVAL\n", cmdstr))
14470Sstevel@tonic-gate 				ret = EINVAL;
14480Sstevel@tonic-gate 				break;
14490Sstevel@tonic-gate 			}
14500Sstevel@tonic-gate 			*rval_p = normal;
14510Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
14520Sstevel@tonic-gate 			    cmdstr, normal))
14530Sstevel@tonic-gate 			ret = 0;
14540Sstevel@tonic-gate 			break;
14550Sstevel@tonic-gate 		}
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 		case PM_GET_CURRENT_POWER:
14585295Srandyf 		{
14590Sstevel@tonic-gate 			if (pm_get_current_power(dip, req.component,
14600Sstevel@tonic-gate 			    rval_p) != DDI_SUCCESS) {
14610Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
14620Sstevel@tonic-gate 				    "EINVAL\n", cmdstr))
14630Sstevel@tonic-gate 				ret = EINVAL;
14640Sstevel@tonic-gate 				break;
14650Sstevel@tonic-gate 			}
14660Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
14670Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component, *rval_p))
14680Sstevel@tonic-gate 			if (*rval_p == PM_LEVEL_UNKNOWN)
14690Sstevel@tonic-gate 				ret = EAGAIN;
14700Sstevel@tonic-gate 			else
14710Sstevel@tonic-gate 				ret = 0;
14720Sstevel@tonic-gate 			break;
14735295Srandyf 		}
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 		case PM_GET_TIME_IDLE:
14760Sstevel@tonic-gate 		{
14770Sstevel@tonic-gate 			time_t timestamp;
14780Sstevel@tonic-gate 			int comp = req.component;
14790Sstevel@tonic-gate 			pm_component_t *cp;
14800Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, &cp)) {
14810Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
14820Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
14830Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), comp,
14840Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
14850Sstevel@tonic-gate 				ret = EINVAL;
14860Sstevel@tonic-gate 				break;
14870Sstevel@tonic-gate 			}
14880Sstevel@tonic-gate 			timestamp = cp->pmc_timestamp;
14890Sstevel@tonic-gate 			if (timestamp) {
14900Sstevel@tonic-gate 				time_t now;
14910Sstevel@tonic-gate 				(void) drv_getparm(TIME, &now);
14920Sstevel@tonic-gate 				*rval_p = (now - timestamp);
14930Sstevel@tonic-gate 			} else {
14940Sstevel@tonic-gate 				*rval_p = 0;
14950Sstevel@tonic-gate 			}
14960Sstevel@tonic-gate 			ret = 0;
14970Sstevel@tonic-gate 			break;
14980Sstevel@tonic-gate 		}
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 		case PM_ADD_DEPENDENT:
15010Sstevel@tonic-gate 		{
15020Sstevel@tonic-gate 			dev_info_t	*kept_dip;
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
15050Sstevel@tonic-gate 			    dep, req.physpath))
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 			/*
15080Sstevel@tonic-gate 			 * hold and install kept while processing dependency
15090Sstevel@tonic-gate 			 * keeper (in .physpath) has already been held.
15100Sstevel@tonic-gate 			 */
15110Sstevel@tonic-gate 			if (dep[0] == '\0') {
15120Sstevel@tonic-gate 				PMD(PMD_ERROR, ("kept NULL or null\n"))
15130Sstevel@tonic-gate 				ret = EINVAL;
15140Sstevel@tonic-gate 				break;
15150Sstevel@tonic-gate 			} else if ((kept_dip =
15160Sstevel@tonic-gate 			    pm_name_to_dip(dep, 1)) == NULL) {
15170Sstevel@tonic-gate 				PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
15180Sstevel@tonic-gate 				ret = ENODEV;
15190Sstevel@tonic-gate 				break;
15200Sstevel@tonic-gate 			} else if (kept_dip == dip) {
15210Sstevel@tonic-gate 				PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
15220Sstevel@tonic-gate 				    "self-dependency not allowed.\n",
15230Sstevel@tonic-gate 				    dep, (void *)kept_dip, req.physpath,
15240Sstevel@tonic-gate 				    (void *) dip))
15250Sstevel@tonic-gate 				PM_RELE(dip);	/* release "double" hold */
15260Sstevel@tonic-gate 				ret = EINVAL;
15270Sstevel@tonic-gate 				break;
15280Sstevel@tonic-gate 			}
15290Sstevel@tonic-gate 			ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 			/*
15320Sstevel@tonic-gate 			 * record dependency, then walk through device tree
15330Sstevel@tonic-gate 			 * independently on behalf of kept and keeper to
15340Sstevel@tonic-gate 			 * establish newly created dependency.
15350Sstevel@tonic-gate 			 */
15360Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
15370Sstevel@tonic-gate 			    req.physpath, dep, PM_DEP_WAIT, NULL, 0);
15380Sstevel@tonic-gate 
15390Sstevel@tonic-gate 			/*
15400Sstevel@tonic-gate 			 * release kept after establishing dependency, keeper
15410Sstevel@tonic-gate 			 * is released as part of ioctl exit processing.
15420Sstevel@tonic-gate 			 */
15430Sstevel@tonic-gate 			PM_RELE(kept_dip);
15440Sstevel@tonic-gate 			*rval_p = 0;
15450Sstevel@tonic-gate 			ret = 0;
15460Sstevel@tonic-gate 			break;
15470Sstevel@tonic-gate 		}
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 		case PM_ADD_DEPENDENT_PROPERTY:
15500Sstevel@tonic-gate 		{
15510Sstevel@tonic-gate 			char *keeper, *kept;
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 			if (dep[0] == '\0') {
15540Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
15550Sstevel@tonic-gate 				    "null\n", cmdstr))
15560Sstevel@tonic-gate 				ret = EINVAL;
15570Sstevel@tonic-gate 				break;
15580Sstevel@tonic-gate 			}
15590Sstevel@tonic-gate 			kept = dep;
15600Sstevel@tonic-gate 			keeper = req.physpath;
15610Sstevel@tonic-gate 			/*
15620Sstevel@tonic-gate 			 * record keeper - kept dependency, then walk through
15630Sstevel@tonic-gate 			 * device tree to find out all attached keeper, walk
15640Sstevel@tonic-gate 			 * through again to apply dependency to all the
15650Sstevel@tonic-gate 			 * potential kept.
15660Sstevel@tonic-gate 			 */
15670Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(
15680Sstevel@tonic-gate 			    PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
15690Sstevel@tonic-gate 			    PM_DEP_WAIT, NULL, 0);
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 			*rval_p = 0;
15720Sstevel@tonic-gate 			ret = 0;
15730Sstevel@tonic-gate 			break;
15740Sstevel@tonic-gate 		}
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate 		case PM_SET_DEVICE_THRESHOLD:
15770Sstevel@tonic-gate 		{
15780Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
15790Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
15800Sstevel@tonic-gate 			int *tp;	/* threshold storage */
15810Sstevel@tonic-gate 			size_t size;
15820Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate 			/*
15850Sstevel@tonic-gate 			 * The header struct plus one entry struct plus one
15860Sstevel@tonic-gate 			 * threshold plus the length of the string
15870Sstevel@tonic-gate 			 */
15880Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
15890Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * 1) +
15900Sstevel@tonic-gate 			    (1 * sizeof (int)) +
15910Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
15940Sstevel@tonic-gate 			rp->ptr_size = size;
15950Sstevel@tonic-gate 			rp->ptr_numcomps = 0;	/* means device threshold */
15960Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
15970Sstevel@tonic-gate 			rp->ptr_entries = ep;
15980Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
15990Sstevel@tonic-gate 			    (1 * sizeof (pm_pte_t)));
16000Sstevel@tonic-gate 			ep->pte_numthresh = 1;
16010Sstevel@tonic-gate 			ep->pte_thresh = tp;
16020Sstevel@tonic-gate 			*tp++ = req.value;
16030Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
16040Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
16050Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
16060Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
16070Sstevel@tonic-gate 			PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
16080Sstevel@tonic-gate 			    "%s\n", cmdstr, req.value, req.physpath))
16090Sstevel@tonic-gate 			pm_record_thresh(rp);
16100Sstevel@tonic-gate 			/*
16110Sstevel@tonic-gate 			 * Don't free rp, pm_record_thresh() keeps it.
16120Sstevel@tonic-gate 			 * We don't try to apply it ourselves because we'd need
16130Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
16140Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
16150Sstevel@tonic-gate 			 * we get here
16160Sstevel@tonic-gate 			 */
16170Sstevel@tonic-gate 			ASSERT(dip == NULL);
16180Sstevel@tonic-gate 			ret = 0;		/* can't fail now */
16190Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
16200Sstevel@tonic-gate 				break;
16210Sstevel@tonic-gate 			}
16220Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
16230Sstevel@tonic-gate 			PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
16240Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
16250Sstevel@tonic-gate 			PM_RELE(dip);
16260Sstevel@tonic-gate 			break;
16270Sstevel@tonic-gate 		}
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 		case PM_RESET_DEVICE_THRESHOLD:
16300Sstevel@tonic-gate 		{
16310Sstevel@tonic-gate 			/*
16320Sstevel@tonic-gate 			 * This only applies to a currently attached and power
16330Sstevel@tonic-gate 			 * managed node
16340Sstevel@tonic-gate 			 */
16350Sstevel@tonic-gate 			/*
16360Sstevel@tonic-gate 			 * We don't do this to old-style drivers
16370Sstevel@tonic-gate 			 */
16380Sstevel@tonic-gate 			info = PM_GET_PM_INFO(dip);
16390Sstevel@tonic-gate 			if (info == NULL) {
16400Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s not power "
16410Sstevel@tonic-gate 				    "managed\n", cmdstr, req.physpath))
16420Sstevel@tonic-gate 				ret = EINVAL;
16430Sstevel@tonic-gate 				break;
16440Sstevel@tonic-gate 			}
16450Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16460Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
16470Sstevel@tonic-gate 				    cmdstr, req.physpath))
16480Sstevel@tonic-gate 				ret = EINVAL;
16490Sstevel@tonic-gate 				break;
16500Sstevel@tonic-gate 			}
16510Sstevel@tonic-gate 			pm_unrecord_threshold(req.physpath);
16523028Smh27603 			if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
16533028Smh27603 				pm_set_device_threshold(dip,
16543028Smh27603 				    pm_cpu_idle_threshold, PMC_CPU_THRESH);
16553028Smh27603 			else
16563028Smh27603 				pm_set_device_threshold(dip,
16573028Smh27603 				    pm_system_idle_threshold, PMC_DEF_THRESH);
16580Sstevel@tonic-gate 			ret = 0;
16590Sstevel@tonic-gate 			break;
16600Sstevel@tonic-gate 		}
16610Sstevel@tonic-gate 
16620Sstevel@tonic-gate 		case PM_GET_NUM_COMPONENTS:
16635295Srandyf 		{
16640Sstevel@tonic-gate 			ret = 0;
16650Sstevel@tonic-gate 			*rval_p = PM_NUMCMPTS(dip);
16660Sstevel@tonic-gate 			break;
16675295Srandyf 		}
16680Sstevel@tonic-gate 
16690Sstevel@tonic-gate 		case PM_GET_DEVICE_TYPE:
16705295Srandyf 		{
16710Sstevel@tonic-gate 			ret = 0;
16720Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
16730Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
16740Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
16750Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
16760Sstevel@tonic-gate 				break;
16770Sstevel@tonic-gate 			}
16780Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16790Sstevel@tonic-gate 				*rval_p = PM_CREATE_COMPONENTS;
16800Sstevel@tonic-gate 			} else {
16810Sstevel@tonic-gate 				*rval_p = PM_AUTOPM;
16820Sstevel@tonic-gate 			}
16830Sstevel@tonic-gate 			break;
16845295Srandyf 		}
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 		case PM_SET_COMPONENT_THRESHOLDS:
16870Sstevel@tonic-gate 		{
16880Sstevel@tonic-gate 			int comps = 0;
16890Sstevel@tonic-gate 			int *end = (int *)req.data + icount;
16900Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
16910Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
16920Sstevel@tonic-gate 			int *tp;	/* threshold storage */
16930Sstevel@tonic-gate 			int *ip;
16940Sstevel@tonic-gate 			int j;
16950Sstevel@tonic-gate 			size_t size;
16960Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
16970Sstevel@tonic-gate 			extern int pm_valid_thresh(dev_info_t *,
16980Sstevel@tonic-gate 			    pm_thresh_rec_t *);
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 			for (ip = req.data; *ip; ip++) {
17010Sstevel@tonic-gate 				if (ip >= end) {
17020Sstevel@tonic-gate 					ret = EFAULT;
17030Sstevel@tonic-gate 					break;
17040Sstevel@tonic-gate 				}
17050Sstevel@tonic-gate 				comps++;
17060Sstevel@tonic-gate 				/* skip over indicated number of entries */
17070Sstevel@tonic-gate 				for (j = *ip; j; j--) {
17080Sstevel@tonic-gate 					if (++ip >= end) {
17090Sstevel@tonic-gate 						ret = EFAULT;
17100Sstevel@tonic-gate 						break;
17110Sstevel@tonic-gate 					}
17120Sstevel@tonic-gate 				}
17130Sstevel@tonic-gate 				if (ret)
17140Sstevel@tonic-gate 					break;
17150Sstevel@tonic-gate 			}
17160Sstevel@tonic-gate 			if (ret)
17170Sstevel@tonic-gate 				break;
17180Sstevel@tonic-gate 			if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
17190Sstevel@tonic-gate 				/* did not exactly fill buffer */
17200Sstevel@tonic-gate 				ret = EINVAL;
17210Sstevel@tonic-gate 				break;
17220Sstevel@tonic-gate 			}
17230Sstevel@tonic-gate 			if (comps == 0) {
17240Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
17250Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, req.physpath))
17260Sstevel@tonic-gate 				ret = EINVAL;
17270Sstevel@tonic-gate 				break;
17280Sstevel@tonic-gate 			}
17290Sstevel@tonic-gate 			/*
17300Sstevel@tonic-gate 			 * The header struct plus one entry struct per component
17310Sstevel@tonic-gate 			 * plus the size of the lists minus the counts
17320Sstevel@tonic-gate 			 * plus the length of the string
17330Sstevel@tonic-gate 			 */
17340Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
17350Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * comps) + req.datasize -
17360Sstevel@tonic-gate 			    ((comps + 1) * sizeof (int)) +
17370Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
17400Sstevel@tonic-gate 			rp->ptr_size = size;
17410Sstevel@tonic-gate 			rp->ptr_numcomps = comps;
17420Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
17430Sstevel@tonic-gate 			rp->ptr_entries = ep;
17440Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
17450Sstevel@tonic-gate 			    (comps * sizeof (pm_pte_t)));
17460Sstevel@tonic-gate 			for (ip = req.data; *ip; ep++) {
17470Sstevel@tonic-gate 				ep->pte_numthresh = *ip;
17480Sstevel@tonic-gate 				ep->pte_thresh = tp;
17490Sstevel@tonic-gate 				for (j = *ip++; j; j--) {
17500Sstevel@tonic-gate 					*tp++ = *ip++;
17510Sstevel@tonic-gate 				}
17520Sstevel@tonic-gate 			}
17530Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
17540Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
17550Sstevel@tonic-gate 			ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
17560Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
17570Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 			ASSERT(dip == NULL);
17600Sstevel@tonic-gate 			/*
17610Sstevel@tonic-gate 			 * If this is not a currently power managed node,
17620Sstevel@tonic-gate 			 * then we can't check for validity of the thresholds
17630Sstevel@tonic-gate 			 */
17640Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
17650Sstevel@tonic-gate 				/* don't free rp, pm_record_thresh uses it */
17660Sstevel@tonic-gate 				pm_record_thresh(rp);
17670Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
17680Sstevel@tonic-gate 				    "for %s failed\n", cmdstr, req.physpath))
17690Sstevel@tonic-gate 				ret = 0;
17700Sstevel@tonic-gate 				break;
17710Sstevel@tonic-gate 			}
17720Sstevel@tonic-gate 			ASSERT(!dipheld);
17730Sstevel@tonic-gate 			dipheld++;
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 			if (!pm_valid_thresh(dip, rp)) {
17760Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
17770Sstevel@tonic-gate 				    "for %s@%s(%s#%d)\n", cmdstr,
17780Sstevel@tonic-gate 				    PM_DEVICE(dip)))
17790Sstevel@tonic-gate 				kmem_free(rp, size);
17800Sstevel@tonic-gate 				ret = EINVAL;
17810Sstevel@tonic-gate 				break;
17820Sstevel@tonic-gate 			}
17830Sstevel@tonic-gate 			/*
17840Sstevel@tonic-gate 			 * We don't just apply it ourselves because we'd need
17850Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
17860Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
17870Sstevel@tonic-gate 			 * we get here
17880Sstevel@tonic-gate 			 */
17890Sstevel@tonic-gate 			pm_record_thresh(rp);
17900Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
17910Sstevel@tonic-gate 			ret = 0;
17920Sstevel@tonic-gate 			break;
17930Sstevel@tonic-gate 		}
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 		case PM_GET_COMPONENT_THRESHOLDS:
17960Sstevel@tonic-gate 		{
17970Sstevel@tonic-gate 			int musthave;
17980Sstevel@tonic-gate 			int numthresholds = 0;
17990Sstevel@tonic-gate 			int wordsize;
18000Sstevel@tonic-gate 			int numcomps;
18010Sstevel@tonic-gate 			caddr_t uaddr = req.data;	/* user address */
18020Sstevel@tonic-gate 			int val;	/* int value to be copied out */
18030Sstevel@tonic-gate 			int32_t val32;	/* int32 value to be copied out */
18040Sstevel@tonic-gate 			caddr_t vaddr;	/* address to copyout from */
18050Sstevel@tonic-gate 			int j;
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
18080Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
18090Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
18100Sstevel@tonic-gate 			} else
18110Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
18120Sstevel@tonic-gate 			{
18130Sstevel@tonic-gate 				wordsize = sizeof (int);
18140Sstevel@tonic-gate 			}
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate 			ASSERT(dip);
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 			numcomps = PM_NUMCMPTS(dip);
18190Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
18200Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18210Sstevel@tonic-gate 				numthresholds += cp->pmc_comp.pmc_numlevels - 1;
18220Sstevel@tonic-gate 			}
18230Sstevel@tonic-gate 			musthave = (numthresholds + numcomps + 1) *  wordsize;
18240Sstevel@tonic-gate 			if (req.datasize < musthave) {
18250Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
18260Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
18270Sstevel@tonic-gate 				    musthave))
18280Sstevel@tonic-gate 				ret = EINVAL;
18290Sstevel@tonic-gate 				break;
18300Sstevel@tonic-gate 			}
18310Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
18320Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
18330Sstevel@tonic-gate 				int *thp;
18340Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18350Sstevel@tonic-gate 				thp = cp->pmc_comp.pmc_thresh;
18360Sstevel@tonic-gate 				/* first copyout the count */
18370Sstevel@tonic-gate 				if (wordsize == sizeof (int32_t)) {
18380Sstevel@tonic-gate 					val32 = cp->pmc_comp.pmc_numlevels - 1;
18390Sstevel@tonic-gate 					vaddr = (caddr_t)&val32;
18400Sstevel@tonic-gate 				} else {
18410Sstevel@tonic-gate 					val = cp->pmc_comp.pmc_numlevels - 1;
18420Sstevel@tonic-gate 					vaddr = (caddr_t)&val;
18430Sstevel@tonic-gate 				}
18440Sstevel@tonic-gate 				if (ddi_copyout(vaddr, (void *)uaddr,
18450Sstevel@tonic-gate 				    wordsize, mode) != 0) {
18460Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
18470Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
18480Sstevel@tonic-gate 					    "(%s#%d) vaddr %p EFAULT\n",
18490Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
18500Sstevel@tonic-gate 					    (void*)vaddr))
18510Sstevel@tonic-gate 					ret = EFAULT;
18520Sstevel@tonic-gate 					break;
18530Sstevel@tonic-gate 				}
18540Sstevel@tonic-gate 				vaddr = uaddr;
18550Sstevel@tonic-gate 				vaddr += wordsize;
18560Sstevel@tonic-gate 				uaddr = (caddr_t)vaddr;
18570Sstevel@tonic-gate 				/* then copyout each threshold value */
18580Sstevel@tonic-gate 				for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
18590Sstevel@tonic-gate 				    j++) {
18600Sstevel@tonic-gate 					if (wordsize == sizeof (int32_t)) {
18610Sstevel@tonic-gate 						val32 = thp[j + 1];
18620Sstevel@tonic-gate 						vaddr = (caddr_t)&val32;
18630Sstevel@tonic-gate 					} else {
18640Sstevel@tonic-gate 						val = thp[i + 1];
18650Sstevel@tonic-gate 						vaddr = (caddr_t)&val;
18660Sstevel@tonic-gate 					}
18670Sstevel@tonic-gate 					if (ddi_copyout(vaddr, (void *) uaddr,
18680Sstevel@tonic-gate 					    wordsize, mode) != 0) {
18690Sstevel@tonic-gate 						PM_UNLOCK_DIP(dip);
18700Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
18710Sstevel@tonic-gate 						    "%s@%s(%s#%d) uaddr %p "
18720Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
18730Sstevel@tonic-gate 						    PM_DEVICE(dip),
18740Sstevel@tonic-gate 						    (void *)uaddr))
18750Sstevel@tonic-gate 						ret = EFAULT;
18760Sstevel@tonic-gate 						break;
18770Sstevel@tonic-gate 					}
18780Sstevel@tonic-gate 					vaddr = uaddr;
18790Sstevel@tonic-gate 					vaddr += wordsize;
18800Sstevel@tonic-gate 					uaddr = (caddr_t)vaddr;
18810Sstevel@tonic-gate 				}
18820Sstevel@tonic-gate 			}
18830Sstevel@tonic-gate 			if (ret)
18840Sstevel@tonic-gate 				break;
18850Sstevel@tonic-gate 			/* last copyout a terminating 0 count */
18860Sstevel@tonic-gate 			if (wordsize == sizeof (int32_t)) {
18870Sstevel@tonic-gate 				val32 = 0;
18880Sstevel@tonic-gate 				vaddr = (caddr_t)&val32;
18890Sstevel@tonic-gate 			} else {
18900Sstevel@tonic-gate 				ASSERT(wordsize == sizeof (int));
18910Sstevel@tonic-gate 				val = 0;
18920Sstevel@tonic-gate 				vaddr = (caddr_t)&val;
18930Sstevel@tonic-gate 			}
18940Sstevel@tonic-gate 			if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
18950Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
18960Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
18970Sstevel@tonic-gate 				    "vaddr %p (0 count) EFAULT\n", cmdstr,
18980Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)vaddr))
18990Sstevel@tonic-gate 				ret = EFAULT;
19000Sstevel@tonic-gate 				break;
19010Sstevel@tonic-gate 			}
19020Sstevel@tonic-gate 			/* finished, so don't need to increment addresses */
19030Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
19040Sstevel@tonic-gate 			ret = 0;
19050Sstevel@tonic-gate 			break;
19060Sstevel@tonic-gate 		}
19070Sstevel@tonic-gate 
19080Sstevel@tonic-gate 		case PM_GET_STATS:
19090Sstevel@tonic-gate 		{
19100Sstevel@tonic-gate 			time_t now;
19110Sstevel@tonic-gate 			time_t *timestamp;
19120Sstevel@tonic-gate 			extern int pm_cur_power(pm_component_t *cp);
19130Sstevel@tonic-gate 			int musthave;
19140Sstevel@tonic-gate 			int wordsize;
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
19170Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
19180Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
19190Sstevel@tonic-gate 			} else
19200Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
19210Sstevel@tonic-gate 			{
19220Sstevel@tonic-gate 				wordsize = sizeof (int);
19230Sstevel@tonic-gate 			}
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 			comps = PM_NUMCMPTS(dip);
19260Sstevel@tonic-gate 			if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
19270Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s no components"
19280Sstevel@tonic-gate 				    " or not power managed--EINVAL\n", cmdstr,
19290Sstevel@tonic-gate 				    req.physpath))
19300Sstevel@tonic-gate 				ret = EINVAL;
19310Sstevel@tonic-gate 				break;
19320Sstevel@tonic-gate 			}
19330Sstevel@tonic-gate 			musthave = comps * 2 * wordsize;
19340Sstevel@tonic-gate 			if (req.datasize < musthave) {
19350Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
19360Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
19370Sstevel@tonic-gate 				    musthave))
19380Sstevel@tonic-gate 				ret = EINVAL;
19390Sstevel@tonic-gate 				break;
19400Sstevel@tonic-gate 			}
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
19430Sstevel@tonic-gate 			(void) drv_getparm(TIME, &now);
19440Sstevel@tonic-gate 			timestamp = kmem_zalloc(comps * sizeof (time_t),
19450Sstevel@tonic-gate 			    KM_SLEEP);
19460Sstevel@tonic-gate 			pm_get_timestamps(dip, timestamp);
19470Sstevel@tonic-gate 			/*
19480Sstevel@tonic-gate 			 * First the current power levels
19490Sstevel@tonic-gate 			 */
19500Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19510Sstevel@tonic-gate 				int curpwr;
19520Sstevel@tonic-gate 				int32_t curpwr32;
19530Sstevel@tonic-gate 				caddr_t cpaddr;
19540Sstevel@tonic-gate 
19550Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19560Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19570Sstevel@tonic-gate 					curpwr = pm_cur_power(cp);
19580Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr;
19590Sstevel@tonic-gate 				} else {
19600Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19610Sstevel@tonic-gate 					curpwr32 = pm_cur_power(cp);
19620Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr32;
19630Sstevel@tonic-gate 				}
19640Sstevel@tonic-gate 				if (ddi_copyout(cpaddr, (void *) req.data,
19650Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19660Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19670Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19680Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19690Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19700Sstevel@tonic-gate 					    (void *)req.data))
19710Sstevel@tonic-gate 					ASSERT(!dipheld);
19720Sstevel@tonic-gate 					return (EFAULT);
19730Sstevel@tonic-gate 				}
19740Sstevel@tonic-gate 				cpaddr = (caddr_t)req.data;
19750Sstevel@tonic-gate 				cpaddr += wordsize;
19760Sstevel@tonic-gate 				req.data = cpaddr;
19770Sstevel@tonic-gate 			}
19780Sstevel@tonic-gate 			/*
19790Sstevel@tonic-gate 			 * Then the times remaining
19800Sstevel@tonic-gate 			 */
19810Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19820Sstevel@tonic-gate 				int retval;
19830Sstevel@tonic-gate 				int32_t retval32;
19840Sstevel@tonic-gate 				caddr_t rvaddr;
19850Sstevel@tonic-gate 				int curpwr;
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19880Sstevel@tonic-gate 				curpwr = cp->pmc_cur_pwr;
19890Sstevel@tonic-gate 				if (curpwr == 0 || timestamp[i] == 0) {
19900Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: "
19910Sstevel@tonic-gate 					    "cur_pwer %x, timestamp %lx\n",
19920Sstevel@tonic-gate 					    cmdstr, curpwr, timestamp[i]))
19930Sstevel@tonic-gate 					retval = INT_MAX;
19940Sstevel@tonic-gate 				} else {
19950Sstevel@tonic-gate 					int thresh;
19960Sstevel@tonic-gate 					(void) pm_current_threshold(dip, i,
19970Sstevel@tonic-gate 					    &thresh);
19980Sstevel@tonic-gate 					retval = thresh - (now - timestamp[i]);
19990Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: current "
20000Sstevel@tonic-gate 					    "thresh %x, now %lx, timestamp %lx,"
20010Sstevel@tonic-gate 					    " retval %x\n", cmdstr, thresh, now,
20020Sstevel@tonic-gate 					    timestamp[i], retval))
20030Sstevel@tonic-gate 				}
20040Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
20050Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval;
20060Sstevel@tonic-gate 				} else {
20070Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
20080Sstevel@tonic-gate 					retval32 = retval;
20090Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval32;
20100Sstevel@tonic-gate 				}
20110Sstevel@tonic-gate 				if (ddi_copyout(rvaddr, (void *) req.data,
20120Sstevel@tonic-gate 				    wordsize, mode) != 0) {
20130Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
20140Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
20150Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
20160Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
20170Sstevel@tonic-gate 					    (void *)req.data))
20180Sstevel@tonic-gate 					ASSERT(!dipheld);
20195295Srandyf 					kmem_free(timestamp,
20205295Srandyf 					    comps * sizeof (time_t));
20210Sstevel@tonic-gate 					return (EFAULT);
20220Sstevel@tonic-gate 				}
20230Sstevel@tonic-gate 				rvaddr = (caddr_t)req.data;
20240Sstevel@tonic-gate 				rvaddr += wordsize;
20250Sstevel@tonic-gate 				req.data = (int *)rvaddr;
20260Sstevel@tonic-gate 			}
20270Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
20280Sstevel@tonic-gate 			*rval_p = comps;
20290Sstevel@tonic-gate 			ret = 0;
20300Sstevel@tonic-gate 			kmem_free(timestamp, comps * sizeof (time_t));
20310Sstevel@tonic-gate 			break;
20320Sstevel@tonic-gate 		}
20330Sstevel@tonic-gate 
20345295Srandyf 		case PM_GET_CMD_NAME:
20355295Srandyf 		{
20365295Srandyf 			PMD(PMD_IOCTL, ("%s: %s\n", cmdstr,
20375295Srandyf 			    pm_decode_cmd(req.value)))
20385295Srandyf 			if (ret = copyoutstr(pm_decode_cmd(req.value),
20395295Srandyf 			    (char *)req.data, req.datasize, &lencopied)) {
20405295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20415295Srandyf 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20425295Srandyf 				    PM_DEVICE(dip), (void *)req.data))
20435295Srandyf 				break;
20445295Srandyf 			}
20455295Srandyf 			*rval_p = lencopied;
20465295Srandyf 			ret = 0;
20475295Srandyf 			break;
20485295Srandyf 		}
20495295Srandyf 
20500Sstevel@tonic-gate 		case PM_GET_COMPONENT_NAME:
20515295Srandyf 		{
20520Sstevel@tonic-gate 			ASSERT(dip);
20530Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20540Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20550Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20560Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20570Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20580Sstevel@tonic-gate 				ret = EINVAL;
20590Sstevel@tonic-gate 				break;
20600Sstevel@tonic-gate 			}
20610Sstevel@tonic-gate 			if (ret = copyoutstr(cp->pmc_comp.pmc_name,
20620Sstevel@tonic-gate 			    (char *)req.data, req.datasize, &lencopied)) {
20630Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20640Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20650Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20660Sstevel@tonic-gate 				break;
20670Sstevel@tonic-gate 			}
20680Sstevel@tonic-gate 			*rval_p = lencopied;
20690Sstevel@tonic-gate 			ret = 0;
20700Sstevel@tonic-gate 			break;
20715295Srandyf 		}
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate 		case PM_GET_POWER_NAME:
20740Sstevel@tonic-gate 		{
20750Sstevel@tonic-gate 			int i;
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 			ASSERT(dip);
20780Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20790Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20800Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20810Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20820Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20830Sstevel@tonic-gate 				ret = EINVAL;
20840Sstevel@tonic-gate 				break;
20850Sstevel@tonic-gate 			}
20860Sstevel@tonic-gate 			if ((i = req.value) < 0 ||
20870Sstevel@tonic-gate 			    i > cp->pmc_comp.pmc_numlevels - 1) {
20880Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20890Sstevel@tonic-gate 				    "value %d > num_levels - 1 %d--EINVAL\n",
20900Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.value,
20910Sstevel@tonic-gate 				    cp->pmc_comp.pmc_numlevels - 1))
20920Sstevel@tonic-gate 				ret = EINVAL;
20930Sstevel@tonic-gate 				break;
20940Sstevel@tonic-gate 			}
20950Sstevel@tonic-gate 			dep = cp->pmc_comp.pmc_lnames[req.value];
20960Sstevel@tonic-gate 			if (ret = copyoutstr(dep,
20970Sstevel@tonic-gate 			    req.data, req.datasize, &lencopied)) {
20980Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20990Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
21000Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
21010Sstevel@tonic-gate 				break;
21020Sstevel@tonic-gate 			}
21030Sstevel@tonic-gate 			*rval_p = lencopied;
21040Sstevel@tonic-gate 			ret = 0;
21050Sstevel@tonic-gate 			break;
21060Sstevel@tonic-gate 		}
21070Sstevel@tonic-gate 
21080Sstevel@tonic-gate 		case PM_GET_POWER_LEVELS:
21090Sstevel@tonic-gate 		{
21100Sstevel@tonic-gate 			int musthave;
21110Sstevel@tonic-gate 			int numlevels;
21120Sstevel@tonic-gate 			int wordsize;
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
21150Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
21160Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
21170Sstevel@tonic-gate 			} else
21180Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
21190Sstevel@tonic-gate 			{
21200Sstevel@tonic-gate 				wordsize = sizeof (int);
21210Sstevel@tonic-gate 			}
21220Sstevel@tonic-gate 			ASSERT(dip);
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21250Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21260Sstevel@tonic-gate 				    "has %d components, component %d requested"
21270Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, PM_DEVICE(dip),
21280Sstevel@tonic-gate 				    PM_NUMCMPTS(dip), req.component))
21290Sstevel@tonic-gate 				ret = EINVAL;
21300Sstevel@tonic-gate 				break;
21310Sstevel@tonic-gate 			}
21320Sstevel@tonic-gate 			numlevels = cp->pmc_comp.pmc_numlevels;
21330Sstevel@tonic-gate 			musthave = numlevels *  wordsize;
21340Sstevel@tonic-gate 			if (req.datasize < musthave) {
21350Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
21360Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
21370Sstevel@tonic-gate 				    musthave))
21380Sstevel@tonic-gate 				ret = EINVAL;
21390Sstevel@tonic-gate 				break;
21400Sstevel@tonic-gate 			}
21410Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21420Sstevel@tonic-gate 			for (i = 0; i < numlevels; i++) {
21430Sstevel@tonic-gate 				int level;
21440Sstevel@tonic-gate 				int32_t level32;
21450Sstevel@tonic-gate 				caddr_t laddr;
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
21480Sstevel@tonic-gate 					level = cp->pmc_comp.pmc_lvals[i];
21490Sstevel@tonic-gate 					laddr = (caddr_t)&level;
21500Sstevel@tonic-gate 				} else {
21510Sstevel@tonic-gate 					level32 = cp->pmc_comp.pmc_lvals[i];
21520Sstevel@tonic-gate 					laddr = (caddr_t)&level32;
21530Sstevel@tonic-gate 				}
21540Sstevel@tonic-gate 				if (ddi_copyout(laddr, (void *) req.data,
21550Sstevel@tonic-gate 				    wordsize, mode) != 0) {
21560Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
21570Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
21580Sstevel@tonic-gate 					    "(%s#%d) laddr %p EFAULT\n",
21590Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
21600Sstevel@tonic-gate 					    (void *)laddr))
21610Sstevel@tonic-gate 					ASSERT(!dipheld);
21620Sstevel@tonic-gate 					return (EFAULT);
21630Sstevel@tonic-gate 				}
21640Sstevel@tonic-gate 				laddr = (caddr_t)req.data;
21650Sstevel@tonic-gate 				laddr += wordsize;
21660Sstevel@tonic-gate 				req.data = (int *)laddr;
21670Sstevel@tonic-gate 			}
21680Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21690Sstevel@tonic-gate 			*rval_p = numlevels;
21700Sstevel@tonic-gate 			ret = 0;
21710Sstevel@tonic-gate 			break;
21720Sstevel@tonic-gate 		}
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate 
21750Sstevel@tonic-gate 		case PM_GET_NUM_POWER_LEVELS:
21765295Srandyf 		{
21770Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21780Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21790Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
21800Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
21810Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
21820Sstevel@tonic-gate 				ret = EINVAL;
21830Sstevel@tonic-gate 				break;
21840Sstevel@tonic-gate 			}
21850Sstevel@tonic-gate 			*rval_p = cp->pmc_comp.pmc_numlevels;
21860Sstevel@tonic-gate 			ret = 0;
21870Sstevel@tonic-gate 			break;
21885295Srandyf 		}
21890Sstevel@tonic-gate 
21900Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD_BASIS:
21915295Srandyf 		{
21920Sstevel@tonic-gate 			ret = 0;
21930Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21940Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
21950Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21960Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
21970Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
21980Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
21990Sstevel@tonic-gate 				break;
22000Sstevel@tonic-gate 			}
22010Sstevel@tonic-gate 			if (PM_ISDIRECT(dip)) {
22020Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
22030Sstevel@tonic-gate 				*rval_p = PM_DIRECTLY_MANAGED;
22040Sstevel@tonic-gate 				break;
22050Sstevel@tonic-gate 			}
22060Sstevel@tonic-gate 			switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
22070Sstevel@tonic-gate 			case PMC_DEF_THRESH:
22080Sstevel@tonic-gate 			case PMC_NEXDEF_THRESH:
22090Sstevel@tonic-gate 				*rval_p = PM_DEFAULT_THRESHOLD;
22100Sstevel@tonic-gate 				break;
22110Sstevel@tonic-gate 			case PMC_DEV_THRESH:
22120Sstevel@tonic-gate 				*rval_p = PM_DEVICE_THRESHOLD;
22130Sstevel@tonic-gate 				break;
22140Sstevel@tonic-gate 			case PMC_COMP_THRESH:
22150Sstevel@tonic-gate 				*rval_p = PM_COMPONENT_THRESHOLD;
22160Sstevel@tonic-gate 				break;
22173028Smh27603 			case PMC_CPU_THRESH:
22183028Smh27603 				*rval_p = PM_CPU_THRESHOLD;
22193028Smh27603 				break;
22200Sstevel@tonic-gate 			default:
22210Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
22220Sstevel@tonic-gate 					*rval_p = PM_OLD_THRESHOLD;
22230Sstevel@tonic-gate 					break;
22240Sstevel@tonic-gate 				}
22250Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: default, not "
22260Sstevel@tonic-gate 				    "BC--EINVAL", cmdstr))
22270Sstevel@tonic-gate 				ret = EINVAL;
22280Sstevel@tonic-gate 				break;
22290Sstevel@tonic-gate 			}
22300Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
22310Sstevel@tonic-gate 			break;
22320Sstevel@tonic-gate 		}
22335295Srandyf 		default:
22345295Srandyf 			/*
22355295Srandyf 			 * Internal error, invalid ioctl description
22365295Srandyf 			 * force debug entry even if pm_debug not set
22375295Srandyf 			 */
22385295Srandyf #ifdef	DEBUG
22395295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
22405295Srandyf 			    pcip->diptype, cmd, pcip->name);
22415295Srandyf #endif
22425295Srandyf 			ASSERT(0);
22435295Srandyf 			return (EIO);
22445295Srandyf 		}
22450Sstevel@tonic-gate 		break;
22465295Srandyf 	}
22470Sstevel@tonic-gate 
22480Sstevel@tonic-gate 	case PM_PSC:
22495295Srandyf 	{
22500Sstevel@tonic-gate 		/*
22510Sstevel@tonic-gate 		 * Commands that require pm_state_change_t as arg
22520Sstevel@tonic-gate 		 */
22530Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
22540Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
22550Sstevel@tonic-gate 			pscp32 = (pm_state_change32_t *)arg;
22560Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc32,
22570Sstevel@tonic-gate 			    sizeof (psc32), 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 			psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
22640Sstevel@tonic-gate 			psc.size = psc32.size;
22650Sstevel@tonic-gate 		} else
22660Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22670Sstevel@tonic-gate 		{
22680Sstevel@tonic-gate 			pscp = (pm_state_change_t *)arg;
22690Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc,
22700Sstevel@tonic-gate 			    sizeof (psc), mode) != 0) {
22710Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
22720Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
22730Sstevel@tonic-gate 				ASSERT(!dipheld);
22740Sstevel@tonic-gate 				return (EFAULT);
22750Sstevel@tonic-gate 			}
22760Sstevel@tonic-gate 		}
22770Sstevel@tonic-gate 		switch (cmd) {
22780Sstevel@tonic-gate 
22790Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE:
22800Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE_WAIT:
22810Sstevel@tonic-gate 		{
22820Sstevel@tonic-gate 			psce_t			*pscep;
22830Sstevel@tonic-gate 			pm_state_change_t	*p;
22840Sstevel@tonic-gate 			caddr_t			physpath;
22850Sstevel@tonic-gate 			size_t			physlen;
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate 			/*
22880Sstevel@tonic-gate 			 * We want to know if any device has changed state.
22890Sstevel@tonic-gate 			 * We look up by clone.  In case we have another thread
22900Sstevel@tonic-gate 			 * from the same process, we loop.
22910Sstevel@tonic-gate 			 * pm_psc_clone_to_interest() returns a locked entry.
22920Sstevel@tonic-gate 			 * We create an internal copy of the event entry prior
22930Sstevel@tonic-gate 			 * to copyout to user space because we don't want to
22940Sstevel@tonic-gate 			 * hold the psce_lock while doing copyout as we might
22950Sstevel@tonic-gate 			 * hit page fault  which eventually brings us back
22960Sstevel@tonic-gate 			 * here requesting the same lock.
22970Sstevel@tonic-gate 			 */
22980Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
22990Sstevel@tonic-gate 			if (!pm_interest_registered(clone))
23000Sstevel@tonic-gate 				pm_register_watcher(clone, NULL);
23010Sstevel@tonic-gate 			while ((pscep =
23020Sstevel@tonic-gate 			    pm_psc_clone_to_interest(clone)) == NULL) {
23030Sstevel@tonic-gate 				if (cmd == PM_GET_STATE_CHANGE) {
23040Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
23050Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
23060Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
23070Sstevel@tonic-gate 					ASSERT(!dipheld);
23080Sstevel@tonic-gate 					return (EWOULDBLOCK);
23090Sstevel@tonic-gate 				} else {
23100Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
23110Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
23120Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
23130Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s "
23140Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
23150Sstevel@tonic-gate 						ASSERT(!dipheld);
23160Sstevel@tonic-gate 						return (EINTR);
23170Sstevel@tonic-gate 					}
23180Sstevel@tonic-gate 				}
23190Sstevel@tonic-gate 			}
23200Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
23210Sstevel@tonic-gate 
23220Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
23230Sstevel@tonic-gate 			physpath = NULL;
23240Sstevel@tonic-gate 			/*
23250Sstevel@tonic-gate 			 * If we were unable to store the path while bringing
23260Sstevel@tonic-gate 			 * up the console fb upon entering the prom, we give
23270Sstevel@tonic-gate 			 * a "" name with the overrun event set
23280Sstevel@tonic-gate 			 */
23290Sstevel@tonic-gate 			if (physlen == (size_t)-1) {	/* kmemalloc failed */
23300Sstevel@tonic-gate 				physpath = kmem_zalloc(1, KM_SLEEP);
23310Sstevel@tonic-gate 				physlen = 1;
23320Sstevel@tonic-gate 			}
23330Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
23340Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
23350Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
23360Sstevel@tonic-gate 				ret = EFAULT;
23370Sstevel@tonic-gate 				break;
23380Sstevel@tonic-gate 			}
23390Sstevel@tonic-gate 			if (physpath == NULL) {
23400Sstevel@tonic-gate 				physpath = kmem_zalloc(physlen, KM_SLEEP);
23410Sstevel@tonic-gate 				bcopy((const void *) pscep->psce_out->physpath,
23420Sstevel@tonic-gate 				    (void *) physpath, physlen);
23430Sstevel@tonic-gate 			}
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate 			p = pscep->psce_out;
23460Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23470Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23480Sstevel@tonic-gate #ifdef DEBUG
23490Sstevel@tonic-gate 				size_t usrcopysize;
23500Sstevel@tonic-gate #endif
23510Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
23520Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
23530Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
23540Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
23550Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
23560Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
23570Sstevel@tonic-gate 				copysize32 = ((intptr_t)&psc32.size -
23580Sstevel@tonic-gate 				    (intptr_t)&psc32.component);
23590Sstevel@tonic-gate #ifdef DEBUG
23600Sstevel@tonic-gate 				usrcopysize = ((intptr_t)&pscp32->size -
23610Sstevel@tonic-gate 				    (intptr_t)&pscp32->component);
23620Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
23630Sstevel@tonic-gate #endif
23640Sstevel@tonic-gate 			} else
23650Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
23660Sstevel@tonic-gate 			{
23670Sstevel@tonic-gate 				psc.flags = p->flags;
23680Sstevel@tonic-gate 				psc.event = p->event;
23690Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
23700Sstevel@tonic-gate 				psc.component = p->component;
23710Sstevel@tonic-gate 				psc.old_level = p->old_level;
23720Sstevel@tonic-gate 				psc.new_level = p->new_level;
23730Sstevel@tonic-gate 				copysize = ((long)&p->size -
23740Sstevel@tonic-gate 				    (long)&p->component);
23750Sstevel@tonic-gate 			}
23760Sstevel@tonic-gate 			if (p->size != (size_t)-1)
23770Sstevel@tonic-gate 				kmem_free(p->physpath, p->size);
23780Sstevel@tonic-gate 			p->size = 0;
23790Sstevel@tonic-gate 			p->physpath = NULL;
23800Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
23810Sstevel@tonic-gate 				p = pscep->psce_first;
23820Sstevel@tonic-gate 			else
23830Sstevel@tonic-gate 				p++;
23840Sstevel@tonic-gate 			pscep->psce_out = p;
23850Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
23860Sstevel@tonic-gate 
23870Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
23880Sstevel@tonic-gate 			    physlen, &lencopied);
23890Sstevel@tonic-gate 			kmem_free(physpath, physlen);
23900Sstevel@tonic-gate 			if (ret) {
23910Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
23920Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
23930Sstevel@tonic-gate 				    (void *)psc.physpath))
23940Sstevel@tonic-gate 				break;
23950Sstevel@tonic-gate 			}
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23980Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23990Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
24000Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
24010Sstevel@tonic-gate 				    != 0) {
24020Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24030Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24040Sstevel@tonic-gate 					ret = EFAULT;
24050Sstevel@tonic-gate 					break;
24060Sstevel@tonic-gate 				}
24070Sstevel@tonic-gate 			} else
24080Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
24090Sstevel@tonic-gate 			{
24100Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
24110Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
24120Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24130Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24140Sstevel@tonic-gate 					ret = EFAULT;
24150Sstevel@tonic-gate 					break;
24160Sstevel@tonic-gate 				}
24170Sstevel@tonic-gate 			}
24180Sstevel@tonic-gate 			ret = 0;
24190Sstevel@tonic-gate 			break;
24200Sstevel@tonic-gate 		}
24210Sstevel@tonic-gate 
24220Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY:
24230Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY_WAIT:
24240Sstevel@tonic-gate 		{
24250Sstevel@tonic-gate 			psce_t			*pscep;
24260Sstevel@tonic-gate 			pm_state_change_t	*p;
24270Sstevel@tonic-gate 			caddr_t			physpath;
24280Sstevel@tonic-gate 			size_t			physlen;
24290Sstevel@tonic-gate 			/*
24300Sstevel@tonic-gate 			 * We want to know if any direct device of ours has
24310Sstevel@tonic-gate 			 * something we should know about.  We look up by clone.
24320Sstevel@tonic-gate 			 * In case we have another thread from the same process,
24330Sstevel@tonic-gate 			 * we loop.
24340Sstevel@tonic-gate 			 * pm_psc_clone_to_direct() returns a locked entry.
24350Sstevel@tonic-gate 			 */
24360Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24370Sstevel@tonic-gate 			while (pm_poll_cnt[clone] == 0 ||
24380Sstevel@tonic-gate 			    (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
24390Sstevel@tonic-gate 				if (cmd == PM_DIRECT_NOTIFY) {
24400Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
24410Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
24420Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
24430Sstevel@tonic-gate 					ASSERT(!dipheld);
24440Sstevel@tonic-gate 					return (EWOULDBLOCK);
24450Sstevel@tonic-gate 				} else {
24460Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
24470Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
24480Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
24490Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
24500Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
24510Sstevel@tonic-gate 						ASSERT(!dipheld);
24520Sstevel@tonic-gate 						return (EINTR);
24530Sstevel@tonic-gate 					}
24540Sstevel@tonic-gate 				}
24550Sstevel@tonic-gate 			}
24560Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24570Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
24580Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
24590Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
24600Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
24610Sstevel@tonic-gate 				    cmdstr))
24620Sstevel@tonic-gate 				ret = EFAULT;
24630Sstevel@tonic-gate 				break;
24640Sstevel@tonic-gate 			}
24650Sstevel@tonic-gate 			physpath = kmem_zalloc(physlen, KM_SLEEP);
24660Sstevel@tonic-gate 			bcopy((const void *) pscep->psce_out->physpath,
24670Sstevel@tonic-gate 			    (void *) physpath, physlen);
24680Sstevel@tonic-gate 
24690Sstevel@tonic-gate 			p = pscep->psce_out;
24700Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
24710Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24720Sstevel@tonic-gate #ifdef DEBUG
24730Sstevel@tonic-gate 				size_t usrcopysize;
24740Sstevel@tonic-gate #endif
24750Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
24760Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
24770Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
24780Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
24790Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
24800Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
24810Sstevel@tonic-gate 				copysize32 = (intptr_t)&psc32.size -
24820Sstevel@tonic-gate 				    (intptr_t)&psc32.component;
24830Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
24840Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24850Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24860Sstevel@tonic-gate #ifdef DEBUG
24870Sstevel@tonic-gate 				usrcopysize = (intptr_t)&pscp32->size -
24880Sstevel@tonic-gate 				    (intptr_t)&pscp32->component;
24890Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
24900Sstevel@tonic-gate #endif
24910Sstevel@tonic-gate 			} else
24920Sstevel@tonic-gate #endif
24930Sstevel@tonic-gate 			{
24940Sstevel@tonic-gate 				psc.component = p->component;
24950Sstevel@tonic-gate 				psc.flags = p->flags;
24960Sstevel@tonic-gate 				psc.event = p->event;
24970Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
24980Sstevel@tonic-gate 				psc.old_level = p->old_level;
24990Sstevel@tonic-gate 				psc.new_level = p->new_level;
25000Sstevel@tonic-gate 				copysize = (intptr_t)&p->size -
25010Sstevel@tonic-gate 				    (intptr_t)&p->component;
25020Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
25030Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
25040Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
25050Sstevel@tonic-gate 			}
25060Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
25070Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
25080Sstevel@tonic-gate 			    "before decrement\n", cmdstr, clone,
25090Sstevel@tonic-gate 			    pm_poll_cnt[clone]))
25100Sstevel@tonic-gate 			pm_poll_cnt[clone]--;
25110Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
25120Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
25130Sstevel@tonic-gate 			p->size = 0;
25140Sstevel@tonic-gate 			p->physpath = NULL;
25150Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
25160Sstevel@tonic-gate 				p = pscep->psce_first;
25170Sstevel@tonic-gate 			else
25180Sstevel@tonic-gate 				p++;
25190Sstevel@tonic-gate 			pscep->psce_out = p;
25200Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
25210Sstevel@tonic-gate 
25220Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
25230Sstevel@tonic-gate 			    physlen, &lencopied);
25240Sstevel@tonic-gate 			kmem_free(physpath, physlen);
25250Sstevel@tonic-gate 			if (ret) {
25260Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
25270Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
25280Sstevel@tonic-gate 				    (void *)psc.physpath))
25290Sstevel@tonic-gate 				break;
25300Sstevel@tonic-gate 			}
25310Sstevel@tonic-gate 
25320Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
25330Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
25340Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
25350Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
25365295Srandyf 				    != 0) {
25370Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
25380Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
25390Sstevel@tonic-gate 					ret = EFAULT;
25400Sstevel@tonic-gate 					break;
25410Sstevel@tonic-gate 				}
25420Sstevel@tonic-gate 			} else
25430Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
25440Sstevel@tonic-gate 			{
25450Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
25460Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
25470Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
25480Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
25490Sstevel@tonic-gate 					ret = EFAULT;
25500Sstevel@tonic-gate 					break;
25510Sstevel@tonic-gate 				}
25520Sstevel@tonic-gate 			}
25530Sstevel@tonic-gate 			ret = 0;
25540Sstevel@tonic-gate 			break;
25550Sstevel@tonic-gate 		}
25560Sstevel@tonic-gate 		default:
25575295Srandyf 			/*
25585295Srandyf 			 * Internal error, invalid ioctl description
25595295Srandyf 			 * force debug entry even if pm_debug not set
25605295Srandyf 			 */
25615295Srandyf #ifdef	DEBUG
25625295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
25635295Srandyf 			    pcip->diptype, cmd, pcip->name);
25645295Srandyf #endif
25650Sstevel@tonic-gate 			ASSERT(0);
25665295Srandyf 			return (EIO);
25670Sstevel@tonic-gate 		}
25680Sstevel@tonic-gate 		break;
25695295Srandyf 	}
25705295Srandyf 
25715295Srandyf 	case PM_SRCH:		/* command that takes a pm_searchargs_t arg */
25725295Srandyf 	{
25735295Srandyf 		/*
25745295Srandyf 		 * If no ppm, then there is nothing to search.
25755295Srandyf 		 */
25765295Srandyf 		if (DEVI(ddi_root_node())->devi_pm_ppm == NULL) {
25775295Srandyf 			ret = ENODEV;
25785295Srandyf 			break;
25795295Srandyf 		}
25805295Srandyf 
25815295Srandyf #ifdef	_MULTI_DATAMODEL
25825295Srandyf 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
25835295Srandyf 			if (ddi_copyin((caddr_t)arg, &psa32,
25845295Srandyf 			    sizeof (psa32), mode) != 0) {
25855295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
25865295Srandyf 				    "EFAULT\n\n", cmdstr))
25875295Srandyf 				return (EFAULT);
25885295Srandyf 			}
25895295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_listname,
25905295Srandyf 			    listname, MAXCOPYBUF, NULL)) {
25915295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
25925295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
25935295Srandyf 				    (void *)(uintptr_t)psa32.pms_listname,
25945295Srandyf 				    MAXCOPYBUF))
25955295Srandyf 				ret = EFAULT;
25965295Srandyf 				break;
25975295Srandyf 			}
25985295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_manufacturer,
25995295Srandyf 			    manufacturer, MAXCOPYBUF, NULL)) {
26005295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26015295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26025295Srandyf 				    (void *)(uintptr_t)psa32.pms_manufacturer,
26035295Srandyf 				    MAXCOPYBUF))
26045295Srandyf 				ret = EFAULT;
26055295Srandyf 				break;
26065295Srandyf 			}
26075295Srandyf 			if (copyinstr((void *)(uintptr_t)psa32.pms_product,
26085295Srandyf 			    product, MAXCOPYBUF, NULL)) {
26095295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26105295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26115295Srandyf 				    (void *)(uintptr_t)psa32.pms_product,
26125295Srandyf 				    MAXCOPYBUF))
26135295Srandyf 				ret = EFAULT;
26145295Srandyf 				break;
26155295Srandyf 			}
26165295Srandyf 		} else
26175295Srandyf #endif /* _MULTI_DATAMODEL */
26185295Srandyf 		{
26195295Srandyf 			if (ddi_copyin((caddr_t)arg, &psa,
26205295Srandyf 			    sizeof (psa), mode) != 0) {
26215295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
26225295Srandyf 				    "EFAULT\n\n", cmdstr))
26235295Srandyf 				return (EFAULT);
26245295Srandyf 			}
26255295Srandyf 			if (copyinstr(psa.pms_listname,
26265295Srandyf 			    listname, MAXCOPYBUF, NULL)) {
26275295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26285295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26295295Srandyf 				    (void *)psa.pms_listname, MAXCOPYBUF))
26305295Srandyf 				ret = EFAULT;
26315295Srandyf 				break;
26325295Srandyf 			}
26335295Srandyf 			if (copyinstr(psa.pms_manufacturer,
26345295Srandyf 			    manufacturer, MAXCOPYBUF, NULL)) {
26355295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26365295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26375295Srandyf 				    (void *)psa.pms_manufacturer, MAXCOPYBUF))
26385295Srandyf 				ret = EFAULT;
26395295Srandyf 				break;
26405295Srandyf 			}
26415295Srandyf 			if (copyinstr(psa.pms_product,
26425295Srandyf 			    product, MAXCOPYBUF, NULL)) {
26435295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
26445295Srandyf 				    "%d, " "EFAULT\n", cmdstr,
26455295Srandyf 				    (void *)psa.pms_product, MAXCOPYBUF))
26465295Srandyf 				ret = EFAULT;
26475295Srandyf 				break;
26485295Srandyf 			}
26495295Srandyf 		}
26505295Srandyf 		psa.pms_listname = listname;
26515295Srandyf 		psa.pms_manufacturer = manufacturer;
26525295Srandyf 		psa.pms_product = product;
26535295Srandyf 		switch (cmd) {
26545295Srandyf 		case PM_SEARCH_LIST:
26555295Srandyf 			ret = pm_ppm_searchlist(&psa);
26565295Srandyf 			break;
26575295Srandyf 
26585295Srandyf 		default:
26595295Srandyf 			/*
26605295Srandyf 			 * Internal error, invalid ioctl description
26615295Srandyf 			 * force debug entry even if pm_debug not set
26625295Srandyf 			 */
26635295Srandyf #ifdef	DEBUG
26645295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
26655295Srandyf 			    pcip->diptype, cmd, pcip->name);
26665295Srandyf #endif
26675295Srandyf 			ASSERT(0);
26685295Srandyf 			return (EIO);
26695295Srandyf 		}
26705295Srandyf 		break;
26715295Srandyf 	}
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	case NOSTRUCT:
26745295Srandyf 	{
26750Sstevel@tonic-gate 		switch (cmd) {
26760Sstevel@tonic-gate 		case PM_START_PM:
26773028Smh27603 		case PM_START_CPUPM:
2678*8906SEric.Saxe@Sun.COM 		case PM_START_CPUPM_EV:
2679*8906SEric.Saxe@Sun.COM 		case PM_START_CPUPM_POLL:
26805295Srandyf 		{
2681*8906SEric.Saxe@Sun.COM 			pm_cpupm_t	new_mode = PM_CPUPM_NOTSET;
2682*8906SEric.Saxe@Sun.COM 			pm_cpupm_t	old_mode = PM_CPUPM_NOTSET;
2683*8906SEric.Saxe@Sun.COM 			int		r;
2684*8906SEric.Saxe@Sun.COM 
26850Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
26863028Smh27603 			if ((cmd == PM_START_PM && autopm_enabled) ||
2687*8906SEric.Saxe@Sun.COM 			    (cmd == PM_START_CPUPM && PM_DEFAULT_CPUPM) ||
2688*8906SEric.Saxe@Sun.COM 			    (cmd == PM_START_CPUPM_EV && PM_EVENT_CPUPM) ||
2689*8906SEric.Saxe@Sun.COM 			    (cmd == PM_START_CPUPM_POLL && PM_POLLING_CPUPM)) {
26900Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
2691*8906SEric.Saxe@Sun.COM 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n", cmdstr))
26920Sstevel@tonic-gate 				ret = EBUSY;
26930Sstevel@tonic-gate 				break;
26940Sstevel@tonic-gate 			}
2695*8906SEric.Saxe@Sun.COM 
2696*8906SEric.Saxe@Sun.COM 			if (cmd == PM_START_PM) {
26975295Srandyf 				autopm_enabled = 1;
2698*8906SEric.Saxe@Sun.COM 			} else if (cmd == PM_START_CPUPM) {
2699*8906SEric.Saxe@Sun.COM 				old_mode = cpupm;
2700*8906SEric.Saxe@Sun.COM 				new_mode = cpupm = cpupm_default_mode;
2701*8906SEric.Saxe@Sun.COM 			} else if (cmd == PM_START_CPUPM_EV) {
2702*8906SEric.Saxe@Sun.COM 				old_mode = cpupm;
2703*8906SEric.Saxe@Sun.COM 				new_mode = cpupm = PM_CPUPM_EVENT;
2704*8906SEric.Saxe@Sun.COM 			} else if (cmd == PM_START_CPUPM_POLL) {
2705*8906SEric.Saxe@Sun.COM 				old_mode = cpupm;
2706*8906SEric.Saxe@Sun.COM 				new_mode = cpupm = PM_CPUPM_POLLING;
2707*8906SEric.Saxe@Sun.COM 			}
2708*8906SEric.Saxe@Sun.COM 
27090Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
2710*8906SEric.Saxe@Sun.COM 
2711*8906SEric.Saxe@Sun.COM 			/*
2712*8906SEric.Saxe@Sun.COM 			 * If we are changing CPUPM modes, and it is active,
2713*8906SEric.Saxe@Sun.COM 			 * then stop it from operating in the old mode.
2714*8906SEric.Saxe@Sun.COM 			 */
2715*8906SEric.Saxe@Sun.COM 			if (old_mode == PM_CPUPM_POLLING) {
2716*8906SEric.Saxe@Sun.COM 				int c = PM_STOP_CPUPM;
2717*8906SEric.Saxe@Sun.COM 				ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk,
2718*8906SEric.Saxe@Sun.COM 				    &c);
2719*8906SEric.Saxe@Sun.COM 			} else if (old_mode == PM_CPUPM_EVENT) {
2720*8906SEric.Saxe@Sun.COM 				r = cpupm_set_policy(CPUPM_POLICY_DISABLED);
2721*8906SEric.Saxe@Sun.COM 
2722*8906SEric.Saxe@Sun.COM 				/*
2723*8906SEric.Saxe@Sun.COM 				 * Disabling CPUPM policy should always
2724*8906SEric.Saxe@Sun.COM 				 * succeed
2725*8906SEric.Saxe@Sun.COM 				 */
2726*8906SEric.Saxe@Sun.COM 				ASSERT(r == 0);
2727*8906SEric.Saxe@Sun.COM 			}
2728*8906SEric.Saxe@Sun.COM 
2729*8906SEric.Saxe@Sun.COM 			/*
2730*8906SEric.Saxe@Sun.COM 			 * If we are changing to event based CPUPM, enable it.
2731*8906SEric.Saxe@Sun.COM 			 * In the event it's not supported, fall back to
2732*8906SEric.Saxe@Sun.COM 			 * polling based CPUPM.
2733*8906SEric.Saxe@Sun.COM 			 */
2734*8906SEric.Saxe@Sun.COM 			if (new_mode == PM_CPUPM_EVENT &&
2735*8906SEric.Saxe@Sun.COM 			    cpupm_set_policy(CPUPM_POLICY_ELASTIC) < 0) {
2736*8906SEric.Saxe@Sun.COM 				mutex_enter(&pm_scan_lock);
2737*8906SEric.Saxe@Sun.COM 				new_mode = cpupm = PM_CPUPM_POLLING;
2738*8906SEric.Saxe@Sun.COM 				cmd = PM_START_CPUPM_POLL;
2739*8906SEric.Saxe@Sun.COM 				mutex_exit(&pm_scan_lock);
2740*8906SEric.Saxe@Sun.COM 			}
2741*8906SEric.Saxe@Sun.COM 			if (new_mode == PM_CPUPM_POLLING ||
2742*8906SEric.Saxe@Sun.COM 			    cmd == PM_START_PM) {
2743*8906SEric.Saxe@Sun.COM 				ddi_walk_devs(ddi_root_node(), pm_start_pm_walk,
2744*8906SEric.Saxe@Sun.COM 				    &cmd);
2745*8906SEric.Saxe@Sun.COM 			}
27460Sstevel@tonic-gate 			ret = 0;
27470Sstevel@tonic-gate 			break;
27485295Srandyf 		}
27490Sstevel@tonic-gate 
27500Sstevel@tonic-gate 		case PM_RESET_PM:
27510Sstevel@tonic-gate 		case PM_STOP_PM:
27523028Smh27603 		case PM_STOP_CPUPM:
27530Sstevel@tonic-gate 		{
27540Sstevel@tonic-gate 			extern void pm_discard_thresholds(void);
2755*8906SEric.Saxe@Sun.COM 			pm_cpupm_t old_mode = PM_CPUPM_NOTSET;
27560Sstevel@tonic-gate 
27570Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
27583028Smh27603 			if ((cmd == PM_STOP_PM && !autopm_enabled) ||
27593028Smh27603 			    (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
27600Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
27610Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
27620Sstevel@tonic-gate 				    cmdstr))
27630Sstevel@tonic-gate 				ret = EINVAL;
27640Sstevel@tonic-gate 				break;
27650Sstevel@tonic-gate 			}
2766*8906SEric.Saxe@Sun.COM 
27675295Srandyf 			if (cmd == PM_STOP_PM) {
27685295Srandyf 				autopm_enabled = 0;
27695295Srandyf 				pm_S3_enabled = 0;
27705295Srandyf 				autoS3_enabled = 0;
27715295Srandyf 			} else if (cmd == PM_STOP_CPUPM) {
2772*8906SEric.Saxe@Sun.COM 				old_mode = cpupm;
27735295Srandyf 				cpupm = PM_CPUPM_DISABLE;
27745295Srandyf 			} else {
27755295Srandyf 				autopm_enabled = 0;
27765295Srandyf 				autoS3_enabled = 0;
2777*8906SEric.Saxe@Sun.COM 				old_mode = cpupm;
27785295Srandyf 				cpupm = PM_CPUPM_NOTSET;
27793028Smh27603 			}
27800Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
27813028Smh27603 
27820Sstevel@tonic-gate 			/*
27830Sstevel@tonic-gate 			 * bring devices to full power level, stop scan
2784*8906SEric.Saxe@Sun.COM 			 * If CPUPM was operating in event driven mode, disable
2785*8906SEric.Saxe@Sun.COM 			 * that.
27860Sstevel@tonic-gate 			 */
2787*8906SEric.Saxe@Sun.COM 			if (old_mode == PM_CPUPM_EVENT) {
2788*8906SEric.Saxe@Sun.COM 				(void) cpupm_set_policy(CPUPM_POLICY_DISABLED);
2789*8906SEric.Saxe@Sun.COM 			}
27900Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
27910Sstevel@tonic-gate 			ret = 0;
27923028Smh27603 			if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
27930Sstevel@tonic-gate 				break;
27940Sstevel@tonic-gate 			/*
27950Sstevel@tonic-gate 			 * Now do only PM_RESET_PM stuff.
27960Sstevel@tonic-gate 			 */
27970Sstevel@tonic-gate 			pm_system_idle_threshold = pm_default_idle_threshold;
27983028Smh27603 			pm_cpu_idle_threshold = 0;
27990Sstevel@tonic-gate 			pm_discard_thresholds();
28000Sstevel@tonic-gate 			pm_all_to_default_thresholds();
28010Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
28020Sstevel@tonic-gate 			    NULL, NULL, PM_DEP_WAIT, NULL, 0);
28030Sstevel@tonic-gate 			break;
28040Sstevel@tonic-gate 		}
28050Sstevel@tonic-gate 
28060Sstevel@tonic-gate 		case PM_GET_SYSTEM_THRESHOLD:
28075295Srandyf 		{
28080Sstevel@tonic-gate 			*rval_p = pm_system_idle_threshold;
28090Sstevel@tonic-gate 			ret = 0;
28100Sstevel@tonic-gate 			break;
28115295Srandyf 		}
28120Sstevel@tonic-gate 
28130Sstevel@tonic-gate 		case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
28145295Srandyf 		{
28150Sstevel@tonic-gate 			*rval_p = pm_default_idle_threshold;
28160Sstevel@tonic-gate 			ret = 0;
28170Sstevel@tonic-gate 			break;
28185295Srandyf 		}
28190Sstevel@tonic-gate 
28203028Smh27603 		case PM_GET_CPU_THRESHOLD:
28215295Srandyf 		{
28223028Smh27603 			*rval_p = pm_cpu_idle_threshold;
28233028Smh27603 			ret = 0;
28243028Smh27603 			break;
28255295Srandyf 		}
28263028Smh27603 
28270Sstevel@tonic-gate 		case PM_SET_SYSTEM_THRESHOLD:
28283028Smh27603 		case PM_SET_CPU_THRESHOLD:
28295295Srandyf 		{
28300Sstevel@tonic-gate 			if ((int)arg < 0) {
28310Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
28320Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, (int)arg))
28330Sstevel@tonic-gate 				ret = EINVAL;
28340Sstevel@tonic-gate 				break;
28350Sstevel@tonic-gate 			}
28360Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
28370Sstevel@tonic-gate 			    (int)arg, (int)arg))
28383028Smh27603 			if (cmd == PM_SET_SYSTEM_THRESHOLD)
28393028Smh27603 				pm_system_idle_threshold = (int)arg;
28403028Smh27603 			else {
28413028Smh27603 				pm_cpu_idle_threshold = (int)arg;
28423028Smh27603 			}
28433028Smh27603 			ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
28445295Srandyf 			    (void *) &cmd);
28453028Smh27603 
28460Sstevel@tonic-gate 			ret = 0;
28470Sstevel@tonic-gate 			break;
28485295Srandyf 		}
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate 		case PM_IDLE_DOWN:
28515295Srandyf 		{
28520Sstevel@tonic-gate 			if (pm_timeout_idledown() != 0) {
28530Sstevel@tonic-gate 				ddi_walk_devs(ddi_root_node(),
28540Sstevel@tonic-gate 				    pm_start_idledown, (void *)PMID_IOC);
28550Sstevel@tonic-gate 			}
28560Sstevel@tonic-gate 			ret = 0;
28570Sstevel@tonic-gate 			break;
28585295Srandyf 		}
28590Sstevel@tonic-gate 
28600Sstevel@tonic-gate 		case PM_GET_PM_STATE:
28615295Srandyf 		{
28620Sstevel@tonic-gate 			if (autopm_enabled) {
28630Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_ENABLED;
28640Sstevel@tonic-gate 			} else {
28650Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_DISABLED;
28660Sstevel@tonic-gate 			}
28670Sstevel@tonic-gate 			ret = 0;
28680Sstevel@tonic-gate 			break;
28695295Srandyf 		}
28703028Smh27603 
28713028Smh27603 		case PM_GET_CPUPM_STATE:
28725295Srandyf 		{
2873*8906SEric.Saxe@Sun.COM 			if (PM_POLLING_CPUPM || PM_EVENT_CPUPM)
28743028Smh27603 				*rval_p = PM_CPU_PM_ENABLED;
28753028Smh27603 			else if (PM_CPUPM_DISABLED)
28763028Smh27603 				*rval_p = PM_CPU_PM_DISABLED;
28773028Smh27603 			else
28783028Smh27603 				*rval_p = PM_CPU_PM_NOTSET;
28793028Smh27603 			ret = 0;
28803028Smh27603 			break;
28810Sstevel@tonic-gate 		}
28825295Srandyf 
28835295Srandyf 		case PM_GET_AUTOS3_STATE:
28845295Srandyf 		{
28855295Srandyf 			if (autoS3_enabled) {
28865295Srandyf 				*rval_p = PM_AUTOS3_ENABLED;
28875295Srandyf 			} else {
28885295Srandyf 				*rval_p = PM_AUTOS3_DISABLED;
28895295Srandyf 			}
28905295Srandyf 			ret = 0;
28915295Srandyf 			break;
28925295Srandyf 		}
28935295Srandyf 
28945295Srandyf 		case PM_GET_S3_SUPPORT_STATE:
28955295Srandyf 		{
28965295Srandyf 			if (pm_S3_enabled) {
28975295Srandyf 				*rval_p = PM_S3_SUPPORT_ENABLED;
28985295Srandyf 			} else {
28995295Srandyf 				*rval_p = PM_S3_SUPPORT_DISABLED;
29005295Srandyf 			}
29015295Srandyf 			ret = 0;
29025295Srandyf 			break;
29035295Srandyf 		}
29045295Srandyf 
29055295Srandyf 		/*
29065295Srandyf 		 * pmconfig tells us if the platform supports S3
29075295Srandyf 		 */
29085295Srandyf 		case PM_ENABLE_S3:
29095295Srandyf 		{
29105295Srandyf 			mutex_enter(&pm_scan_lock);
29115295Srandyf 			if (pm_S3_enabled) {
29125295Srandyf 				mutex_exit(&pm_scan_lock);
29135295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
29145295Srandyf 				    cmdstr))
29155295Srandyf 				ret = EBUSY;
29165295Srandyf 				break;
29175295Srandyf 			}
29185295Srandyf 			pm_S3_enabled = 1;
29195295Srandyf 			mutex_exit(&pm_scan_lock);
29205295Srandyf 			ret = 0;
29215295Srandyf 			break;
29225295Srandyf 		}
29235295Srandyf 
29245295Srandyf 		case PM_DISABLE_S3:
29255295Srandyf 		{
29265295Srandyf 			mutex_enter(&pm_scan_lock);
29275295Srandyf 			pm_S3_enabled = 0;
29285295Srandyf 			mutex_exit(&pm_scan_lock);
29295295Srandyf 			ret = 0;
29305295Srandyf 			break;
29315295Srandyf 		}
29325295Srandyf 
29335295Srandyf 		case PM_START_AUTOS3:
29345295Srandyf 		{
29355295Srandyf 			mutex_enter(&pm_scan_lock);
29365295Srandyf 			if (autoS3_enabled) {
29375295Srandyf 				mutex_exit(&pm_scan_lock);
29385295Srandyf 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
29395295Srandyf 				    cmdstr))
29405295Srandyf 				ret = EBUSY;
29415295Srandyf 				break;
29425295Srandyf 			}
29435295Srandyf 			autoS3_enabled = 1;
29445295Srandyf 			mutex_exit(&pm_scan_lock);
29455295Srandyf 			ret = 0;
29465295Srandyf 			break;
29475295Srandyf 		}
29485295Srandyf 
29495295Srandyf 		case PM_STOP_AUTOS3:
29505295Srandyf 		{
29515295Srandyf 			mutex_enter(&pm_scan_lock);
29525295Srandyf 			autoS3_enabled = 0;
29535295Srandyf 			mutex_exit(&pm_scan_lock);
29545295Srandyf 			ret = 0;
29555295Srandyf 			break;
29565295Srandyf 		}
29575295Srandyf 
2958*8906SEric.Saxe@Sun.COM 		case PM_ENABLE_CPU_DEEP_IDLE:
2959*8906SEric.Saxe@Sun.COM 		{
2960*8906SEric.Saxe@Sun.COM 			if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2961*8906SEric.Saxe@Sun.COM 			    PM_ENABLE_CPU_DEEP_IDLE) == NULL)
2962*8906SEric.Saxe@Sun.COM 				ret = 0;
2963*8906SEric.Saxe@Sun.COM 			else
2964*8906SEric.Saxe@Sun.COM 				ret = EBUSY;
2965*8906SEric.Saxe@Sun.COM 			break;
2966*8906SEric.Saxe@Sun.COM 		}
2967*8906SEric.Saxe@Sun.COM 		case PM_DISABLE_CPU_DEEP_IDLE:
2968*8906SEric.Saxe@Sun.COM 		{
2969*8906SEric.Saxe@Sun.COM 			if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2970*8906SEric.Saxe@Sun.COM 			    PM_DISABLE_CPU_DEEP_IDLE) == NULL)
2971*8906SEric.Saxe@Sun.COM 				ret = 0;
2972*8906SEric.Saxe@Sun.COM 			else
2973*8906SEric.Saxe@Sun.COM 				ret = EINVAL;
2974*8906SEric.Saxe@Sun.COM 			break;
2975*8906SEric.Saxe@Sun.COM 		}
2976*8906SEric.Saxe@Sun.COM 		case PM_DEFAULT_CPU_DEEP_IDLE:
2977*8906SEric.Saxe@Sun.COM 		{
2978*8906SEric.Saxe@Sun.COM 			if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2979*8906SEric.Saxe@Sun.COM 			    PM_DEFAULT_CPU_DEEP_IDLE) == NULL)
2980*8906SEric.Saxe@Sun.COM 				ret = 0;
2981*8906SEric.Saxe@Sun.COM 			else
2982*8906SEric.Saxe@Sun.COM 				ret = EBUSY;
2983*8906SEric.Saxe@Sun.COM 			break;
2984*8906SEric.Saxe@Sun.COM 		}
2985*8906SEric.Saxe@Sun.COM 
29865295Srandyf 		default:
29875295Srandyf 			/*
29885295Srandyf 			 * Internal error, invalid ioctl description
29895295Srandyf 			 * force debug entry even if pm_debug not set
29905295Srandyf 			 */
29915295Srandyf #ifdef	DEBUG
29925295Srandyf 			pm_log("invalid diptype %d for cmd %d (%s)\n",
29935295Srandyf 			    pcip->diptype, cmd, pcip->name);
29945295Srandyf #endif
29955295Srandyf 			ASSERT(0);
29965295Srandyf 			return (EIO);
29975295Srandyf 		}
29980Sstevel@tonic-gate 		break;
29995295Srandyf 	}
30000Sstevel@tonic-gate 
3001*8906SEric.Saxe@Sun.COM default:
30020Sstevel@tonic-gate 		/*
30030Sstevel@tonic-gate 		 * Internal error, invalid ioctl description
30040Sstevel@tonic-gate 		 * force debug entry even if pm_debug not set
30050Sstevel@tonic-gate 		 */
30060Sstevel@tonic-gate #ifdef	DEBUG
30070Sstevel@tonic-gate 		pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
30080Sstevel@tonic-gate 		    pcip->str_type, cmd, pcip->name);
30090Sstevel@tonic-gate #endif
30100Sstevel@tonic-gate 		ASSERT(0);
30110Sstevel@tonic-gate 		return (EIO);
30120Sstevel@tonic-gate 	}
30130Sstevel@tonic-gate 	ASSERT(ret != 0x0badcafe);	/* some cmd in wrong case! */
30140Sstevel@tonic-gate 	if (dipheld) {
30150Sstevel@tonic-gate 		ASSERT(dip);
30160Sstevel@tonic-gate 		PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
30170Sstevel@tonic-gate 		    "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
30180Sstevel@tonic-gate 		PM_RELE(dip);
30190Sstevel@tonic-gate 	}
30200Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
30210Sstevel@tonic-gate 	return (ret);
30220Sstevel@tonic-gate }
3023