xref: /onnv-gate/usr/src/uts/common/io/ppm/ppm.c (revision 10488:296c315b92df)
14667Smh27603 /*
24667Smh27603  * CDDL HEADER START
34667Smh27603  *
44667Smh27603  * The contents of this file are subject to the terms of the
54667Smh27603  * Common Development and Distribution License (the "License").
64667Smh27603  * You may not use this file except in compliance with the License.
74667Smh27603  *
84667Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94667Smh27603  * or http://www.opensolaris.org/os/licensing.
104667Smh27603  * See the License for the specific language governing permissions
114667Smh27603  * and limitations under the License.
124667Smh27603  *
134667Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
144667Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154667Smh27603  * If applicable, add the following below this CDDL HEADER, with the
164667Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
174667Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
184667Smh27603  *
194667Smh27603  * CDDL HEADER END
204667Smh27603  */
214667Smh27603 /*
229694SScott.Rotondo@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234667Smh27603  * Use is subject to license terms.
244667Smh27603  */
25*10488SMark.Haywood@Sun.COM /*
26*10488SMark.Haywood@Sun.COM  * Copyright (c) 2009,  Intel Corporation.
27*10488SMark.Haywood@Sun.COM  * All Rights Reserved.
28*10488SMark.Haywood@Sun.COM  */
294667Smh27603 
304667Smh27603 
314667Smh27603 /*
324667Smh27603  * Platform Power Management master pseudo driver -
334667Smh27603  *    - attaches only  when ppm.conf file is present, indicating a
344667Smh27603  *      workstation (since Excalibur era ) that is designed to
354667Smh27603  *      be MOU-3 EPA compliant and which uses platform-specific
364667Smh27603  *	hardware to do so;
374667Smh27603  *    - this pseudo driver uses a set of simple satellite
384667Smh27603  *      device drivers responsible for accessing platform
394667Smh27603  *      specific devices to modify the registers they own.
404667Smh27603  *	ppm drivers tells these	satellite drivers what to do
414667Smh27603  *	according to using command values taken from ppm.conf.
424667Smh27603  */
434667Smh27603 #include <sys/conf.h>
444667Smh27603 #include <sys/stat.h>
454667Smh27603 #include <sys/file.h>
464667Smh27603 #include <sys/types.h>
474667Smh27603 #include <sys/param.h>
484667Smh27603 #include <sys/open.h>
494667Smh27603 #include <sys/callb.h>
504667Smh27603 #include <sys/va_list.h>
514667Smh27603 #include <sys/errno.h>
524667Smh27603 #include <sys/modctl.h>
534667Smh27603 #include <sys/sysmacros.h>
544667Smh27603 #include <sys/ddi_impldefs.h>
554667Smh27603 #include <sys/promif.h>
564667Smh27603 #include <sys/epm.h>
574667Smh27603 #include <sys/sunpm.h>
584667Smh27603 #include <sys/ppmio.h>
594667Smh27603 #include <sys/sunldi.h>
604667Smh27603 #include <sys/ppmvar.h>
614667Smh27603 #include <sys/ddi.h>
624667Smh27603 #include <sys/sunddi.h>
634667Smh27603 #include <sys/ppm_plat.h>
644667Smh27603 
654667Smh27603 /*
664667Smh27603  * Note: When pm_power() is called (directly or indirectly) to change the
674667Smh27603  * power level of a device and the call returns failure, DO NOT assume the
684667Smh27603  * level is unchanged.  Doublecheck it against ppmd->level.
694667Smh27603  */
704667Smh27603 
714667Smh27603 /*
724667Smh27603  * cb_ops
734667Smh27603  */
744667Smh27603 static int	ppm_open(dev_t *, int, int, cred_t *);
754667Smh27603 static int	ppm_close(dev_t, int, int, cred_t *);
764667Smh27603 static int	ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
774667Smh27603 
784667Smh27603 static struct cb_ops ppm_cb_ops = {
794667Smh27603 	ppm_open,		/* open	*/
804667Smh27603 	ppm_close,		/* close */
814667Smh27603 	nodev,			/* strategy */
824667Smh27603 	nodev,			/* print */
834667Smh27603 	nodev,			/* dump */
844667Smh27603 	nodev,			/* read */
854667Smh27603 	nodev,			/* write */
864667Smh27603 	ppm_ioctl,		/* ioctl */
874667Smh27603 	nodev,			/* devmap */
884667Smh27603 	nodev,			/* mmap */
894667Smh27603 	nodev,			/* segmap */
904667Smh27603 	nochpoll,		/* poll */
914667Smh27603 	ddi_prop_op,		/* prop_op */
924667Smh27603 	NULL,			/* streamtab */
934667Smh27603 	D_MP | D_NEW,		/* driver compatibility flag */
944667Smh27603 	CB_REV,			/* cb_ops revision */
954667Smh27603 	nodev,			/* async read */
964667Smh27603 	nodev			/* async write */
974667Smh27603 };
984667Smh27603 
994667Smh27603 /*
1004667Smh27603  * bus_ops
1014667Smh27603  */
1024667Smh27603 static int	ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
1034667Smh27603     void *);
1044667Smh27603 
1054667Smh27603 static struct bus_ops ppm_bus_ops = {
1064667Smh27603 	BUSO_REV,		/* busops_rev		*/
1074667Smh27603 	0,			/* bus_map		*/
1084667Smh27603 	0,			/* bus_get_intrspec	*/
1094667Smh27603 	0,			/* bus_add_intrspec	*/
1104667Smh27603 	0,			/* bus_remove_intrspec	*/
1114667Smh27603 	0,			/* bus_map_fault	*/
1124667Smh27603 	ddi_no_dma_map,		/* bus_dma_map		*/
1134667Smh27603 	ddi_no_dma_allochdl,	/* bus_dma_allochdl	*/
1144667Smh27603 	NULL,			/* bus_dma_freehdl	*/
1154667Smh27603 	NULL,			/* bus_dma_bindhdl	*/
1164667Smh27603 	NULL,			/* bus_dma_unbindhdl	*/
1174667Smh27603 	NULL,			/* bus_dma_flush	*/
1184667Smh27603 	NULL,			/* bus_dma_win		*/
1194667Smh27603 	NULL,			/* bus_dma_ctl		*/
1204667Smh27603 	ppm_ctlops,		/* bus_ctl		*/
1214667Smh27603 	0,			/* bus_prop_op		*/
1224667Smh27603 	0,			/* bus_get_eventcookie	*/
1234667Smh27603 	0,			/* bus_add_eventcall	*/
1244667Smh27603 	0,			/* bus_remove_eventcall	*/
1254667Smh27603 	0,			/* bus_post_event	*/
1264667Smh27603 	0			/* bus_intr_ctl		*/
1274667Smh27603 };
1284667Smh27603 
1294667Smh27603 /*
1304667Smh27603  * dev_ops
1314667Smh27603  */
1324667Smh27603 static int	ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1334667Smh27603 static int	ppm_attach(dev_info_t *, ddi_attach_cmd_t);
1344667Smh27603 static int	ppm_detach(dev_info_t *, ddi_detach_cmd_t);
1354667Smh27603 
1364667Smh27603 static struct dev_ops ppm_ops = {
1374667Smh27603 	DEVO_REV,		/* devo_rev */
1384667Smh27603 	0,			/* refcnt */
1394667Smh27603 	ppm_getinfo,		/* info */
1404667Smh27603 	nulldev,		/* identify */
1414667Smh27603 	nulldev,		/* probe */
1424667Smh27603 	ppm_attach,		/* attach */
1434667Smh27603 	ppm_detach,		/* detach */
1444667Smh27603 	nodev,			/* reset */
1454667Smh27603 	&ppm_cb_ops,		/* cb_ops */
1464667Smh27603 	&ppm_bus_ops,		/* bus_ops */
1477656SSherry.Moore@Sun.COM 	nulldev,		/* power */
1487656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1494667Smh27603 };
1504667Smh27603 
1514667Smh27603 extern struct mod_ops mod_driverops;
1524667Smh27603 
1534667Smh27603 static struct modldrv modldrv = {
1544667Smh27603 	&mod_driverops,
1557656SSherry.Moore@Sun.COM 	"platform pm driver",
1564667Smh27603 	&ppm_ops
1574667Smh27603 };
1584667Smh27603 
1594667Smh27603 static struct modlinkage modlinkage = {
1604667Smh27603 	MODREV_1,
1614667Smh27603 	&modldrv,
1624667Smh27603 	NULL
1634667Smh27603 };
1644667Smh27603 
1654667Smh27603 /*
1664667Smh27603  * Global data structure and variables
1674667Smh27603  */
1684667Smh27603 int	ppm_inst = -1;
1694667Smh27603 void	*ppm_statep;
1704667Smh27603 ppm_domain_t *ppm_domain_p;
1714667Smh27603 callb_id_t   *ppm_cprcb_id;
1724667Smh27603 static kmutex_t ppm_cpr_window_lock;	/* guard ppm_cpr_window_flag */
1734667Smh27603 static	boolean_t ppm_cpr_window_flag;	/* set indicating chpt-resume period */
1744667Smh27603 
1754667Smh27603 /* LED actions */
1764667Smh27603 #define	PPM_LED_SOLIDON		0
1774667Smh27603 #define	PPM_LED_BLINKING	1
1784667Smh27603 
1794667Smh27603 /*
1804667Smh27603  * Debug
1814667Smh27603  */
1824667Smh27603 #ifdef	DEBUG
1834667Smh27603 uint_t	ppm_debug = 0;
1844667Smh27603 #endif
1854667Smh27603 
1864667Smh27603 /*
1874667Smh27603  * Local function prototypes and data
1884667Smh27603  */
1894667Smh27603 static boolean_t	ppm_cpr_callb(void *, int);
1904667Smh27603 static int		ppm_fetset(ppm_domain_t *, uint8_t);
1914667Smh27603 static int		ppm_fetget(ppm_domain_t *, uint8_t *);
1924667Smh27603 static int		ppm_gpioset(ppm_domain_t *, int);
1934667Smh27603 static int		ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
1944667Smh27603 static int		ppm_manage_pci(dev_info_t *, power_req_t *, int *);
1954667Smh27603 static int		ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
1964667Smh27603 static int		ppm_manage_fet(dev_info_t *, power_req_t *, int *);
1974667Smh27603 static void		ppm_manage_led(int);
1984667Smh27603 static void		ppm_set_led(ppm_domain_t *, int);
1994667Smh27603 static void		ppm_blink_led(void *);
2004667Smh27603 static void		ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
2014667Smh27603 static int		ppm_set_level(ppm_dev_t *, int, int, boolean_t);
2024667Smh27603 static int		ppm_change_power_level(ppm_dev_t *, int, int);
2034667Smh27603 static int		ppm_record_level_change(ppm_dev_t *, int, int);
2044667Smh27603 static int		ppm_switch_clock(ppm_domain_t *, int);
2054667Smh27603 static int		ppm_pcie_pwr(ppm_domain_t *, int);
2064667Smh27603 static int		ppm_power_up_domain(dev_info_t *dip);
2074667Smh27603 static int		ppm_power_down_domain(dev_info_t *dip);
2084667Smh27603 
2094667Smh27603 int
_init(void)2104667Smh27603 _init(void)
2114667Smh27603 {
2124667Smh27603 	if (ddi_soft_state_init(
2135295Srandyf 	    &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
2145295Srandyf 		PPMD(D_INIT, ("ppm: soft state init\n"))
2154667Smh27603 		return (DDI_FAILURE);
2165295Srandyf 	}
2174667Smh27603 
2184667Smh27603 	if (mod_install(&modlinkage) != DDI_SUCCESS) {
2194667Smh27603 		ddi_soft_state_fini(&ppm_statep);
2204667Smh27603 		return (DDI_FAILURE);
2214667Smh27603 	}
2224667Smh27603 	return (DDI_SUCCESS);
2234667Smh27603 }
2244667Smh27603 
2254667Smh27603 
2264667Smh27603 int
_fini(void)2274667Smh27603 _fini(void)
2284667Smh27603 {
2295295Srandyf 	int error;
2305295Srandyf 
2315295Srandyf 	if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
2325295Srandyf 		ddi_soft_state_fini(&ppm_statep);
2335295Srandyf 
2345295Srandyf 	return (error);
2354667Smh27603 }
2364667Smh27603 
2374667Smh27603 
2384667Smh27603 int
_info(struct modinfo * modinfop)2394667Smh27603 _info(struct modinfo *modinfop)
2404667Smh27603 {
2414667Smh27603 	return (mod_info(&modlinkage, modinfop));
2424667Smh27603 }
2434667Smh27603 
2444667Smh27603 
2454667Smh27603 /* ARGSUSED */
2464667Smh27603 int
ppm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2474667Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2484667Smh27603 {
2494667Smh27603 	struct ppm_unit *unitp;
2504667Smh27603 	dev_t	dev;
2514667Smh27603 	int	instance;
2524667Smh27603 	int	rval;
2534667Smh27603 
2544667Smh27603 	if (ppm_inst == -1)
2554667Smh27603 		return (DDI_FAILURE);
2564667Smh27603 
2574667Smh27603 	switch (cmd) {
2584667Smh27603 	case DDI_INFO_DEVT2DEVINFO:
2594667Smh27603 		if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
2604667Smh27603 			*resultp = unitp->dip;
2614667Smh27603 			rval = DDI_SUCCESS;
2624667Smh27603 		} else
2634667Smh27603 			rval = DDI_FAILURE;
2644667Smh27603 
2654667Smh27603 		return (rval);
2664667Smh27603 
2674667Smh27603 	case DDI_INFO_DEVT2INSTANCE:
2684667Smh27603 		dev = (dev_t)arg;
2694667Smh27603 		instance = getminor(dev);
2704667Smh27603 		*resultp = (void *)(uintptr_t)instance;
2714667Smh27603 		return (DDI_SUCCESS);
2724667Smh27603 
2734667Smh27603 	default:
2744667Smh27603 		return (DDI_FAILURE);
2754667Smh27603 	}
2764667Smh27603 }
2774667Smh27603 
2784667Smh27603 
2794667Smh27603 /*
2804667Smh27603  * attach(9E)
2814667Smh27603  */
2824667Smh27603 static int
ppm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2834667Smh27603 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2844667Smh27603 {
2854667Smh27603 	ppm_unit_t *unitp;
2864667Smh27603 	int ret;
2874667Smh27603 #ifdef	DEBUG
2884667Smh27603 	char *str = "ppm_attach";
2894667Smh27603 #endif
2904667Smh27603 
2914667Smh27603 
2924667Smh27603 	switch (cmd) {
2934667Smh27603 	case DDI_ATTACH:
2944667Smh27603 		PPMD(D_ATTACH, ("%s: attaching ...\n", str))
2954667Smh27603 		break;
2964667Smh27603 
2974667Smh27603 	case DDI_RESUME:
2984667Smh27603 		PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
2994667Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3004667Smh27603 		mutex_enter(&unitp->lock);
3014667Smh27603 		unitp->states &= ~PPM_STATE_SUSPENDED;
3024667Smh27603 		mutex_exit(&unitp->lock);
3034667Smh27603 		return (DDI_SUCCESS);
3044667Smh27603 
3054667Smh27603 	default:
3064667Smh27603 		cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
3074667Smh27603 		    cmd, (void *)dip);
3084667Smh27603 		return (DDI_FAILURE);
3094667Smh27603 	}
3104667Smh27603 
3114667Smh27603 	if (ppm_inst != -1) {
3124667Smh27603 		PPMD(D_ATTACH, ("%s: Already attached !", str))
3134667Smh27603 		return (DDI_FAILURE);
3144667Smh27603 	}
3154667Smh27603 
3164667Smh27603 	ppm_inst = ddi_get_instance(dip);
3174667Smh27603 	if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
3184667Smh27603 		PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
3194667Smh27603 		return (DDI_FAILURE);
3204667Smh27603 	}
3214667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3224667Smh27603 
3234667Smh27603 	ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
3244667Smh27603 	    "ddi_ppm", 0);
3254667Smh27603 	if (ret != DDI_SUCCESS) {
3264667Smh27603 		PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
3274667Smh27603 		goto fail1;
3284667Smh27603 	}
3294667Smh27603 
3304667Smh27603 	unitp->dip = dip;
3314667Smh27603 	mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
3324667Smh27603 
3334667Smh27603 	/*
3344667Smh27603 	 * read ppm.conf, construct ppm_domain data structure and
3354667Smh27603 	 * their sub data structure.
3364667Smh27603 	 */
3374667Smh27603 	if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
3384667Smh27603 		goto fail2;
3394667Smh27603 
3404667Smh27603 	/*
3414667Smh27603 	 * walk down ppm domain control from each domain, initialize
3424667Smh27603 	 * domain control orthogonal function call handle
3434667Smh27603 	 */
3444667Smh27603 	ppm_init_cb(dip);
3454667Smh27603 
3464667Smh27603 	if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
3474667Smh27603 		cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
3484667Smh27603 		goto fail2;
3494667Smh27603 	}
3504667Smh27603 
3514667Smh27603 	mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
3524667Smh27603 	ppm_cpr_window_flag = B_FALSE;
3534667Smh27603 	ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
3544667Smh27603 	    CB_CL_CPR_PM, "ppm_cpr");
3554667Smh27603 
3564667Smh27603 #if defined(__x86)
3574667Smh27603 	/*
3584667Smh27603 	 * Register callback so that once CPUs have been added to
359*10488SMark.Haywood@Sun.COM 	 * the device tree, ppm CPU domains can be allocated using ACPI
3604667Smh27603 	 * data.
3614667Smh27603 	 */
362*10488SMark.Haywood@Sun.COM 	cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
363*10488SMark.Haywood@Sun.COM 	cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
3644667Smh27603 
3654667Smh27603 	/*
3664667Smh27603 	 * Register callback so that whenever max speed throttle requests
3674667Smh27603 	 * are received, ppm can redefine the high power level for
3684667Smh27603 	 * all CPUs in the domain.
3694667Smh27603 	 */
3704667Smh27603 	cpupm_redefine_topspeed = ppm_redefine_topspeed;
3714667Smh27603 #endif
3724667Smh27603 
3734667Smh27603 	ddi_report_dev(dip);
3744667Smh27603 	return (DDI_SUCCESS);
3754667Smh27603 
3764667Smh27603 fail2:
3774667Smh27603 	ddi_remove_minor_node(dip, "ddi_ppm");
3784667Smh27603 	mutex_destroy(&unitp->lock);
3794667Smh27603 fail1:
3804667Smh27603 	ddi_soft_state_free(ppm_statep, ppm_inst);
3814667Smh27603 	ppm_inst = -1;
3824667Smh27603 	return (DDI_FAILURE);
3834667Smh27603 }
3844667Smh27603 
3854667Smh27603 
3864667Smh27603 /* ARGSUSED */
3874667Smh27603 static int
ppm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3884667Smh27603 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3894667Smh27603 {
3904667Smh27603 	ppm_unit_t *unitp;
3914667Smh27603 #ifdef	DEBUG
3924667Smh27603 	char *str = "ppm_detach";
3934667Smh27603 #endif
3944667Smh27603 
3954667Smh27603 	switch (cmd) {
3964667Smh27603 	case DDI_DETACH:
3974667Smh27603 		PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
3984667Smh27603 		return (DDI_FAILURE);
3994667Smh27603 
4004667Smh27603 	case DDI_SUSPEND:
4014667Smh27603 		PPMD(D_DETACH, ("%s: suspending ...\n", str))
4024667Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
4034667Smh27603 		mutex_enter(&unitp->lock);
4044667Smh27603 		unitp->states |= PPM_STATE_SUSPENDED;
4054667Smh27603 		mutex_exit(&unitp->lock);
4064667Smh27603 
4074667Smh27603 		/*
4084667Smh27603 		 * Suspend requires that timeout callouts to be canceled.
4094667Smh27603 		 * Turning off the LED blinking will cancel the timeout.
4104667Smh27603 		 */
4114667Smh27603 		ppm_manage_led(PPM_LED_SOLIDON);
4124667Smh27603 		return (DDI_SUCCESS);
4134667Smh27603 
4144667Smh27603 	default:
4154667Smh27603 		cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
4164667Smh27603 		    cmd, (void *)dip);
4174667Smh27603 		return (DDI_FAILURE);
4184667Smh27603 	}
4194667Smh27603 }
4204667Smh27603 
4214667Smh27603 
4224667Smh27603 /* ARGSUSED */
4234667Smh27603 int
ppm_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)4244667Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
4254667Smh27603 {
4264667Smh27603 	if (otyp != OTYP_CHR)
4274667Smh27603 		return (EINVAL);
4284667Smh27603 	PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
4294667Smh27603 	    (void *)devp, flag, otyp))
4304667Smh27603 	return (0);
4314667Smh27603 }
4324667Smh27603 
4334667Smh27603 
4344667Smh27603 /* ARGSUSED */
4354667Smh27603 int
ppm_close(dev_t dev,int flag,int otyp,cred_t * credp)4364667Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
4374667Smh27603 {
4384667Smh27603 	PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
4394667Smh27603 	    dev, flag, otyp))
4404667Smh27603 	return (0);
4414667Smh27603 }
4424667Smh27603 
4434667Smh27603 
4444667Smh27603 /* ARGSUSED */
4454667Smh27603 int
ppm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)4464667Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
4474667Smh27603     int *rval_p)
4484667Smh27603 {
4494667Smh27603 #ifdef DEBUG
4504667Smh27603 	char *str = "ppm_ioctl";
4514667Smh27603 #endif
4524667Smh27603 	ppm_domain_t *domp = NULL;
4534667Smh27603 	uint8_t level, lvl;
4544667Smh27603 	int ret = 0;
4554667Smh27603 
4564667Smh27603 	PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
4574667Smh27603 	    str, dev, cmd, mode))
4584667Smh27603 
4594667Smh27603 	switch (cmd) {
4604667Smh27603 	case PPMGET_DPWR:
4614667Smh27603 	{
4624667Smh27603 		STRUCT_DECL(ppm_dpwr, dpwr);
4634667Smh27603 		struct ppm_unit *unitp;
4644667Smh27603 		char *domain;
4654667Smh27603 
4664667Smh27603 		STRUCT_INIT(dpwr, mode);
4674667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
4684667Smh27603 		    STRUCT_SIZE(dpwr), mode);
4694667Smh27603 		if (ret != 0)
4704667Smh27603 			return (EFAULT);
4714667Smh27603 
4724667Smh27603 		/* copyin domain name */
4734667Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
4744667Smh27603 		ret = copyinstr(
4754667Smh27603 		    STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
4764667Smh27603 		if (ret != 0) {
4774667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
4784667Smh27603 			    str, __LINE__))
4794667Smh27603 			ret = EFAULT;
4804667Smh27603 			goto err_dpwr;
4814667Smh27603 		}
4824667Smh27603 
4834667Smh27603 		/* locate domain */
4844667Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
4854667Smh27603 			PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
4864667Smh27603 			ret = ENODEV;
4874667Smh27603 			goto err_dpwr;
4884667Smh27603 		}
4894667Smh27603 
4904667Smh27603 		switch (domp->model) {
4914667Smh27603 		case PPMD_FET:	/* report power fet ON or OFF */
4924667Smh27603 			if ((ret = ppm_fetget(domp, &lvl)) != 0) {
4934667Smh27603 				ret = EIO;
4944667Smh27603 				goto err_dpwr;
4954667Smh27603 			}
4964667Smh27603 			level = (lvl == PPMD_ON) ?
4974667Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
4984667Smh27603 			break;
4994667Smh27603 
5004667Smh27603 		case PPMD_PCI:	/* report pci slot clock ON or OFF */
5014667Smh27603 		case PPMD_PCI_PROP:
5024667Smh27603 		case PPMD_PCIE:
5034667Smh27603 			level = (domp->status == PPMD_ON) ?
5044667Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
5054667Smh27603 			break;
5064667Smh27603 
5074667Smh27603 		case PPMD_LED:	/* report LED blinking or solid on */
5084667Smh27603 
5094667Smh27603 			unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
5104667Smh27603 			if (unitp->led_tid == 0)
5114667Smh27603 				level = PPMIO_LED_SOLIDON;
5124667Smh27603 			else
5134667Smh27603 				level = PPMIO_LED_BLINKING;
5144667Smh27603 			break;
5154667Smh27603 
5164667Smh27603 		case PPMD_CPU:	/* report cpu speed divisor */
5174667Smh27603 			level = domp->devlist->level;
5184667Smh27603 			break;
5194667Smh27603 
5204667Smh27603 		default:
5214667Smh27603 			ret = EINVAL;
5224667Smh27603 			goto err_dpwr;
5234667Smh27603 		}
5244667Smh27603 
5254667Smh27603 		STRUCT_FSET(dpwr, level, level);
5264667Smh27603 		ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
5274667Smh27603 		    STRUCT_SIZE(dpwr), mode);
5284667Smh27603 		if (ret != 0) {
5294667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
5304667Smh27603 			    str, __LINE__))
5314667Smh27603 			ret = EFAULT;
5324667Smh27603 		}
5334667Smh27603 err_dpwr:
5344667Smh27603 		kmem_free(domain, MAXNAMELEN);
5354667Smh27603 
5364667Smh27603 		break;
5374667Smh27603 	}
5384667Smh27603 
5394667Smh27603 	case PPMGET_DOMBYDEV:
5404667Smh27603 	{
5414667Smh27603 		STRUCT_DECL(ppm_bydev, bydev);
5424667Smh27603 		char *path = NULL;
5434667Smh27603 		size_t   size, l;
5444667Smh27603 
5454667Smh27603 		STRUCT_INIT(bydev, mode);
5464667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
5474667Smh27603 		    STRUCT_SIZE(bydev), mode);
5484667Smh27603 		if (ret != 0)
5494667Smh27603 			return (EFAULT);
5504667Smh27603 
5514667Smh27603 		/* copyin .path */
5524667Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5534667Smh27603 		ret = copyinstr(
5544667Smh27603 		    STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
5554667Smh27603 		if (ret != 0) {
5564667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
5574667Smh27603 			    str, __LINE__))
5584667Smh27603 			kmem_free(path, MAXPATHLEN);
5594667Smh27603 			return (EFAULT);
5604667Smh27603 		}
5614667Smh27603 
5624667Smh27603 		/* so far we have up to one domain for a given device */
5634667Smh27603 		size = STRUCT_FGET(bydev, size);
5644667Smh27603 		domp = ppm_get_domain_by_dev(path);
5654667Smh27603 		kmem_free(path, MAXPATHLEN);
5664667Smh27603 		if (domp != NULL) {
5674667Smh27603 			l = strlen(domp->name) + 1;
5684667Smh27603 			if (l > size) {
5694667Smh27603 				PPMD(D_IOCTL, ("%s: buffer too small\n", str))
5704667Smh27603 				return ((size == 0) ? EINVAL : EFAULT);
5714667Smh27603 			}
5724667Smh27603 		} else	/* no domain found to be associated with given device */
5734667Smh27603 			return (ENODEV);
5744667Smh27603 
5754667Smh27603 		ret = copyoutstr(
5764667Smh27603 		    domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
5774667Smh27603 		if (ret != 0) {
5784667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
5794667Smh27603 			    " \n", str, __LINE__))
5804667Smh27603 			return (EFAULT);
5814667Smh27603 		}
5824667Smh27603 
5834667Smh27603 		break;
5844667Smh27603 	}
5854667Smh27603 
5864667Smh27603 
5874667Smh27603 	case PPMGET_DEVBYDOM:
5884667Smh27603 	{
5894667Smh27603 		STRUCT_DECL(ppm_bydom, bydom);
5904667Smh27603 		char *domain = NULL;
5914667Smh27603 		char *devlist = NULL;
5924667Smh27603 		ppm_dev_t *ppmd;
5934667Smh27603 		dev_info_t *odip = NULL;
5944667Smh27603 		char *s, *d;
5954667Smh27603 		size_t  size, l;
5964667Smh27603 
5974667Smh27603 		STRUCT_INIT(bydom, mode);
5984667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
5994667Smh27603 		    STRUCT_SIZE(bydom), mode);
6004667Smh27603 		if (ret != 0)
6014667Smh27603 			return (EFAULT);
6024667Smh27603 
6034667Smh27603 		/* copyin .domain */
6044667Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
6054667Smh27603 		ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
6064667Smh27603 		    MAXNAMELEN, NULL);
6074667Smh27603 		if (ret != 0) {
6084667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
6094667Smh27603 			    str, __LINE__))
6104667Smh27603 			ret = EFAULT;
6114667Smh27603 			goto err_bydom;
6124667Smh27603 		}
6134667Smh27603 
6144667Smh27603 		/* locate domain */
6154667Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
6164667Smh27603 			ret = ENODEV;
6174667Smh27603 			goto err_bydom;
6184667Smh27603 		}
6194667Smh27603 
6204667Smh27603 		l = 0;
6214667Smh27603 		if ((size = STRUCT_FGET(bydom, size)) == 0)
6224667Smh27603 			ret = EINVAL;
6234667Smh27603 		else
6244667Smh27603 			if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
6254667Smh27603 				ret = EFAULT;
6264667Smh27603 		if (ret != 0)
6274667Smh27603 			goto err_bydom;
6284667Smh27603 
6294667Smh27603 		for (ppmd = domp->devlist; ppmd;
6304667Smh27603 		    odip = ppmd->dip, ppmd = ppmd->next) {
6314667Smh27603 
6324667Smh27603 			if (ppmd->dip == odip)
6334667Smh27603 				continue;
6344667Smh27603 			if (ppmd != domp->devlist)
6354667Smh27603 				*d++ = ' ';
6364667Smh27603 
6374667Smh27603 			l += strlen(ppmd->path) + 1;
6384667Smh27603 			if (l > size) {
6394667Smh27603 				PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
6404667Smh27603 				ret = EFAULT;
6414667Smh27603 				goto err_bydom;
6424667Smh27603 			}
6434667Smh27603 
6444667Smh27603 			for (s = ppmd->path; *s != 0; )
6454667Smh27603 				*d++ = *s++;
6464667Smh27603 		}
6474667Smh27603 		*d = 0;
6484667Smh27603 
6494667Smh27603 		if (*devlist == 0)
6504667Smh27603 			goto err_bydom;
6514667Smh27603 
6524667Smh27603 		ret = copyoutstr(
6534667Smh27603 		    devlist, STRUCT_FGETP(bydom, devlist), l, &l);
6544667Smh27603 		if (ret != 0) {
6554667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
6564667Smh27603 			    " \n", str, __LINE__))
6574667Smh27603 			ret = EFAULT;
6584667Smh27603 		}
6594667Smh27603 
6604667Smh27603 err_bydom:
6614667Smh27603 		if (devlist)
6624667Smh27603 			kmem_free(devlist, size);
6634667Smh27603 		if (domain)
6644667Smh27603 			kmem_free(domain, MAXNAMELEN);
6654667Smh27603 
6664667Smh27603 		break;
6674667Smh27603 	}
6684667Smh27603 
6694667Smh27603 #if defined(__x86)
6704667Smh27603 	/*
6714667Smh27603 	 * Note that these two ioctls exist for test purposes only.
6724667Smh27603 	 * Unfortunately, there really isn't any other good way of
6734667Smh27603 	 * unit testing the dynamic redefinition of the top speed as it
6744667Smh27603 	 * usually occurs due to environmental conditions.
6754667Smh27603 	 */
6764667Smh27603 	case PPMGET_NORMAL:
6774667Smh27603 	case PPMSET_NORMAL:
6784667Smh27603 	{
6794667Smh27603 		STRUCT_DECL(ppm_norm, norm);
6804667Smh27603 		char *path = NULL;
6814667Smh27603 		struct pm_component *dcomps;
6824667Smh27603 		struct pm_comp *pm_comp;
6834667Smh27603 		ppm_dev_t *ppmd;
6844667Smh27603 		int i;
6854667Smh27603 
6864667Smh27603 		STRUCT_INIT(norm, mode);
6874667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
6885295Srandyf 		    STRUCT_SIZE(norm), mode);
6894667Smh27603 		if (ret != 0)
6904667Smh27603 			return (EFAULT);
6914667Smh27603 
6924667Smh27603 		/* copyin .path */
6934667Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
6944667Smh27603 		ret = copyinstr(
6954667Smh27603 		    STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
6964667Smh27603 		if (ret != 0) {
6974667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
6984667Smh27603 			    str, __LINE__))
6994667Smh27603 			kmem_free(path, MAXPATHLEN);
7004667Smh27603 			return (EFAULT);
7014667Smh27603 		}
7024667Smh27603 
7034667Smh27603 		domp = ppm_get_domain_by_dev(path);
7044667Smh27603 		kmem_free(path, MAXPATHLEN);
7054667Smh27603 
7064667Smh27603 		if (domp == NULL)
7074667Smh27603 			return (ENODEV);
7084667Smh27603 
7094667Smh27603 		ppmd = domp->devlist;
7104667Smh27603 		if (cmd == PPMSET_NORMAL) {
7114667Smh27603 			if (domp->model != PPMD_CPU)
7124667Smh27603 				return (EINVAL);
7134667Smh27603 			level = STRUCT_FGET(norm, norm);
7144667Smh27603 			dcomps = DEVI(ppmd->dip)->devi_pm_components;
7154667Smh27603 			pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
7164667Smh27603 			for (i = pm_comp->pmc_numlevels; i > 0; i--) {
7174667Smh27603 				if (pm_comp->pmc_lvals[i-1] == level)
7184667Smh27603 					break;
7194667Smh27603 			}
7204667Smh27603 			if (i == 0)
7214667Smh27603 				return (EINVAL);
7224667Smh27603 
7234667Smh27603 			ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
7244667Smh27603 		}
7254667Smh27603 
7264667Smh27603 		level = pm_get_normal_power(ppmd->dip, 0);
7274667Smh27603 
7284667Smh27603 		STRUCT_FSET(norm, norm, level);
7294667Smh27603 		ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
7304667Smh27603 		    STRUCT_SIZE(norm), mode);
7314667Smh27603 		if (ret != 0) {
7324667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
7334667Smh27603 			    str, __LINE__))
7344667Smh27603 			ret = EFAULT;
7354667Smh27603 		}
7364667Smh27603 		break;
7374667Smh27603 	}
7384667Smh27603 #endif
7394667Smh27603 	default:
7404667Smh27603 		PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
7414667Smh27603 		return (EINVAL);
7424667Smh27603 	}
7434667Smh27603 
7444667Smh27603 	return (ret);
7454667Smh27603 }
7464667Smh27603 
7474667Smh27603 
7489694SScott.Rotondo@Sun.COM static int	ppm_manage_sx(s3a_t *, int);
7499694SScott.Rotondo@Sun.COM static int	ppm_search_list(pm_searchargs_t *);
7509694SScott.Rotondo@Sun.COM 
7514667Smh27603 /*
7524667Smh27603  * interface between pm framework and ppm driver
7534667Smh27603  */
7544667Smh27603 /* ARGSUSED */
7554667Smh27603 static int
ppm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)7564667Smh27603 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
7574667Smh27603     ddi_ctl_enum_t ctlop, void *arg, void *result)
7584667Smh27603 {
7594667Smh27603 	power_req_t	*reqp = (power_req_t *)arg;
7604667Smh27603 	ppm_unit_t	*unitp;
7614667Smh27603 	ppm_domain_t	*domp;
7624667Smh27603 	ppm_dev_t	*ppmd;
7634667Smh27603 	char		path[MAXNAMELEN];
7644667Smh27603 	ppm_owned_t	*owned;
7654667Smh27603 	int		mode;
7664667Smh27603 	int		ret = DDI_SUCCESS;
7675295Srandyf 	int 		*res = (int *)result;
7685295Srandyf 	s3a_t s3args;
7694667Smh27603 
7704667Smh27603 #ifdef DEBUG
7714667Smh27603 	char	*str = "ppm_ctlops";
7724667Smh27603 	int	mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
7734667Smh27603 	char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
7744667Smh27603 	if (mask && ctlstr)
7754667Smh27603 		PPMD(mask, ("%s: %s, %s\n",
7764667Smh27603 		    str, ddi_binding_name(rdip), ctlstr))
7774667Smh27603 #endif
7784667Smh27603 
7795295Srandyf 	if (ctlop != DDI_CTLOPS_POWER) {
7804667Smh27603 		return (DDI_FAILURE);
7815295Srandyf 	}
7824667Smh27603 
7834667Smh27603 	unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
7844667Smh27603 
7854667Smh27603 	switch (reqp->request_type) {
7864667Smh27603 
7874667Smh27603 	/* attempt to blink led if indeed all at lowest */
7884667Smh27603 	case PMR_PPM_ALL_LOWEST:
7894667Smh27603 		mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
7904667Smh27603 		if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
7914667Smh27603 			ppm_manage_led(PPM_LED_BLINKING);
7924667Smh27603 		else
7934667Smh27603 			ppm_manage_led(PPM_LED_SOLIDON);
7944667Smh27603 		return (DDI_SUCCESS);
7954667Smh27603 
7964667Smh27603 	/* undo the claiming of 'rdip' at attach time */
7974667Smh27603 	case PMR_PPM_POST_DETACH:
7984667Smh27603 		ASSERT(reqp->req.ppm_set_power_req.who == rdip);
7994667Smh27603 		mutex_enter(&unitp->lock);
8004667Smh27603 		if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
8014667Smh27603 		    (PPM_GET_PRIVATE(rdip) == NULL)) {
8024667Smh27603 			mutex_exit(&unitp->lock);
8034667Smh27603 			return (DDI_FAILURE);
8044667Smh27603 		}
8054667Smh27603 		mutex_exit(&unitp->lock);
8064667Smh27603 		ppm_rem_dev(rdip);
8074667Smh27603 		return (DDI_SUCCESS);
8084667Smh27603 
8094667Smh27603 	/* chance to adjust pwr_cnt if resume is about to power up rdip */
8104667Smh27603 	case PMR_PPM_PRE_RESUME:
8114667Smh27603 		ppm_svc_resume_ctlop(rdip, reqp);
8124667Smh27603 		return (DDI_SUCCESS);
8134667Smh27603 
8144667Smh27603 	/*
8154667Smh27603 	 * synchronizing, so that only the owner of the power lock is
8164667Smh27603 	 * permitted to change device and component's power level.
8174667Smh27603 	 */
8184667Smh27603 	case PMR_PPM_UNLOCK_POWER:
8194667Smh27603 	case PMR_PPM_TRY_LOCK_POWER:
8204667Smh27603 	case PMR_PPM_LOCK_POWER:
8214667Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
8224667Smh27603 		if (ppmd)
8234667Smh27603 			domp = ppmd->domp;
8244667Smh27603 		else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
8254667Smh27603 			domp = ppm_lookup_dev(rdip);
8264667Smh27603 			ASSERT(domp);
8274667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
8284667Smh27603 		}
8294667Smh27603 
8304667Smh27603 		PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
8314667Smh27603 		    (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
8324667Smh27603 		    ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
8334667Smh27603 
8344667Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
8354667Smh27603 			ppm_lock_all(domp, reqp, result);
8364667Smh27603 		else
8374667Smh27603 			ppm_lock_one(ppmd, reqp, result);
8384667Smh27603 		return (DDI_SUCCESS);
8394667Smh27603 
8404667Smh27603 	case PMR_PPM_POWER_LOCK_OWNER:
8414667Smh27603 		ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
8424667Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
8434667Smh27603 		if (ppmd)
8444667Smh27603 			domp = ppmd->domp;
8454667Smh27603 		else {
8464667Smh27603 			domp = ppm_lookup_dev(rdip);
8474667Smh27603 			ASSERT(domp);
8484667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
8494667Smh27603 		}
8504667Smh27603 
8514667Smh27603 		/*
8524667Smh27603 		 * In case of LOCK_ALL, effective owner of the power lock
8534667Smh27603 		 * is the owner of the domain lock. otherwise, it is the owner
8544667Smh27603 		 * of the power lock.
8554667Smh27603 		 */
8564667Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
8574667Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
8584667Smh27603 			    mutex_owner(&domp->lock);
8594667Smh27603 		else {
8604667Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
8614667Smh27603 			    DEVI(rdip)->devi_busy_thread;
8624667Smh27603 		}
8634667Smh27603 		return (DDI_SUCCESS);
8644667Smh27603 
8654667Smh27603 	case PMR_PPM_INIT_CHILD:
8664667Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
8674667Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
8684667Smh27603 			return (DDI_SUCCESS);
8694667Smh27603 
8704667Smh27603 		/*
8714667Smh27603 		 * We keep track of power-manageable devices starting with
8724667Smh27603 		 * initialization process.  The initializing flag remains
8734667Smh27603 		 * set until it is cleared by ppm_add_dev().  Power management
8744667Smh27603 		 * policy for some domains are affected even during device
8754667Smh27603 		 * initialization.  For example, PCI domains should leave
8764667Smh27603 		 * their clock running meanwhile a device in that domain
8774667Smh27603 		 * is initializing.
8784667Smh27603 		 */
8794667Smh27603 		mutex_enter(&domp->lock);
8804667Smh27603 		owned = ppm_add_owned(rdip, domp);
8814667Smh27603 		ASSERT(owned->initializing == 0);
8824667Smh27603 		owned->initializing = 1;
8834667Smh27603 
8844667Smh27603 		if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
8854667Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
8864667Smh27603 			if (ret == DDI_SUCCESS)
8874667Smh27603 				domp->dflags |= PPMD_INITCHILD_CLKON;
8884667Smh27603 		}
8894667Smh27603 		mutex_exit(&domp->lock);
8904667Smh27603 		return (ret);
8914667Smh27603 
8924667Smh27603 	case PMR_PPM_POST_ATTACH:
8934667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
8944667Smh27603 		domp = ppm_lookup_dev(rdip);
8954667Smh27603 		ASSERT(domp);
8964667Smh27603 		ASSERT(domp->status == PPMD_ON);
8974667Smh27603 		if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
8984667Smh27603 			/*
8994667Smh27603 			 * call ppm_get_dev, which will increment the
9004667Smh27603 			 * domain power count by the right number.
9014667Smh27603 			 * Undo the power count increment, done in PRE_PROBE.
9024667Smh27603 			 */
9034667Smh27603 			if (PM_GET_PM_INFO(rdip))
9044667Smh27603 				ppmd = ppm_get_dev(rdip, domp);
9054667Smh27603 			mutex_enter(&domp->lock);
9064667Smh27603 			ASSERT(domp->pwr_cnt > 0);
9074667Smh27603 			domp->pwr_cnt--;
9084667Smh27603 			mutex_exit(&domp->lock);
9094667Smh27603 			return (DDI_SUCCESS);
9104667Smh27603 		}
9114667Smh27603 
9124667Smh27603 		ret = ppm_power_down_domain(rdip);
9134667Smh27603 		/* FALLTHROUGH */
9144667Smh27603 	case PMR_PPM_UNINIT_CHILD:
9154667Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
9164667Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
9174667Smh27603 			return (DDI_SUCCESS);
9184667Smh27603 
9194667Smh27603 		(void) ddi_pathname(rdip, path);
9204667Smh27603 		mutex_enter(&domp->lock);
9214667Smh27603 		for (owned = domp->owned; owned; owned = owned->next)
9224667Smh27603 			if (strcmp(owned->path, path) == 0)
9234667Smh27603 				break;
9244667Smh27603 
9254667Smh27603 		/*
9264667Smh27603 		 * In case we didn't go through a complete attach and detach,
9274667Smh27603 		 * the initializing flag will still be set, so clear it.
9284667Smh27603 		 */
9294667Smh27603 		if ((owned != NULL) && (owned->initializing))
9304667Smh27603 			owned->initializing = 0;
9314667Smh27603 
9324667Smh27603 		if (PPMD_IS_PCI(domp->model) &&
9334667Smh27603 		    domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
9344667Smh27603 		    (domp->dflags & PPMD_INITCHILD_CLKON) &&
9354667Smh27603 		    ppm_none_else_holds_power(domp)) {
9364667Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
9374667Smh27603 			if (ret == DDI_SUCCESS)
9384667Smh27603 				domp->dflags &= ~PPMD_INITCHILD_CLKON;
9394667Smh27603 		}
9404667Smh27603 		mutex_exit(&domp->lock);
9414667Smh27603 		return (ret);
9424667Smh27603 
9434667Smh27603 	/* place holders */
9444667Smh27603 	case PMR_PPM_UNMANAGE:
9454667Smh27603 	case PMR_PPM_PRE_DETACH:
9464667Smh27603 		return (DDI_SUCCESS);
9474667Smh27603 
9484667Smh27603 	case PMR_PPM_PRE_PROBE:
9494667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9504667Smh27603 		return (ppm_power_up_domain(rdip));
9514667Smh27603 
9524667Smh27603 	case PMR_PPM_POST_PROBE:
9534667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9544667Smh27603 		if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
9554667Smh27603 		    reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
9564667Smh27603 			return (DDI_SUCCESS);
9574667Smh27603 
9584667Smh27603 		/* Probe failed */
9594667Smh27603 		PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
9604667Smh27603 		    "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
9614667Smh27603 		    reqp->req.ppm_config_req.result))
9624667Smh27603 		return (ppm_power_down_domain(rdip));
9634667Smh27603 
9644667Smh27603 	case PMR_PPM_PRE_ATTACH:
9654667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9664667Smh27603 		/* Domain has already been powered up in PRE_PROBE */
9674667Smh27603 		domp = ppm_lookup_dev(rdip);
9684667Smh27603 		ASSERT(domp);
9694667Smh27603 		ASSERT(domp->status == PPMD_ON);
9704667Smh27603 		return (DDI_SUCCESS);
9714667Smh27603 
9724667Smh27603 	/* ppm intercepts power change process to the claimed devices */
9734667Smh27603 	case PMR_PPM_SET_POWER:
9744667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
9754667Smh27603 		if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
9764667Smh27603 			domp = ppm_lookup_dev(rdip);
9774667Smh27603 			ASSERT(domp);
9784667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
9794667Smh27603 		}
9804667Smh27603 		switch (ppmd->domp->model) {
9814667Smh27603 		case PPMD_CPU:
9824667Smh27603 			return (ppm_manage_cpus(rdip, reqp, result));
9834667Smh27603 		case PPMD_FET:
9844667Smh27603 			return (ppm_manage_fet(rdip, reqp, result));
9854667Smh27603 		case PPMD_PCI:
9864667Smh27603 		case PPMD_PCI_PROP:
9874667Smh27603 			return (ppm_manage_pci(rdip, reqp, result));
9884667Smh27603 		case PPMD_PCIE:
9894667Smh27603 			return (ppm_manage_pcie(rdip, reqp, result));
9904667Smh27603 		default:
9914667Smh27603 			cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
9924667Smh27603 			    " not support PMR_PPM_SET_POWER ctlop",
9934667Smh27603 			    ppmd->domp->model);
9944667Smh27603 			return (DDI_FAILURE);
9954667Smh27603 		}
9964667Smh27603 
9975295Srandyf 	case PMR_PPM_ENTER_SX:
9985295Srandyf 	case PMR_PPM_EXIT_SX:
9995295Srandyf 		s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
10005295Srandyf 		s3args.s3a_test_point =
10015295Srandyf 		    reqp->req.ppm_power_enter_sx_req.test_point;
10025295Srandyf 		s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
10035295Srandyf 		s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
10045295Srandyf 		ret = ppm_manage_sx(&s3args,
10055295Srandyf 		    reqp->request_type == PMR_PPM_ENTER_SX);
10065295Srandyf 		if (ret) {
10075295Srandyf 			PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
10085295Srandyf 			return (DDI_FAILURE);
10095295Srandyf 		} else {
10105295Srandyf 			return (DDI_SUCCESS);
10115295Srandyf 		}
10125295Srandyf 
10135295Srandyf 	case PMR_PPM_SEARCH_LIST:
10145295Srandyf 		ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
10155295Srandyf 		reqp->req.ppm_search_list_req.result = ret;
10165295Srandyf 		*res = ret;
10175295Srandyf 		if (ret) {
10185295Srandyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10195295Srandyf 			return (DDI_FAILURE);
10205295Srandyf 		} else {
10215295Srandyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10225295Srandyf 			return (DDI_SUCCESS);
10235295Srandyf 		}
10245295Srandyf 
10254667Smh27603 	default:
10264667Smh27603 		cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
10275295Srandyf 		    reqp->request_type);
10284667Smh27603 		return (DDI_FAILURE);
10294667Smh27603 	}
10304667Smh27603 }
10314667Smh27603 
10324667Smh27603 
10334667Smh27603 /*
10344667Smh27603  * Raise the power level of a subrange of cpus.  Used when cpu driver
10354667Smh27603  * failed an attempt to lower the power of a cpu (probably because
10364667Smh27603  * it got busy).  Need to revert the ones we already changed.
10374667Smh27603  *
10384667Smh27603  * ecpup = the ppm_dev_t for the cpu which failed to lower power
10394667Smh27603  * level = power level to reset prior cpus to
10404667Smh27603  */
10414667Smh27603 int
ppm_revert_cpu_power(ppm_dev_t * ecpup,int level)10424667Smh27603 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
10434667Smh27603 {
10444667Smh27603 	ppm_dev_t *cpup;
10454667Smh27603 	int ret = DDI_SUCCESS;
10464667Smh27603 
10474667Smh27603 	for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
10484667Smh27603 		PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
10494667Smh27603 		    "level %d\n", cpup->path, level))
10504667Smh27603 
10514667Smh27603 		ret = pm_power(cpup->dip, 0, level);
10524667Smh27603 		if (ret == DDI_SUCCESS) {
10534667Smh27603 			cpup->level = level;
10544667Smh27603 			cpup->rplvl = PM_LEVEL_UNKNOWN;
10554667Smh27603 		}
10564667Smh27603 	}
10574667Smh27603 	return (ret);
10584667Smh27603 }
10594667Smh27603 
10604667Smh27603 
10614667Smh27603 /*
10624667Smh27603  * ppm_manage_cpus - Process a request to change the power level of a cpu.
10634667Smh27603  * If not all cpus want to be at the same level, OR if we are currently
10644667Smh27603  * refusing slowdown requests due to thermal stress, we cache the request.
10654667Smh27603  * Otherwise, set all cpus to the new power level.
10664667Smh27603  */
10674667Smh27603 /* ARGSUSED */
10684667Smh27603 static int
ppm_manage_cpus(dev_info_t * dip,power_req_t * reqp,int * result)10694667Smh27603 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
10704667Smh27603 {
10714667Smh27603 #ifdef	DEBUG
10724667Smh27603 	char *str = "ppm_manage_cpus";
10734667Smh27603 #endif
10744667Smh27603 	int old, new, ret, kmflag;
10754667Smh27603 	ppm_dev_t *ppmd, *cpup;
10764667Smh27603 	int change_notify = 0;
10774667Smh27603 	pm_ppm_devlist_t *devlist = NULL, *p;
10784667Smh27603 	int		do_rescan = 0;
10794667Smh27603 
10804667Smh27603 	*result = DDI_SUCCESS;
10814667Smh27603 
10824667Smh27603 	switch (reqp->request_type) {
10834667Smh27603 	case PMR_PPM_SET_POWER:
10844667Smh27603 		break;
10854667Smh27603 
10864667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
10874667Smh27603 		change_notify = 1;
10884667Smh27603 		break;
10894667Smh27603 
10904667Smh27603 	default:
10914667Smh27603 		return (DDI_FAILURE);
10924667Smh27603 	}
10934667Smh27603 
10944667Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
10954667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
10964667Smh27603 	old = reqp->req.ppm_set_power_req.old_level;
10974667Smh27603 	new = reqp->req.ppm_set_power_req.new_level;
10984667Smh27603 
10994667Smh27603 	if (change_notify) {
11004667Smh27603 		ppmd->level = new;
11014667Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
11024667Smh27603 
11034667Smh27603 		PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
11044667Smh27603 		    "from %d to %d", str, (void *)dip, old, new))
11054667Smh27603 		return (DDI_SUCCESS);
11064667Smh27603 	}
11074667Smh27603 
11084667Smh27603 	if (ppm_manage_early_cpus(dip, new, result))
11094667Smh27603 		return (*result);
11104667Smh27603 
11114667Smh27603 	if (new == ppmd->level) {
11124667Smh27603 		PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
11134667Smh27603 		return (DDI_SUCCESS);
11144667Smh27603 	}
11154667Smh27603 
11164667Smh27603 	/*
11174667Smh27603 	 * A request from lower to higher level transition is granted and
11184667Smh27603 	 * made effective on all cpus. A request from higher to lower must
11194667Smh27603 	 * be agreed upon by all cpus.
11204667Smh27603 	 */
11214667Smh27603 	ppmd->rplvl = new;
11224667Smh27603 	for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11234667Smh27603 		if (cpup->rplvl == new)
11244667Smh27603 			continue;
11254667Smh27603 
11264667Smh27603 		if (new < old) {
11274667Smh27603 			PPMD(D_SOME, ("%s: not all cpus wants to be at new "
11284667Smh27603 			    "level %d yet.\n", str, new))
11294667Smh27603 			return (DDI_SUCCESS);
11304667Smh27603 		}
11314667Smh27603 
11324667Smh27603 		/*
11334667Smh27603 		 * If a single cpu requests power up, honor the request
11344667Smh27603 		 * powering up all cpus.
11354667Smh27603 		 */
11364667Smh27603 		if (new > old) {
11374667Smh27603 			PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
11384667Smh27603 			    "because of request from dip(%s@%s, %p), "
11394667Smh27603 			    "need pm_rescan\n", str, PM_NAME(cpup->dip),
11404667Smh27603 			    PM_ADDR(cpup->dip), (void *)cpup->dip,
11414667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), (void *)dip))
11424667Smh27603 			do_rescan++;
11434667Smh27603 		}
11444667Smh27603 	}
11454667Smh27603 
11464667Smh27603 	PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
11474667Smh27603 	    str, ppmd->path, ppmd->level, new))
11484667Smh27603 	ret = ppm_change_cpu_power(ppmd, new);
11494667Smh27603 	*result = ret;
11504667Smh27603 
11514667Smh27603 	if (ret == DDI_SUCCESS) {
11524667Smh27603 		if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
11534667Smh27603 			kmflag = KM_SLEEP;
11544667Smh27603 		else
11554667Smh27603 			kmflag = KM_NOSLEEP;
11564667Smh27603 
11574667Smh27603 		for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11584667Smh27603 			if (cpup->dip == dip)
11594667Smh27603 				continue;
11604667Smh27603 
11614667Smh27603 			if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
11624667Smh27603 			    kmflag)) == NULL) {
11634667Smh27603 				break;
11644667Smh27603 			}
11654667Smh27603 			p->ppd_who = cpup->dip;
11664667Smh27603 			p->ppd_cmpt = cpup->cmpt;
11674667Smh27603 			p->ppd_old_level = old;
11684667Smh27603 			p->ppd_new_level = new;
11694667Smh27603 			p->ppd_next = devlist;
11704667Smh27603 
11714667Smh27603 			PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
11724667Smh27603 			    str, cpup->path, old, new))
11734667Smh27603 
11744667Smh27603 			devlist = p;
11754667Smh27603 		}
11764667Smh27603 		reqp->req.ppm_set_power_req.cookie = (void *) devlist;
11774667Smh27603 
11784667Smh27603 		if (do_rescan > 0) {
11794667Smh27603 			for (cpup = ppmd->domp->devlist; cpup;
11804667Smh27603 			    cpup = cpup->next) {
11814667Smh27603 				if (cpup->dip == dip)
11824667Smh27603 					continue;
11834667Smh27603 				pm_rescan(cpup->dip);
11844667Smh27603 			}
11854667Smh27603 		}
11864667Smh27603 	}
11874667Smh27603 
11884667Smh27603 	return (ret);
11894667Smh27603 }
11904667Smh27603 
11914667Smh27603 
11924667Smh27603 /*
11934667Smh27603  * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
11944667Smh27603  * increments its FET domain power count, in anticipation of that
11954667Smh27603  * the indicated device(dip) would be powered up by its driver as
11964667Smh27603  * a result of cpr resuming.
11974667Smh27603  */
11984667Smh27603 /* ARGSUSED */
11994667Smh27603 static void
ppm_svc_resume_ctlop(dev_info_t * dip,power_req_t * reqp)12004667Smh27603 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
12014667Smh27603 {
12024667Smh27603 	ppm_domain_t *domp;
12034667Smh27603 	ppm_dev_t *ppmd;
12044667Smh27603 	int powered;	/* power up count per dip */
12054667Smh27603 
12064667Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
12074667Smh27603 	if (ppmd == NULL)
12084667Smh27603 		return;
12094667Smh27603 
12104667Smh27603 	/*
12114667Smh27603 	 * Maintain correct powered count for domain which cares
12124667Smh27603 	 */
12134667Smh27603 	powered = 0;
12144667Smh27603 	domp = ppmd->domp;
12154667Smh27603 	mutex_enter(&domp->lock);
12164667Smh27603 	if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
12174667Smh27603 	    (domp->model == PPMD_PCIE)) {
12184667Smh27603 		for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12194667Smh27603 			if (ppmd->dip == dip && ppmd->level)
12204667Smh27603 				powered++;
12214667Smh27603 		}
12224667Smh27603 
12234667Smh27603 		/*
12244667Smh27603 		 * All fets and clocks are held on during suspend -
12254667Smh27603 		 * resume window regardless their domain devices' power
12264667Smh27603 		 * level.
12274667Smh27603 		 */
12284667Smh27603 		ASSERT(domp->status == PPMD_ON);
12294667Smh27603 
12304667Smh27603 		/*
12314667Smh27603 		 * The difference indicates the number of components
12324667Smh27603 		 * being off prior to suspend operation, that is the
12334667Smh27603 		 * amount needs to be compensated in order to sync up
12344667Smh27603 		 * bookkeeping with reality, for PROM reset would have
12354667Smh27603 		 * brought up all devices.
12364667Smh27603 		 */
12374667Smh27603 		if (powered < PM_NUMCMPTS(dip))
12384667Smh27603 			domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
12394667Smh27603 	}
12404667Smh27603 	for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12414667Smh27603 		if (ppmd->dip == dip)
12424667Smh27603 			ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
12434667Smh27603 	}
12444667Smh27603 	mutex_exit(&domp->lock);
12454667Smh27603 }
12464667Smh27603 
12474667Smh27603 #ifdef	DEBUG
12484667Smh27603 static int ppmbringup = 0;
12494667Smh27603 #endif
12504667Smh27603 
12514667Smh27603 int
ppm_bringup_domains()12524667Smh27603 ppm_bringup_domains()
12534667Smh27603 {
12544667Smh27603 #ifdef DEBUG
12554667Smh27603 	char *str = "ppm_bringup_domains";
12564667Smh27603 #endif
12574667Smh27603 	ppm_domain_t	*domp;
12584667Smh27603 	int	ret = DDI_SUCCESS;
12594667Smh27603 
12604667Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
12614667Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
12624667Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
12634667Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
12644667Smh27603 			continue;
12654667Smh27603 
12664667Smh27603 		mutex_enter(&domp->lock);
12674667Smh27603 		if (domp->status == PPMD_ON) {
12684667Smh27603 			mutex_exit(&domp->lock);
12694667Smh27603 			continue;
12704667Smh27603 		}
12714667Smh27603 		switch (domp->model) {
12724667Smh27603 		case PPMD_FET:
12734667Smh27603 			ret = ppm_fetset(domp, PPMD_ON);
12744667Smh27603 			break;
12754667Smh27603 		case PPMD_PCI:
12764667Smh27603 		case PPMD_PCI_PROP:
12774667Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
12784667Smh27603 			break;
12794667Smh27603 		case PPMD_PCIE:
12804667Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_ON);
12814667Smh27603 			break;
12824667Smh27603 		default:
12834667Smh27603 			break;
12844667Smh27603 		}
12854667Smh27603 		mutex_exit(&domp->lock);
12864667Smh27603 	}
12875295Srandyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
12884667Smh27603 
12894667Smh27603 	return (ret);
12904667Smh27603 }
12914667Smh27603 
12924667Smh27603 #ifdef	DEBUG
12934667Smh27603 static int ppmsyncbp = 0;
12944667Smh27603 #endif
12954667Smh27603 
12964667Smh27603 int
ppm_sync_bookkeeping()12974667Smh27603 ppm_sync_bookkeeping()
12984667Smh27603 {
12994667Smh27603 #ifdef DEBUG
13004667Smh27603 	char *str = "ppm_sync_bookkeeping";
13014667Smh27603 #endif
13024667Smh27603 	ppm_domain_t	*domp;
13034667Smh27603 	int	ret = DDI_SUCCESS;
13044667Smh27603 
13054667Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
13064667Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
13074667Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
13084667Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
13094667Smh27603 			continue;
13104667Smh27603 
13114667Smh27603 		mutex_enter(&domp->lock);
13124667Smh27603 		if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
13134667Smh27603 			mutex_exit(&domp->lock);
13144667Smh27603 			continue;
13154667Smh27603 		}
13165295Srandyf 
13175295Srandyf 		/*
13185295Srandyf 		 * skip NULL .devlist slot, for some may host pci device
13195295Srandyf 		 * that can not tolerate clock off or not even participate
13205295Srandyf 		 * in PM.
13215295Srandyf 		 */
13225295Srandyf 		if (domp->devlist == NULL)
13235295Srandyf 			continue;
13245295Srandyf 
13254667Smh27603 		switch (domp->model) {
13264667Smh27603 		case PPMD_FET:
13274667Smh27603 			ret = ppm_fetset(domp, PPMD_OFF);
13284667Smh27603 			break;
13294667Smh27603 		case PPMD_PCI:
13304667Smh27603 		case PPMD_PCI_PROP:
13314667Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
13324667Smh27603 			break;
13334667Smh27603 		case PPMD_PCIE:
13344667Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_OFF);
13354667Smh27603 			break;
13364667Smh27603 		default:
13374667Smh27603 			break;
13384667Smh27603 		}
13394667Smh27603 		mutex_exit(&domp->lock);
13404667Smh27603 	}
13415295Srandyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
13424667Smh27603 
13434667Smh27603 	return (ret);
13444667Smh27603 }
13454667Smh27603 
13464667Smh27603 
13474667Smh27603 
13484667Smh27603 /*
13494667Smh27603  * pre-suspend window;
13504667Smh27603  *
13514667Smh27603  * power up every FET and PCI clock that are off;
13524667Smh27603  *
13534667Smh27603  * set ppm_cpr_window global flag to indicate
13544667Smh27603  * that even though all pm_scan requested power transitions
13554667Smh27603  * will be honored as usual but that until we're out
13564667Smh27603  * of this window,  no FET or clock will be turned off
13574667Smh27603  * for domains with pwr_cnt decremented down to 0.
13584667Smh27603  * Such is to avoid accessing the orthogonal drivers that own
13594667Smh27603  * the FET and clock registers that may not be resumed yet.
13604667Smh27603  *
13614667Smh27603  * at post-resume window, walk through each FET and PCI domains,
13624667Smh27603  * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
13634667Smh27603  * and noinvol check okays, power down the FET or PCI.  At last,
13644667Smh27603  * clear the global flag ppm_cpr_window.
13654667Smh27603  *
13664667Smh27603  * ASSERT case 1, during cpr window, checks pwr_cnt against power
13674667Smh27603  *	transitions;
13684667Smh27603  * ASSERT case 2, out of cpr window, checks four things:
13694667Smh27603  *	pwr_cnt <> power transition in/out of 0
13704667Smh27603  *	<> status <> record of noinvol device detached
13714667Smh27603  *
13724667Smh27603  */
13734667Smh27603 /* ARGSUSED */
13744667Smh27603 static boolean_t
ppm_cpr_callb(void * arg,int code)13754667Smh27603 ppm_cpr_callb(void *arg, int code)
13764667Smh27603 {
13774667Smh27603 	int	ret;
13784667Smh27603 
13794667Smh27603 	switch (code) {
13804667Smh27603 	case CB_CODE_CPR_CHKPT:
13814667Smh27603 
13824667Smh27603 		/* pre-suspend: start of cpr window */
13834667Smh27603 		mutex_enter(&ppm_cpr_window_lock);
13844667Smh27603 		ASSERT(ppm_cpr_window_flag == B_FALSE);
13854667Smh27603 		ppm_cpr_window_flag = B_TRUE;
13864667Smh27603 		mutex_exit(&ppm_cpr_window_lock);
13874667Smh27603 
13884667Smh27603 		ret = ppm_bringup_domains();
13894667Smh27603 
13904667Smh27603 		break;
13914667Smh27603 
13924667Smh27603 	case CB_CODE_CPR_RESUME:
13934667Smh27603 
13944667Smh27603 		/* post-resume: end of cpr window */
13954667Smh27603 		ret = ppm_sync_bookkeeping();
13964667Smh27603 
13974667Smh27603 		mutex_enter(&ppm_cpr_window_lock);
13984667Smh27603 		ASSERT(ppm_cpr_window_flag == B_TRUE);
13994667Smh27603 		ppm_cpr_window_flag = B_FALSE;
14004667Smh27603 		mutex_exit(&ppm_cpr_window_lock);
14014667Smh27603 
14024667Smh27603 		break;
14034667Smh27603 	}
14044667Smh27603 
14054667Smh27603 	return (ret == DDI_SUCCESS);
14064667Smh27603 }
14074667Smh27603 
14084667Smh27603 
14094667Smh27603 /*
14104667Smh27603  * Initialize our private version of real power level
14114667Smh27603  * as well as lowest and highest levels the device supports;
14124667Smh27603  * relate to ppm_add_dev
14134667Smh27603  */
14144667Smh27603 void
ppm_dev_init(ppm_dev_t * ppmd)14154667Smh27603 ppm_dev_init(ppm_dev_t *ppmd)
14164667Smh27603 {
14174667Smh27603 	struct pm_component *dcomps;
14184667Smh27603 	struct pm_comp *pm_comp;
14194667Smh27603 	dev_info_t *dip;
14204667Smh27603 	int maxi, i;
14214667Smh27603 
14224667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14234667Smh27603 	ppmd->level = PM_LEVEL_UNKNOWN;
14244667Smh27603 	ppmd->rplvl = PM_LEVEL_UNKNOWN;
14254667Smh27603 
14264667Smh27603 	/* increment pwr_cnt per component */
14274667Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
14284667Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
14294667Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
14304667Smh27603 		ppmd->domp->pwr_cnt++;
14314667Smh27603 
14324667Smh27603 	dip = ppmd->dip;
14334667Smh27603 
14344667Smh27603 	/*
14354667Smh27603 	 * ppm exists to handle power-manageable devices which require
14364667Smh27603 	 * special handling on the current platform.  However, a
14374667Smh27603 	 * driver for such a device may choose not to support power
14384667Smh27603 	 * management on a particular load/attach.  In this case we
14394667Smh27603 	 * we create a structure to represent a single-component device
14404667Smh27603 	 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
14414667Smh27603 	 * are effectively constant.
14424667Smh27603 	 */
14434667Smh27603 	if (PM_GET_PM_INFO(dip)) {
14444667Smh27603 		dcomps = DEVI(dip)->devi_pm_components;
14454667Smh27603 		pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
14464667Smh27603 
14474667Smh27603 		ppmd->lowest = pm_comp->pmc_lvals[0];
14484667Smh27603 		ASSERT(ppmd->lowest >= 0);
14494667Smh27603 		maxi = pm_comp->pmc_numlevels - 1;
14504667Smh27603 		ppmd->highest = pm_comp->pmc_lvals[maxi];
14514667Smh27603 
14524667Smh27603 		/*
14534667Smh27603 		 * If 66mhz PCI device on pci 66mhz bus supports D2 state
14544667Smh27603 		 * (config reg PMC bit 10 set), ppm could turn off its bus
14554667Smh27603 		 * clock once it is at D3hot.
14564667Smh27603 		 */
14574667Smh27603 		if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
14584667Smh27603 			for (i = 0; i < maxi; i++)
14594667Smh27603 				if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
14604667Smh27603 					ppmd->flags |= PPMDEV_PCI66_D2;
14614667Smh27603 					break;
14624667Smh27603 				}
14634667Smh27603 		}
14644667Smh27603 	}
14654667Smh27603 
14664667Smh27603 	/*
14674667Smh27603 	 * If device is in PCI_PROP domain and has exported the
14684667Smh27603 	 * property listed in ppm.conf, its clock will be turned
14694667Smh27603 	 * off when all pm'able devices in that domain are at D3.
14704667Smh27603 	 */
14714667Smh27603 	if ((ppmd->domp->model == PPMD_PCI_PROP) &&
14724667Smh27603 	    (ppmd->domp->propname != NULL) &&
14734667Smh27603 	    ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
14744667Smh27603 	    ppmd->domp->propname))
14754667Smh27603 		ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
14764667Smh27603 }
14774667Smh27603 
14784667Smh27603 
14794667Smh27603 /*
14804667Smh27603  * relate to ppm_rem_dev
14814667Smh27603  */
14824667Smh27603 void
ppm_dev_fini(ppm_dev_t * ppmd)14834667Smh27603 ppm_dev_fini(ppm_dev_t *ppmd)
14844667Smh27603 {
14854667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14864667Smh27603 
14874667Smh27603 	/* decrement pwr_cnt per component */
14884667Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
14894667Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
14904667Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
14914667Smh27603 		if (ppmd->level != ppmd->lowest)
14924667Smh27603 			ppmd->domp->pwr_cnt--;
14934667Smh27603 }
14944667Smh27603 
14954667Smh27603 /*
14964667Smh27603  * Each power fet controls the power of one or more platform
14974667Smh27603  * device(s) within their domain.  Hence domain devices' power
14984667Smh27603  * level change has been monitored, such that once all devices
14994667Smh27603  * are powered off, the fet is turned off to save more power.
15004667Smh27603  *
15014667Smh27603  * To power on any domain device, the domain power fet
15024667Smh27603  * needs to be turned on first. always one fet per domain.
15034667Smh27603  */
15044667Smh27603 static int
ppm_manage_fet(dev_info_t * dip,power_req_t * reqp,int * result)15054667Smh27603 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
15064667Smh27603 {
15074667Smh27603 #ifdef DEBUG
15084667Smh27603 	char *str = "ppm_manage_fet";
15094667Smh27603 #endif
15104667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
15114667Smh27603 	int		new, old, cmpt;
15124667Smh27603 	ppm_dev_t	*ppmd;
15134667Smh27603 	ppm_domain_t	*domp;
15144667Smh27603 	int		incr = 0;
15154667Smh27603 	int		dummy_ret;
15164667Smh27603 
15174667Smh27603 
15184667Smh27603 	*result = DDI_SUCCESS;
15194667Smh27603 	switch (reqp->request_type) {
15204667Smh27603 	case PMR_PPM_SET_POWER:
15214667Smh27603 		pwr_func = ppm_change_power_level;
15224667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
15234667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
15244667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
15254667Smh27603 		break;
15264667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
15274667Smh27603 		pwr_func = ppm_record_level_change;
15284667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
15294667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
15304667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
15314667Smh27603 		break;
15324667Smh27603 	default:
15334667Smh27603 		*result = DDI_FAILURE;
15344667Smh27603 		PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
15354667Smh27603 		    str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
15364667Smh27603 		return (DDI_FAILURE);
15374667Smh27603 	}
15384667Smh27603 
15394667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
15404667Smh27603 		if (cmpt == ppmd->cmpt)
15414667Smh27603 			break;
15424667Smh27603 	if (!ppmd) {
15434667Smh27603 		PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
15444667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
15454667Smh27603 		*result = DDI_FAILURE;
15464667Smh27603 		return (DDI_FAILURE);
15474667Smh27603 	}
15484667Smh27603 	domp = ppmd->domp;
15494667Smh27603 	PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
15504667Smh27603 	    "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
15514667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
15524667Smh27603 	    ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
15534667Smh27603 
15544667Smh27603 
15554667Smh27603 	ASSERT(old == ppmd->level);
15564667Smh27603 
15574667Smh27603 	if (new == ppmd->level) {
15584667Smh27603 		PPMD(D_FET, ("nop\n"))
15594667Smh27603 		return (DDI_SUCCESS);
15604667Smh27603 	}
15614667Smh27603 
15624667Smh27603 	PPM_LOCK_DOMAIN(domp);
15634667Smh27603 
15644667Smh27603 	/*
15654667Smh27603 	 * In general, a device's published lowest power level does not
15664667Smh27603 	 * have to be 0 if power-off is not tolerated. i.e. a device
15674667Smh27603 	 * instance may export its lowest level > 0.  It is reasonable to
15684667Smh27603 	 * assume that level 0 indicates off state, positive level values
15694667Smh27603 	 * indicate power states above off, include full power state.
15704667Smh27603 	 */
15714667Smh27603 	if (new > 0) { /* device powering up or to different positive level */
15724667Smh27603 		if (domp->status == PPMD_OFF) {
15734667Smh27603 
15744667Smh27603 			/* can not be in (chpt, resume) window */
15754667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
15764667Smh27603 
15774667Smh27603 			ASSERT(old == 0 && domp->pwr_cnt == 0);
15784667Smh27603 
15794667Smh27603 			PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
15804667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
15814667Smh27603 
15824667Smh27603 			*result = ppm_fetset(domp, PPMD_ON);
15834667Smh27603 			if (*result != DDI_SUCCESS) {
15844667Smh27603 				PPMD(D_FET, ("\tCan't turn on power FET: "
15854667Smh27603 				    "ret(%d)\n", *result))
15864667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
15874667Smh27603 				return (DDI_FAILURE);
15884667Smh27603 			}
15894667Smh27603 		}
15904667Smh27603 
15914667Smh27603 		/*
15924667Smh27603 		 * If powering up, pre-increment the count before
15934667Smh27603 		 * calling pwr_func, because we are going to release
15944667Smh27603 		 * the domain lock and another thread might turn off
15954667Smh27603 		 * domain power otherwise.
15964667Smh27603 		 */
15974667Smh27603 		if (old == 0) {
15984667Smh27603 			domp->pwr_cnt++;
15994667Smh27603 			incr = 1;
16004667Smh27603 		}
16014667Smh27603 
16024667Smh27603 		PPMD(D_FET, ("\t%s domain power count: %d\n",
16034667Smh27603 		    domp->name, domp->pwr_cnt))
16044667Smh27603 	}
16054667Smh27603 
16064667Smh27603 
16074667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
16084667Smh27603 
16094667Smh27603 	ASSERT(domp->pwr_cnt > 0);
16104667Smh27603 
16114667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
16124667Smh27603 		PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
16134667Smh27603 		    ppmd->path, *result))
16144667Smh27603 	}
16154667Smh27603 
16164667Smh27603 	PPM_LOCK_DOMAIN(domp);
16174667Smh27603 
16184667Smh27603 	/*
16194667Smh27603 	 * Decr the power count in two cases:
16204667Smh27603 	 *
16214667Smh27603 	 *   1) request was to power device down and was successful
16224667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
16234667Smh27603 	 */
16244667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
16254667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
16264667Smh27603 		ASSERT(domp->pwr_cnt > 0);
16274667Smh27603 		domp->pwr_cnt--;
16284667Smh27603 	}
16294667Smh27603 
16304667Smh27603 	PPMD(D_FET, ("\t%s domain power count: %d\n",
16314667Smh27603 	    domp->name, domp->pwr_cnt))
16324667Smh27603 
16334667Smh27603 	/*
16344667Smh27603 	 * call to pwr_func will update ppm data structures, if it
16354667Smh27603 	 * succeeds. ppm should return whatever is the return value
16364667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
16374667Smh27603 	 * always in sync. Use dummy_ret from here for any further
16384667Smh27603 	 * return values.
16394667Smh27603 	 */
16404667Smh27603 	if ((domp->pwr_cnt == 0) &&
16414667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
16424667Smh27603 	    ppm_none_else_holds_power(domp)) {
16434667Smh27603 
16444667Smh27603 		PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
16454667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
16464667Smh27603 
16474667Smh27603 		dummy_ret = ppm_fetset(domp, PPMD_OFF);
16484667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
16494667Smh27603 			PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
16504667Smh27603 			    dummy_ret))
16514667Smh27603 		}
16524667Smh27603 	}
16534667Smh27603 
16544667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
16554667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
16564667Smh27603 	return (*result);
16574667Smh27603 }
16584667Smh27603 
16594667Smh27603 
16604667Smh27603 /*
16614667Smh27603  * the actual code that turn on or off domain power fet and
16624667Smh27603  * update domain status
16634667Smh27603  */
16644667Smh27603 static int
ppm_fetset(ppm_domain_t * domp,uint8_t value)16654667Smh27603 ppm_fetset(ppm_domain_t *domp, uint8_t value)
16664667Smh27603 {
16674667Smh27603 	char	*str = "ppm_fetset";
16684667Smh27603 	int	key;
16694667Smh27603 	ppm_dc_t *dc;
16704667Smh27603 	int	ret;
16714667Smh27603 	clock_t	temp;
16724667Smh27603 	clock_t delay = 0;
16734667Smh27603 
16744667Smh27603 	key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
16754667Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
16764667Smh27603 		if (dc->cmd == key)
16774667Smh27603 			break;
16784667Smh27603 	if (!dc || !dc->lh) {
16794667Smh27603 		PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
16804667Smh27603 		    str, domp->name))
16814667Smh27603 		return (DDI_FAILURE);
16824667Smh27603 	}
16834667Smh27603 
16844667Smh27603 	if (key == PPMDC_FET_ON) {
16854667Smh27603 		PPM_GET_IO_DELAY(dc, delay);
16864667Smh27603 		if (delay > 0 && domp->last_off_time > 0) {
16874667Smh27603 			/*
16884667Smh27603 			 * provide any delay required before turning on.
16894667Smh27603 			 * some devices e.g. Samsung DVD require minimum
16904667Smh27603 			 * of 1 sec between OFF->ON. no delay is required
16914667Smh27603 			 * for the first time.
16924667Smh27603 			 */
16934667Smh27603 			temp = ddi_get_lbolt();
16944667Smh27603 			temp -= domp->last_off_time;
16954667Smh27603 			temp = drv_hztousec(temp);
16964667Smh27603 
16974667Smh27603 			if (temp < delay) {
16984667Smh27603 				/*
16994667Smh27603 				 * busy wait untill we meet the
17004667Smh27603 				 * required delay. Since we maintain
17014667Smh27603 				 * time stamps in terms of clock ticks
17024667Smh27603 				 * we might wait for longer than required
17034667Smh27603 				 */
17044667Smh27603 				PPMD(D_FET, ("%s : waiting %lu micro seconds "
17055295Srandyf 				    "before on\n", domp->name,
17065295Srandyf 				    delay - temp));
17074667Smh27603 				drv_usecwait(delay - temp);
17084667Smh27603 			}
17094667Smh27603 		}
17104667Smh27603 	}
17114667Smh27603 	switch (dc->method) {
17125295Srandyf #ifdef sun4u
17134667Smh27603 	case PPMDC_I2CKIO: {
17144667Smh27603 		i2c_gpio_t i2c_req;
17154667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
17164667Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
17174667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
17184667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
17194667Smh27603 		break;
17204667Smh27603 	}
17214667Smh27603 #endif
17224667Smh27603 
17234667Smh27603 	case PPMDC_KIO:
17244667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
17254667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
17264667Smh27603 		    NULL);
17274667Smh27603 		break;
17284667Smh27603 
17294667Smh27603 	default:
17304667Smh27603 		PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
17314667Smh27603 		    str, domp->dc->method))
17324667Smh27603 		return (DDI_FAILURE);
17334667Smh27603 	}
17344667Smh27603 
17354667Smh27603 	PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
17364667Smh27603 	    (ret == 0) ? "turned" : "failed to turn",
17374667Smh27603 	    domp->name,
17384667Smh27603 	    (domp->status == PPMD_ON) ? "ON" : "OFF",
17394667Smh27603 	    (value == PPMD_ON) ? "ON" : "OFF"))
17404667Smh27603 
17414667Smh27603 	if (ret == DDI_SUCCESS) {
17424667Smh27603 		domp->status = value;
17434667Smh27603 
17444667Smh27603 		if (key == PPMDC_FET_OFF)
17454667Smh27603 			/*
17464667Smh27603 			 * record the time, when it is off. time is recorded
17474667Smh27603 			 * in clock ticks
17484667Smh27603 			 */
17494667Smh27603 			domp->last_off_time = ddi_get_lbolt();
17504667Smh27603 
17514667Smh27603 		/* implement any post op delay. */
17524667Smh27603 		if (key == PPMDC_FET_ON) {
17536464Smh27603 			PPM_GET_IO_POST_DELAY(dc, delay);
17544667Smh27603 			PPMD(D_FET, ("%s : waiting %lu micro seconds "
17554667Smh27603 			    "after on\n", domp->name, delay))
17564667Smh27603 			if (delay > 0)
17574667Smh27603 				drv_usecwait(delay);
17584667Smh27603 		}
17594667Smh27603 	}
17604667Smh27603 
17614667Smh27603 	return (ret);
17624667Smh27603 }
17634667Smh27603 
17644667Smh27603 
17654667Smh27603 /*
17664667Smh27603  * read power fet status
17674667Smh27603  */
17684667Smh27603 static int
ppm_fetget(ppm_domain_t * domp,uint8_t * lvl)17694667Smh27603 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
17704667Smh27603 {
17714667Smh27603 	char	*str = "ppm_fetget";
17724667Smh27603 	ppm_dc_t *dc = domp->dc;
17734667Smh27603 	uint_t	kio_val;
17744667Smh27603 	int	off_val;
17754667Smh27603 	int	ret;
17764667Smh27603 
17774667Smh27603 	if (!dc->lh) {
17784667Smh27603 		PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
17794667Smh27603 		    str, domp->name))
17804667Smh27603 		return (DDI_FAILURE);
17814667Smh27603 	}
17824667Smh27603 	if (!dc->next) {
17834667Smh27603 		cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
17844667Smh27603 		    "defined, found only one in domain(%s)", str, domp->name);
17854667Smh27603 		return (DDI_FAILURE);
17864667Smh27603 	}
17874667Smh27603 
17884667Smh27603 	switch (dc->method) {
17895295Srandyf #ifdef sun4u
17904667Smh27603 	case PPMDC_I2CKIO: {
17914667Smh27603 		i2c_gpio_t i2c_req;
17924667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
17934667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
17944667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
17954667Smh27603 
17964667Smh27603 		if (ret) {
17974667Smh27603 			PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
17984667Smh27603 			    str, ret))
17994667Smh27603 			return (ret);
18004667Smh27603 		}
18014667Smh27603 
18024667Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
18034667Smh27603 		    dc->next->m_un.i2c.val;
18044667Smh27603 		*lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
18054667Smh27603 
18064667Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18074667Smh27603 		    (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
18084667Smh27603 
18094667Smh27603 		break;
18104667Smh27603 	}
18114667Smh27603 #endif
18124667Smh27603 
18134667Smh27603 	case PPMDC_KIO:
18144667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
18154667Smh27603 		    (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
18164667Smh27603 		if (ret) {
18174667Smh27603 			PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
18184667Smh27603 			    str, ret))
18194667Smh27603 			return (ret);
18204667Smh27603 		}
18214667Smh27603 
18224667Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
18235295Srandyf 		    dc->next->m_un.kio.val;
18244667Smh27603 		*lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
18254667Smh27603 
18264667Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18274667Smh27603 		    (kio_val == off_val) ? "OFF" : "ON"))
18284667Smh27603 
18294667Smh27603 		break;
18304667Smh27603 
18314667Smh27603 	default:
18324667Smh27603 		PPMD(D_FET, ("%s: unsupported domain control method %d\n",
18334667Smh27603 		    str, domp->dc->method))
18344667Smh27603 		return (DDI_FAILURE);
18354667Smh27603 	}
18364667Smh27603 
18374667Smh27603 	return (DDI_SUCCESS);
18384667Smh27603 }
18394667Smh27603 
18404667Smh27603 
18414667Smh27603 /*
18424667Smh27603  * the actual code that switches pci clock and update domain status
18434667Smh27603  */
18444667Smh27603 static int
ppm_switch_clock(ppm_domain_t * domp,int onoff)18454667Smh27603 ppm_switch_clock(ppm_domain_t *domp, int onoff)
18464667Smh27603 {
18474667Smh27603 #ifdef DEBUG
18484667Smh27603 	char *str = "ppm_switch_clock";
18494667Smh27603 #endif
18504667Smh27603 	int	cmd, pio_save;
18514667Smh27603 	ppm_dc_t *dc;
18524667Smh27603 	int ret;
18534667Smh27603 	extern int do_polled_io;
18544667Smh27603 	extern uint_t cfb_inuse;
18554667Smh27603 	ppm_dev_t	*pdev;
18564667Smh27603 
18574667Smh27603 	cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
18584667Smh27603 	dc = ppm_lookup_dc(domp, cmd);
18594667Smh27603 	if (!dc) {
18604667Smh27603 		PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
18614667Smh27603 		    str, domp->name))
18624667Smh27603 		return (DDI_FAILURE);
18634667Smh27603 	}
18644667Smh27603 
18654667Smh27603 	switch (dc->method) {
18664667Smh27603 	case PPMDC_KIO:
18674667Smh27603 		/*
18684667Smh27603 		 * If we're powering up cfb on a Stop-A, we only
18694667Smh27603 		 * want to do polled i/o to turn ON the clock
18704667Smh27603 		 */
18714667Smh27603 		pio_save = do_polled_io;
18724667Smh27603 		if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
18734667Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
18744667Smh27603 				if (pm_is_cfb(pdev->dip)) {
18754667Smh27603 					do_polled_io = 1;
18764667Smh27603 					break;
18774667Smh27603 				}
18784667Smh27603 			}
18794667Smh27603 		}
18804667Smh27603 
18814667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
18824667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
18834667Smh27603 		    kcred, NULL);
18844667Smh27603 
18854667Smh27603 		do_polled_io = pio_save;
18864667Smh27603 
18874667Smh27603 		if (ret == 0) {
18884667Smh27603 			if (cmd == PPMDC_CLK_ON) {
18894667Smh27603 				domp->status = PPMD_ON;
18904667Smh27603 
18914667Smh27603 				/*
18924667Smh27603 				 * PCI PM spec requires 50ms delay
18934667Smh27603 				 */
18944667Smh27603 				drv_usecwait(50000);
18954667Smh27603 			} else
18964667Smh27603 				domp->status = PPMD_OFF;
18974667Smh27603 		}
18984667Smh27603 
18994667Smh27603 		PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
19004667Smh27603 		    (ret == 0) ? "turned" : "failed to turn",
19014667Smh27603 		    (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
19024667Smh27603 		    domp->name))
19034667Smh27603 
19044667Smh27603 		break;
19054667Smh27603 
19064667Smh27603 	default:
19074667Smh27603 		PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
19084667Smh27603 		    str, dc->method))
19094667Smh27603 		return (DDI_FAILURE);
19104667Smh27603 	}
19114667Smh27603 
19124667Smh27603 	return (DDI_SUCCESS);
19134667Smh27603 }
19144667Smh27603 
19154667Smh27603 
19164667Smh27603 /*
19174667Smh27603  * pci slot domain is formed of pci device(s) reside in a pci slot.
19184667Smh27603  * This function monitors domain device's power level change, such
19194667Smh27603  * that,
19204667Smh27603  *   when all domain power count has gone to 0, it attempts to turn off
19214667Smh27603  *        the pci slot's clock;
19224667Smh27603  *   if any domain device is powering up, it'll turn on the pci slot's
19234667Smh27603  *        clock as the first thing.
19244667Smh27603  */
19254667Smh27603 /* ARGUSED */
19264667Smh27603 static int
ppm_manage_pci(dev_info_t * dip,power_req_t * reqp,int * result)19274667Smh27603 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
19284667Smh27603 {
19294667Smh27603 #ifdef DEBUG
19304667Smh27603 	char *str = "ppm_manage_pci";
19314667Smh27603 #endif
19324667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
19334667Smh27603 	int old, new, cmpt;
19344667Smh27603 	ppm_dev_t *ppmd;
19354667Smh27603 	ppm_domain_t *domp;
19364667Smh27603 	int incr = 0;
19374667Smh27603 	int dummy_ret;
19384667Smh27603 
19394667Smh27603 	*result = DDI_SUCCESS;
19404667Smh27603 	switch (reqp->request_type) {
19414667Smh27603 	case PMR_PPM_SET_POWER:
19424667Smh27603 		pwr_func = ppm_change_power_level;
19434667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
19444667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
19454667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
19464667Smh27603 		break;
19474667Smh27603 
19484667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
19494667Smh27603 		pwr_func = ppm_record_level_change;
19504667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
19514667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
19524667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
19534667Smh27603 		break;
19544667Smh27603 
19554667Smh27603 	default:
19564667Smh27603 		*result = DDI_FAILURE;
19574667Smh27603 		return (DDI_FAILURE);
19584667Smh27603 	}
19594667Smh27603 
19604667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
19614667Smh27603 		if (cmpt == ppmd->cmpt)
19624667Smh27603 			break;
19634667Smh27603 	if (!ppmd) {
19644667Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
19654667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
19664667Smh27603 		*result = DDI_FAILURE;
19674667Smh27603 		return (DDI_FAILURE);
19684667Smh27603 	}
19694667Smh27603 	domp = ppmd->domp;
19704667Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
19714667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
19724667Smh27603 	    ppmd->path, cmpt, old, new))
19734667Smh27603 
19744667Smh27603 	ASSERT(old == ppmd->level);
19754667Smh27603 	if (new == ppmd->level)
19764667Smh27603 		return (DDI_SUCCESS);
19774667Smh27603 
19784667Smh27603 	PPM_LOCK_DOMAIN(domp);
19794667Smh27603 
19804667Smh27603 	if (new > 0) {		/* device powering up */
19814667Smh27603 		if (domp->status == PPMD_OFF) {
19824667Smh27603 
19834667Smh27603 			/* cannot be off during (chpt, resume) window */
19844667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
19854667Smh27603 
19864667Smh27603 			/* either both OFF or both ON */
19874667Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
19884667Smh27603 
19894667Smh27603 			PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
19904667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
19914667Smh27603 
19924667Smh27603 			*result = ppm_switch_clock(domp, PPMD_ON);
19934667Smh27603 			if (*result != DDI_SUCCESS) {
19944667Smh27603 				PPMD(D_PCI, ("\tcan't switch on pci clock: "
19954667Smh27603 				    "ret(%d)\n", *result))
19964667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
19974667Smh27603 				return (DDI_FAILURE);
19984667Smh27603 			}
19994667Smh27603 		}
20004667Smh27603 
20014667Smh27603 		if (old == 0) {
20024667Smh27603 			domp->pwr_cnt++;
20034667Smh27603 			incr = 1;
20044667Smh27603 		}
20054667Smh27603 
20064667Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
20074667Smh27603 		    domp->name, domp->pwr_cnt))
20084667Smh27603 	}
20094667Smh27603 
20104667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
20114667Smh27603 
20124667Smh27603 	ASSERT(domp->pwr_cnt > 0);
20134667Smh27603 
20144667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
20154667Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
20164667Smh27603 		    ppmd->path, *result))
20174667Smh27603 	}
20184667Smh27603 
20194667Smh27603 	PPM_LOCK_DOMAIN(domp);
20204667Smh27603 
20214667Smh27603 	/*
20224667Smh27603 	 * Decr the power count in two cases:
20234667Smh27603 	 *
20244667Smh27603 	 *   1) request was to power device down and was successful
20254667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
20264667Smh27603 	 */
20274667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
20284667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
20294667Smh27603 		ASSERT(domp->pwr_cnt > 0);
20304667Smh27603 		domp->pwr_cnt--;
20314667Smh27603 	}
20324667Smh27603 
20334667Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
20344667Smh27603 	    domp->name, domp->pwr_cnt))
20354667Smh27603 
20364667Smh27603 	/*
20374667Smh27603 	 * call to pwr_func will update ppm data structures, if it
20384667Smh27603 	 * succeeds. ppm should return whatever is the return value
20394667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
20404667Smh27603 	 * always in sync. Use dummy_ret from here for any further
20414667Smh27603 	 * return values.
20424667Smh27603 	 */
20434667Smh27603 	if ((domp->pwr_cnt == 0) &&
20444667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
20454667Smh27603 	    ppm_none_else_holds_power(domp)) {
20464667Smh27603 
20474667Smh27603 		PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
20484667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
20494667Smh27603 
20504667Smh27603 		dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
20514667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
20524667Smh27603 			PPMD(D_PCI, ("\tCan't switch clock off: "
20534667Smh27603 			    "ret(%d)\n", dummy_ret))
20544667Smh27603 		}
20554667Smh27603 	}
20564667Smh27603 
20574667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
20584667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
20594667Smh27603 	return (*result);
20604667Smh27603 }
20614667Smh27603 
20624667Smh27603 /*
20634667Smh27603  * When the driver for the primary PCI-Express child has set the device to
20644667Smh27603  * lowest power (D3hot), we come here to save even more power by transitioning
20654667Smh27603  * the slot to D3cold.  Similarly, if the slot is in D3cold and we need to
20664667Smh27603  * power up the child, we come here first to power up the slot.
20674667Smh27603  */
20684667Smh27603 /* ARGUSED */
20694667Smh27603 static int
ppm_manage_pcie(dev_info_t * dip,power_req_t * reqp,int * result)20704667Smh27603 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
20714667Smh27603 {
20724667Smh27603 #ifdef DEBUG
20734667Smh27603 	char *str = "ppm_manage_pcie";
20744667Smh27603 #endif
20754667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
20764667Smh27603 	int old, new, cmpt;
20774667Smh27603 	ppm_dev_t *ppmd;
20784667Smh27603 	ppm_domain_t *domp;
20794667Smh27603 	int incr = 0;
20804667Smh27603 	int dummy_ret;
20814667Smh27603 
20824667Smh27603 	*result = DDI_SUCCESS;
20834667Smh27603 	switch (reqp->request_type) {
20844667Smh27603 	case PMR_PPM_SET_POWER:
20854667Smh27603 		pwr_func = ppm_change_power_level;
20864667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
20874667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
20884667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
20894667Smh27603 		break;
20904667Smh27603 
20914667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
20924667Smh27603 		pwr_func = ppm_record_level_change;
20934667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
20944667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
20954667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
20964667Smh27603 		break;
20974667Smh27603 
20984667Smh27603 	default:
20994667Smh27603 		*result = DDI_FAILURE;
21004667Smh27603 		return (DDI_FAILURE);
21014667Smh27603 	}
21024667Smh27603 
21034667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
21044667Smh27603 		if (cmpt == ppmd->cmpt)
21054667Smh27603 			break;
21064667Smh27603 	if (!ppmd) {
21074667Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
21084667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
21094667Smh27603 		*result = DDI_FAILURE;
21104667Smh27603 		return (DDI_FAILURE);
21114667Smh27603 	}
21124667Smh27603 	domp = ppmd->domp;
21134667Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
21144667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
21154667Smh27603 	    ppmd->path, cmpt, old, new))
21164667Smh27603 
21174667Smh27603 	ASSERT(old == ppmd->level);
21184667Smh27603 	if (new == ppmd->level)
21194667Smh27603 		return (DDI_SUCCESS);
21204667Smh27603 
21214667Smh27603 	PPM_LOCK_DOMAIN(domp);
21224667Smh27603 
21234667Smh27603 	if (new > 0) {		/* device powering up */
21244667Smh27603 		if (domp->status == PPMD_OFF) {
21254667Smh27603 
21264667Smh27603 			/* cannot be off during (chpt, resume) window */
21274667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
21284667Smh27603 
21294667Smh27603 			/* either both OFF or both ON */
21304667Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
21314667Smh27603 
21324667Smh27603 			PPMD(D_PCI, ("About to turn on pcie slot for "
21334667Smh27603 			    "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
21344667Smh27603 
21354667Smh27603 			*result = ppm_pcie_pwr(domp, PPMD_ON);
21364667Smh27603 			if (*result != DDI_SUCCESS) {
21374667Smh27603 				PPMD(D_PCI, ("\tcan't switch on pcie slot: "
21384667Smh27603 				    "ret(%d)\n", *result))
21394667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
21404667Smh27603 				return (DDI_FAILURE);
21414667Smh27603 			}
21424667Smh27603 		}
21434667Smh27603 
21444667Smh27603 		if (old == 0) {
21454667Smh27603 			domp->pwr_cnt++;
21464667Smh27603 			incr = 1;
21474667Smh27603 		}
21484667Smh27603 
21494667Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
21504667Smh27603 		    domp->name, domp->pwr_cnt))
21514667Smh27603 	}
21524667Smh27603 
21534667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
21544667Smh27603 
21554667Smh27603 	ASSERT(domp->pwr_cnt > 0);
21564667Smh27603 
21574667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
21584667Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
21594667Smh27603 		    ppmd->path, *result))
21604667Smh27603 	}
21614667Smh27603 
21624667Smh27603 	PPM_LOCK_DOMAIN(domp);
21634667Smh27603 
21644667Smh27603 	/*
21654667Smh27603 	 * Decr the power count in two cases:
21664667Smh27603 	 *
21674667Smh27603 	 *   1) request was to power device down and was successful
21684667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
21694667Smh27603 	 */
21704667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
21714667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
21724667Smh27603 		ASSERT(domp->pwr_cnt > 0);
21734667Smh27603 		domp->pwr_cnt--;
21744667Smh27603 	}
21754667Smh27603 
21764667Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
21774667Smh27603 	    domp->name, domp->pwr_cnt))
21784667Smh27603 
21794667Smh27603 	/*
21804667Smh27603 	 * call to pwr_func will update ppm data structures, if it
21814667Smh27603 	 * succeeds. ppm should return whatever is the return value
21824667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
21834667Smh27603 	 * always in sync. Use dummy_ret from here for any further
21844667Smh27603 	 * return values.
21854667Smh27603 	 */
21864667Smh27603 	if ((domp->pwr_cnt == 0) &&
21874667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
21884667Smh27603 	    ppm_none_else_holds_power(domp)) {
21894667Smh27603 
21904667Smh27603 		PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
21914667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
21924667Smh27603 
21934667Smh27603 		dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
21944667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
21954667Smh27603 			PPMD(D_PCI, ("\tCan't switch pcie slot off: "
21964667Smh27603 			    "ret(%d)\n", dummy_ret))
21974667Smh27603 		}
21984667Smh27603 	}
21994667Smh27603 
22004667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
22014667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
22024667Smh27603 	return (*result);
22034667Smh27603 
22044667Smh27603 }
22054667Smh27603 
22064667Smh27603 /*
22074667Smh27603  * Set or clear a bit on a GPIO device.  These bits are used for various device-
22084667Smh27603  * specific purposes.
22094667Smh27603  */
22104667Smh27603 static int
ppm_gpioset(ppm_domain_t * domp,int key)22114667Smh27603 ppm_gpioset(ppm_domain_t *domp, int key)
22124667Smh27603 {
22134667Smh27603 #ifdef DEBUG
22144667Smh27603 	char	*str = "ppm_gpioset";
22154667Smh27603 #endif
22164667Smh27603 	ppm_dc_t *dc;
22174667Smh27603 	int	ret;
22184667Smh27603 	clock_t delay = 0;
22194667Smh27603 
22204667Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
22214667Smh27603 		if (dc->cmd == key)
22224667Smh27603 			break;
22234667Smh27603 	if (!dc || !dc->lh) {
22244667Smh27603 		PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
22254667Smh27603 		    str, domp->name))
22264667Smh27603 		return (DDI_FAILURE);
22274667Smh27603 	}
22284667Smh27603 
22294667Smh27603 	PPM_GET_IO_DELAY(dc, delay);
22304667Smh27603 	if (delay > 0) {
22314667Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
22324667Smh27603 		    "before change\n", domp->name, delay))
22334667Smh27603 		drv_usecwait(delay);
22344667Smh27603 	}
22354667Smh27603 
22364667Smh27603 	switch (dc->method) {
22375295Srandyf #ifdef sun4u
22384667Smh27603 	case PPMDC_I2CKIO: {
22394667Smh27603 		i2c_gpio_t i2c_req;
22404667Smh27603 		ppm_dev_t *pdev;
22414667Smh27603 		int pio_save;
22424667Smh27603 		extern int do_polled_io;
22434667Smh27603 		extern uint_t cfb_inuse;
22444667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
22454667Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
22464667Smh27603 
22474667Smh27603 		pio_save = do_polled_io;
22484667Smh27603 		if (cfb_inuse) {
22494667Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
22504667Smh27603 				if (pm_is_cfb(pdev->dip)) {
22514667Smh27603 					do_polled_io = 1;
22524667Smh27603 					PPMD(D_GPIO, ("%s: cfb is in use, "
22534667Smh27603 					    "i2c transaction is done in "
22544667Smh27603 					    "poll-mode.\n", str))
22554667Smh27603 					break;
22564667Smh27603 				}
22574667Smh27603 			}
22584667Smh27603 		}
22594667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
22604667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
22614667Smh27603 		do_polled_io = pio_save;
22624667Smh27603 
22634667Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22644667Smh27603 		    "to gpio\n",
22654667Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
22664667Smh27603 		    domp->name,
22674667Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
22684667Smh27603 		    dc->m_un.i2c.val))
22694667Smh27603 
22704667Smh27603 		break;
22714667Smh27603 	}
22724667Smh27603 #endif
22735295Srandyf 
22744667Smh27603 	case PPMDC_KIO:
22754667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
22764667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
22774667Smh27603 		    NULL);
22784667Smh27603 
22794667Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22804667Smh27603 		    "to gpio\n",
22814667Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
22824667Smh27603 		    domp->name,
22834667Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
22844667Smh27603 		    dc->m_un.kio.val))
22854667Smh27603 
22864667Smh27603 		break;
22874667Smh27603 
22884667Smh27603 	default:
22894667Smh27603 		PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
22904667Smh27603 		    str, domp->dc->method))
22914667Smh27603 		return (DDI_FAILURE);
22924667Smh27603 	}
22934667Smh27603 
22944667Smh27603 	/* implement any post op delay. */
22956464Smh27603 	PPM_GET_IO_POST_DELAY(dc, delay);
22964667Smh27603 	if (delay > 0) {
22974667Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
22984667Smh27603 		    "after change\n", domp->name, delay))
22994667Smh27603 		drv_usecwait(delay);
23004667Smh27603 	}
23014667Smh27603 
23024667Smh27603 	return (ret);
23034667Smh27603 }
23044667Smh27603 
23054667Smh27603 static int
ppm_pcie_pwr(ppm_domain_t * domp,int onoff)23064667Smh27603 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
23074667Smh27603 {
23084667Smh27603 #ifdef DEBUG
23094667Smh27603 	char *str = "ppm_pcie_pwr";
23104667Smh27603 #endif
23114667Smh27603 	int ret = DDI_FAILURE;
23124667Smh27603 	ppm_dc_t *dc;
23134667Smh27603 	clock_t delay;
23144667Smh27603 
23154667Smh27603 	ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
23164667Smh27603 
23174667Smh27603 	dc = ppm_lookup_dc(domp,
23184667Smh27603 	    onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
23194667Smh27603 	if (dc) {
23204667Smh27603 
23214667Smh27603 		/*
23224667Smh27603 		 * Invoke layered ioctl for pcie root complex nexus to
23234667Smh27603 		 * transition the link
23244667Smh27603 		 */
23254667Smh27603 		ASSERT(dc->method == PPMDC_KIO);
23264667Smh27603 		delay = dc->m_un.kio.delay;
23274667Smh27603 		if (delay > 0) {
23284667Smh27603 			PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23294667Smh27603 			    "before change\n", domp->name, delay))
23304667Smh27603 			drv_usecwait(delay);
23314667Smh27603 		}
23324667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
23334667Smh27603 		    (intptr_t)&(dc->m_un.kio.val),
23344667Smh27603 		    FWRITE | FKIOCTL, kcred, NULL);
23354667Smh27603 		if (ret == DDI_SUCCESS) {
23364667Smh27603 			delay = dc->m_un.kio.post_delay;
23374667Smh27603 			if (delay > 0) {
23384667Smh27603 				PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23394667Smh27603 				    "after change\n", domp->name, delay))
23404667Smh27603 				drv_usecwait(delay);
23414667Smh27603 			}
23424667Smh27603 		} else {
23434667Smh27603 			PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
23444667Smh27603 			    str, domp->name))
23454667Smh27603 			return (ret);
23464667Smh27603 		}
23474667Smh27603 	}
23484667Smh27603 
23494667Smh27603 	switch (onoff) {
23504667Smh27603 	case PPMD_OFF:
23514667Smh27603 		/* Turn off the clock for this slot. */
23524667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
23534667Smh27603 			PPMD(D_GPIO,
23544667Smh27603 			    ("%s: failed to turn off domain(%s) clock\n",
23554667Smh27603 			    str, domp->name))
23564667Smh27603 			return (ret);
23574667Smh27603 		}
23584667Smh27603 
23594667Smh27603 		/* Turn off the power to this slot */
23604667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
23614667Smh27603 			PPMD(D_GPIO,
23624667Smh27603 			    ("%s: failed to turn off domain(%s) power\n",
23634667Smh27603 			    str, domp->name))
23644667Smh27603 			return (ret);
23654667Smh27603 		}
23664667Smh27603 		break;
23674667Smh27603 	case PPMD_ON:
23684667Smh27603 		/* Assert RESET for this slot. */
23694667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
23704667Smh27603 			PPMD(D_GPIO,
23714667Smh27603 			    ("%s: failed to assert reset for domain(%s)\n",
23724667Smh27603 			    str, domp->name))
23734667Smh27603 			return (ret);
23744667Smh27603 		}
23754667Smh27603 
23764667Smh27603 		/* Turn on the power to this slot */
23774667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
23784667Smh27603 			PPMD(D_GPIO,
23794667Smh27603 			    ("%s: failed to turn on domain(%s) power\n",
23804667Smh27603 			    str, domp->name))
23814667Smh27603 			return (ret);
23824667Smh27603 		}
23834667Smh27603 
23844667Smh27603 		/* Turn on the clock for this slot */
23854667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
23864667Smh27603 			PPMD(D_GPIO,
23874667Smh27603 			    ("%s: failed to turn on domain(%s) clock\n",
23884667Smh27603 			    str, domp->name))
23894667Smh27603 			return (ret);
23904667Smh27603 		}
23914667Smh27603 
23924667Smh27603 		/* De-assert RESET for this slot. */
23934667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
23944667Smh27603 			PPMD(D_GPIO,
23954667Smh27603 			    ("%s: failed to de-assert reset for domain(%s)\n",
23964667Smh27603 			    str, domp->name))
23974667Smh27603 			return (ret);
23984667Smh27603 		}
23994667Smh27603 
24004667Smh27603 		dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
24014667Smh27603 		if (dc) {
24024667Smh27603 			/*
24034667Smh27603 			 * Invoke layered ioctl to PCIe root complex nexus
24044667Smh27603 			 * to transition the link.
24054667Smh27603 			 */
24064667Smh27603 			ASSERT(dc->method == PPMDC_KIO);
24074667Smh27603 			delay = dc->m_un.kio.delay;
24084667Smh27603 			if (delay > 0) {
24094667Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
24104667Smh27603 				    "before change\n", domp->name, delay))
24114667Smh27603 				drv_usecwait(delay);
24124667Smh27603 			}
24134667Smh27603 			ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
24144667Smh27603 			    (intptr_t)&(dc->m_un.kio.val),
24154667Smh27603 			    FWRITE | FKIOCTL, kcred, NULL);
24164667Smh27603 
24174667Smh27603 			if (ret != DDI_SUCCESS) {
24184667Smh27603 				PPMD(D_PCI, ("%s: layered ioctl to PCIe"
24194667Smh27603 				    "root complex nexus FAILed\n", str))
24204667Smh27603 				return (ret);
24214667Smh27603 			}
24224667Smh27603 
24234667Smh27603 			delay = dc->m_un.kio.post_delay;
24244667Smh27603 			if (delay > 0) {
24254667Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro "
24264667Smh27603 				    "seconds after change\n",
24274667Smh27603 				    domp->name, delay))
24284667Smh27603 				drv_usecwait(delay);
24294667Smh27603 			}
24304667Smh27603 		}
24314667Smh27603 		break;
24324667Smh27603 	default:
24334667Smh27603 		ASSERT(0);
24344667Smh27603 	}
24354667Smh27603 
24364667Smh27603 	PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
24374667Smh27603 	    str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
24384667Smh27603 	    onoff == PPMD_ON ? "ON" : "OFF"))
24394667Smh27603 
24404667Smh27603 	domp->status = onoff;
24414667Smh27603 	return (ret);
24424667Smh27603 }
24434667Smh27603 
24444667Smh27603 
24454667Smh27603 /*
24464667Smh27603  * Change the power level for a component of a device.  If the change
24474667Smh27603  * arg is true, we call the framework to actually change the device's
24484667Smh27603  * power; otherwise, we just update our own copy of the power level.
24494667Smh27603  */
24504667Smh27603 static int
ppm_set_level(ppm_dev_t * ppmd,int cmpt,int level,boolean_t change)24514667Smh27603 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
24524667Smh27603 {
24534667Smh27603 #ifdef DEBUG
24544667Smh27603 	char *str = "ppm_set_level";
24554667Smh27603 #endif
24564667Smh27603 	int ret;
24574667Smh27603 
24584667Smh27603 	ret = DDI_SUCCESS;
24594667Smh27603 	if (change)
24604667Smh27603 		ret = pm_power(ppmd->dip, cmpt, level);
24614667Smh27603 
24624667Smh27603 	PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
24634667Smh27603 	    str, ppmd->path, change, ppmd->level, level, ret))
24644667Smh27603 
24654667Smh27603 	if (ret == DDI_SUCCESS) {
24664667Smh27603 		ppmd->level = level;
24674667Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
24684667Smh27603 	}
24694667Smh27603 
24704667Smh27603 	return (ret);
24714667Smh27603 }
24724667Smh27603 
24734667Smh27603 
24744667Smh27603 static int
ppm_change_power_level(ppm_dev_t * ppmd,int cmpt,int level)24754667Smh27603 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
24764667Smh27603 {
24774667Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
24784667Smh27603 }
24794667Smh27603 
24804667Smh27603 
24814667Smh27603 static int
ppm_record_level_change(ppm_dev_t * ppmd,int cmpt,int level)24824667Smh27603 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
24834667Smh27603 {
24844667Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
24854667Smh27603 }
24864667Smh27603 
24874667Smh27603 
24884667Smh27603 static void
ppm_manage_led(int action)24894667Smh27603 ppm_manage_led(int action)
24904667Smh27603 {
24914667Smh27603 	ppm_domain_t *domp;
24924667Smh27603 	ppm_unit_t *unitp;
24934667Smh27603 	timeout_id_t	tid;
24944667Smh27603 
24954667Smh27603 
24964667Smh27603 	PPMD(D_LED, ("ppm_manage_led: action: %s\n",
24974667Smh27603 	    (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
24984667Smh27603 	    "PPM_LED_SOLIDON"))
24994667Smh27603 
25004667Smh27603 	/*
25014667Smh27603 	 * test whether led operation is practically supported,
25024667Smh27603 	 * if not, we waive without pressing for reasons
25034667Smh27603 	 */
25044667Smh27603 	if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
25054667Smh27603 		return;
25064667Smh27603 
25074667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25084667Smh27603 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
25094667Smh27603 		domp = domp->next;
25104667Smh27603 
25114667Smh27603 	mutex_enter(&unitp->lock);
25124667Smh27603 	if (action == PPM_LED_BLINKING) {
25134667Smh27603 		ppm_set_led(domp, PPMD_OFF);
25144667Smh27603 		unitp->led_tid = timeout(
25154667Smh27603 		    ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
25164667Smh27603 
25174667Smh27603 	} else {	/* PPM_LED_SOLIDON */
25184667Smh27603 		ASSERT(action == PPM_LED_SOLIDON);
25194667Smh27603 		tid = unitp->led_tid;
25204667Smh27603 		unitp->led_tid = 0;
25214667Smh27603 
25224667Smh27603 		mutex_exit(&unitp->lock);
25234667Smh27603 		(void) untimeout(tid);
25244667Smh27603 
25254667Smh27603 		mutex_enter(&unitp->lock);
25264667Smh27603 		ppm_set_led(domp, PPMD_ON);
25274667Smh27603 	}
25284667Smh27603 	mutex_exit(&unitp->lock);
25294667Smh27603 }
25304667Smh27603 
25314667Smh27603 
25324667Smh27603 static void
ppm_set_led(ppm_domain_t * domp,int val)25334667Smh27603 ppm_set_led(ppm_domain_t *domp, int val)
25344667Smh27603 {
25354667Smh27603 	int ret;
25364667Smh27603 
25374667Smh27603 	ret = ppm_gpioset(domp,
25384667Smh27603 	    (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
25394667Smh27603 
25404667Smh27603 	PPMD(D_LED, ("ppm_set_led:  %s LED from %s\n",
25414667Smh27603 	    (ret == 0) ? "turned" : "FAILed to turn",
25424667Smh27603 	    (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
25434667Smh27603 
25444667Smh27603 	if (ret == DDI_SUCCESS)
25454667Smh27603 		domp->status = val;
25464667Smh27603 }
25474667Smh27603 
25484667Smh27603 
25494667Smh27603 static void
ppm_blink_led(void * arg)25504667Smh27603 ppm_blink_led(void *arg)
25514667Smh27603 {
25524667Smh27603 	ppm_unit_t *unitp;
25534667Smh27603 	clock_t intvl;
25544667Smh27603 	ppm_domain_t *domp = arg;
25554667Smh27603 
25564667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25574667Smh27603 
25584667Smh27603 	mutex_enter(&unitp->lock);
25594667Smh27603 	if (unitp->led_tid == 0) {
25604667Smh27603 		mutex_exit(&unitp->lock);
25614667Smh27603 		return;
25624667Smh27603 	}
25634667Smh27603 
25644667Smh27603 	if (domp->status == PPMD_ON) {
25654667Smh27603 		ppm_set_led(domp, PPMD_OFF);
25664667Smh27603 		intvl = PPM_LEDOFF_INTERVAL;
25674667Smh27603 	} else {
25684667Smh27603 		ppm_set_led(domp, PPMD_ON);
25694667Smh27603 		intvl = PPM_LEDON_INTERVAL;
25704667Smh27603 	}
25714667Smh27603 
25724667Smh27603 	unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
25734667Smh27603 	mutex_exit(&unitp->lock);
25744667Smh27603 }
25754667Smh27603 
25764667Smh27603 /*
25774667Smh27603  * Function to power up a domain, if required. It also increments the
25784667Smh27603  * domain pwr_cnt to prevent it from going down.
25794667Smh27603  */
25804667Smh27603 static int
ppm_power_up_domain(dev_info_t * dip)25814667Smh27603 ppm_power_up_domain(dev_info_t *dip)
25824667Smh27603 {
25834667Smh27603 	int		ret = DDI_SUCCESS;
25844667Smh27603 	ppm_domain_t	*domp;
25854667Smh27603 	char		*str = "ppm_power_up_domain";
25864667Smh27603 
25874667Smh27603 	domp = ppm_lookup_dev(dip);
25884667Smh27603 	ASSERT(domp);
25894667Smh27603 	mutex_enter(&domp->lock);
25904667Smh27603 	switch (domp->model) {
25914667Smh27603 	case PPMD_FET:
25924667Smh27603 		if (domp->status == PPMD_OFF) {
25934667Smh27603 			if ((ret = ppm_fetset(domp,  PPMD_ON)) ==
25944667Smh27603 			    DDI_SUCCESS) {
25954667Smh27603 				PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
25964667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
25974667Smh27603 			} else {
25984667Smh27603 				PPMD(D_FET, ("%s: couldn't turn on fet "
25994667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26004667Smh27603 				    PM_ADDR(dip)))
26014667Smh27603 			}
26024667Smh27603 		}
26034667Smh27603 		break;
26044667Smh27603 
26054667Smh27603 	case PPMD_PCI:
26064667Smh27603 	case PPMD_PCI_PROP:
26074667Smh27603 		if (domp->status == PPMD_OFF) {
26084667Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
26094667Smh27603 			    DDI_SUCCESS) {
26104667Smh27603 				PPMD(D_PCI, ("%s: turned on clock for "
26114667Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
26124667Smh27603 				    PM_ADDR(dip)))
26134667Smh27603 			} else {
26144667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on clock "
26154667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26164667Smh27603 				    PM_ADDR(dip)))
26174667Smh27603 			}
26184667Smh27603 		}
26194667Smh27603 		break;
26204667Smh27603 
26214667Smh27603 	case PPMD_PCIE:
26224667Smh27603 		if (domp->status == PPMD_OFF) {
26234667Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
26244667Smh27603 			    DDI_SUCCESS) {
26254667Smh27603 				PPMD(D_PCI, ("%s: turned on link for "
26264667Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
26274667Smh27603 				    PM_ADDR(dip)))
26284667Smh27603 			} else {
26294667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on link "
26304667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26314667Smh27603 				    PM_ADDR(dip)))
26324667Smh27603 			}
26334667Smh27603 		}
26344667Smh27603 		break;
26354667Smh27603 
26364667Smh27603 	default:
26374667Smh27603 		break;
26384667Smh27603 	}
26394667Smh27603 	if (ret == DDI_SUCCESS)
26404667Smh27603 		domp->pwr_cnt++;
26414667Smh27603 	mutex_exit(&domp->lock);
26424667Smh27603 	return (ret);
26434667Smh27603 }
26444667Smh27603 
26454667Smh27603 /*
26464667Smh27603  * Decrements the domain pwr_cnt. if conditions to power down the domain
26474667Smh27603  * are met, powers down the domain,.
26484667Smh27603  */
26494667Smh27603 static int
ppm_power_down_domain(dev_info_t * dip)26504667Smh27603 ppm_power_down_domain(dev_info_t *dip)
26514667Smh27603 {
26524667Smh27603 	int		ret = DDI_SUCCESS;
26534667Smh27603 	char		*str = "ppm_power_down_domain";
26544667Smh27603 	ppm_domain_t	*domp;
26554667Smh27603 
26564667Smh27603 	domp = ppm_lookup_dev(dip);
26574667Smh27603 	ASSERT(domp);
26584667Smh27603 	mutex_enter(&domp->lock);
26594667Smh27603 	ASSERT(domp->pwr_cnt > 0);
26604667Smh27603 	domp->pwr_cnt--;
26614667Smh27603 	switch (domp->model) {
26624667Smh27603 	case PPMD_FET:
26634667Smh27603 		if ((domp->pwr_cnt == 0) &&
26644667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
26654667Smh27603 		    ppm_none_else_holds_power(domp)) {
26664667Smh27603 			if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
26674667Smh27603 			    DDI_SUCCESS) {
26684667Smh27603 				PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
26694667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
26704667Smh27603 			} else {
26714667Smh27603 				PPMD(D_FET, ("%s: couldn't turn off FET for "
26724667Smh27603 				    " %s@%s\n", str, PM_NAME(dip),
26734667Smh27603 				    PM_ADDR(dip)))
26744667Smh27603 			}
26754667Smh27603 		}
26764667Smh27603 		break;
26774667Smh27603 
26784667Smh27603 	case PPMD_PCI:
26794667Smh27603 	case PPMD_PCI_PROP:
26804667Smh27603 		if ((domp->pwr_cnt == 0) &&
26814667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
26824667Smh27603 		    ppm_none_else_holds_power(domp)) {
26834667Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
26844667Smh27603 			    DDI_SUCCESS) {
26854667Smh27603 				PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
26864667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
26874667Smh27603 			} else {
26884667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off clock "
26894667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26904667Smh27603 				    PM_ADDR(dip)))
26914667Smh27603 			}
26924667Smh27603 		}
26934667Smh27603 		break;
26944667Smh27603 
26954667Smh27603 	case PPMD_PCIE:
26964667Smh27603 		if ((domp->pwr_cnt == 0) &&
26974667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
26984667Smh27603 		    ppm_none_else_holds_power(domp)) {
26994667Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
27004667Smh27603 			    DDI_SUCCESS) {
27014667Smh27603 				PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
27024667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
27034667Smh27603 			} else {
27044667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off link "
27054667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
27064667Smh27603 				    PM_ADDR(dip)))
27074667Smh27603 			}
27084667Smh27603 		}
27094667Smh27603 		break;
27104667Smh27603 
27114667Smh27603 	default:
27124667Smh27603 		break;
27134667Smh27603 	}
27144667Smh27603 	mutex_exit(&domp->lock);
27154667Smh27603 	return (ret);
27164667Smh27603 }
27175295Srandyf 
27185295Srandyf static int
ppm_manage_sx(s3a_t * s3ap,int enter)27195295Srandyf ppm_manage_sx(s3a_t *s3ap, int enter)
27205295Srandyf {
27215295Srandyf 	ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
27225295Srandyf 	ppm_dc_t *dc;
27235295Srandyf 	int ret = 0;
27245295Srandyf 
27255295Srandyf 	if (domp == NULL) {
27265295Srandyf 		PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
27275295Srandyf 		return (ENODEV);
27285295Srandyf 	}
27295295Srandyf 	PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
27305295Srandyf 	    enter))
27315295Srandyf 	switch (s3ap->s3a_state) {
27325295Srandyf 	case S3:
27335295Srandyf 		if (enter) {
27345295Srandyf 			dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
27355295Srandyf 		} else {
27365295Srandyf 			dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
27375295Srandyf 		}
27385295Srandyf 		ASSERT(dc && dc->method == PPMDC_KIO);
27395295Srandyf 		PPMD(D_CPR,
27405295Srandyf 		    ("ppm_manage_sx: calling acpi driver (handle %p)"
27415295Srandyf 		    " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
27425295Srandyf 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
27435295Srandyf 		    (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
27445295Srandyf 		break;
27455295Srandyf 
27465295Srandyf 	case S4:
27475295Srandyf 		/* S4 is not supported yet */
27485295Srandyf 		return (EINVAL);
27495295Srandyf 	default:
27505295Srandyf 		ASSERT(0);
27515295Srandyf 	}
27525295Srandyf 	return (ret);
27535295Srandyf }
27545295Srandyf 
27555295Srandyf /*
27565295Srandyf  * Search enable/disable lists, which are encoded in ppm.conf as an array
27575295Srandyf  * of char strings.
27585295Srandyf  */
27595295Srandyf static int
ppm_search_list(pm_searchargs_t * sl)27605295Srandyf ppm_search_list(pm_searchargs_t *sl)
27615295Srandyf {
27625295Srandyf 	int i;
27635295Srandyf 	int flags = DDI_PROP_DONTPASS;
27645295Srandyf 	ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
27655295Srandyf 	char **pp;
27665295Srandyf 	char *starp;
27675295Srandyf 	uint_t nelements;
27685295Srandyf 	char *manuf = sl->pms_manufacturer;
27695295Srandyf 	char *prod = sl->pms_product;
27705295Srandyf 
27715295Srandyf 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
27725295Srandyf 	    sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
27735295Srandyf 		PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
27745295Srandyf 		    sl->pms_listname))
27755295Srandyf 		return (EINVAL);
27765295Srandyf 	}
27775295Srandyf 	ASSERT((nelements & 1) == 0);		/* must be even */
27785295Srandyf 
27795295Srandyf 	PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
27805295Srandyf 
27815295Srandyf 	for (i = 0; i < nelements; i += 2) {
27825295Srandyf 		PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
27835295Srandyf 		/* we support only a trailing '*' pattern match */
27845295Srandyf 		if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
27855295Srandyf 			/* LINTED - ptrdiff overflow */
27865295Srandyf 			if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
27875295Srandyf 				PPMD(D_CPR, (" no match %s with %s\n",
27885295Srandyf 				    manuf, pp[i + 1]))
27895295Srandyf 				continue;
27905295Srandyf 			}
27915295Srandyf 		}
27925295Srandyf 		if ((starp = strchr(pp[i + 1], '*')) != NULL &&
27935295Srandyf 		    *(starp + 1) == 0) {
27945295Srandyf 			if (strncmp(prod,
27955295Srandyf 			    /* LINTED - ptrdiff overflow */
27965295Srandyf 			    pp[i + 1], (starp - pp[i + 1])) != 0) {
27975295Srandyf 				PPMD(D_CPR, (" no match %s with %s\n",
27985295Srandyf 				    prod, pp[i + 1]))
27995295Srandyf 				continue;
28005295Srandyf 			}
28015295Srandyf 		}
28025295Srandyf 		if (strcmp(manuf, pp[i]) == 0 &&
28035295Srandyf 		    (strcmp(prod, pp[i + 1]) == 0)) {
28045295Srandyf 			PPMD(D_CPR, (" match\n"))
28055295Srandyf 			ddi_prop_free(pp);
28065295Srandyf 			return (0);
28075295Srandyf 		}
28085295Srandyf 		PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
28095295Srandyf 		    manuf, pp[i], prod, pp[i + 1]))
28105295Srandyf 	}
28115295Srandyf 	ddi_prop_free(pp);
28125295Srandyf 	return (ENODEV);
28135295Srandyf }
2814