xref: /titanic_52/usr/src/uts/common/io/pm.c (revision 9681b4a118f2414b550ab72cae27eb733d0b15f3)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5c42872d4Smh27603  * Common Development and Distribution License (the "License").
6c42872d4Smh27603  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*9681b4a1Skchow  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * pm	This driver now only handles the ioctl interface.  The scanning
307c478bd9Sstevel@tonic-gate  *	and policy stuff now lives in common/os/sunpm.c.
317c478bd9Sstevel@tonic-gate  *	Not DDI compliant
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <sys/errno.h>
367c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
377c478bd9Sstevel@tonic-gate #include <sys/conf.h>		/* driver flags and functions */
387c478bd9Sstevel@tonic-gate #include <sys/open.h>		/* OTYP_CHR definition */
397c478bd9Sstevel@tonic-gate #include <sys/stat.h>		/* S_IFCHR definition */
407c478bd9Sstevel@tonic-gate #include <sys/pathname.h>	/* name -> dev_info xlation */
417c478bd9Sstevel@tonic-gate #include <sys/kmem.h>		/* memory alloc stuff */
427c478bd9Sstevel@tonic-gate #include <sys/debug.h>
437c478bd9Sstevel@tonic-gate #include <sys/pm.h>
447c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
457c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
467c478bd9Sstevel@tonic-gate #include <sys/epm.h>
477c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
487c478bd9Sstevel@tonic-gate #include <sys/mode.h>
497c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
507c478bd9Sstevel@tonic-gate #include <sys/promif.h>
517c478bd9Sstevel@tonic-gate #include <sys/consdev.h>
527c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
537c478bd9Sstevel@tonic-gate #include <sys/poll.h>
547c478bd9Sstevel@tonic-gate #include <sys/note.h>
557c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
567c478bd9Sstevel@tonic-gate #include <sys/policy.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * Minor number is instance<<8 + clone minor from range 1-255; (0 reserved
607c478bd9Sstevel@tonic-gate  * for "original"
617c478bd9Sstevel@tonic-gate  */
627c478bd9Sstevel@tonic-gate #define	PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE - 1))
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #define	PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
657c478bd9Sstevel@tonic-gate #define	PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
667c478bd9Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_driver_major(dip)
677c478bd9Sstevel@tonic-gate #define	PM_RELE(dip) ddi_release_devi(dip)
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #define	PM_IDLEDOWN_TIME	10
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate extern kmutex_t	pm_scan_lock;	/* protects autopm_enable, pm_scans_disabled */
727c478bd9Sstevel@tonic-gate extern kmutex_t	pm_clone_lock;	/* protects pm_clones array */
737c478bd9Sstevel@tonic-gate extern int	autopm_enabled;
74c42872d4Smh27603 extern pm_cpupm_t cpupm;
75c42872d4Smh27603 extern int	pm_default_idle_threshold;
76c42872d4Smh27603 extern int	pm_system_idle_threshold;
77c42872d4Smh27603 extern int	pm_cpu_idle_threshold;
787c478bd9Sstevel@tonic-gate extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
797c478bd9Sstevel@tonic-gate extern uint_t	pm_poll_cnt[PM_MAX_CLONE];
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * The soft state of the power manager.  Since there will only
837c478bd9Sstevel@tonic-gate  * one of these, just reference it through a static pointer.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate static struct pmstate {
867c478bd9Sstevel@tonic-gate 	dev_info_t	*pm_dip;		/* ptr to our dev_info node */
877c478bd9Sstevel@tonic-gate 	int		pm_instance;		/* for ddi_get_instance() */
887c478bd9Sstevel@tonic-gate 	timeout_id_t	pm_idledown_id;		/* pm idledown timeout id */
897c478bd9Sstevel@tonic-gate 	uchar_t		pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */
907c478bd9Sstevel@tonic-gate 	struct cred	*pm_cred[PM_MAX_CLONE];	/* cred for each unique open */
917c478bd9Sstevel@tonic-gate } pm_state = { NULL, -1, (timeout_id_t)0 };
927c478bd9Sstevel@tonic-gate typedef struct pmstate *pm_state_t;
937c478bd9Sstevel@tonic-gate static pm_state_t pmstp = &pm_state;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate static int	pm_open(dev_t *, int, int, cred_t *);
967c478bd9Sstevel@tonic-gate static int	pm_close(dev_t, int, int, cred_t *);
977c478bd9Sstevel@tonic-gate static int	pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
987c478bd9Sstevel@tonic-gate static int	pm_chpoll(dev_t, short, int, short *, struct pollhead **);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate static struct cb_ops pm_cb_ops = {
1017c478bd9Sstevel@tonic-gate 	pm_open,	/* open */
1027c478bd9Sstevel@tonic-gate 	pm_close,	/* close */
1037c478bd9Sstevel@tonic-gate 	nodev,		/* strategy */
1047c478bd9Sstevel@tonic-gate 	nodev,		/* print */
1057c478bd9Sstevel@tonic-gate 	nodev,		/* dump */
1067c478bd9Sstevel@tonic-gate 	nodev,		/* read */
1077c478bd9Sstevel@tonic-gate 	nodev,		/* write */
1087c478bd9Sstevel@tonic-gate 	pm_ioctl,	/* ioctl */
1097c478bd9Sstevel@tonic-gate 	nodev,		/* devmap */
1107c478bd9Sstevel@tonic-gate 	nodev,		/* mmap */
1117c478bd9Sstevel@tonic-gate 	nodev,		/* segmap */
1127c478bd9Sstevel@tonic-gate 	pm_chpoll,	/* poll */
1137c478bd9Sstevel@tonic-gate 	ddi_prop_op,	/* prop_op */
1147c478bd9Sstevel@tonic-gate 	NULL,		/* streamtab */
1157c478bd9Sstevel@tonic-gate 	D_NEW | D_MP	/* driver compatibility flag */
1167c478bd9Sstevel@tonic-gate };
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1197c478bd9Sstevel@tonic-gate     void **result);
1207c478bd9Sstevel@tonic-gate static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1217c478bd9Sstevel@tonic-gate static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate static struct dev_ops pm_ops = {
1247c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1257c478bd9Sstevel@tonic-gate 	0,			/* refcnt */
1267c478bd9Sstevel@tonic-gate 	pm_getinfo,		/* info */
1277c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
1287c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
1297c478bd9Sstevel@tonic-gate 	pm_attach,		/* attach */
1307c478bd9Sstevel@tonic-gate 	pm_detach,		/* detach */
1317c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
1327c478bd9Sstevel@tonic-gate 	&pm_cb_ops,		/* driver operations */
1337c478bd9Sstevel@tonic-gate 	NULL,			/* bus operations */
1347c478bd9Sstevel@tonic-gate 	NULL			/* power */
1357c478bd9Sstevel@tonic-gate };
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
1387c478bd9Sstevel@tonic-gate 	&mod_driverops,
1397c478bd9Sstevel@tonic-gate 	"power management driver v%I%",
1407c478bd9Sstevel@tonic-gate 	&pm_ops
1417c478bd9Sstevel@tonic-gate };
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1447c478bd9Sstevel@tonic-gate 	MODREV_1, &modldrv, 0
1457c478bd9Sstevel@tonic-gate };
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate /* Local functions */
1487c478bd9Sstevel@tonic-gate #ifdef DEBUG
1497c478bd9Sstevel@tonic-gate static int	print_info(dev_info_t *, void *);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate #endif
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate int
1547c478bd9Sstevel@tonic-gate _init(void)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate int
1607c478bd9Sstevel@tonic-gate _fini(void)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate int
1667c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate static int
1727c478bd9Sstevel@tonic-gate pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	int		i;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	switch (cmd) {
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
1797c478bd9Sstevel@tonic-gate 		if (pmstp->pm_instance != -1)	/* Only allow one instance */
1807c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
1817c478bd9Sstevel@tonic-gate 		pmstp->pm_instance = ddi_get_instance(dip);
1827c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "pm", S_IFCHR,
1837c478bd9Sstevel@tonic-gate 		    (pmstp->pm_instance << 8) + 0,
1847c478bd9Sstevel@tonic-gate 			DDI_PSEUDO, 0) != DDI_SUCCESS) {
1857c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
1867c478bd9Sstevel@tonic-gate 		}
1877c478bd9Sstevel@tonic-gate 		pmstp->pm_dip = dip;	/* pm_init and getinfo depend on it */
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
1907c478bd9Sstevel@tonic-gate 			cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		ddi_report_dev(dip);
1937c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	default:
1967c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /* ARGSUSED */
2017c478bd9Sstevel@tonic-gate static int
2027c478bd9Sstevel@tonic-gate pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2037c478bd9Sstevel@tonic-gate {
2047c478bd9Sstevel@tonic-gate 	int i;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	switch (cmd) {
2077c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
2087c478bd9Sstevel@tonic-gate 		/*
2097c478bd9Sstevel@tonic-gate 		 * Don't detach while idledown timeout is pending.  Note that
2107c478bd9Sstevel@tonic-gate 		 * we already know we're not in pm_ioctl() due to framework
2117c478bd9Sstevel@tonic-gate 		 * synchronization, so this is a sufficient test
2127c478bd9Sstevel@tonic-gate 		 */
2137c478bd9Sstevel@tonic-gate 		if (pmstp->pm_idledown_id)
2147c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2177c478bd9Sstevel@tonic-gate 			cv_destroy(&pm_clones_cv[i]);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
2207c478bd9Sstevel@tonic-gate 		pmstp->pm_instance = -1;
2217c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	default:
2247c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2257c478bd9Sstevel@tonic-gate 	}
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate static int
2297c478bd9Sstevel@tonic-gate pm_close_direct_pm_device(dev_info_t *dip, void *arg)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate 	int clone;
2327c478bd9Sstevel@tonic-gate 	char *pathbuf;
2337c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	clone = *((int *)arg);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	if (!info)
2387c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2417c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
2427c478bd9Sstevel@tonic-gate 	if (clone == info->pmi_clone) {
2437c478bd9Sstevel@tonic-gate 		PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n",
2447c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
2457c478bd9Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip));
2467c478bd9Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
2477c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2487c478bd9Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
2497c478bd9Sstevel@tonic-gate 		/* Bring ourselves up if there is a keeper that is up */
2507c478bd9Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
2517c478bd9Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL,
2527c478bd9Sstevel@tonic-gate 		    pathbuf, PM_DEP_NOWAIT, NULL, 0);
2537c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
2547c478bd9Sstevel@tonic-gate 		info->pmi_clone = 0;
2557c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2567c478bd9Sstevel@tonic-gate 	} else {
2577c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	/* restart autopm on device released from direct pm */
2627c478bd9Sstevel@tonic-gate 	pm_rescan(dip);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate #define	PM_REQ		1
2687c478bd9Sstevel@tonic-gate #define	NOSTRUCT	2
2697c478bd9Sstevel@tonic-gate #define	DIP		3
2707c478bd9Sstevel@tonic-gate #define	NODIP		4
2717c478bd9Sstevel@tonic-gate #define	NODEP		5
2727c478bd9Sstevel@tonic-gate #define	DEP		6
2737c478bd9Sstevel@tonic-gate #define	PM_PSC		7
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate #define	CHECKPERMS	0x001
2767c478bd9Sstevel@tonic-gate #define	SU		0x002
2777c478bd9Sstevel@tonic-gate #define	SG		0x004
2787c478bd9Sstevel@tonic-gate #define	OWNER		0x008
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate #define	INWHO		0x001
2817c478bd9Sstevel@tonic-gate #define	INDATAINT	0x002
2827c478bd9Sstevel@tonic-gate #define	INDATASTRING	0x004
2837c478bd9Sstevel@tonic-gate #define	INDEP		0x008
2847c478bd9Sstevel@tonic-gate #define	INDATAOUT	0x010
2857c478bd9Sstevel@tonic-gate #define	INDATA	(INDATAOUT | INDATAINT | INDATASTRING | INDEP)
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate struct pm_cmd_info {
2887c478bd9Sstevel@tonic-gate 	int cmd;		/* command code */
2897c478bd9Sstevel@tonic-gate 	char *name;		/* printable string */
2907c478bd9Sstevel@tonic-gate 	int supported;		/* true if still supported */
2917c478bd9Sstevel@tonic-gate 	int str_type;		/* PM_REQ or NOSTRUCT */
2927c478bd9Sstevel@tonic-gate 	int inargs;		/* INWHO, INDATAINT, INDATASTRING, INDEP, */
2937c478bd9Sstevel@tonic-gate 				/* INDATAOUT */
2947c478bd9Sstevel@tonic-gate 	int diptype;		/* DIP or NODIP */
2957c478bd9Sstevel@tonic-gate 	int deptype;		/* DEP or NODEP */
2967c478bd9Sstevel@tonic-gate 	int permission;		/* SU, GU, or CHECKPERMS */
2977c478bd9Sstevel@tonic-gate };
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate #ifdef DEBUG
3007c478bd9Sstevel@tonic-gate char *pm_cmd_string;
3017c478bd9Sstevel@tonic-gate int pm_cmd;
3027c478bd9Sstevel@tonic-gate #endif
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate  * Returns true if permission granted by credentials
3067c478bd9Sstevel@tonic-gate  */
3077c478bd9Sstevel@tonic-gate static int
3087c478bd9Sstevel@tonic-gate pm_perms(int perm, cred_t *cr)
3097c478bd9Sstevel@tonic-gate {
3107c478bd9Sstevel@tonic-gate 	if (perm == 0)			/* no restrictions */
3117c478bd9Sstevel@tonic-gate 		return (1);
3127c478bd9Sstevel@tonic-gate 	if (perm == CHECKPERMS)		/* ok for now (is checked later) */
3137c478bd9Sstevel@tonic-gate 		return (1);
3147c478bd9Sstevel@tonic-gate 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
3157c478bd9Sstevel@tonic-gate 		return (1);
3167c478bd9Sstevel@tonic-gate 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
3177c478bd9Sstevel@tonic-gate 		return (1);
3187c478bd9Sstevel@tonic-gate 	return (0);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate #ifdef DEBUG
3227c478bd9Sstevel@tonic-gate static int
3237c478bd9Sstevel@tonic-gate print_info(dev_info_t *dip, void *arg)
3247c478bd9Sstevel@tonic-gate {
3257c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
3267c478bd9Sstevel@tonic-gate 	pm_info_t	*info;
3277c478bd9Sstevel@tonic-gate 	int		i, j;
3287c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
3297c478bd9Sstevel@tonic-gate 	extern int pm_cur_power(pm_component_t *cp);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
3327c478bd9Sstevel@tonic-gate 	if (!info)
3337c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
3347c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip));
3357c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
3367c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
3377c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tThresholds[%d] =",  i);
3387c478bd9Sstevel@tonic-gate 		for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++)
3397c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]);
3407c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "\n");
3417c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i,
3427c478bd9Sstevel@tonic-gate 		    pm_cur_power(cp));
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 	if (PM_ISDIRECT(dip))
3457c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tDirect power management\n");
3467c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate #endif
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * command, name, supported, str_type, inargs, diptype, deptype, permission
3527c478bd9Sstevel@tonic-gate  */
3537c478bd9Sstevel@tonic-gate static struct pm_cmd_info pmci[] = {
3547c478bd9Sstevel@tonic-gate 	{PM_SCHEDULE, "PM_SCHEDULE", 0},
3557c478bd9Sstevel@tonic-gate 	{PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
3567c478bd9Sstevel@tonic-gate 	{PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
3577c478bd9Sstevel@tonic-gate 	{PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
3587c478bd9Sstevel@tonic-gate 	{PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
3597c478bd9Sstevel@tonic-gate 	{PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
3607c478bd9Sstevel@tonic-gate 	{PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
3617c478bd9Sstevel@tonic-gate 	{PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
3627c478bd9Sstevel@tonic-gate 	{PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
3637c478bd9Sstevel@tonic-gate 	{PM_GET_DEP, "PM_GET_DEP", 0},
3647c478bd9Sstevel@tonic-gate 	{PM_ADD_DEP, "PM_ADD_DEP", 0},
3657c478bd9Sstevel@tonic-gate 	{PM_REM_DEP, "PM_REM_DEP", 0},
3667c478bd9Sstevel@tonic-gate 	{PM_REM_DEVICE, "PM_REM_DEVICE", 0},
3677c478bd9Sstevel@tonic-gate 	{PM_REM_DEVICES, "PM_REM_DEVICES", 0},
3687c478bd9Sstevel@tonic-gate 	{PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP,
3697c478bd9Sstevel@tonic-gate 	    NODEP},
3707c478bd9Sstevel@tonic-gate 	{PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
3717c478bd9Sstevel@tonic-gate 	{PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
3727c478bd9Sstevel@tonic-gate 	{PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
3737c478bd9Sstevel@tonic-gate 	{PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ,
3747c478bd9Sstevel@tonic-gate 	    INWHO, NODIP, NODEP, SU},
3757c478bd9Sstevel@tonic-gate 	{PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT},
3767c478bd9Sstevel@tonic-gate 	{PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
3777c478bd9Sstevel@tonic-gate 	    1, NOSTRUCT},
3787c478bd9Sstevel@tonic-gate 	{PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT,
3797c478bd9Sstevel@tonic-gate 	    0, 0, 0, SU},
3807c478bd9Sstevel@tonic-gate 	{PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3817c478bd9Sstevel@tonic-gate 	{PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3827c478bd9Sstevel@tonic-gate 	{PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3837c478bd9Sstevel@tonic-gate 	{PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT,
3847c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
3857c478bd9Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO,
3867c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
3877c478bd9Sstevel@tonic-gate 	{PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT,
3887c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
3897c478bd9Sstevel@tonic-gate 	{PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ,
3907c478bd9Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
3917c478bd9Sstevel@tonic-gate 	{PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO,
3927c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
3937c478bd9Sstevel@tonic-gate 	{PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ,
3947c478bd9Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
3957c478bd9Sstevel@tonic-gate 	{PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO,
3967c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
3977c478bd9Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC},
3987c478bd9Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC},
3997c478bd9Sstevel@tonic-gate 	{PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP,
4007c478bd9Sstevel@tonic-gate 	    (SU | SG)},
4017c478bd9Sstevel@tonic-gate 	{PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO,
4027c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
4037c478bd9Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC},
4047c478bd9Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC},
4057c478bd9Sstevel@tonic-gate 	{PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
4067c478bd9Sstevel@tonic-gate 	    INWHO, DIP, NODEP, SU},
4077c478bd9Sstevel@tonic-gate 	{PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
4087c478bd9Sstevel@tonic-gate 	{PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
4097c478bd9Sstevel@tonic-gate 	    DIP, NODEP},
4107c478bd9Sstevel@tonic-gate 	{PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4117c478bd9Sstevel@tonic-gate 	    INWHO | INDATAINT, NODIP, NODEP, SU},
4127c478bd9Sstevel@tonic-gate 	{PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4137c478bd9Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4147c478bd9Sstevel@tonic-gate 	{PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU},
4157c478bd9Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1,
4167c478bd9Sstevel@tonic-gate 	    PM_REQ, INWHO, DIP, NODEP},
4177c478bd9Sstevel@tonic-gate 	{PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4187c478bd9Sstevel@tonic-gate 	    NODEP},
4197c478bd9Sstevel@tonic-gate 	{PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4207c478bd9Sstevel@tonic-gate 	    NODEP},
4217c478bd9Sstevel@tonic-gate 	{PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP,
4227c478bd9Sstevel@tonic-gate 	    NODEP},
4237c478bd9Sstevel@tonic-gate 	{PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING,
4247c478bd9Sstevel@tonic-gate 	    DIP, DEP, SU},
4257c478bd9Sstevel@tonic-gate 	{PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP},
4267c478bd9Sstevel@tonic-gate 	{PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ,
4277c478bd9Sstevel@tonic-gate 	    INWHO | INDATASTRING, NODIP, DEP, SU},
428c42872d4Smh27603 	{PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
429c42872d4Smh27603 	{PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
430c42872d4Smh27603 	{PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
431c42872d4Smh27603 	{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
432c42872d4Smh27603 	    0, 0, 0, SU},
433c42872d4Smh27603 	{PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
4347c478bd9Sstevel@tonic-gate 	{0, NULL}
4357c478bd9Sstevel@tonic-gate };
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate struct pm_cmd_info *
4387c478bd9Sstevel@tonic-gate pc_info(int cmd)
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate 	struct pm_cmd_info *pcip;
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	for (pcip = pmci; pcip->name; pcip++) {
4437c478bd9Sstevel@tonic-gate 		if (cmd == pcip->cmd)
4447c478bd9Sstevel@tonic-gate 			return (pcip);
4457c478bd9Sstevel@tonic-gate 	}
4467c478bd9Sstevel@tonic-gate 	return (NULL);
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate static char *
4507c478bd9Sstevel@tonic-gate pm_decode_cmd(int cmd)
4517c478bd9Sstevel@tonic-gate {
4527c478bd9Sstevel@tonic-gate 	static char invbuf[64];
4537c478bd9Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
4547c478bd9Sstevel@tonic-gate 	if (pcip != NULL)
4557c478bd9Sstevel@tonic-gate 		return (pcip->name);
4567c478bd9Sstevel@tonic-gate 	(void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd);
4577c478bd9Sstevel@tonic-gate 	return (invbuf);
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * Allocate scan resource, create taskq, then dispatch scan,
4627c478bd9Sstevel@tonic-gate  * called only if autopm is enabled.
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate int
4657c478bd9Sstevel@tonic-gate pm_start_pm_walk(dev_info_t *dip, void *arg)
4667c478bd9Sstevel@tonic-gate {
467c42872d4Smh27603 	int cmd = *((int *)arg);
468*9681b4a1Skchow #ifdef PMDDEBUG
469c42872d4Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
470*9681b4a1Skchow #endif
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
4737c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
4747c478bd9Sstevel@tonic-gate 
475c42872d4Smh27603 	switch (cmd) {
476c42872d4Smh27603 	case PM_START_CPUPM:
477c42872d4Smh27603 		if (!PM_ISCPU(dip))
478c42872d4Smh27603 			return (DDI_WALK_CONTINUE);
4797c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
480c42872d4Smh27603 		if (!PM_CPUPM_DISABLED)
481c42872d4Smh27603 			pm_scan_init(dip);
482c42872d4Smh27603 		mutex_exit(&pm_scan_lock);
483c42872d4Smh27603 		break;
484c42872d4Smh27603 	case PM_START_PM:
485c42872d4Smh27603 		mutex_enter(&pm_scan_lock);
486c42872d4Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_DISABLED) {
487c42872d4Smh27603 			mutex_exit(&pm_scan_lock);
488c42872d4Smh27603 			return (DDI_WALK_CONTINUE);
489c42872d4Smh27603 		}
4907c478bd9Sstevel@tonic-gate 		if (autopm_enabled)
4917c478bd9Sstevel@tonic-gate 			pm_scan_init(dip);
4927c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
493c42872d4Smh27603 		break;
494c42872d4Smh27603 	}
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	/*
4977c478bd9Sstevel@tonic-gate 	 * Start doing pm on device: ensure pm_scan data structure initiated,
498c42872d4Smh27603 	 * no need to guarantee a successful scan run.
4997c478bd9Sstevel@tonic-gate 	 */
5007c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
5017c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip)))
5027c478bd9Sstevel@tonic-gate 	pm_rescan(dip);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate /*
5087c478bd9Sstevel@tonic-gate  * Bring devices to full power level, then stop scan
5097c478bd9Sstevel@tonic-gate  */
5107c478bd9Sstevel@tonic-gate int
5117c478bd9Sstevel@tonic-gate pm_stop_pm_walk(dev_info_t *dip, void *arg)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
514c42872d4Smh27603 	int cmd = *((int *)arg);
515*9681b4a1Skchow #ifdef PMDDEBUG
516c42872d4Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
517*9681b4a1Skchow #endif
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	if (!info)
5207c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
521c42872d4Smh27603 
522c42872d4Smh27603 	switch (cmd) {
523c42872d4Smh27603 	case PM_STOP_PM:
524c42872d4Smh27603 		/*
525c42872d4Smh27603 		 * If CPU devices are being managed independently, then don't
526c42872d4Smh27603 		 * stop them as part of PM_STOP_PM. Only stop them as part of
527c42872d4Smh27603 		 * PM_STOP_CPUPM and PM_RESET_PM.
528c42872d4Smh27603 		 */
529c42872d4Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_ENABLED)
530c42872d4Smh27603 			return (DDI_WALK_CONTINUE);
531c42872d4Smh27603 		break;
532c42872d4Smh27603 	case PM_STOP_CPUPM:
533c42872d4Smh27603 		/*
534c42872d4Smh27603 		 * If stopping CPU devices and this device is not marked
535c42872d4Smh27603 		 * as a CPU device, then skip.
536c42872d4Smh27603 		 */
537c42872d4Smh27603 		if (!PM_ISCPU(dip))
538c42872d4Smh27603 			return (DDI_WALK_CONTINUE);
539c42872d4Smh27603 		break;
540c42872d4Smh27603 	}
541c42872d4Smh27603 
5427c478bd9Sstevel@tonic-gate 	/*
5437c478bd9Sstevel@tonic-gate 	 * Stop the current scan, and then bring it back to normal power.
5447c478bd9Sstevel@tonic-gate 	 */
5457c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
5467c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
5477c478bd9Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
5487c478bd9Sstevel@tonic-gate 		pm_scan_stop(dip);
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
5527c478bd9Sstevel@tonic-gate 	    !pm_all_at_normal(dip)) {
5537c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
5547c478bd9Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_DETACHING) {
5557c478bd9Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
5567c478bd9Sstevel@tonic-gate 			    "all_to_normal because %s@%s(%s#%d) is detaching\n",
5577c478bd9Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
5587c478bd9Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
5597c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
5607c478bd9Sstevel@tonic-gate 			return (DDI_WALK_CONTINUE);
5617c478bd9Sstevel@tonic-gate 		}
5627c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
5637c478bd9Sstevel@tonic-gate 		if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
5647c478bd9Sstevel@tonic-gate 			PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
5657c478bd9Sstevel@tonic-gate 			    "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 	}
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate static int
5737c478bd9Sstevel@tonic-gate pm_start_idledown(dev_info_t *dip, void *arg)
5747c478bd9Sstevel@tonic-gate {
5757c478bd9Sstevel@tonic-gate 	int		flag = (int)(intptr_t)arg;
5767c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	if (!scanp)
5797c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5827c478bd9Sstevel@tonic-gate 	scanp->ps_idle_down |= flag;
5837c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
5847c478bd9Sstevel@tonic-gate 	pm_rescan(dip);
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5877c478bd9Sstevel@tonic-gate }
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5907c478bd9Sstevel@tonic-gate static int
5917c478bd9Sstevel@tonic-gate pm_end_idledown(dev_info_t *dip, void *ignore)
5927c478bd9Sstevel@tonic-gate {
5937c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	if (!scanp)
5967c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5997c478bd9Sstevel@tonic-gate 	/*
6007c478bd9Sstevel@tonic-gate 	 * The PMID_TIMERS bits are place holder till idledown expires.
6017c478bd9Sstevel@tonic-gate 	 * The bits are also the base for regenerating PMID_SCANS bits.
6027c478bd9Sstevel@tonic-gate 	 * While it's up to scan thread to clear up the PMID_SCANS bits
6037c478bd9Sstevel@tonic-gate 	 * after each scan run, PMID_TIMERS ensure aggressive scan down
6047c478bd9Sstevel@tonic-gate 	 * performance throughout the idledown period.
6057c478bd9Sstevel@tonic-gate 	 */
6067c478bd9Sstevel@tonic-gate 	scanp->ps_idle_down &= ~PMID_TIMERS;
6077c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6107c478bd9Sstevel@tonic-gate }
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6137c478bd9Sstevel@tonic-gate static void
6147c478bd9Sstevel@tonic-gate pm_end_idledown_walk(void *ignore)
6157c478bd9Sstevel@tonic-gate {
6167c478bd9Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
6177c478bd9Sstevel@tonic-gate 	    "off\n", (ulong_t)pmstp->pm_idledown_id));
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6207c478bd9Sstevel@tonic-gate 	pmstp->pm_idledown_id = 0;
6217c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate /*
6277c478bd9Sstevel@tonic-gate  * pm_timeout_idledown - keep idledown effect for 10 seconds.
6287c478bd9Sstevel@tonic-gate  *
6297c478bd9Sstevel@tonic-gate  * Return 0 if another competing caller scheduled idledown timeout,
6307c478bd9Sstevel@tonic-gate  * otherwise, return idledown timeout_id.
6317c478bd9Sstevel@tonic-gate  */
6327c478bd9Sstevel@tonic-gate static timeout_id_t
6337c478bd9Sstevel@tonic-gate pm_timeout_idledown(void)
6347c478bd9Sstevel@tonic-gate {
6357c478bd9Sstevel@tonic-gate 	timeout_id_t	to_id;
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	/*
6387c478bd9Sstevel@tonic-gate 	 * Keep idle-down in effect for either 10 seconds
6397c478bd9Sstevel@tonic-gate 	 * or length of a scan interval, which ever is greater.
6407c478bd9Sstevel@tonic-gate 	 */
6417c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6427c478bd9Sstevel@tonic-gate 	if (pmstp->pm_idledown_id != 0) {
6437c478bd9Sstevel@tonic-gate 		to_id = pmstp->pm_idledown_id;
6447c478bd9Sstevel@tonic-gate 		pmstp->pm_idledown_id = 0;
6457c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6467c478bd9Sstevel@tonic-gate 		(void) untimeout(to_id);
6477c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6487c478bd9Sstevel@tonic-gate 		if (pmstp->pm_idledown_id != 0) {
6497c478bd9Sstevel@tonic-gate 			PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
6507c478bd9Sstevel@tonic-gate 			    "another caller got it, idledown_id(%lx)!\n",
6517c478bd9Sstevel@tonic-gate 			    (ulong_t)pmstp->pm_idledown_id))
6527c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
6537c478bd9Sstevel@tonic-gate 			return (0);
6547c478bd9Sstevel@tonic-gate 		}
6557c478bd9Sstevel@tonic-gate 	}
6567c478bd9Sstevel@tonic-gate 	pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
6577c478bd9Sstevel@tonic-gate 	    PM_IDLEDOWN_TIME * hz);
6587c478bd9Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
6597c478bd9Sstevel@tonic-gate 	    (ulong_t)pmstp->pm_idledown_id))
6607c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	return (pmstp->pm_idledown_id);
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate static int
6667c478bd9Sstevel@tonic-gate pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
6677c478bd9Sstevel@tonic-gate 	struct pollhead **phpp)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	extern struct pollhead pm_pollhead;	/* common/os/sunpm.c */
6707c478bd9Sstevel@tonic-gate 	int	clone;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
6737c478bd9Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
6747c478bd9Sstevel@tonic-gate 	if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
6757c478bd9Sstevel@tonic-gate 		*reventsp |= (POLLIN | POLLRDNORM);
6767c478bd9Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
6777c478bd9Sstevel@tonic-gate 	} else {
6787c478bd9Sstevel@tonic-gate 		*reventsp = 0;
6797c478bd9Sstevel@tonic-gate 		if (!anyyet) {
6807c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
6817c478bd9Sstevel@tonic-gate 			*phpp = &pm_pollhead;
6827c478bd9Sstevel@tonic-gate 		}
6837c478bd9Sstevel@tonic-gate #ifdef DEBUG
6847c478bd9Sstevel@tonic-gate 		else {
6857c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
6867c478bd9Sstevel@tonic-gate 		}
6877c478bd9Sstevel@tonic-gate #endif
6887c478bd9Sstevel@tonic-gate 	}
6897c478bd9Sstevel@tonic-gate 	return (0);
6907c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate /*
6937c478bd9Sstevel@tonic-gate  * called by pm_dicard_entries to free up the memory. It also decrements
6947c478bd9Sstevel@tonic-gate  * pm_poll_cnt, if direct is non zero.
6957c478bd9Sstevel@tonic-gate  */
6967c478bd9Sstevel@tonic-gate static void
6977c478bd9Sstevel@tonic-gate pm_free_entries(psce_t *pscep, int clone, int direct)
6987c478bd9Sstevel@tonic-gate {
6997c478bd9Sstevel@tonic-gate 	pm_state_change_t	*p;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	if (pscep) {
7027c478bd9Sstevel@tonic-gate 		p = pscep->psce_out;
7037c478bd9Sstevel@tonic-gate 		while (p->size) {
7047c478bd9Sstevel@tonic-gate 			if (direct) {
7057c478bd9Sstevel@tonic-gate 				PMD(PMD_IOCTL, ("ioctl: discard: "
7067c478bd9Sstevel@tonic-gate 				    "pm_poll_cnt[%d] is %d before "
7077c478bd9Sstevel@tonic-gate 				    "ASSERT\n", clone,
7087c478bd9Sstevel@tonic-gate 				    pm_poll_cnt[clone]))
7097c478bd9Sstevel@tonic-gate 				ASSERT(pm_poll_cnt[clone]);
7107c478bd9Sstevel@tonic-gate 				pm_poll_cnt[clone]--;
7117c478bd9Sstevel@tonic-gate 			}
7127c478bd9Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
7137c478bd9Sstevel@tonic-gate 			p->size = 0;
7147c478bd9Sstevel@tonic-gate 			if (p == pscep->psce_last)
7157c478bd9Sstevel@tonic-gate 				p = pscep->psce_first;
7167c478bd9Sstevel@tonic-gate 			else
7177c478bd9Sstevel@tonic-gate 				p++;
7187c478bd9Sstevel@tonic-gate 		}
7197c478bd9Sstevel@tonic-gate 		pscep->psce_out = pscep->psce_first;
7207c478bd9Sstevel@tonic-gate 		pscep->psce_in = pscep->psce_first;
7217c478bd9Sstevel@tonic-gate 		mutex_exit(&pscep->psce_lock);
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate }
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate /*
7267c478bd9Sstevel@tonic-gate  * Discard entries for this clone. Calls pm_free_entries to free up memory.
7277c478bd9Sstevel@tonic-gate  */
7287c478bd9Sstevel@tonic-gate static void
7297c478bd9Sstevel@tonic-gate pm_discard_entries(int clone)
7307c478bd9Sstevel@tonic-gate {
7317c478bd9Sstevel@tonic-gate 	psce_t	*pscep;
7327c478bd9Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_direct(int);
7337c478bd9Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_interest(int);
7347c478bd9Sstevel@tonic-gate 	int			direct = 0;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
7377c478bd9Sstevel@tonic-gate 	if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
7387c478bd9Sstevel@tonic-gate 		direct = 1;
7397c478bd9Sstevel@tonic-gate 	pm_free_entries(pscep, clone, direct);
7407c478bd9Sstevel@tonic-gate 	pscep = pm_psc_clone_to_interest(clone);
7417c478bd9Sstevel@tonic-gate 	pm_free_entries(pscep, clone, 0);
7427c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate 
745c42872d4Smh27603 
746c42872d4Smh27603 static void
747c42872d4Smh27603 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
7507c478bd9Sstevel@tonic-gate 		switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
7517c478bd9Sstevel@tonic-gate 		case PMC_DEF_THRESH:
752c42872d4Smh27603 		case PMC_CPU_THRESH:
753c42872d4Smh27603 			PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
7547c478bd9Sstevel@tonic-gate 			    "%s@%s(%s#%d) default thresh to 0t%d\n",
755c42872d4Smh27603 			    PM_DEVICE(dip), thresh))
756c42872d4Smh27603 			pm_set_device_threshold(dip, thresh, flag);
7577c478bd9Sstevel@tonic-gate 			break;
7587c478bd9Sstevel@tonic-gate 		default:
7597c478bd9Sstevel@tonic-gate 			break;
7607c478bd9Sstevel@tonic-gate 		}
7617c478bd9Sstevel@tonic-gate 	}
762c42872d4Smh27603 }
7637c478bd9Sstevel@tonic-gate 
764c42872d4Smh27603 static int
765c42872d4Smh27603 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
766c42872d4Smh27603 {
767c42872d4Smh27603 	int cmd = *((int *)arg);
768c42872d4Smh27603 
769c42872d4Smh27603 	if (!PM_GET_PM_INFO(dip))
770c42872d4Smh27603 		return (DDI_WALK_CONTINUE);
771c42872d4Smh27603 
772c42872d4Smh27603 	switch (cmd) {
773c42872d4Smh27603 	case PM_SET_SYSTEM_THRESHOLD:
774c42872d4Smh27603 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
775c42872d4Smh27603 			break;
776c42872d4Smh27603 		pm_set_idle_threshold(dip, pm_system_idle_threshold,
777c42872d4Smh27603 		    PMC_DEF_THRESH);
7787c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
779c42872d4Smh27603 		break;
780c42872d4Smh27603 	case PM_SET_CPU_THRESHOLD:
781c42872d4Smh27603 		if (!PM_ISCPU(dip))
782c42872d4Smh27603 			break;
783c42872d4Smh27603 		pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
784c42872d4Smh27603 		    PMC_CPU_THRESH);
785c42872d4Smh27603 		pm_rescan(dip);
786c42872d4Smh27603 		break;
787c42872d4Smh27603 	}
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
7907c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7937c478bd9Sstevel@tonic-gate static int
7947c478bd9Sstevel@tonic-gate pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
7957c478bd9Sstevel@tonic-gate {
7967c478bd9Sstevel@tonic-gate 	dev_t	dev;
7977c478bd9Sstevel@tonic-gate 	int	instance;
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	switch (infocmd) {
8007c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
8017c478bd9Sstevel@tonic-gate 		if (pmstp->pm_instance == -1)
8027c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8037c478bd9Sstevel@tonic-gate 		*result = pmstp->pm_dip;
8047c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8077c478bd9Sstevel@tonic-gate 		dev = (dev_t)arg;
8087c478bd9Sstevel@tonic-gate 		instance = getminor(dev) >> 8;
8097c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8107c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	default:
8137c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8147c478bd9Sstevel@tonic-gate 	}
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
8197c478bd9Sstevel@tonic-gate static int
8207c478bd9Sstevel@tonic-gate pm_open(dev_t *devp, int flag, int otyp, cred_t *cr)
8217c478bd9Sstevel@tonic-gate {
8227c478bd9Sstevel@tonic-gate 	int		clone;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8257c478bd9Sstevel@tonic-gate 		return (EINVAL);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
8287c478bd9Sstevel@tonic-gate 	for (clone = 1; clone < PM_MAX_CLONE; clone++)
8297c478bd9Sstevel@tonic-gate 		if (!pmstp->pm_clones[clone])
8307c478bd9Sstevel@tonic-gate 			break;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	if (clone == PM_MAX_CLONE) {
8337c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
8347c478bd9Sstevel@tonic-gate 		return (ENXIO);
8357c478bd9Sstevel@tonic-gate 	}
8367c478bd9Sstevel@tonic-gate 	pmstp->pm_cred[clone] = cr;
8377c478bd9Sstevel@tonic-gate 	crhold(cr);
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
8407c478bd9Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 1;
8417c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	return (0);
8447c478bd9Sstevel@tonic-gate }
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
8477c478bd9Sstevel@tonic-gate static int
8487c478bd9Sstevel@tonic-gate pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate 	int clone;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8537c478bd9Sstevel@tonic-gate 		return (EINVAL);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
8567c478bd9Sstevel@tonic-gate 	PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
8577c478bd9Sstevel@tonic-gate 	    clone))
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	/*
8607c478bd9Sstevel@tonic-gate 	 * Walk the entire device tree to find the corresponding
8617c478bd9Sstevel@tonic-gate 	 * device and operate on it.
8627c478bd9Sstevel@tonic-gate 	 */
8637c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
8647c478bd9Sstevel@tonic-gate 	    (void *) &clone);
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	crfree(pmstp->pm_cred[clone]);
8677c478bd9Sstevel@tonic-gate 	pmstp->pm_cred[clone] = 0;
8687c478bd9Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 0;
8697c478bd9Sstevel@tonic-gate 	pm_discard_entries(clone);
8707c478bd9Sstevel@tonic-gate 	ASSERT(pm_poll_cnt[clone] == 0);
8717c478bd9Sstevel@tonic-gate 	pm_deregister_watcher(clone, NULL);
8727c478bd9Sstevel@tonic-gate 	return (0);
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8767c478bd9Sstevel@tonic-gate static int
8777c478bd9Sstevel@tonic-gate pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
8787c478bd9Sstevel@tonic-gate {
8797c478bd9Sstevel@tonic-gate 	struct pm_cmd_info *pc_info(int);
8807c478bd9Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
8817c478bd9Sstevel@tonic-gate 	pm_req_t	req;
8827c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
8837c478bd9Sstevel@tonic-gate 	pm_info_t	*info = NULL;
8847c478bd9Sstevel@tonic-gate 	int		clone;
8857c478bd9Sstevel@tonic-gate 	char		*cmdstr = pm_decode_cmd(cmd);
8867c478bd9Sstevel@tonic-gate 	/*
8877c478bd9Sstevel@tonic-gate 	 * To keep devinfo nodes from going away while we're holding a
8887c478bd9Sstevel@tonic-gate 	 * pointer to their dip, pm_name_to_dip() optionally holds
8897c478bd9Sstevel@tonic-gate 	 * the devinfo node.  If we've done that, we set dipheld
8907c478bd9Sstevel@tonic-gate 	 * so we know at the end of the ioctl processing to release the
8917c478bd9Sstevel@tonic-gate 	 * node again.
8927c478bd9Sstevel@tonic-gate 	 */
8937c478bd9Sstevel@tonic-gate 	int		dipheld = 0;
8947c478bd9Sstevel@tonic-gate 	int		icount = 0;
8957c478bd9Sstevel@tonic-gate 	int		i;
8967c478bd9Sstevel@tonic-gate 	int		comps;
8977c478bd9Sstevel@tonic-gate 	size_t		lencopied;
8987c478bd9Sstevel@tonic-gate 	int		ret = ENOTTY;
8997c478bd9Sstevel@tonic-gate 	int		curpower;
9007c478bd9Sstevel@tonic-gate 	char		who[MAXNAMELEN];
9017c478bd9Sstevel@tonic-gate 	size_t		wholen;			/* copyinstr length */
9027c478bd9Sstevel@tonic-gate 	size_t		deplen = MAXNAMELEN;
9037c478bd9Sstevel@tonic-gate 	char		*dep, i_dep_buf[MAXNAMELEN];
9047c478bd9Sstevel@tonic-gate 	char		*pathbuf;
9057c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
9067c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9077c478bd9Sstevel@tonic-gate 	pm_state_change32_t		*pscp32;
9087c478bd9Sstevel@tonic-gate 	pm_state_change32_t		psc32;
9097c478bd9Sstevel@tonic-gate 	size_t				copysize32;
9107c478bd9Sstevel@tonic-gate #endif
9117c478bd9Sstevel@tonic-gate 	pm_state_change_t		*pscp;
9127c478bd9Sstevel@tonic-gate 	pm_state_change_t		psc;
9137c478bd9Sstevel@tonic-gate 	size_t		copysize;
9147c478bd9Sstevel@tonic-gate 	extern void	pm_record_thresh(pm_thresh_rec_t *);
9157c478bd9Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_direct(int);
9167c478bd9Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_interest(int);
9177c478bd9Sstevel@tonic-gate 	extern	void	pm_register_watcher(int, dev_info_t *);
9187c478bd9Sstevel@tonic-gate 	extern	int	pm_get_current_power(dev_info_t *, int, int *);
9197c478bd9Sstevel@tonic-gate 	extern	int	pm_interest_registered(int);
9207c478bd9Sstevel@tonic-gate 	extern	void	pm_all_to_default_thresholds(void);
9217c478bd9Sstevel@tonic-gate 	extern	int	pm_current_threshold(dev_info_t *, int, int *);
9227c478bd9Sstevel@tonic-gate 	extern void	pm_deregister_watcher(int, dev_info_t *);
9237c478bd9Sstevel@tonic-gate 	extern void	pm_unrecord_threshold(char *);
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate #ifdef DEBUG
9287c478bd9Sstevel@tonic-gate 	if (cmd == 666) {
9297c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), print_info, NULL);
9307c478bd9Sstevel@tonic-gate 		return (0);
9317c478bd9Sstevel@tonic-gate 	}
9327c478bd9Sstevel@tonic-gate 	ret = 0x0badcafe;			/* sanity checking */
9337c478bd9Sstevel@tonic-gate 	pm_cmd = cmd;				/* for ASSERT debugging */
9347c478bd9Sstevel@tonic-gate 	pm_cmd_string = cmdstr;	/* for ASSERT debugging */
9357c478bd9Sstevel@tonic-gate #endif
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	if (pcip == NULL) {
9397c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
9407c478bd9Sstevel@tonic-gate 		return (ENOTTY);
9417c478bd9Sstevel@tonic-gate 	}
9427c478bd9Sstevel@tonic-gate 	if (pcip == NULL || pcip->supported == 0) {
9437c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
9447c478bd9Sstevel@tonic-gate 		    pcip->name))
9457c478bd9Sstevel@tonic-gate 		return (ENOTTY);
9467c478bd9Sstevel@tonic-gate 	}
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	wholen = 0;
9497c478bd9Sstevel@tonic-gate 	dep = i_dep_buf;
9507c478bd9Sstevel@tonic-gate 	i_dep_buf[0] = 0;
9517c478bd9Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
9527c478bd9Sstevel@tonic-gate 	if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
9537c478bd9Sstevel@tonic-gate 		ret = EPERM;
9547c478bd9Sstevel@tonic-gate 		return (ret);
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 	switch (pcip->str_type) {
9577c478bd9Sstevel@tonic-gate 	case PM_REQ:
9587c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9597c478bd9Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9607c478bd9Sstevel@tonic-gate 			pm_req32_t	req32;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &req32,
9637c478bd9Sstevel@tonic-gate 			    sizeof (req32), mode) != 0) {
9647c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
9657c478bd9Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
9667c478bd9Sstevel@tonic-gate 				ret = EFAULT;
9677c478bd9Sstevel@tonic-gate 				break;
9687c478bd9Sstevel@tonic-gate 			}
9697c478bd9Sstevel@tonic-gate 			req.component = req32.component;
9707c478bd9Sstevel@tonic-gate 			req.value = req32.value;
9717c478bd9Sstevel@tonic-gate 			req.datasize = req32.datasize;
9727c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
9737c478bd9Sstevel@tonic-gate 				ret = copyinstr((char *)(uintptr_t)
9747c478bd9Sstevel@tonic-gate 				    req32.physpath, who, MAXNAMELEN, &wholen);
9757c478bd9Sstevel@tonic-gate 				if (ret) {
9767c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9777c478bd9Sstevel@tonic-gate 					    "copyinstr fails returning %d\n",
9787c478bd9Sstevel@tonic-gate 					    cmdstr, ret))
9797c478bd9Sstevel@tonic-gate 					break;
9807c478bd9Sstevel@tonic-gate 				}
9817c478bd9Sstevel@tonic-gate 				req.physpath = who;
9827c478bd9Sstevel@tonic-gate 			}
9837c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
9847c478bd9Sstevel@tonic-gate 			    req.physpath))
9857c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INDATA) {
9867c478bd9Sstevel@tonic-gate 				req.data = (void *)(uintptr_t)req32.data;
9877c478bd9Sstevel@tonic-gate 				req.datasize = req32.datasize;
9887c478bd9Sstevel@tonic-gate 			} else {
9897c478bd9Sstevel@tonic-gate 				req.data = NULL;
9907c478bd9Sstevel@tonic-gate 				req.datasize = 0;
9917c478bd9Sstevel@tonic-gate 			}
9927c478bd9Sstevel@tonic-gate 			switch (pcip->diptype) {
9937c478bd9Sstevel@tonic-gate 			case DIP:
9947c478bd9Sstevel@tonic-gate 				if (!(dip =
9957c478bd9Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
9967c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9977c478bd9Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
9987c478bd9Sstevel@tonic-gate 					    cmdstr, req.physpath))
9997c478bd9Sstevel@tonic-gate 					return (ENODEV);
10007c478bd9Sstevel@tonic-gate 				}
10017c478bd9Sstevel@tonic-gate 				ASSERT(!dipheld);
10027c478bd9Sstevel@tonic-gate 				dipheld++;
10037c478bd9Sstevel@tonic-gate 				break;
10047c478bd9Sstevel@tonic-gate 			case NODIP:
10057c478bd9Sstevel@tonic-gate 				break;
10067c478bd9Sstevel@tonic-gate 			default:
10077c478bd9Sstevel@tonic-gate 				/*
10087c478bd9Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
10097c478bd9Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
10107c478bd9Sstevel@tonic-gate 				 */
10117c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10127c478bd9Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
10137c478bd9Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
10147c478bd9Sstevel@tonic-gate #endif
10157c478bd9Sstevel@tonic-gate 				ASSERT(0);
10167c478bd9Sstevel@tonic-gate 				return (EIO);
10177c478bd9Sstevel@tonic-gate 			}
10187c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
10197c478bd9Sstevel@tonic-gate 				int32_t int32buf;
10207c478bd9Sstevel@tonic-gate 				int32_t *i32p;
10217c478bd9Sstevel@tonic-gate 				int *ip;
10227c478bd9Sstevel@tonic-gate 				icount = req32.datasize / sizeof (int32_t);
10237c478bd9Sstevel@tonic-gate 				if (icount <= 0) {
10247c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
10257c478bd9Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
10267c478bd9Sstevel@tonic-gate 					ret = EFAULT;
10277c478bd9Sstevel@tonic-gate 					break;
10287c478bd9Sstevel@tonic-gate 				}
10297c478bd9Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
10307c478bd9Sstevel@tonic-gate 				req.datasize = icount * sizeof (int);
10317c478bd9Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
10327c478bd9Sstevel@tonic-gate 				ip = req.data;
10337c478bd9Sstevel@tonic-gate 				ret = 0;
10347c478bd9Sstevel@tonic-gate 				for (i = 0,
10357c478bd9Sstevel@tonic-gate 				    i32p = (int32_t *)(uintptr_t)req32.data;
10367c478bd9Sstevel@tonic-gate 				    i < icount; i++, i32p++) {
10377c478bd9Sstevel@tonic-gate 					if (ddi_copyin((void *)i32p, &int32buf,
10387c478bd9Sstevel@tonic-gate 					    sizeof (int32_t), mode)) {
10397c478bd9Sstevel@tonic-gate 						kmem_free(req.data,
10407c478bd9Sstevel@tonic-gate 						    req.datasize);
10417c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10427c478bd9Sstevel@tonic-gate 						    "entry %d EFAULT\n",
10437c478bd9Sstevel@tonic-gate 						    cmdstr, i))
10447c478bd9Sstevel@tonic-gate 						ret = EFAULT;
10457c478bd9Sstevel@tonic-gate 						break;
10467c478bd9Sstevel@tonic-gate 					}
10477c478bd9Sstevel@tonic-gate 					*ip++ = (int)int32buf;
10487c478bd9Sstevel@tonic-gate 				}
10497c478bd9Sstevel@tonic-gate 				if (ret)
10507c478bd9Sstevel@tonic-gate 					break;
10517c478bd9Sstevel@tonic-gate 			}
10527c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
10537c478bd9Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
10547c478bd9Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
10557c478bd9Sstevel@tonic-gate 				if (req32.data != NULL) {
10567c478bd9Sstevel@tonic-gate 					size_t dummy;
10577c478bd9Sstevel@tonic-gate 					if (copyinstr((void *)(uintptr_t)
10587c478bd9Sstevel@tonic-gate 					    req32.data, dep, deplen, &dummy)) {
10597c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10607c478bd9Sstevel@tonic-gate 						    "0x%p dep size %lx, EFAULT"
10617c478bd9Sstevel@tonic-gate 						    "\n", cmdstr,
10627c478bd9Sstevel@tonic-gate 						    (void *)req.data, deplen))
10637c478bd9Sstevel@tonic-gate 						ret = EFAULT;
10647c478bd9Sstevel@tonic-gate 						break;
10657c478bd9Sstevel@tonic-gate 					}
10667c478bd9Sstevel@tonic-gate #ifdef DEBUG
10677c478bd9Sstevel@tonic-gate 					else {
10687c478bd9Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
10697c478bd9Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
10707c478bd9Sstevel@tonic-gate 					}
10717c478bd9Sstevel@tonic-gate #endif
10727c478bd9Sstevel@tonic-gate 				} else {
10737c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
10747c478bd9Sstevel@tonic-gate 					    "dependent\n", cmdstr))
10757c478bd9Sstevel@tonic-gate 					ret = EINVAL;
10767c478bd9Sstevel@tonic-gate 					break;
10777c478bd9Sstevel@tonic-gate 				}
10787c478bd9Sstevel@tonic-gate 			}
10797c478bd9Sstevel@tonic-gate 		} else
10807c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
10817c478bd9Sstevel@tonic-gate 		{
10827c478bd9Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg,
10837c478bd9Sstevel@tonic-gate 			    &req, sizeof (req), mode) != 0) {
10847c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
10857c478bd9Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
10867c478bd9Sstevel@tonic-gate 				ret = EFAULT;
10877c478bd9Sstevel@tonic-gate 				break;
10887c478bd9Sstevel@tonic-gate 			}
10897c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
10907c478bd9Sstevel@tonic-gate 				ret = copyinstr((char *)req.physpath, who,
10917c478bd9Sstevel@tonic-gate 				    MAXNAMELEN, &wholen);
10927c478bd9Sstevel@tonic-gate 				if (ret) {
10937c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s copyinstr"
10947c478bd9Sstevel@tonic-gate 					    " fails returning %d\n", cmdstr,
10957c478bd9Sstevel@tonic-gate 					    ret))
10967c478bd9Sstevel@tonic-gate 					break;
10977c478bd9Sstevel@tonic-gate 				}
10987c478bd9Sstevel@tonic-gate 				req.physpath = who;
10997c478bd9Sstevel@tonic-gate 			}
11007c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
11017c478bd9Sstevel@tonic-gate 			    req.physpath))
11027c478bd9Sstevel@tonic-gate 			if (!(pcip->inargs & INDATA)) {
11037c478bd9Sstevel@tonic-gate 				req.data = NULL;
11047c478bd9Sstevel@tonic-gate 				req.datasize = 0;
11057c478bd9Sstevel@tonic-gate 			}
11067c478bd9Sstevel@tonic-gate 			switch (pcip->diptype) {
11077c478bd9Sstevel@tonic-gate 			case DIP:
11087c478bd9Sstevel@tonic-gate 				if (!(dip =
11097c478bd9Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
11107c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
11117c478bd9Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
11127c478bd9Sstevel@tonic-gate 					    cmdstr, req.physpath))
11137c478bd9Sstevel@tonic-gate 					return (ENODEV);
11147c478bd9Sstevel@tonic-gate 				}
11157c478bd9Sstevel@tonic-gate 				ASSERT(!dipheld);
11167c478bd9Sstevel@tonic-gate 				dipheld++;
11177c478bd9Sstevel@tonic-gate 				break;
11187c478bd9Sstevel@tonic-gate 			case NODIP:
11197c478bd9Sstevel@tonic-gate 				break;
11207c478bd9Sstevel@tonic-gate 			default:
11217c478bd9Sstevel@tonic-gate 				/*
11227c478bd9Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
11237c478bd9Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
11247c478bd9Sstevel@tonic-gate 				 */
11257c478bd9Sstevel@tonic-gate #ifdef	DEBUG
11267c478bd9Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
11277c478bd9Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
11287c478bd9Sstevel@tonic-gate #endif
11297c478bd9Sstevel@tonic-gate 				ASSERT(0);
11307c478bd9Sstevel@tonic-gate 				return (EIO);
11317c478bd9Sstevel@tonic-gate 			}
11327c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
11337c478bd9Sstevel@tonic-gate 				int *ip;
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
11367c478bd9Sstevel@tonic-gate 				ip = req.data;
11377c478bd9Sstevel@tonic-gate 				icount = req.datasize / sizeof (int);
11387c478bd9Sstevel@tonic-gate 				if (icount <= 0) {
11397c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
11407c478bd9Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
11417c478bd9Sstevel@tonic-gate 					ret = EFAULT;
11427c478bd9Sstevel@tonic-gate 					break;
11437c478bd9Sstevel@tonic-gate 				}
11447c478bd9Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
11457c478bd9Sstevel@tonic-gate 				if (ddi_copyin((caddr_t)ip, req.data,
11467c478bd9Sstevel@tonic-gate 				    req.datasize, mode) != 0) {
11477c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11487c478bd9Sstevel@tonic-gate 					    "EFAULT\n\n", cmdstr))
11497c478bd9Sstevel@tonic-gate 					ret = EFAULT;
11507c478bd9Sstevel@tonic-gate 					break;
11517c478bd9Sstevel@tonic-gate 				}
11527c478bd9Sstevel@tonic-gate 			}
11537c478bd9Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
11547c478bd9Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
11557c478bd9Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
11567c478bd9Sstevel@tonic-gate 				if (req.data != NULL) {
11577c478bd9Sstevel@tonic-gate 					size_t dummy;
11587c478bd9Sstevel@tonic-gate 					if (copyinstr((caddr_t)req.data,
11597c478bd9Sstevel@tonic-gate 					    dep, deplen, &dummy)) {
11607c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
11617c478bd9Sstevel@tonic-gate 						    "0x%p dep size %lu, "
11627c478bd9Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
11637c478bd9Sstevel@tonic-gate 						    (void *)req.data, deplen))
11647c478bd9Sstevel@tonic-gate 						ret = EFAULT;
11657c478bd9Sstevel@tonic-gate 						break;
11667c478bd9Sstevel@tonic-gate 					}
11677c478bd9Sstevel@tonic-gate #ifdef DEBUG
11687c478bd9Sstevel@tonic-gate 					else {
11697c478bd9Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
11707c478bd9Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
11717c478bd9Sstevel@tonic-gate 					}
11727c478bd9Sstevel@tonic-gate #endif
11737c478bd9Sstevel@tonic-gate 				} else {
11747c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
11757c478bd9Sstevel@tonic-gate 					    "dependent\n", cmdstr))
11767c478bd9Sstevel@tonic-gate 					ret = EINVAL;
11777c478bd9Sstevel@tonic-gate 					break;
11787c478bd9Sstevel@tonic-gate 				}
11797c478bd9Sstevel@tonic-gate 			}
11807c478bd9Sstevel@tonic-gate 		}
11817c478bd9Sstevel@tonic-gate 		/*
11827c478bd9Sstevel@tonic-gate 		 * Now we've got all the args in for the commands that
11837c478bd9Sstevel@tonic-gate 		 * use the new pm_req struct.
11847c478bd9Sstevel@tonic-gate 		 */
11857c478bd9Sstevel@tonic-gate 		switch (cmd) {
11867c478bd9Sstevel@tonic-gate 		case PM_REPARSE_PM_PROPS:
11877c478bd9Sstevel@tonic-gate 		{
11887c478bd9Sstevel@tonic-gate 			struct dev_ops	*drv;
11897c478bd9Sstevel@tonic-gate 			struct cb_ops	*cb;
11907c478bd9Sstevel@tonic-gate 			void		*propval;
11917c478bd9Sstevel@tonic-gate 			int length;
11927c478bd9Sstevel@tonic-gate 			/*
11937c478bd9Sstevel@tonic-gate 			 * This ioctl is provided only for the ddivs pm test.
11947c478bd9Sstevel@tonic-gate 			 * We only do it to a driver which explicitly allows
11957c478bd9Sstevel@tonic-gate 			 * us to do so by exporting a pm-reparse-ok property.
11967c478bd9Sstevel@tonic-gate 			 * We only care whether the property exists or not.
11977c478bd9Sstevel@tonic-gate 			 */
11987c478bd9Sstevel@tonic-gate 			if ((drv = ddi_get_driver(dip)) == NULL) {
11997c478bd9Sstevel@tonic-gate 				ret = EINVAL;
12007c478bd9Sstevel@tonic-gate 				break;
12017c478bd9Sstevel@tonic-gate 			}
12027c478bd9Sstevel@tonic-gate 			if ((cb = drv->devo_cb_ops) != NULL) {
12037c478bd9Sstevel@tonic-gate 				if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
12047c478bd9Sstevel@tonic-gate 				    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12057c478bd9Sstevel@tonic-gate 				    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12067c478bd9Sstevel@tonic-gate 				    "pm-reparse-ok", (caddr_t)&propval,
12077c478bd9Sstevel@tonic-gate 				    &length) != DDI_SUCCESS) {
12087c478bd9Sstevel@tonic-gate 					ret = EINVAL;
12097c478bd9Sstevel@tonic-gate 					break;
12107c478bd9Sstevel@tonic-gate 				}
12117c478bd9Sstevel@tonic-gate 			} else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
12127c478bd9Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12137c478bd9Sstevel@tonic-gate 			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12147c478bd9Sstevel@tonic-gate 			    "pm-reparse-ok", (caddr_t)&propval,
12157c478bd9Sstevel@tonic-gate 			    &length) != DDI_SUCCESS) {
12167c478bd9Sstevel@tonic-gate 				ret = EINVAL;
12177c478bd9Sstevel@tonic-gate 				break;
12187c478bd9Sstevel@tonic-gate 			}
12197c478bd9Sstevel@tonic-gate 			kmem_free(propval, length);
12207c478bd9Sstevel@tonic-gate 			ret =  e_new_pm_props(dip);
12217c478bd9Sstevel@tonic-gate 			break;
12227c478bd9Sstevel@tonic-gate 		}
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD:
12257c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12267c478bd9Sstevel@tonic-gate 			if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
12277c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12287c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
12297c478bd9Sstevel@tonic-gate 				    cmdstr))
12307c478bd9Sstevel@tonic-gate 				ret = ENODEV;
12317c478bd9Sstevel@tonic-gate 				break;
12327c478bd9Sstevel@tonic-gate 			}
12337c478bd9Sstevel@tonic-gate 			*rval_p = DEVI(dip)->devi_pm_dev_thresh;
12347c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12357c478bd9Sstevel@tonic-gate 			ret = 0;
12367c478bd9Sstevel@tonic-gate 			break;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 		case PM_DIRECT_PM:
12397c478bd9Sstevel@tonic-gate 		{
12407c478bd9Sstevel@tonic-gate 			int has_dep;
12417c478bd9Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12427c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12437c478bd9Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12447c478bd9Sstevel@tonic-gate 				ret = ENODEV;
12457c478bd9Sstevel@tonic-gate 				break;
12467c478bd9Sstevel@tonic-gate 			}
12477c478bd9Sstevel@tonic-gate 			/*
12487c478bd9Sstevel@tonic-gate 			 * Check to see if we are there is a dependency on
12497c478bd9Sstevel@tonic-gate 			 * this kept device, if so, return EBUSY.
12507c478bd9Sstevel@tonic-gate 			 */
12517c478bd9Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
12527c478bd9Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
12537c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
12547c478bd9Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
12557c478bd9Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
12567c478bd9Sstevel@tonic-gate 			if (has_dep) {
12577c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
12587c478bd9Sstevel@tonic-gate 				    cmdstr))
12597c478bd9Sstevel@tonic-gate 				ret = EBUSY;
12607c478bd9Sstevel@tonic-gate 				break;
12617c478bd9Sstevel@tonic-gate 			}
12627c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12637c478bd9Sstevel@tonic-gate 			if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
12647c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12657c478bd9Sstevel@tonic-gate 				    "%s@%s(%s#%d): EBUSY\n", cmdstr,
12667c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12677c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12687c478bd9Sstevel@tonic-gate 				ret = EBUSY;
12697c478bd9Sstevel@tonic-gate 				break;
12707c478bd9Sstevel@tonic-gate 			}
12717c478bd9Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_DIRECT;
12727c478bd9Sstevel@tonic-gate 			info->pmi_clone = clone;
12737c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12747c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
12757c478bd9Sstevel@tonic-gate 			    cmdstr, (void *)info, clone))
12767c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
12777c478bd9Sstevel@tonic-gate 			pm_register_watcher(clone, dip);
12787c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
12797c478bd9Sstevel@tonic-gate 			ret = 0;
12807c478bd9Sstevel@tonic-gate 			break;
12817c478bd9Sstevel@tonic-gate 		}
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 		case PM_RELEASE_DIRECT_PM:
12847c478bd9Sstevel@tonic-gate 		{
12857c478bd9Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12867c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12877c478bd9Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12887c478bd9Sstevel@tonic-gate 				ret = ENODEV;
12897c478bd9Sstevel@tonic-gate 				break;
12907c478bd9Sstevel@tonic-gate 			}
12917c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12927c478bd9Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
12937c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12947c478bd9Sstevel@tonic-gate 				    "%s@%s(%s#%d) EINVAL\n", cmdstr,
12957c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12967c478bd9Sstevel@tonic-gate 				ret = EINVAL;
12977c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12987c478bd9Sstevel@tonic-gate 				break;
12997c478bd9Sstevel@tonic-gate 			}
13007c478bd9Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13017c478bd9Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_DIRECT;
13027c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13037c478bd9Sstevel@tonic-gate 			/* Bring ourselves up if there is a keeper. */
13047c478bd9Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
13057c478bd9Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
13067c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
13077c478bd9Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
13087c478bd9Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
13097c478bd9Sstevel@tonic-gate 			pm_discard_entries(clone);
13107c478bd9Sstevel@tonic-gate 			pm_deregister_watcher(clone, dip);
13117c478bd9Sstevel@tonic-gate 			/*
13127c478bd9Sstevel@tonic-gate 			 * Now we could let the other threads that are
13137c478bd9Sstevel@tonic-gate 			 * trying to do a DIRECT_PM thru
13147c478bd9Sstevel@tonic-gate 			 */
13157c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13167c478bd9Sstevel@tonic-gate 			info->pmi_clone = 0;
13177c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13187c478bd9Sstevel@tonic-gate 			pm_proceed(dip, PMP_RELEASE, -1, -1);
13197c478bd9Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13207c478bd9Sstevel@tonic-gate 			    cmdstr))
13217c478bd9Sstevel@tonic-gate 			pm_rescan(dip);
13227c478bd9Sstevel@tonic-gate 			ret = 0;
13237c478bd9Sstevel@tonic-gate 			break;
13247c478bd9Sstevel@tonic-gate 		}
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 		case PM_SET_CURRENT_POWER:
13277c478bd9Sstevel@tonic-gate 		{
13287c478bd9Sstevel@tonic-gate 			int comp = req.component;
13297c478bd9Sstevel@tonic-gate 			int  value = req.value;
13307c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
13317c478bd9Sstevel@tonic-gate 			    "%d\n", cmdstr, req.physpath, comp, value))
13327c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, NULL) ||
13337c478bd9Sstevel@tonic-gate 			    !e_pm_valid_power(dip, comp, value)) {
13347c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13357c478bd9Sstevel@tonic-gate 				    "physpath=%s, comp=%d, level=%d, fails\n",
13367c478bd9Sstevel@tonic-gate 				    cmdstr, req.physpath, comp, value))
13377c478bd9Sstevel@tonic-gate 				ret = EINVAL;
13387c478bd9Sstevel@tonic-gate 				break;
13397c478bd9Sstevel@tonic-gate 			}
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13427c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13437c478bd9Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13447c478bd9Sstevel@tonic-gate 				ret = ENODEV;
13457c478bd9Sstevel@tonic-gate 				break;
13467c478bd9Sstevel@tonic-gate 			}
13477c478bd9Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13487c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13497c478bd9Sstevel@tonic-gate 				    "(not owner) %s fails; clone %d, owner %d"
13507c478bd9Sstevel@tonic-gate 				    "\n", cmdstr, req.physpath, clone,
13517c478bd9Sstevel@tonic-gate 				    info->pmi_clone))
13527c478bd9Sstevel@tonic-gate 				ret = EINVAL;
13537c478bd9Sstevel@tonic-gate 				break;
13547c478bd9Sstevel@tonic-gate 			}
13557c478bd9Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate 			if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
13587c478bd9Sstevel@tonic-gate 			    PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
13597c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13607c478bd9Sstevel@tonic-gate 				    "pm_set_power for %s fails, errno=%d\n",
13617c478bd9Sstevel@tonic-gate 				    cmdstr, req.physpath, ret))
13627c478bd9Sstevel@tonic-gate 				break;
13637c478bd9Sstevel@tonic-gate 			}
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 			pm_proceed(dip, PMP_SETPOWER, comp, value);
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 			/*
13687c478bd9Sstevel@tonic-gate 			 * Power down all idle components if console framebuffer
13697c478bd9Sstevel@tonic-gate 			 * is powered off.
13707c478bd9Sstevel@tonic-gate 			 */
13717c478bd9Sstevel@tonic-gate 			if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
13727c478bd9Sstevel@tonic-gate 			    pm_default_idle_threshold)) {
13737c478bd9Sstevel@tonic-gate 				dev_info_t	*root = ddi_root_node();
13747c478bd9Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
13757c478bd9Sstevel@tonic-gate 					if (comp == 0 && value == 0 &&
13767c478bd9Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13777c478bd9Sstevel@tonic-gate 						ddi_walk_devs(root,
13787c478bd9Sstevel@tonic-gate 						    pm_start_idledown,
13797c478bd9Sstevel@tonic-gate 						    (void *)PMID_CFB);
13807c478bd9Sstevel@tonic-gate 					}
13817c478bd9Sstevel@tonic-gate 				} else {
13827c478bd9Sstevel@tonic-gate 					int count = 0;
13837c478bd9Sstevel@tonic-gate 					for (i = 0; i < PM_NUMCMPTS(dip); i++) {
13847c478bd9Sstevel@tonic-gate 						ret = pm_get_current_power(dip,
13857c478bd9Sstevel@tonic-gate 						    i, &curpower);
13867c478bd9Sstevel@tonic-gate 						if (ret == DDI_SUCCESS &&
13877c478bd9Sstevel@tonic-gate 						    curpower == 0)
13887c478bd9Sstevel@tonic-gate 							count++;
13897c478bd9Sstevel@tonic-gate 					}
13907c478bd9Sstevel@tonic-gate 					if ((count == PM_NUMCMPTS(dip)) &&
13917c478bd9Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13927c478bd9Sstevel@tonic-gate 						ddi_walk_devs(root,
13937c478bd9Sstevel@tonic-gate 						    pm_start_idledown,
13947c478bd9Sstevel@tonic-gate 						    (void *)PMID_CFB);
13957c478bd9Sstevel@tonic-gate 					}
13967c478bd9Sstevel@tonic-gate 				}
13977c478bd9Sstevel@tonic-gate 			}
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
14007c478bd9Sstevel@tonic-gate 			    cmdstr))
14017c478bd9Sstevel@tonic-gate 			pm_rescan(dip);
14027c478bd9Sstevel@tonic-gate 			*rval_p = 0;
14037c478bd9Sstevel@tonic-gate 			ret = 0;
14047c478bd9Sstevel@tonic-gate 			break;
14057c478bd9Sstevel@tonic-gate 		}
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 		case PM_GET_FULL_POWER:
14087c478bd9Sstevel@tonic-gate 		{
14097c478bd9Sstevel@tonic-gate 			int normal;
14107c478bd9Sstevel@tonic-gate 			ASSERT(dip);
14117c478bd9Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
14127c478bd9Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component))
14137c478bd9Sstevel@tonic-gate 			normal =  pm_get_normal_power(dip, req.component);
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 			if (normal == DDI_FAILURE) {
14167c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
14177c478bd9Sstevel@tonic-gate 				    "returns EINVAL\n", cmdstr))
14187c478bd9Sstevel@tonic-gate 				ret = EINVAL;
14197c478bd9Sstevel@tonic-gate 				break;
14207c478bd9Sstevel@tonic-gate 			}
14217c478bd9Sstevel@tonic-gate 			*rval_p = normal;
14227c478bd9Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
14237c478bd9Sstevel@tonic-gate 			    cmdstr, normal))
14247c478bd9Sstevel@tonic-gate 			ret = 0;
14257c478bd9Sstevel@tonic-gate 			break;
14267c478bd9Sstevel@tonic-gate 		}
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 		case PM_GET_CURRENT_POWER:
14297c478bd9Sstevel@tonic-gate 			if (pm_get_current_power(dip, req.component,
14307c478bd9Sstevel@tonic-gate 			    rval_p) != DDI_SUCCESS) {
14317c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
14327c478bd9Sstevel@tonic-gate 				    "EINVAL\n", cmdstr))
14337c478bd9Sstevel@tonic-gate 				ret = EINVAL;
14347c478bd9Sstevel@tonic-gate 				break;
14357c478bd9Sstevel@tonic-gate 			}
14367c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
14377c478bd9Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component, *rval_p))
14387c478bd9Sstevel@tonic-gate 			if (*rval_p == PM_LEVEL_UNKNOWN)
14397c478bd9Sstevel@tonic-gate 				ret = EAGAIN;
14407c478bd9Sstevel@tonic-gate 			else
14417c478bd9Sstevel@tonic-gate 				ret = 0;
14427c478bd9Sstevel@tonic-gate 			break;
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 		case PM_GET_TIME_IDLE:
14457c478bd9Sstevel@tonic-gate 		{
14467c478bd9Sstevel@tonic-gate 			time_t timestamp;
14477c478bd9Sstevel@tonic-gate 			int comp = req.component;
14487c478bd9Sstevel@tonic-gate 			pm_component_t *cp;
14497c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, &cp)) {
14507c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
14517c478bd9Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
14527c478bd9Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), comp,
14537c478bd9Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
14547c478bd9Sstevel@tonic-gate 				ret = EINVAL;
14557c478bd9Sstevel@tonic-gate 				break;
14567c478bd9Sstevel@tonic-gate 			}
14577c478bd9Sstevel@tonic-gate 			timestamp = cp->pmc_timestamp;
14587c478bd9Sstevel@tonic-gate 			if (timestamp) {
14597c478bd9Sstevel@tonic-gate 				time_t now;
14607c478bd9Sstevel@tonic-gate 				(void) drv_getparm(TIME, &now);
14617c478bd9Sstevel@tonic-gate 				*rval_p = (now - timestamp);
14627c478bd9Sstevel@tonic-gate 			} else {
14637c478bd9Sstevel@tonic-gate 				*rval_p = 0;
14647c478bd9Sstevel@tonic-gate 			}
14657c478bd9Sstevel@tonic-gate 			ret = 0;
14667c478bd9Sstevel@tonic-gate 			break;
14677c478bd9Sstevel@tonic-gate 		}
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 		case PM_ADD_DEPENDENT:
14707c478bd9Sstevel@tonic-gate 		{
14717c478bd9Sstevel@tonic-gate 			dev_info_t	*kept_dip;
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
14747c478bd9Sstevel@tonic-gate 			    dep, req.physpath))
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 			/*
14777c478bd9Sstevel@tonic-gate 			 * hold and install kept while processing dependency
14787c478bd9Sstevel@tonic-gate 			 * keeper (in .physpath) has already been held.
14797c478bd9Sstevel@tonic-gate 			 */
14807c478bd9Sstevel@tonic-gate 			if (dep[0] == '\0') {
14817c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("kept NULL or null\n"))
14827c478bd9Sstevel@tonic-gate 				ret = EINVAL;
14837c478bd9Sstevel@tonic-gate 				break;
14847c478bd9Sstevel@tonic-gate 			} else if ((kept_dip =
14857c478bd9Sstevel@tonic-gate 			    pm_name_to_dip(dep, 1)) == NULL) {
14867c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
14877c478bd9Sstevel@tonic-gate 				ret = ENODEV;
14887c478bd9Sstevel@tonic-gate 				break;
14897c478bd9Sstevel@tonic-gate 			} else if (kept_dip == dip) {
14907c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
14917c478bd9Sstevel@tonic-gate 				    "self-dependency not allowed.\n",
14927c478bd9Sstevel@tonic-gate 				    dep, (void *)kept_dip, req.physpath,
14937c478bd9Sstevel@tonic-gate 				    (void *) dip))
14947c478bd9Sstevel@tonic-gate 				PM_RELE(dip);	/* release "double" hold */
14957c478bd9Sstevel@tonic-gate 				ret = EINVAL;
14967c478bd9Sstevel@tonic-gate 				break;
14977c478bd9Sstevel@tonic-gate 			}
14987c478bd9Sstevel@tonic-gate 			ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 			/*
15017c478bd9Sstevel@tonic-gate 			 * record dependency, then walk through device tree
15027c478bd9Sstevel@tonic-gate 			 * independently on behalf of kept and keeper to
15037c478bd9Sstevel@tonic-gate 			 * establish newly created dependency.
15047c478bd9Sstevel@tonic-gate 			 */
15057c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
15067c478bd9Sstevel@tonic-gate 			    req.physpath, dep, PM_DEP_WAIT, NULL, 0);
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 			/*
15097c478bd9Sstevel@tonic-gate 			 * release kept after establishing dependency, keeper
15107c478bd9Sstevel@tonic-gate 			 * is released as part of ioctl exit processing.
15117c478bd9Sstevel@tonic-gate 			 */
15127c478bd9Sstevel@tonic-gate 			PM_RELE(kept_dip);
15137c478bd9Sstevel@tonic-gate 			*rval_p = 0;
15147c478bd9Sstevel@tonic-gate 			ret = 0;
15157c478bd9Sstevel@tonic-gate 			break;
15167c478bd9Sstevel@tonic-gate 		}
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 		case PM_ADD_DEPENDENT_PROPERTY:
15197c478bd9Sstevel@tonic-gate 		{
15207c478bd9Sstevel@tonic-gate 			char *keeper, *kept;
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 			if (dep[0] == '\0') {
15237c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
15247c478bd9Sstevel@tonic-gate 				    "null\n", cmdstr))
15257c478bd9Sstevel@tonic-gate 				ret = EINVAL;
15267c478bd9Sstevel@tonic-gate 				break;
15277c478bd9Sstevel@tonic-gate 			}
15287c478bd9Sstevel@tonic-gate 			kept = dep;
15297c478bd9Sstevel@tonic-gate 			keeper = req.physpath;
15307c478bd9Sstevel@tonic-gate 			/*
15317c478bd9Sstevel@tonic-gate 			 * record keeper - kept dependency, then walk through
15327c478bd9Sstevel@tonic-gate 			 * device tree to find out all attached keeper, walk
15337c478bd9Sstevel@tonic-gate 			 * through again to apply dependency to all the
15347c478bd9Sstevel@tonic-gate 			 * potential kept.
15357c478bd9Sstevel@tonic-gate 			 */
15367c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(
15377c478bd9Sstevel@tonic-gate 			    PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
15387c478bd9Sstevel@tonic-gate 			    PM_DEP_WAIT, NULL, 0);
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 			*rval_p = 0;
15417c478bd9Sstevel@tonic-gate 			ret = 0;
15427c478bd9Sstevel@tonic-gate 			break;
15437c478bd9Sstevel@tonic-gate 		}
15447c478bd9Sstevel@tonic-gate 
15457c478bd9Sstevel@tonic-gate 		case PM_SET_DEVICE_THRESHOLD:
15467c478bd9Sstevel@tonic-gate 		{
15477c478bd9Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
15487c478bd9Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
15497c478bd9Sstevel@tonic-gate 			int *tp;	/* threshold storage */
15507c478bd9Sstevel@tonic-gate 			size_t size;
15517c478bd9Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate 			/*
15547c478bd9Sstevel@tonic-gate 			 * The header struct plus one entry struct plus one
15557c478bd9Sstevel@tonic-gate 			 * threshold plus the length of the string
15567c478bd9Sstevel@tonic-gate 			 */
15577c478bd9Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
15587c478bd9Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * 1) +
15597c478bd9Sstevel@tonic-gate 			    (1 * sizeof (int)) +
15607c478bd9Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
15637c478bd9Sstevel@tonic-gate 			rp->ptr_size = size;
15647c478bd9Sstevel@tonic-gate 			rp->ptr_numcomps = 0;	/* means device threshold */
15657c478bd9Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
15667c478bd9Sstevel@tonic-gate 			rp->ptr_entries = ep;
15677c478bd9Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
15687c478bd9Sstevel@tonic-gate 			    (1 * sizeof (pm_pte_t)));
15697c478bd9Sstevel@tonic-gate 			ep->pte_numthresh = 1;
15707c478bd9Sstevel@tonic-gate 			ep->pte_thresh = tp;
15717c478bd9Sstevel@tonic-gate 			*tp++ = req.value;
15727c478bd9Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
15737c478bd9Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
15747c478bd9Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
15757c478bd9Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
15767c478bd9Sstevel@tonic-gate 			PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
15777c478bd9Sstevel@tonic-gate 			    "%s\n", cmdstr, req.value, req.physpath))
15787c478bd9Sstevel@tonic-gate 			pm_record_thresh(rp);
15797c478bd9Sstevel@tonic-gate 			/*
15807c478bd9Sstevel@tonic-gate 			 * Don't free rp, pm_record_thresh() keeps it.
15817c478bd9Sstevel@tonic-gate 			 * We don't try to apply it ourselves because we'd need
15827c478bd9Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
15837c478bd9Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
15847c478bd9Sstevel@tonic-gate 			 * we get here
15857c478bd9Sstevel@tonic-gate 			 */
15867c478bd9Sstevel@tonic-gate 			ASSERT(dip == NULL);
15877c478bd9Sstevel@tonic-gate 			ret = 0;		/* can't fail now */
15887c478bd9Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
15897c478bd9Sstevel@tonic-gate 				break;
15907c478bd9Sstevel@tonic-gate 			}
15917c478bd9Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
15927c478bd9Sstevel@tonic-gate 			PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
15937c478bd9Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
15947c478bd9Sstevel@tonic-gate 			PM_RELE(dip);
15957c478bd9Sstevel@tonic-gate 			break;
15967c478bd9Sstevel@tonic-gate 		}
15977c478bd9Sstevel@tonic-gate 
15987c478bd9Sstevel@tonic-gate 		case PM_RESET_DEVICE_THRESHOLD:
15997c478bd9Sstevel@tonic-gate 		{
16007c478bd9Sstevel@tonic-gate 			/*
16017c478bd9Sstevel@tonic-gate 			 * This only applies to a currently attached and power
16027c478bd9Sstevel@tonic-gate 			 * managed node
16037c478bd9Sstevel@tonic-gate 			 */
16047c478bd9Sstevel@tonic-gate 			/*
16057c478bd9Sstevel@tonic-gate 			 * We don't do this to old-style drivers
16067c478bd9Sstevel@tonic-gate 			 */
16077c478bd9Sstevel@tonic-gate 			info = PM_GET_PM_INFO(dip);
16087c478bd9Sstevel@tonic-gate 			if (info == NULL) {
16097c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s not power "
16107c478bd9Sstevel@tonic-gate 				    "managed\n", cmdstr, req.physpath))
16117c478bd9Sstevel@tonic-gate 				ret = EINVAL;
16127c478bd9Sstevel@tonic-gate 				break;
16137c478bd9Sstevel@tonic-gate 			}
16147c478bd9Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16157c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
16167c478bd9Sstevel@tonic-gate 				    cmdstr, req.physpath))
16177c478bd9Sstevel@tonic-gate 				ret = EINVAL;
16187c478bd9Sstevel@tonic-gate 				break;
16197c478bd9Sstevel@tonic-gate 			}
16207c478bd9Sstevel@tonic-gate 			pm_unrecord_threshold(req.physpath);
1621c42872d4Smh27603 			if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
1622c42872d4Smh27603 				pm_set_device_threshold(dip,
1623c42872d4Smh27603 				    pm_cpu_idle_threshold, PMC_CPU_THRESH);
1624c42872d4Smh27603 			else
1625c42872d4Smh27603 				pm_set_device_threshold(dip,
1626c42872d4Smh27603 				    pm_system_idle_threshold, PMC_DEF_THRESH);
16277c478bd9Sstevel@tonic-gate 			ret = 0;
16287c478bd9Sstevel@tonic-gate 			break;
16297c478bd9Sstevel@tonic-gate 		}
16307c478bd9Sstevel@tonic-gate 
16317c478bd9Sstevel@tonic-gate 		case PM_GET_NUM_COMPONENTS:
16327c478bd9Sstevel@tonic-gate 			ret = 0;
16337c478bd9Sstevel@tonic-gate 			*rval_p = PM_NUMCMPTS(dip);
16347c478bd9Sstevel@tonic-gate 			break;
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 		case PM_GET_DEVICE_TYPE:
16377c478bd9Sstevel@tonic-gate 			ret = 0;
16387c478bd9Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
16397c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
16407c478bd9Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
16417c478bd9Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
16427c478bd9Sstevel@tonic-gate 				break;
16437c478bd9Sstevel@tonic-gate 			}
16447c478bd9Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16457c478bd9Sstevel@tonic-gate 				*rval_p = PM_CREATE_COMPONENTS;
16467c478bd9Sstevel@tonic-gate 			} else {
16477c478bd9Sstevel@tonic-gate 				*rval_p = PM_AUTOPM;
16487c478bd9Sstevel@tonic-gate 			}
16497c478bd9Sstevel@tonic-gate 			break;
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		case PM_SET_COMPONENT_THRESHOLDS:
16527c478bd9Sstevel@tonic-gate 		{
16537c478bd9Sstevel@tonic-gate 			int comps = 0;
16547c478bd9Sstevel@tonic-gate 			int *end = (int *)req.data + icount;
16557c478bd9Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
16567c478bd9Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
16577c478bd9Sstevel@tonic-gate 			int *tp;	/* threshold storage */
16587c478bd9Sstevel@tonic-gate 			int *ip;
16597c478bd9Sstevel@tonic-gate 			int j;
16607c478bd9Sstevel@tonic-gate 			size_t size;
16617c478bd9Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
16627c478bd9Sstevel@tonic-gate 			extern int pm_valid_thresh(dev_info_t *,
16637c478bd9Sstevel@tonic-gate 			    pm_thresh_rec_t *);
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 			for (ip = req.data; *ip; ip++) {
16667c478bd9Sstevel@tonic-gate 				if (ip >= end) {
16677c478bd9Sstevel@tonic-gate 					ret = EFAULT;
16687c478bd9Sstevel@tonic-gate 					break;
16697c478bd9Sstevel@tonic-gate 				}
16707c478bd9Sstevel@tonic-gate 				comps++;
16717c478bd9Sstevel@tonic-gate 				/* skip over indicated number of entries */
16727c478bd9Sstevel@tonic-gate 				for (j = *ip; j; j--) {
16737c478bd9Sstevel@tonic-gate 					if (++ip >= end) {
16747c478bd9Sstevel@tonic-gate 						ret = EFAULT;
16757c478bd9Sstevel@tonic-gate 						break;
16767c478bd9Sstevel@tonic-gate 					}
16777c478bd9Sstevel@tonic-gate 				}
16787c478bd9Sstevel@tonic-gate 				if (ret)
16797c478bd9Sstevel@tonic-gate 					break;
16807c478bd9Sstevel@tonic-gate 			}
16817c478bd9Sstevel@tonic-gate 			if (ret)
16827c478bd9Sstevel@tonic-gate 				break;
16837c478bd9Sstevel@tonic-gate 			if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
16847c478bd9Sstevel@tonic-gate 				/* did not exactly fill buffer */
16857c478bd9Sstevel@tonic-gate 				ret = EINVAL;
16867c478bd9Sstevel@tonic-gate 				break;
16877c478bd9Sstevel@tonic-gate 			}
16887c478bd9Sstevel@tonic-gate 			if (comps == 0) {
16897c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
16907c478bd9Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, req.physpath))
16917c478bd9Sstevel@tonic-gate 				ret = EINVAL;
16927c478bd9Sstevel@tonic-gate 				break;
16937c478bd9Sstevel@tonic-gate 			}
16947c478bd9Sstevel@tonic-gate 			/*
16957c478bd9Sstevel@tonic-gate 			 * The header struct plus one entry struct per component
16967c478bd9Sstevel@tonic-gate 			 * plus the size of the lists minus the counts
16977c478bd9Sstevel@tonic-gate 			 * plus the length of the string
16987c478bd9Sstevel@tonic-gate 			 */
16997c478bd9Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
17007c478bd9Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * comps) + req.datasize -
17017c478bd9Sstevel@tonic-gate 			    ((comps + 1) * sizeof (int)) +
17027c478bd9Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
17057c478bd9Sstevel@tonic-gate 			rp->ptr_size = size;
17067c478bd9Sstevel@tonic-gate 			rp->ptr_numcomps = comps;
17077c478bd9Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
17087c478bd9Sstevel@tonic-gate 			rp->ptr_entries = ep;
17097c478bd9Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
17107c478bd9Sstevel@tonic-gate 			    (comps * sizeof (pm_pte_t)));
17117c478bd9Sstevel@tonic-gate 			for (ip = req.data; *ip; ep++) {
17127c478bd9Sstevel@tonic-gate 				ep->pte_numthresh = *ip;
17137c478bd9Sstevel@tonic-gate 				ep->pte_thresh = tp;
17147c478bd9Sstevel@tonic-gate 				for (j = *ip++; j; j--) {
17157c478bd9Sstevel@tonic-gate 					*tp++ = *ip++;
17167c478bd9Sstevel@tonic-gate 				}
17177c478bd9Sstevel@tonic-gate 			}
17187c478bd9Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
17197c478bd9Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
17207c478bd9Sstevel@tonic-gate 			ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
17217c478bd9Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
17227c478bd9Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 			ASSERT(dip == NULL);
17257c478bd9Sstevel@tonic-gate 			/*
17267c478bd9Sstevel@tonic-gate 			 * If this is not a currently power managed node,
17277c478bd9Sstevel@tonic-gate 			 * then we can't check for validity of the thresholds
17287c478bd9Sstevel@tonic-gate 			 */
17297c478bd9Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
17307c478bd9Sstevel@tonic-gate 				/* don't free rp, pm_record_thresh uses it */
17317c478bd9Sstevel@tonic-gate 				pm_record_thresh(rp);
17327c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
17337c478bd9Sstevel@tonic-gate 				    "for %s failed\n", cmdstr, req.physpath))
17347c478bd9Sstevel@tonic-gate 				ret = 0;
17357c478bd9Sstevel@tonic-gate 				break;
17367c478bd9Sstevel@tonic-gate 			}
17377c478bd9Sstevel@tonic-gate 			ASSERT(!dipheld);
17387c478bd9Sstevel@tonic-gate 			dipheld++;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 			if (!pm_valid_thresh(dip, rp)) {
17417c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
17427c478bd9Sstevel@tonic-gate 				    "for %s@%s(%s#%d)\n", cmdstr,
17437c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip)))
17447c478bd9Sstevel@tonic-gate 				kmem_free(rp, size);
17457c478bd9Sstevel@tonic-gate 				ret = EINVAL;
17467c478bd9Sstevel@tonic-gate 				break;
17477c478bd9Sstevel@tonic-gate 			}
17487c478bd9Sstevel@tonic-gate 			/*
17497c478bd9Sstevel@tonic-gate 			 * We don't just apply it ourselves because we'd need
17507c478bd9Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
17517c478bd9Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
17527c478bd9Sstevel@tonic-gate 			 * we get here
17537c478bd9Sstevel@tonic-gate 			 */
17547c478bd9Sstevel@tonic-gate 			pm_record_thresh(rp);
17557c478bd9Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
17567c478bd9Sstevel@tonic-gate 			ret = 0;
17577c478bd9Sstevel@tonic-gate 			break;
17587c478bd9Sstevel@tonic-gate 		}
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 		case PM_GET_COMPONENT_THRESHOLDS:
17617c478bd9Sstevel@tonic-gate 		{
17627c478bd9Sstevel@tonic-gate 			int musthave;
17637c478bd9Sstevel@tonic-gate 			int numthresholds = 0;
17647c478bd9Sstevel@tonic-gate 			int wordsize;
17657c478bd9Sstevel@tonic-gate 			int numcomps;
17667c478bd9Sstevel@tonic-gate 			caddr_t uaddr = req.data;	/* user address */
17677c478bd9Sstevel@tonic-gate 			int val;	/* int value to be copied out */
17687c478bd9Sstevel@tonic-gate 			int32_t val32;	/* int32 value to be copied out */
17697c478bd9Sstevel@tonic-gate 			caddr_t vaddr;	/* address to copyout from */
17707c478bd9Sstevel@tonic-gate 			int j;
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
17737c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
17747c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
17757c478bd9Sstevel@tonic-gate 			} else
17767c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
17777c478bd9Sstevel@tonic-gate 			{
17787c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int);
17797c478bd9Sstevel@tonic-gate 			}
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate 			ASSERT(dip);
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 			numcomps = PM_NUMCMPTS(dip);
17847c478bd9Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17857c478bd9Sstevel@tonic-gate 				cp = PM_CP(dip, i);
17867c478bd9Sstevel@tonic-gate 				numthresholds += cp->pmc_comp.pmc_numlevels - 1;
17877c478bd9Sstevel@tonic-gate 			}
17887c478bd9Sstevel@tonic-gate 			musthave = (numthresholds + numcomps + 1) *  wordsize;
17897c478bd9Sstevel@tonic-gate 			if (req.datasize < musthave) {
17907c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
17917c478bd9Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
17927c478bd9Sstevel@tonic-gate 				    musthave))
17937c478bd9Sstevel@tonic-gate 				ret = EINVAL;
17947c478bd9Sstevel@tonic-gate 				break;
17957c478bd9Sstevel@tonic-gate 			}
17967c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
17977c478bd9Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17987c478bd9Sstevel@tonic-gate 				int *thp;
17997c478bd9Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18007c478bd9Sstevel@tonic-gate 				thp = cp->pmc_comp.pmc_thresh;
18017c478bd9Sstevel@tonic-gate 				/* first copyout the count */
18027c478bd9Sstevel@tonic-gate 				if (wordsize == sizeof (int32_t)) {
18037c478bd9Sstevel@tonic-gate 					val32 = cp->pmc_comp.pmc_numlevels - 1;
18047c478bd9Sstevel@tonic-gate 					vaddr = (caddr_t)&val32;
18057c478bd9Sstevel@tonic-gate 				} else {
18067c478bd9Sstevel@tonic-gate 					val = cp->pmc_comp.pmc_numlevels - 1;
18077c478bd9Sstevel@tonic-gate 					vaddr = (caddr_t)&val;
18087c478bd9Sstevel@tonic-gate 				}
18097c478bd9Sstevel@tonic-gate 				if (ddi_copyout(vaddr, (void *)uaddr,
18107c478bd9Sstevel@tonic-gate 				    wordsize, mode) != 0) {
18117c478bd9Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
18127c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
18137c478bd9Sstevel@tonic-gate 					    "(%s#%d) vaddr %p EFAULT\n",
18147c478bd9Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
18157c478bd9Sstevel@tonic-gate 					    (void*)vaddr))
18167c478bd9Sstevel@tonic-gate 					ret = EFAULT;
18177c478bd9Sstevel@tonic-gate 					break;
18187c478bd9Sstevel@tonic-gate 				}
18197c478bd9Sstevel@tonic-gate 				vaddr = uaddr;
18207c478bd9Sstevel@tonic-gate 				vaddr += wordsize;
18217c478bd9Sstevel@tonic-gate 				uaddr = (caddr_t)vaddr;
18227c478bd9Sstevel@tonic-gate 				/* then copyout each threshold value */
18237c478bd9Sstevel@tonic-gate 				for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
18247c478bd9Sstevel@tonic-gate 				    j++) {
18257c478bd9Sstevel@tonic-gate 					if (wordsize == sizeof (int32_t)) {
18267c478bd9Sstevel@tonic-gate 						val32 = thp[j + 1];
18277c478bd9Sstevel@tonic-gate 						vaddr = (caddr_t)&val32;
18287c478bd9Sstevel@tonic-gate 					} else {
18297c478bd9Sstevel@tonic-gate 						val = thp[i + 1];
18307c478bd9Sstevel@tonic-gate 						vaddr = (caddr_t)&val;
18317c478bd9Sstevel@tonic-gate 					}
18327c478bd9Sstevel@tonic-gate 					if (ddi_copyout(vaddr, (void *) uaddr,
18337c478bd9Sstevel@tonic-gate 					    wordsize, mode) != 0) {
18347c478bd9Sstevel@tonic-gate 						PM_UNLOCK_DIP(dip);
18357c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
18367c478bd9Sstevel@tonic-gate 						    "%s@%s(%s#%d) uaddr %p "
18377c478bd9Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
18387c478bd9Sstevel@tonic-gate 						    PM_DEVICE(dip),
18397c478bd9Sstevel@tonic-gate 						    (void *)uaddr))
18407c478bd9Sstevel@tonic-gate 						ret = EFAULT;
18417c478bd9Sstevel@tonic-gate 						break;
18427c478bd9Sstevel@tonic-gate 					}
18437c478bd9Sstevel@tonic-gate 					vaddr = uaddr;
18447c478bd9Sstevel@tonic-gate 					vaddr += wordsize;
18457c478bd9Sstevel@tonic-gate 					uaddr = (caddr_t)vaddr;
18467c478bd9Sstevel@tonic-gate 				}
18477c478bd9Sstevel@tonic-gate 			}
18487c478bd9Sstevel@tonic-gate 			if (ret)
18497c478bd9Sstevel@tonic-gate 				break;
18507c478bd9Sstevel@tonic-gate 			/* last copyout a terminating 0 count */
18517c478bd9Sstevel@tonic-gate 			if (wordsize == sizeof (int32_t)) {
18527c478bd9Sstevel@tonic-gate 				val32 = 0;
18537c478bd9Sstevel@tonic-gate 				vaddr = (caddr_t)&val32;
18547c478bd9Sstevel@tonic-gate 			} else {
18557c478bd9Sstevel@tonic-gate 				ASSERT(wordsize == sizeof (int));
18567c478bd9Sstevel@tonic-gate 				val = 0;
18577c478bd9Sstevel@tonic-gate 				vaddr = (caddr_t)&val;
18587c478bd9Sstevel@tonic-gate 			}
18597c478bd9Sstevel@tonic-gate 			if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
18607c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
18617c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
18627c478bd9Sstevel@tonic-gate 				    "vaddr %p (0 count) EFAULT\n", cmdstr,
18637c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)vaddr))
18647c478bd9Sstevel@tonic-gate 				ret = EFAULT;
18657c478bd9Sstevel@tonic-gate 				break;
18667c478bd9Sstevel@tonic-gate 			}
18677c478bd9Sstevel@tonic-gate 			/* finished, so don't need to increment addresses */
18687c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
18697c478bd9Sstevel@tonic-gate 			ret = 0;
18707c478bd9Sstevel@tonic-gate 			break;
18717c478bd9Sstevel@tonic-gate 		}
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 		case PM_GET_STATS:
18747c478bd9Sstevel@tonic-gate 		{
18757c478bd9Sstevel@tonic-gate 			time_t now;
18767c478bd9Sstevel@tonic-gate 			time_t *timestamp;
18777c478bd9Sstevel@tonic-gate 			extern int pm_cur_power(pm_component_t *cp);
18787c478bd9Sstevel@tonic-gate 			int musthave;
18797c478bd9Sstevel@tonic-gate 			int wordsize;
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
18827c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
18837c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
18847c478bd9Sstevel@tonic-gate 			} else
18857c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
18867c478bd9Sstevel@tonic-gate 			{
18877c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int);
18887c478bd9Sstevel@tonic-gate 			}
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 			comps = PM_NUMCMPTS(dip);
18917c478bd9Sstevel@tonic-gate 			if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
18927c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s no components"
18937c478bd9Sstevel@tonic-gate 				    " or not power managed--EINVAL\n", cmdstr,
18947c478bd9Sstevel@tonic-gate 				    req.physpath))
18957c478bd9Sstevel@tonic-gate 				ret = EINVAL;
18967c478bd9Sstevel@tonic-gate 				break;
18977c478bd9Sstevel@tonic-gate 			}
18987c478bd9Sstevel@tonic-gate 			musthave = comps * 2 * wordsize;
18997c478bd9Sstevel@tonic-gate 			if (req.datasize < musthave) {
19007c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
19017c478bd9Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
19027c478bd9Sstevel@tonic-gate 				    musthave))
19037c478bd9Sstevel@tonic-gate 				ret = EINVAL;
19047c478bd9Sstevel@tonic-gate 				break;
19057c478bd9Sstevel@tonic-gate 			}
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
19087c478bd9Sstevel@tonic-gate 			(void) drv_getparm(TIME, &now);
19097c478bd9Sstevel@tonic-gate 			timestamp = kmem_zalloc(comps * sizeof (time_t),
19107c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
19117c478bd9Sstevel@tonic-gate 			pm_get_timestamps(dip, timestamp);
19127c478bd9Sstevel@tonic-gate 			/*
19137c478bd9Sstevel@tonic-gate 			 * First the current power levels
19147c478bd9Sstevel@tonic-gate 			 */
19157c478bd9Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19167c478bd9Sstevel@tonic-gate 				int curpwr;
19177c478bd9Sstevel@tonic-gate 				int32_t curpwr32;
19187c478bd9Sstevel@tonic-gate 				caddr_t cpaddr;
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19217c478bd9Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19227c478bd9Sstevel@tonic-gate 					curpwr = pm_cur_power(cp);
19237c478bd9Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr;
19247c478bd9Sstevel@tonic-gate 				} else {
19257c478bd9Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19267c478bd9Sstevel@tonic-gate 					curpwr32 = pm_cur_power(cp);
19277c478bd9Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr32;
19287c478bd9Sstevel@tonic-gate 				}
19297c478bd9Sstevel@tonic-gate 				if (ddi_copyout(cpaddr, (void *) req.data,
19307c478bd9Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19317c478bd9Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19327c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19337c478bd9Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19347c478bd9Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19357c478bd9Sstevel@tonic-gate 					    (void *)req.data))
19367c478bd9Sstevel@tonic-gate 					ASSERT(!dipheld);
19377c478bd9Sstevel@tonic-gate 					return (EFAULT);
19387c478bd9Sstevel@tonic-gate 				}
19397c478bd9Sstevel@tonic-gate 				cpaddr = (caddr_t)req.data;
19407c478bd9Sstevel@tonic-gate 				cpaddr += wordsize;
19417c478bd9Sstevel@tonic-gate 				req.data = cpaddr;
19427c478bd9Sstevel@tonic-gate 			}
19437c478bd9Sstevel@tonic-gate 			/*
19447c478bd9Sstevel@tonic-gate 			 * Then the times remaining
19457c478bd9Sstevel@tonic-gate 			 */
19467c478bd9Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19477c478bd9Sstevel@tonic-gate 				int retval;
19487c478bd9Sstevel@tonic-gate 				int32_t retval32;
19497c478bd9Sstevel@tonic-gate 				caddr_t rvaddr;
19507c478bd9Sstevel@tonic-gate 				int curpwr;
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19537c478bd9Sstevel@tonic-gate 				curpwr = cp->pmc_cur_pwr;
19547c478bd9Sstevel@tonic-gate 				if (curpwr == 0 || timestamp[i] == 0) {
19557c478bd9Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: "
19567c478bd9Sstevel@tonic-gate 					    "cur_pwer %x, timestamp %lx\n",
19577c478bd9Sstevel@tonic-gate 					    cmdstr, curpwr, timestamp[i]))
19587c478bd9Sstevel@tonic-gate 					retval = INT_MAX;
19597c478bd9Sstevel@tonic-gate 				} else {
19607c478bd9Sstevel@tonic-gate 					int thresh;
19617c478bd9Sstevel@tonic-gate 					(void) pm_current_threshold(dip, i,
19627c478bd9Sstevel@tonic-gate 					    &thresh);
19637c478bd9Sstevel@tonic-gate 					retval = thresh - (now - timestamp[i]);
19647c478bd9Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: current "
19657c478bd9Sstevel@tonic-gate 					    "thresh %x, now %lx, timestamp %lx,"
19667c478bd9Sstevel@tonic-gate 					    " retval %x\n", cmdstr, thresh, now,
19677c478bd9Sstevel@tonic-gate 					    timestamp[i], retval))
19687c478bd9Sstevel@tonic-gate 				}
19697c478bd9Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19707c478bd9Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval;
19717c478bd9Sstevel@tonic-gate 				} else {
19727c478bd9Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19737c478bd9Sstevel@tonic-gate 					retval32 = retval;
19747c478bd9Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval32;
19757c478bd9Sstevel@tonic-gate 				}
19767c478bd9Sstevel@tonic-gate 				if (ddi_copyout(rvaddr, (void *) req.data,
19777c478bd9Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19787c478bd9Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19797c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19807c478bd9Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19817c478bd9Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19827c478bd9Sstevel@tonic-gate 					    (void *)req.data))
19837c478bd9Sstevel@tonic-gate 					ASSERT(!dipheld);
19847c478bd9Sstevel@tonic-gate 					return (EFAULT);
19857c478bd9Sstevel@tonic-gate 				}
19867c478bd9Sstevel@tonic-gate 				rvaddr = (caddr_t)req.data;
19877c478bd9Sstevel@tonic-gate 				rvaddr += wordsize;
19887c478bd9Sstevel@tonic-gate 				req.data = (int *)rvaddr;
19897c478bd9Sstevel@tonic-gate 			}
19907c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
19917c478bd9Sstevel@tonic-gate 			*rval_p = comps;
19927c478bd9Sstevel@tonic-gate 			ret = 0;
19937c478bd9Sstevel@tonic-gate 			kmem_free(timestamp, comps * sizeof (time_t));
19947c478bd9Sstevel@tonic-gate 			break;
19957c478bd9Sstevel@tonic-gate 		}
19967c478bd9Sstevel@tonic-gate 
19977c478bd9Sstevel@tonic-gate 		case PM_GET_COMPONENT_NAME:
19987c478bd9Sstevel@tonic-gate 			ASSERT(dip);
19997c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20007c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20017c478bd9Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20027c478bd9Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20037c478bd9Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20047c478bd9Sstevel@tonic-gate 				ret = EINVAL;
20057c478bd9Sstevel@tonic-gate 				break;
20067c478bd9Sstevel@tonic-gate 			}
20077c478bd9Sstevel@tonic-gate 			if (ret = copyoutstr(cp->pmc_comp.pmc_name,
20087c478bd9Sstevel@tonic-gate 			    (char *)req.data, req.datasize, &lencopied)) {
20097c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20107c478bd9Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20117c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20127c478bd9Sstevel@tonic-gate 				break;
20137c478bd9Sstevel@tonic-gate 			}
20147c478bd9Sstevel@tonic-gate 			*rval_p = lencopied;
20157c478bd9Sstevel@tonic-gate 			ret = 0;
20167c478bd9Sstevel@tonic-gate 			break;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 		case PM_GET_POWER_NAME:
20197c478bd9Sstevel@tonic-gate 		{
20207c478bd9Sstevel@tonic-gate 			int i;
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 			ASSERT(dip);
20237c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20247c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20257c478bd9Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20267c478bd9Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20277c478bd9Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20287c478bd9Sstevel@tonic-gate 				ret = EINVAL;
20297c478bd9Sstevel@tonic-gate 				break;
20307c478bd9Sstevel@tonic-gate 			}
20317c478bd9Sstevel@tonic-gate 			if ((i = req.value) < 0 ||
20327c478bd9Sstevel@tonic-gate 			    i > cp->pmc_comp.pmc_numlevels - 1) {
20337c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20347c478bd9Sstevel@tonic-gate 				    "value %d > num_levels - 1 %d--EINVAL\n",
20357c478bd9Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.value,
20367c478bd9Sstevel@tonic-gate 				    cp->pmc_comp.pmc_numlevels - 1))
20377c478bd9Sstevel@tonic-gate 				ret = EINVAL;
20387c478bd9Sstevel@tonic-gate 				break;
20397c478bd9Sstevel@tonic-gate 			}
20407c478bd9Sstevel@tonic-gate 			dep = cp->pmc_comp.pmc_lnames[req.value];
20417c478bd9Sstevel@tonic-gate 			if (ret = copyoutstr(dep,
20427c478bd9Sstevel@tonic-gate 			    req.data, req.datasize, &lencopied)) {
20437c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20447c478bd9Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20457c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20467c478bd9Sstevel@tonic-gate 				break;
20477c478bd9Sstevel@tonic-gate 			}
20487c478bd9Sstevel@tonic-gate 			*rval_p = lencopied;
20497c478bd9Sstevel@tonic-gate 			ret = 0;
20507c478bd9Sstevel@tonic-gate 			break;
20517c478bd9Sstevel@tonic-gate 		}
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 		case PM_GET_POWER_LEVELS:
20547c478bd9Sstevel@tonic-gate 		{
20557c478bd9Sstevel@tonic-gate 			int musthave;
20567c478bd9Sstevel@tonic-gate 			int numlevels;
20577c478bd9Sstevel@tonic-gate 			int wordsize;
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
20607c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
20617c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
20627c478bd9Sstevel@tonic-gate 			} else
20637c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
20647c478bd9Sstevel@tonic-gate 			{
20657c478bd9Sstevel@tonic-gate 				wordsize = sizeof (int);
20667c478bd9Sstevel@tonic-gate 			}
20677c478bd9Sstevel@tonic-gate 			ASSERT(dip);
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20707c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20717c478bd9Sstevel@tonic-gate 				    "has %d components, component %d requested"
20727c478bd9Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, PM_DEVICE(dip),
20737c478bd9Sstevel@tonic-gate 				    PM_NUMCMPTS(dip), req.component))
20747c478bd9Sstevel@tonic-gate 				ret = EINVAL;
20757c478bd9Sstevel@tonic-gate 				break;
20767c478bd9Sstevel@tonic-gate 			}
20777c478bd9Sstevel@tonic-gate 			numlevels = cp->pmc_comp.pmc_numlevels;
20787c478bd9Sstevel@tonic-gate 			musthave = numlevels *  wordsize;
20797c478bd9Sstevel@tonic-gate 			if (req.datasize < musthave) {
20807c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
20817c478bd9Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
20827c478bd9Sstevel@tonic-gate 				    musthave))
20837c478bd9Sstevel@tonic-gate 				ret = EINVAL;
20847c478bd9Sstevel@tonic-gate 				break;
20857c478bd9Sstevel@tonic-gate 			}
20867c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
20877c478bd9Sstevel@tonic-gate 			for (i = 0; i < numlevels; i++) {
20887c478bd9Sstevel@tonic-gate 				int level;
20897c478bd9Sstevel@tonic-gate 				int32_t level32;
20907c478bd9Sstevel@tonic-gate 				caddr_t laddr;
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
20937c478bd9Sstevel@tonic-gate 					level = cp->pmc_comp.pmc_lvals[i];
20947c478bd9Sstevel@tonic-gate 					laddr = (caddr_t)&level;
20957c478bd9Sstevel@tonic-gate 				} else {
20967c478bd9Sstevel@tonic-gate 					level32 = cp->pmc_comp.pmc_lvals[i];
20977c478bd9Sstevel@tonic-gate 					laddr = (caddr_t)&level32;
20987c478bd9Sstevel@tonic-gate 				}
20997c478bd9Sstevel@tonic-gate 				if (ddi_copyout(laddr, (void *) req.data,
21007c478bd9Sstevel@tonic-gate 				    wordsize, mode) != 0) {
21017c478bd9Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
21027c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
21037c478bd9Sstevel@tonic-gate 					    "(%s#%d) laddr %p EFAULT\n",
21047c478bd9Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
21057c478bd9Sstevel@tonic-gate 					    (void *)laddr))
21067c478bd9Sstevel@tonic-gate 					ASSERT(!dipheld);
21077c478bd9Sstevel@tonic-gate 					return (EFAULT);
21087c478bd9Sstevel@tonic-gate 				}
21097c478bd9Sstevel@tonic-gate 				laddr = (caddr_t)req.data;
21107c478bd9Sstevel@tonic-gate 				laddr += wordsize;
21117c478bd9Sstevel@tonic-gate 				req.data = (int *)laddr;
21127c478bd9Sstevel@tonic-gate 			}
21137c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21147c478bd9Sstevel@tonic-gate 			*rval_p = numlevels;
21157c478bd9Sstevel@tonic-gate 			ret = 0;
21167c478bd9Sstevel@tonic-gate 			break;
21177c478bd9Sstevel@tonic-gate 		}
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 		case PM_GET_NUM_POWER_LEVELS:
21217c478bd9Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21227c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21237c478bd9Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
21247c478bd9Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
21257c478bd9Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
21267c478bd9Sstevel@tonic-gate 				ret = EINVAL;
21277c478bd9Sstevel@tonic-gate 				break;
21287c478bd9Sstevel@tonic-gate 			}
21297c478bd9Sstevel@tonic-gate 			*rval_p = cp->pmc_comp.pmc_numlevels;
21307c478bd9Sstevel@tonic-gate 			ret = 0;
21317c478bd9Sstevel@tonic-gate 			break;
21327c478bd9Sstevel@tonic-gate 
21337c478bd9Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD_BASIS:
21347c478bd9Sstevel@tonic-gate 			ret = 0;
21357c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21367c478bd9Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
21377c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21387c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
21397c478bd9Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
21407c478bd9Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
21417c478bd9Sstevel@tonic-gate 				break;
21427c478bd9Sstevel@tonic-gate 			}
21437c478bd9Sstevel@tonic-gate 			if (PM_ISDIRECT(dip)) {
21447c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21457c478bd9Sstevel@tonic-gate 				*rval_p = PM_DIRECTLY_MANAGED;
21467c478bd9Sstevel@tonic-gate 				break;
21477c478bd9Sstevel@tonic-gate 			}
21487c478bd9Sstevel@tonic-gate 			switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
21497c478bd9Sstevel@tonic-gate 			case PMC_DEF_THRESH:
21507c478bd9Sstevel@tonic-gate 			case PMC_NEXDEF_THRESH:
21517c478bd9Sstevel@tonic-gate 				*rval_p = PM_DEFAULT_THRESHOLD;
21527c478bd9Sstevel@tonic-gate 				break;
21537c478bd9Sstevel@tonic-gate 			case PMC_DEV_THRESH:
21547c478bd9Sstevel@tonic-gate 				*rval_p = PM_DEVICE_THRESHOLD;
21557c478bd9Sstevel@tonic-gate 				break;
21567c478bd9Sstevel@tonic-gate 			case PMC_COMP_THRESH:
21577c478bd9Sstevel@tonic-gate 				*rval_p = PM_COMPONENT_THRESHOLD;
21587c478bd9Sstevel@tonic-gate 				break;
2159c42872d4Smh27603 			case PMC_CPU_THRESH:
2160c42872d4Smh27603 				*rval_p = PM_CPU_THRESHOLD;
2161c42872d4Smh27603 				break;
21627c478bd9Sstevel@tonic-gate 			default:
21637c478bd9Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
21647c478bd9Sstevel@tonic-gate 					*rval_p = PM_OLD_THRESHOLD;
21657c478bd9Sstevel@tonic-gate 					break;
21667c478bd9Sstevel@tonic-gate 				}
21677c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: default, not "
21687c478bd9Sstevel@tonic-gate 				    "BC--EINVAL", cmdstr))
21697c478bd9Sstevel@tonic-gate 				ret = EINVAL;
21707c478bd9Sstevel@tonic-gate 				break;
21717c478bd9Sstevel@tonic-gate 			}
21727c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21737c478bd9Sstevel@tonic-gate 			break;
21747c478bd9Sstevel@tonic-gate 		}
21757c478bd9Sstevel@tonic-gate 		break;
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 	case PM_PSC:
21787c478bd9Sstevel@tonic-gate 		/*
21797c478bd9Sstevel@tonic-gate 		 * Commands that require pm_state_change_t as arg
21807c478bd9Sstevel@tonic-gate 		 */
21817c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
21827c478bd9Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
21837c478bd9Sstevel@tonic-gate 			pscp32 = (pm_state_change32_t *)arg;
21847c478bd9Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc32,
21857c478bd9Sstevel@tonic-gate 			    sizeof (psc32), mode) != 0) {
21867c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
21877c478bd9Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
21887c478bd9Sstevel@tonic-gate 				ASSERT(!dipheld);
21897c478bd9Sstevel@tonic-gate 				return (EFAULT);
21907c478bd9Sstevel@tonic-gate 			}
21917c478bd9Sstevel@tonic-gate 			psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
21927c478bd9Sstevel@tonic-gate 			psc.size = psc32.size;
21937c478bd9Sstevel@tonic-gate 		} else
21947c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
21957c478bd9Sstevel@tonic-gate 		{
21967c478bd9Sstevel@tonic-gate 			pscp = (pm_state_change_t *)arg;
21977c478bd9Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc,
21987c478bd9Sstevel@tonic-gate 			    sizeof (psc), mode) != 0) {
21997c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
22007c478bd9Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
22017c478bd9Sstevel@tonic-gate 				ASSERT(!dipheld);
22027c478bd9Sstevel@tonic-gate 				return (EFAULT);
22037c478bd9Sstevel@tonic-gate 			}
22047c478bd9Sstevel@tonic-gate 		}
22057c478bd9Sstevel@tonic-gate 		switch (cmd) {
22067c478bd9Sstevel@tonic-gate 
22077c478bd9Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE:
22087c478bd9Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE_WAIT:
22097c478bd9Sstevel@tonic-gate 		{
22107c478bd9Sstevel@tonic-gate 			psce_t			*pscep;
22117c478bd9Sstevel@tonic-gate 			pm_state_change_t	*p;
22127c478bd9Sstevel@tonic-gate 			caddr_t			physpath;
22137c478bd9Sstevel@tonic-gate 			size_t			physlen;
22147c478bd9Sstevel@tonic-gate 
22157c478bd9Sstevel@tonic-gate 			/*
22167c478bd9Sstevel@tonic-gate 			 * We want to know if any device has changed state.
22177c478bd9Sstevel@tonic-gate 			 * We look up by clone.  In case we have another thread
22187c478bd9Sstevel@tonic-gate 			 * from the same process, we loop.
22197c478bd9Sstevel@tonic-gate 			 * pm_psc_clone_to_interest() returns a locked entry.
22207c478bd9Sstevel@tonic-gate 			 * We create an internal copy of the event entry prior
22217c478bd9Sstevel@tonic-gate 			 * to copyout to user space because we don't want to
22227c478bd9Sstevel@tonic-gate 			 * hold the psce_lock while doing copyout as we might
22237c478bd9Sstevel@tonic-gate 			 * hit page fault  which eventually brings us back
22247c478bd9Sstevel@tonic-gate 			 * here requesting the same lock.
22257c478bd9Sstevel@tonic-gate 			 */
22267c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
22277c478bd9Sstevel@tonic-gate 			if (!pm_interest_registered(clone))
22287c478bd9Sstevel@tonic-gate 				pm_register_watcher(clone, NULL);
22297c478bd9Sstevel@tonic-gate 			while ((pscep =
22307c478bd9Sstevel@tonic-gate 			    pm_psc_clone_to_interest(clone)) == NULL) {
22317c478bd9Sstevel@tonic-gate 				if (cmd == PM_GET_STATE_CHANGE) {
22327c478bd9Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
22337c478bd9Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
22347c478bd9Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
22357c478bd9Sstevel@tonic-gate 					ASSERT(!dipheld);
22367c478bd9Sstevel@tonic-gate 					return (EWOULDBLOCK);
22377c478bd9Sstevel@tonic-gate 				} else {
22387c478bd9Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
22397c478bd9Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
22407c478bd9Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
22417c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s "
22427c478bd9Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
22437c478bd9Sstevel@tonic-gate 						ASSERT(!dipheld);
22447c478bd9Sstevel@tonic-gate 						return (EINTR);
22457c478bd9Sstevel@tonic-gate 					}
22467c478bd9Sstevel@tonic-gate 				}
22477c478bd9Sstevel@tonic-gate 			}
22487c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
22497c478bd9Sstevel@tonic-gate 
22507c478bd9Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
22517c478bd9Sstevel@tonic-gate 			physpath = NULL;
22527c478bd9Sstevel@tonic-gate 			/*
22537c478bd9Sstevel@tonic-gate 			 * If we were unable to store the path while bringing
22547c478bd9Sstevel@tonic-gate 			 * up the console fb upon entering the prom, we give
22557c478bd9Sstevel@tonic-gate 			 * a "" name with the overrun event set
22567c478bd9Sstevel@tonic-gate 			 */
22577c478bd9Sstevel@tonic-gate 			if (physlen == (size_t)-1) {	/* kmemalloc failed */
22587c478bd9Sstevel@tonic-gate 				physpath = kmem_zalloc(1, KM_SLEEP);
22597c478bd9Sstevel@tonic-gate 				physlen = 1;
22607c478bd9Sstevel@tonic-gate 			}
22617c478bd9Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
22627c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
22637c478bd9Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
22647c478bd9Sstevel@tonic-gate 				ret = EFAULT;
22657c478bd9Sstevel@tonic-gate 				break;
22667c478bd9Sstevel@tonic-gate 			}
22677c478bd9Sstevel@tonic-gate 			if (physpath == NULL) {
22687c478bd9Sstevel@tonic-gate 				physpath = kmem_zalloc(physlen, KM_SLEEP);
22697c478bd9Sstevel@tonic-gate 				bcopy((const void *) pscep->psce_out->physpath,
22707c478bd9Sstevel@tonic-gate 				    (void *) physpath, physlen);
22717c478bd9Sstevel@tonic-gate 			}
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 			p = pscep->psce_out;
22747c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
22757c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
22767c478bd9Sstevel@tonic-gate #ifdef DEBUG
22777c478bd9Sstevel@tonic-gate 				size_t usrcopysize;
22787c478bd9Sstevel@tonic-gate #endif
22797c478bd9Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
22807c478bd9Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
22817c478bd9Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
22827c478bd9Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
22837c478bd9Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
22847c478bd9Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
22857c478bd9Sstevel@tonic-gate 				copysize32 = ((intptr_t)&psc32.size -
22867c478bd9Sstevel@tonic-gate 				    (intptr_t)&psc32.component);
22877c478bd9Sstevel@tonic-gate #ifdef DEBUG
22887c478bd9Sstevel@tonic-gate 				usrcopysize = ((intptr_t)&pscp32->size -
22897c478bd9Sstevel@tonic-gate 				    (intptr_t)&pscp32->component);
22907c478bd9Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
22917c478bd9Sstevel@tonic-gate #endif
22927c478bd9Sstevel@tonic-gate 			} else
22937c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22947c478bd9Sstevel@tonic-gate 			{
22957c478bd9Sstevel@tonic-gate 				psc.flags = p->flags;
22967c478bd9Sstevel@tonic-gate 				psc.event = p->event;
22977c478bd9Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
22987c478bd9Sstevel@tonic-gate 				psc.component = p->component;
22997c478bd9Sstevel@tonic-gate 				psc.old_level = p->old_level;
23007c478bd9Sstevel@tonic-gate 				psc.new_level = p->new_level;
23017c478bd9Sstevel@tonic-gate 				copysize = ((long)&p->size -
23027c478bd9Sstevel@tonic-gate 				    (long)&p->component);
23037c478bd9Sstevel@tonic-gate 			}
23047c478bd9Sstevel@tonic-gate 			if (p->size != (size_t)-1)
23057c478bd9Sstevel@tonic-gate 				kmem_free(p->physpath, p->size);
23067c478bd9Sstevel@tonic-gate 			p->size = 0;
23077c478bd9Sstevel@tonic-gate 			p->physpath = NULL;
23087c478bd9Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
23097c478bd9Sstevel@tonic-gate 				p = pscep->psce_first;
23107c478bd9Sstevel@tonic-gate 			else
23117c478bd9Sstevel@tonic-gate 				p++;
23127c478bd9Sstevel@tonic-gate 			pscep->psce_out = p;
23137c478bd9Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
23167c478bd9Sstevel@tonic-gate 			    physlen, &lencopied);
23177c478bd9Sstevel@tonic-gate 			kmem_free(physpath, physlen);
23187c478bd9Sstevel@tonic-gate 			if (ret) {
23197c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
23207c478bd9Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
23217c478bd9Sstevel@tonic-gate 				    (void *)psc.physpath))
23227c478bd9Sstevel@tonic-gate 				break;
23237c478bd9Sstevel@tonic-gate 			}
23247c478bd9Sstevel@tonic-gate 
23257c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23267c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23277c478bd9Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
23287c478bd9Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
23297c478bd9Sstevel@tonic-gate 				    != 0) {
23307c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23317c478bd9Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23327c478bd9Sstevel@tonic-gate 					ret = EFAULT;
23337c478bd9Sstevel@tonic-gate 					break;
23347c478bd9Sstevel@tonic-gate 				}
23357c478bd9Sstevel@tonic-gate 			} else
23367c478bd9Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
23377c478bd9Sstevel@tonic-gate 			{
23387c478bd9Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
23397c478bd9Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
23407c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23417c478bd9Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23427c478bd9Sstevel@tonic-gate 					ret = EFAULT;
23437c478bd9Sstevel@tonic-gate 					break;
23447c478bd9Sstevel@tonic-gate 				}
23457c478bd9Sstevel@tonic-gate 			}
23467c478bd9Sstevel@tonic-gate 			ret = 0;
23477c478bd9Sstevel@tonic-gate 			break;
23487c478bd9Sstevel@tonic-gate 		}
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY:
23517c478bd9Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY_WAIT:
23527c478bd9Sstevel@tonic-gate 		{
23537c478bd9Sstevel@tonic-gate 			psce_t			*pscep;
23547c478bd9Sstevel@tonic-gate 			pm_state_change_t	*p;
23557c478bd9Sstevel@tonic-gate 			caddr_t			physpath;
23567c478bd9Sstevel@tonic-gate 			size_t			physlen;
23577c478bd9Sstevel@tonic-gate 			/*
23587c478bd9Sstevel@tonic-gate 			 * We want to know if any direct device of ours has
23597c478bd9Sstevel@tonic-gate 			 * something we should know about.  We look up by clone.
23607c478bd9Sstevel@tonic-gate 			 * In case we have another thread from the same process,
23617c478bd9Sstevel@tonic-gate 			 * we loop.
23627c478bd9Sstevel@tonic-gate 			 * pm_psc_clone_to_direct() returns a locked entry.
23637c478bd9Sstevel@tonic-gate 			 */
23647c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
23657c478bd9Sstevel@tonic-gate 			while (pm_poll_cnt[clone] == 0 ||
23667c478bd9Sstevel@tonic-gate 			    (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
23677c478bd9Sstevel@tonic-gate 				if (cmd == PM_DIRECT_NOTIFY) {
23687c478bd9Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
23697c478bd9Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
23707c478bd9Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
23717c478bd9Sstevel@tonic-gate 					ASSERT(!dipheld);
23727c478bd9Sstevel@tonic-gate 					return (EWOULDBLOCK);
23737c478bd9Sstevel@tonic-gate 				} else {
23747c478bd9Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
23757c478bd9Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
23767c478bd9Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
23777c478bd9Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
23787c478bd9Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
23797c478bd9Sstevel@tonic-gate 						ASSERT(!dipheld);
23807c478bd9Sstevel@tonic-gate 						return (EINTR);
23817c478bd9Sstevel@tonic-gate 					}
23827c478bd9Sstevel@tonic-gate 				}
23837c478bd9Sstevel@tonic-gate 			}
23847c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
23857c478bd9Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
23867c478bd9Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
23877c478bd9Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
23887c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
23897c478bd9Sstevel@tonic-gate 				    cmdstr))
23907c478bd9Sstevel@tonic-gate 				ret = EFAULT;
23917c478bd9Sstevel@tonic-gate 				break;
23927c478bd9Sstevel@tonic-gate 			}
23937c478bd9Sstevel@tonic-gate 			physpath = kmem_zalloc(physlen, KM_SLEEP);
23947c478bd9Sstevel@tonic-gate 			bcopy((const void *) pscep->psce_out->physpath,
23957c478bd9Sstevel@tonic-gate 			    (void *) physpath, physlen);
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate 			p = pscep->psce_out;
23987c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23997c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24007c478bd9Sstevel@tonic-gate #ifdef DEBUG
24017c478bd9Sstevel@tonic-gate 				size_t usrcopysize;
24027c478bd9Sstevel@tonic-gate #endif
24037c478bd9Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
24047c478bd9Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
24057c478bd9Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
24067c478bd9Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
24077c478bd9Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
24087c478bd9Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
24097c478bd9Sstevel@tonic-gate 				copysize32 = (intptr_t)&psc32.size -
24107c478bd9Sstevel@tonic-gate 				    (intptr_t)&psc32.component;
24117c478bd9Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
24127c478bd9Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24137c478bd9Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24147c478bd9Sstevel@tonic-gate #ifdef DEBUG
24157c478bd9Sstevel@tonic-gate 				usrcopysize = (intptr_t)&pscp32->size -
24167c478bd9Sstevel@tonic-gate 				    (intptr_t)&pscp32->component;
24177c478bd9Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
24187c478bd9Sstevel@tonic-gate #endif
24197c478bd9Sstevel@tonic-gate 			} else
24207c478bd9Sstevel@tonic-gate #endif
24217c478bd9Sstevel@tonic-gate 			{
24227c478bd9Sstevel@tonic-gate 				psc.component = p->component;
24237c478bd9Sstevel@tonic-gate 				psc.flags = p->flags;
24247c478bd9Sstevel@tonic-gate 				psc.event = p->event;
24257c478bd9Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
24267c478bd9Sstevel@tonic-gate 				psc.old_level = p->old_level;
24277c478bd9Sstevel@tonic-gate 				psc.new_level = p->new_level;
24287c478bd9Sstevel@tonic-gate 				copysize = (intptr_t)&p->size -
24297c478bd9Sstevel@tonic-gate 				    (intptr_t)&p->component;
24307c478bd9Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
24317c478bd9Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24327c478bd9Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24337c478bd9Sstevel@tonic-gate 			}
24347c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24357c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
24367c478bd9Sstevel@tonic-gate 			    "before decrement\n", cmdstr, clone,
24377c478bd9Sstevel@tonic-gate 			    pm_poll_cnt[clone]))
24387c478bd9Sstevel@tonic-gate 			pm_poll_cnt[clone]--;
24397c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24407c478bd9Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
24417c478bd9Sstevel@tonic-gate 			p->size = 0;
24427c478bd9Sstevel@tonic-gate 			p->physpath = NULL;
24437c478bd9Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
24447c478bd9Sstevel@tonic-gate 				p = pscep->psce_first;
24457c478bd9Sstevel@tonic-gate 			else
24467c478bd9Sstevel@tonic-gate 				p++;
24477c478bd9Sstevel@tonic-gate 			pscep->psce_out = p;
24487c478bd9Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
24517c478bd9Sstevel@tonic-gate 			    physlen, &lencopied);
24527c478bd9Sstevel@tonic-gate 			kmem_free(physpath, physlen);
24537c478bd9Sstevel@tonic-gate 			if (ret) {
24547c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
24557c478bd9Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
24567c478bd9Sstevel@tonic-gate 				    (void *)psc.physpath))
24577c478bd9Sstevel@tonic-gate 				break;
24587c478bd9Sstevel@tonic-gate 			}
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
24617c478bd9Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24627c478bd9Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
24637c478bd9Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
24647c478bd9Sstevel@tonic-gate 					!= 0) {
24657c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24667c478bd9Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24677c478bd9Sstevel@tonic-gate 					ret = EFAULT;
24687c478bd9Sstevel@tonic-gate 					break;
24697c478bd9Sstevel@tonic-gate 				}
24707c478bd9Sstevel@tonic-gate 			} else
24717c478bd9Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
24727c478bd9Sstevel@tonic-gate 			{
24737c478bd9Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
24747c478bd9Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
24757c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24767c478bd9Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24777c478bd9Sstevel@tonic-gate 					ret = EFAULT;
24787c478bd9Sstevel@tonic-gate 					break;
24797c478bd9Sstevel@tonic-gate 				}
24807c478bd9Sstevel@tonic-gate 			}
24817c478bd9Sstevel@tonic-gate 			ret = 0;
24827c478bd9Sstevel@tonic-gate 			break;
24837c478bd9Sstevel@tonic-gate 		}
24847c478bd9Sstevel@tonic-gate 		default:
24857c478bd9Sstevel@tonic-gate 			ASSERT(0);
24867c478bd9Sstevel@tonic-gate 		}
24877c478bd9Sstevel@tonic-gate 		break;
24887c478bd9Sstevel@tonic-gate 
24897c478bd9Sstevel@tonic-gate 	case NOSTRUCT:
24907c478bd9Sstevel@tonic-gate 		switch (cmd) {
24917c478bd9Sstevel@tonic-gate 		case PM_START_PM:
2492c42872d4Smh27603 		case PM_START_CPUPM:
24937c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
2494c42872d4Smh27603 			if ((cmd == PM_START_PM && autopm_enabled) ||
2495c42872d4Smh27603 			    (cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) {
24967c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
24977c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
24987c478bd9Sstevel@tonic-gate 				    cmdstr))
24997c478bd9Sstevel@tonic-gate 				ret = EBUSY;
25007c478bd9Sstevel@tonic-gate 				break;
25017c478bd9Sstevel@tonic-gate 			}
2502c42872d4Smh27603 			if (cmd == PM_START_PM)
25037c478bd9Sstevel@tonic-gate 			    autopm_enabled = 1;
2504c42872d4Smh27603 			else
2505c42872d4Smh27603 			    cpupm = PM_CPUPM_ENABLE;
25067c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
25077c478bd9Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd);
25087c478bd9Sstevel@tonic-gate 			ret = 0;
25097c478bd9Sstevel@tonic-gate 			break;
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 		case PM_RESET_PM:
25127c478bd9Sstevel@tonic-gate 		case PM_STOP_PM:
2513c42872d4Smh27603 		case PM_STOP_CPUPM:
25147c478bd9Sstevel@tonic-gate 		{
25157c478bd9Sstevel@tonic-gate 			extern void pm_discard_thresholds(void);
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
2518c42872d4Smh27603 			if ((cmd == PM_STOP_PM && !autopm_enabled) ||
2519c42872d4Smh27603 			    (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
25207c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
25217c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
25227c478bd9Sstevel@tonic-gate 				    cmdstr))
25237c478bd9Sstevel@tonic-gate 				ret = EINVAL;
25247c478bd9Sstevel@tonic-gate 				break;
25257c478bd9Sstevel@tonic-gate 			}
2526c42872d4Smh27603 			if (cmd == PM_STOP_PM)
25277c478bd9Sstevel@tonic-gate 			    autopm_enabled = 0;
2528c42872d4Smh27603 			else if (cmd == PM_STOP_CPUPM)
2529c42872d4Smh27603 			    cpupm = PM_CPUPM_DISABLE;
2530c42872d4Smh27603 			else {
2531c42872d4Smh27603 			    autopm_enabled = 0;
2532c42872d4Smh27603 			    cpupm = PM_CPUPM_NOTSET;
2533c42872d4Smh27603 			}
25347c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
2535c42872d4Smh27603 
25367c478bd9Sstevel@tonic-gate 			/*
25377c478bd9Sstevel@tonic-gate 			 * bring devices to full power level, stop scan
25387c478bd9Sstevel@tonic-gate 			 */
25397c478bd9Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
25407c478bd9Sstevel@tonic-gate 			ret = 0;
2541c42872d4Smh27603 			if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
25427c478bd9Sstevel@tonic-gate 				break;
25437c478bd9Sstevel@tonic-gate 			/*
25447c478bd9Sstevel@tonic-gate 			 * Now do only PM_RESET_PM stuff.
25457c478bd9Sstevel@tonic-gate 			 */
25467c478bd9Sstevel@tonic-gate 			pm_system_idle_threshold = pm_default_idle_threshold;
2547c42872d4Smh27603 			pm_cpu_idle_threshold = 0;
25487c478bd9Sstevel@tonic-gate 			pm_discard_thresholds();
25497c478bd9Sstevel@tonic-gate 			pm_all_to_default_thresholds();
25507c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
25517c478bd9Sstevel@tonic-gate 			    NULL, NULL, PM_DEP_WAIT, NULL, 0);
25527c478bd9Sstevel@tonic-gate 			break;
25537c478bd9Sstevel@tonic-gate 		}
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 		case PM_GET_SYSTEM_THRESHOLD:
25567c478bd9Sstevel@tonic-gate 			*rval_p = pm_system_idle_threshold;
25577c478bd9Sstevel@tonic-gate 			ret = 0;
25587c478bd9Sstevel@tonic-gate 			break;
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 		case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
25617c478bd9Sstevel@tonic-gate 			*rval_p = pm_default_idle_threshold;
25627c478bd9Sstevel@tonic-gate 			ret = 0;
25637c478bd9Sstevel@tonic-gate 			break;
25647c478bd9Sstevel@tonic-gate 
2565c42872d4Smh27603 		case PM_GET_CPU_THRESHOLD:
2566c42872d4Smh27603 			*rval_p = pm_cpu_idle_threshold;
2567c42872d4Smh27603 			ret = 0;
2568c42872d4Smh27603 			break;
2569c42872d4Smh27603 
25707c478bd9Sstevel@tonic-gate 		case PM_SET_SYSTEM_THRESHOLD:
2571c42872d4Smh27603 		case PM_SET_CPU_THRESHOLD:
25727c478bd9Sstevel@tonic-gate 			if ((int)arg < 0) {
25737c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
25747c478bd9Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, (int)arg))
25757c478bd9Sstevel@tonic-gate 				ret = EINVAL;
25767c478bd9Sstevel@tonic-gate 				break;
25777c478bd9Sstevel@tonic-gate 			}
25787c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
25797c478bd9Sstevel@tonic-gate 			    (int)arg, (int)arg))
2580c42872d4Smh27603 			if (cmd == PM_SET_SYSTEM_THRESHOLD)
25817c478bd9Sstevel@tonic-gate 				pm_system_idle_threshold = (int)arg;
2582c42872d4Smh27603 			else {
2583c42872d4Smh27603 				pm_cpu_idle_threshold = (int)arg;
2584c42872d4Smh27603 			}
2585c42872d4Smh27603 			ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
2586c42872d4Smh27603 				    (void *) &cmd);
2587c42872d4Smh27603 
25887c478bd9Sstevel@tonic-gate 			ret = 0;
25897c478bd9Sstevel@tonic-gate 			break;
25907c478bd9Sstevel@tonic-gate 
25917c478bd9Sstevel@tonic-gate 		case PM_IDLE_DOWN:
25927c478bd9Sstevel@tonic-gate 			if (pm_timeout_idledown() != 0) {
25937c478bd9Sstevel@tonic-gate 				ddi_walk_devs(ddi_root_node(),
25947c478bd9Sstevel@tonic-gate 				    pm_start_idledown, (void *)PMID_IOC);
25957c478bd9Sstevel@tonic-gate 			}
25967c478bd9Sstevel@tonic-gate 			ret = 0;
25977c478bd9Sstevel@tonic-gate 			break;
25987c478bd9Sstevel@tonic-gate 
25997c478bd9Sstevel@tonic-gate 		case PM_GET_PM_STATE:
26007c478bd9Sstevel@tonic-gate 			if (autopm_enabled) {
26017c478bd9Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_ENABLED;
26027c478bd9Sstevel@tonic-gate 			} else {
26037c478bd9Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_DISABLED;
26047c478bd9Sstevel@tonic-gate 			}
26057c478bd9Sstevel@tonic-gate 			ret = 0;
26067c478bd9Sstevel@tonic-gate 			break;
2607c42872d4Smh27603 
2608c42872d4Smh27603 		case PM_GET_CPUPM_STATE:
2609c42872d4Smh27603 			if (PM_CPUPM_ENABLED)
2610c42872d4Smh27603 				*rval_p = PM_CPU_PM_ENABLED;
2611c42872d4Smh27603 			else if (PM_CPUPM_DISABLED)
2612c42872d4Smh27603 				*rval_p = PM_CPU_PM_DISABLED;
2613c42872d4Smh27603 			else
2614c42872d4Smh27603 				*rval_p = PM_CPU_PM_NOTSET;
2615c42872d4Smh27603 			ret = 0;
2616c42872d4Smh27603 			break;
26177c478bd9Sstevel@tonic-gate 		}
26187c478bd9Sstevel@tonic-gate 		break;
26197c478bd9Sstevel@tonic-gate 
26207c478bd9Sstevel@tonic-gate 	default:
26217c478bd9Sstevel@tonic-gate 		/*
26227c478bd9Sstevel@tonic-gate 		 * Internal error, invalid ioctl description
26237c478bd9Sstevel@tonic-gate 		 * force debug entry even if pm_debug not set
26247c478bd9Sstevel@tonic-gate 		 */
26257c478bd9Sstevel@tonic-gate #ifdef	DEBUG
26267c478bd9Sstevel@tonic-gate 		pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
26277c478bd9Sstevel@tonic-gate 		    pcip->str_type, cmd, pcip->name);
26287c478bd9Sstevel@tonic-gate #endif
26297c478bd9Sstevel@tonic-gate 		ASSERT(0);
26307c478bd9Sstevel@tonic-gate 		return (EIO);
26317c478bd9Sstevel@tonic-gate 	}
26327c478bd9Sstevel@tonic-gate 	ASSERT(ret != 0x0badcafe);	/* some cmd in wrong case! */
26337c478bd9Sstevel@tonic-gate 	if (dipheld) {
26347c478bd9Sstevel@tonic-gate 		ASSERT(dip);
26357c478bd9Sstevel@tonic-gate 		PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
26367c478bd9Sstevel@tonic-gate 		    "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
26377c478bd9Sstevel@tonic-gate 		PM_RELE(dip);
26387c478bd9Sstevel@tonic-gate 	}
26397c478bd9Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
26407c478bd9Sstevel@tonic-gate 	return (ret);
26417c478bd9Sstevel@tonic-gate }
2642