xref: /illumos-gate/usr/src/uts/common/io/ppm/ppm.c (revision 584b574a)
15cff7825Smh27603 /*
25cff7825Smh27603  * CDDL HEADER START
35cff7825Smh27603  *
45cff7825Smh27603  * The contents of this file are subject to the terms of the
55cff7825Smh27603  * Common Development and Distribution License (the "License").
65cff7825Smh27603  * You may not use this file except in compliance with the License.
75cff7825Smh27603  *
85cff7825Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95cff7825Smh27603  * or http://www.opensolaris.org/os/licensing.
105cff7825Smh27603  * See the License for the specific language governing permissions
115cff7825Smh27603  * and limitations under the License.
125cff7825Smh27603  *
135cff7825Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
145cff7825Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155cff7825Smh27603  * If applicable, add the following below this CDDL HEADER, with the
165cff7825Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
175cff7825Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
185cff7825Smh27603  *
195cff7825Smh27603  * CDDL HEADER END
205cff7825Smh27603  */
215cff7825Smh27603 /*
22d67944fbSScott Rotondo  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235cff7825Smh27603  * Use is subject to license terms.
245cff7825Smh27603  */
25444f66e7SMark Haywood /*
26444f66e7SMark Haywood  * Copyright (c) 2009,  Intel Corporation.
27444f66e7SMark Haywood  * All Rights Reserved.
28444f66e7SMark Haywood  */
295cff7825Smh27603 
305cff7825Smh27603 
315cff7825Smh27603 /*
325cff7825Smh27603  * Platform Power Management master pseudo driver -
335cff7825Smh27603  *    - attaches only  when ppm.conf file is present, indicating a
345cff7825Smh27603  *      workstation (since Excalibur era ) that is designed to
355cff7825Smh27603  *      be MOU-3 EPA compliant and which uses platform-specific
365cff7825Smh27603  *	hardware to do so;
375cff7825Smh27603  *    - this pseudo driver uses a set of simple satellite
385cff7825Smh27603  *      device drivers responsible for accessing platform
395cff7825Smh27603  *      specific devices to modify the registers they own.
405cff7825Smh27603  *	ppm drivers tells these	satellite drivers what to do
415cff7825Smh27603  *	according to using command values taken from ppm.conf.
425cff7825Smh27603  */
435cff7825Smh27603 #include <sys/conf.h>
445cff7825Smh27603 #include <sys/stat.h>
455cff7825Smh27603 #include <sys/file.h>
465cff7825Smh27603 #include <sys/types.h>
475cff7825Smh27603 #include <sys/param.h>
485cff7825Smh27603 #include <sys/open.h>
495cff7825Smh27603 #include <sys/callb.h>
505cff7825Smh27603 #include <sys/va_list.h>
515cff7825Smh27603 #include <sys/errno.h>
525cff7825Smh27603 #include <sys/modctl.h>
535cff7825Smh27603 #include <sys/sysmacros.h>
545cff7825Smh27603 #include <sys/ddi_impldefs.h>
555cff7825Smh27603 #include <sys/promif.h>
565cff7825Smh27603 #include <sys/epm.h>
575cff7825Smh27603 #include <sys/sunpm.h>
585cff7825Smh27603 #include <sys/ppmio.h>
595cff7825Smh27603 #include <sys/sunldi.h>
605cff7825Smh27603 #include <sys/ppmvar.h>
615cff7825Smh27603 #include <sys/ddi.h>
625cff7825Smh27603 #include <sys/sunddi.h>
635cff7825Smh27603 #include <sys/ppm_plat.h>
645cff7825Smh27603 
655cff7825Smh27603 /*
665cff7825Smh27603  * Note: When pm_power() is called (directly or indirectly) to change the
675cff7825Smh27603  * power level of a device and the call returns failure, DO NOT assume the
685cff7825Smh27603  * level is unchanged.  Doublecheck it against ppmd->level.
695cff7825Smh27603  */
705cff7825Smh27603 
715cff7825Smh27603 /*
725cff7825Smh27603  * cb_ops
735cff7825Smh27603  */
745cff7825Smh27603 static int	ppm_open(dev_t *, int, int, cred_t *);
755cff7825Smh27603 static int	ppm_close(dev_t, int, int, cred_t *);
765cff7825Smh27603 static int	ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
775cff7825Smh27603 
785cff7825Smh27603 static struct cb_ops ppm_cb_ops = {
795cff7825Smh27603 	ppm_open,		/* open	*/
805cff7825Smh27603 	ppm_close,		/* close */
815cff7825Smh27603 	nodev,			/* strategy */
825cff7825Smh27603 	nodev,			/* print */
835cff7825Smh27603 	nodev,			/* dump */
845cff7825Smh27603 	nodev,			/* read */
855cff7825Smh27603 	nodev,			/* write */
865cff7825Smh27603 	ppm_ioctl,		/* ioctl */
875cff7825Smh27603 	nodev,			/* devmap */
885cff7825Smh27603 	nodev,			/* mmap */
895cff7825Smh27603 	nodev,			/* segmap */
905cff7825Smh27603 	nochpoll,		/* poll */
915cff7825Smh27603 	ddi_prop_op,		/* prop_op */
925cff7825Smh27603 	NULL,			/* streamtab */
935cff7825Smh27603 	D_MP | D_NEW,		/* driver compatibility flag */
945cff7825Smh27603 	CB_REV,			/* cb_ops revision */
955cff7825Smh27603 	nodev,			/* async read */
965cff7825Smh27603 	nodev			/* async write */
975cff7825Smh27603 };
985cff7825Smh27603 
995cff7825Smh27603 /*
1005cff7825Smh27603  * bus_ops
1015cff7825Smh27603  */
1025cff7825Smh27603 static int	ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
1035cff7825Smh27603     void *);
1045cff7825Smh27603 
1055cff7825Smh27603 static struct bus_ops ppm_bus_ops = {
1065cff7825Smh27603 	BUSO_REV,		/* busops_rev		*/
1075cff7825Smh27603 	0,			/* bus_map		*/
1085cff7825Smh27603 	0,			/* bus_get_intrspec	*/
1095cff7825Smh27603 	0,			/* bus_add_intrspec	*/
1105cff7825Smh27603 	0,			/* bus_remove_intrspec	*/
1115cff7825Smh27603 	0,			/* bus_map_fault	*/
1125cff7825Smh27603 	ddi_no_dma_map,		/* bus_dma_map		*/
1135cff7825Smh27603 	ddi_no_dma_allochdl,	/* bus_dma_allochdl	*/
1145cff7825Smh27603 	NULL,			/* bus_dma_freehdl	*/
1155cff7825Smh27603 	NULL,			/* bus_dma_bindhdl	*/
1165cff7825Smh27603 	NULL,			/* bus_dma_unbindhdl	*/
1175cff7825Smh27603 	NULL,			/* bus_dma_flush	*/
1185cff7825Smh27603 	NULL,			/* bus_dma_win		*/
1195cff7825Smh27603 	NULL,			/* bus_dma_ctl		*/
1205cff7825Smh27603 	ppm_ctlops,		/* bus_ctl		*/
1215cff7825Smh27603 	0,			/* bus_prop_op		*/
1225cff7825Smh27603 	0,			/* bus_get_eventcookie	*/
1235cff7825Smh27603 	0,			/* bus_add_eventcall	*/
1245cff7825Smh27603 	0,			/* bus_remove_eventcall	*/
1255cff7825Smh27603 	0,			/* bus_post_event	*/
1265cff7825Smh27603 	0			/* bus_intr_ctl		*/
1275cff7825Smh27603 };
1285cff7825Smh27603 
1295cff7825Smh27603 /*
1305cff7825Smh27603  * dev_ops
1315cff7825Smh27603  */
1325cff7825Smh27603 static int	ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1335cff7825Smh27603 static int	ppm_attach(dev_info_t *, ddi_attach_cmd_t);
1345cff7825Smh27603 static int	ppm_detach(dev_info_t *, ddi_detach_cmd_t);
1355cff7825Smh27603 
1365cff7825Smh27603 static struct dev_ops ppm_ops = {
1375cff7825Smh27603 	DEVO_REV,		/* devo_rev */
1385cff7825Smh27603 	0,			/* refcnt */
1395cff7825Smh27603 	ppm_getinfo,		/* info */
1405cff7825Smh27603 	nulldev,		/* identify */
1415cff7825Smh27603 	nulldev,		/* probe */
1425cff7825Smh27603 	ppm_attach,		/* attach */
1435cff7825Smh27603 	ppm_detach,		/* detach */
1445cff7825Smh27603 	nodev,			/* reset */
1455cff7825Smh27603 	&ppm_cb_ops,		/* cb_ops */
1465cff7825Smh27603 	&ppm_bus_ops,		/* bus_ops */
14719397407SSherry Moore 	nulldev,		/* power */
14819397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
1495cff7825Smh27603 };
1505cff7825Smh27603 
1515cff7825Smh27603 extern struct mod_ops mod_driverops;
1525cff7825Smh27603 
1535cff7825Smh27603 static struct modldrv modldrv = {
1545cff7825Smh27603 	&mod_driverops,
15519397407SSherry Moore 	"platform pm driver",
1565cff7825Smh27603 	&ppm_ops
1575cff7825Smh27603 };
1585cff7825Smh27603 
1595cff7825Smh27603 static struct modlinkage modlinkage = {
1605cff7825Smh27603 	MODREV_1,
1615cff7825Smh27603 	&modldrv,
1625cff7825Smh27603 	NULL
1635cff7825Smh27603 };
1645cff7825Smh27603 
1655cff7825Smh27603 /*
1665cff7825Smh27603  * Global data structure and variables
1675cff7825Smh27603  */
1685cff7825Smh27603 int	ppm_inst = -1;
1695cff7825Smh27603 void	*ppm_statep;
1705cff7825Smh27603 ppm_domain_t *ppm_domain_p;
1715cff7825Smh27603 callb_id_t   *ppm_cprcb_id;
1725cff7825Smh27603 static kmutex_t ppm_cpr_window_lock;	/* guard ppm_cpr_window_flag */
1735cff7825Smh27603 static	boolean_t ppm_cpr_window_flag;	/* set indicating chpt-resume period */
1745cff7825Smh27603 
1755cff7825Smh27603 /* LED actions */
1765cff7825Smh27603 #define	PPM_LED_SOLIDON		0
1775cff7825Smh27603 #define	PPM_LED_BLINKING	1
1785cff7825Smh27603 
1795cff7825Smh27603 /*
1805cff7825Smh27603  * Debug
1815cff7825Smh27603  */
1825cff7825Smh27603 #ifdef	DEBUG
1835cff7825Smh27603 uint_t	ppm_debug = 0;
1845cff7825Smh27603 #endif
1855cff7825Smh27603 
1865cff7825Smh27603 /*
1875cff7825Smh27603  * Local function prototypes and data
1885cff7825Smh27603  */
1895cff7825Smh27603 static boolean_t	ppm_cpr_callb(void *, int);
1905cff7825Smh27603 static int		ppm_fetset(ppm_domain_t *, uint8_t);
1915cff7825Smh27603 static int		ppm_fetget(ppm_domain_t *, uint8_t *);
1925cff7825Smh27603 static int		ppm_gpioset(ppm_domain_t *, int);
1935cff7825Smh27603 static int		ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
1945cff7825Smh27603 static int		ppm_manage_pci(dev_info_t *, power_req_t *, int *);
1955cff7825Smh27603 static int		ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
1965cff7825Smh27603 static int		ppm_manage_fet(dev_info_t *, power_req_t *, int *);
1975cff7825Smh27603 static void		ppm_manage_led(int);
1985cff7825Smh27603 static void		ppm_set_led(ppm_domain_t *, int);
1995cff7825Smh27603 static void		ppm_blink_led(void *);
2005cff7825Smh27603 static void		ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
2015cff7825Smh27603 static int		ppm_set_level(ppm_dev_t *, int, int, boolean_t);
2025cff7825Smh27603 static int		ppm_change_power_level(ppm_dev_t *, int, int);
2035cff7825Smh27603 static int		ppm_record_level_change(ppm_dev_t *, int, int);
2045cff7825Smh27603 static int		ppm_switch_clock(ppm_domain_t *, int);
2055cff7825Smh27603 static int		ppm_pcie_pwr(ppm_domain_t *, int);
2065cff7825Smh27603 static int		ppm_power_up_domain(dev_info_t *dip);
2075cff7825Smh27603 static int		ppm_power_down_domain(dev_info_t *dip);
2085cff7825Smh27603 
2095cff7825Smh27603 int
_init(void)2105cff7825Smh27603 _init(void)
2115cff7825Smh27603 {
2125cff7825Smh27603 	if (ddi_soft_state_init(
2132df1fe9cSrandyf 	    &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
2142df1fe9cSrandyf 		PPMD(D_INIT, ("ppm: soft state init\n"))
2155cff7825Smh27603 		return (DDI_FAILURE);
2162df1fe9cSrandyf 	}
2175cff7825Smh27603 
2185cff7825Smh27603 	if (mod_install(&modlinkage) != DDI_SUCCESS) {
2195cff7825Smh27603 		ddi_soft_state_fini(&ppm_statep);
2205cff7825Smh27603 		return (DDI_FAILURE);
2215cff7825Smh27603 	}
2225cff7825Smh27603 	return (DDI_SUCCESS);
2235cff7825Smh27603 }
2245cff7825Smh27603 
2255cff7825Smh27603 
2265cff7825Smh27603 int
_fini(void)2275cff7825Smh27603 _fini(void)
2285cff7825Smh27603 {
2292df1fe9cSrandyf 	int error;
2302df1fe9cSrandyf 
2312df1fe9cSrandyf 	if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
2322df1fe9cSrandyf 		ddi_soft_state_fini(&ppm_statep);
2332df1fe9cSrandyf 
2342df1fe9cSrandyf 	return (error);
2355cff7825Smh27603 }
2365cff7825Smh27603 
2375cff7825Smh27603 
2385cff7825Smh27603 int
_info(struct modinfo * modinfop)2395cff7825Smh27603 _info(struct modinfo *modinfop)
2405cff7825Smh27603 {
2415cff7825Smh27603 	return (mod_info(&modlinkage, modinfop));
2425cff7825Smh27603 }
2435cff7825Smh27603 
2445cff7825Smh27603 
2455cff7825Smh27603 /* ARGSUSED */
2465cff7825Smh27603 int
ppm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2475cff7825Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2485cff7825Smh27603 {
2495cff7825Smh27603 	struct ppm_unit *unitp;
2505cff7825Smh27603 	dev_t	dev;
2515cff7825Smh27603 	int	instance;
2525cff7825Smh27603 	int	rval;
2535cff7825Smh27603 
2545cff7825Smh27603 	if (ppm_inst == -1)
2555cff7825Smh27603 		return (DDI_FAILURE);
2565cff7825Smh27603 
2575cff7825Smh27603 	switch (cmd) {
2585cff7825Smh27603 	case DDI_INFO_DEVT2DEVINFO:
2595cff7825Smh27603 		if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
2605cff7825Smh27603 			*resultp = unitp->dip;
2615cff7825Smh27603 			rval = DDI_SUCCESS;
2625cff7825Smh27603 		} else
2635cff7825Smh27603 			rval = DDI_FAILURE;
2645cff7825Smh27603 
2655cff7825Smh27603 		return (rval);
2665cff7825Smh27603 
2675cff7825Smh27603 	case DDI_INFO_DEVT2INSTANCE:
2685cff7825Smh27603 		dev = (dev_t)arg;
2695cff7825Smh27603 		instance = getminor(dev);
2705cff7825Smh27603 		*resultp = (void *)(uintptr_t)instance;
2715cff7825Smh27603 		return (DDI_SUCCESS);
2725cff7825Smh27603 
2735cff7825Smh27603 	default:
2745cff7825Smh27603 		return (DDI_FAILURE);
2755cff7825Smh27603 	}
2765cff7825Smh27603 }
2775cff7825Smh27603 
2785cff7825Smh27603 
2795cff7825Smh27603 /*
2805cff7825Smh27603  * attach(9E)
2815cff7825Smh27603  */
2825cff7825Smh27603 static int
ppm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2835cff7825Smh27603 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2845cff7825Smh27603 {
2855cff7825Smh27603 	ppm_unit_t *unitp;
2865cff7825Smh27603 	int ret;
2875cff7825Smh27603 #ifdef	DEBUG
2885cff7825Smh27603 	char *str = "ppm_attach";
2895cff7825Smh27603 #endif
2905cff7825Smh27603 
2915cff7825Smh27603 
2925cff7825Smh27603 	switch (cmd) {
2935cff7825Smh27603 	case DDI_ATTACH:
2945cff7825Smh27603 		PPMD(D_ATTACH, ("%s: attaching ...\n", str))
2955cff7825Smh27603 		break;
2965cff7825Smh27603 
2975cff7825Smh27603 	case DDI_RESUME:
2985cff7825Smh27603 		PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
2995cff7825Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3005cff7825Smh27603 		mutex_enter(&unitp->lock);
3015cff7825Smh27603 		unitp->states &= ~PPM_STATE_SUSPENDED;
3025cff7825Smh27603 		mutex_exit(&unitp->lock);
3035cff7825Smh27603 		return (DDI_SUCCESS);
3045cff7825Smh27603 
3055cff7825Smh27603 	default:
3065cff7825Smh27603 		cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
3075cff7825Smh27603 		    cmd, (void *)dip);
3085cff7825Smh27603 		return (DDI_FAILURE);
3095cff7825Smh27603 	}
3105cff7825Smh27603 
3115cff7825Smh27603 	if (ppm_inst != -1) {
3125cff7825Smh27603 		PPMD(D_ATTACH, ("%s: Already attached !", str))
3135cff7825Smh27603 		return (DDI_FAILURE);
3145cff7825Smh27603 	}
3155cff7825Smh27603 
3165cff7825Smh27603 	ppm_inst = ddi_get_instance(dip);
3175cff7825Smh27603 	if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
3185cff7825Smh27603 		PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
3195cff7825Smh27603 		return (DDI_FAILURE);
3205cff7825Smh27603 	}
3215cff7825Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3225cff7825Smh27603 
3235cff7825Smh27603 	ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
3245cff7825Smh27603 	    "ddi_ppm", 0);
3255cff7825Smh27603 	if (ret != DDI_SUCCESS) {
3265cff7825Smh27603 		PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
3275cff7825Smh27603 		goto fail1;
3285cff7825Smh27603 	}
3295cff7825Smh27603 
3305cff7825Smh27603 	unitp->dip = dip;
3315cff7825Smh27603 	mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
3325cff7825Smh27603 
3335cff7825Smh27603 	/*
3345cff7825Smh27603 	 * read ppm.conf, construct ppm_domain data structure and
3355cff7825Smh27603 	 * their sub data structure.
3365cff7825Smh27603 	 */
3375cff7825Smh27603 	if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
3385cff7825Smh27603 		goto fail2;
3395cff7825Smh27603 
3405cff7825Smh27603 	/*
3415cff7825Smh27603 	 * walk down ppm domain control from each domain, initialize
3425cff7825Smh27603 	 * domain control orthogonal function call handle
3435cff7825Smh27603 	 */
3445cff7825Smh27603 	ppm_init_cb(dip);
3455cff7825Smh27603 
3465cff7825Smh27603 	if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
3475cff7825Smh27603 		cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
3485cff7825Smh27603 		goto fail2;
3495cff7825Smh27603 	}
3505cff7825Smh27603 
3515cff7825Smh27603 	mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
3525cff7825Smh27603 	ppm_cpr_window_flag = B_FALSE;
3535cff7825Smh27603 	ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
3545cff7825Smh27603 	    CB_CL_CPR_PM, "ppm_cpr");
3555cff7825Smh27603 
3565cff7825Smh27603 #if defined(__x86)
3575cff7825Smh27603 	/*
3585cff7825Smh27603 	 * Register callback so that once CPUs have been added to
359444f66e7SMark Haywood 	 * the device tree, ppm CPU domains can be allocated using ACPI
3605cff7825Smh27603 	 * data.
3615cff7825Smh27603 	 */
362444f66e7SMark Haywood 	cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
363444f66e7SMark Haywood 	cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
3645cff7825Smh27603 
3655cff7825Smh27603 	/*
3665cff7825Smh27603 	 * Register callback so that whenever max speed throttle requests
3675cff7825Smh27603 	 * are received, ppm can redefine the high power level for
3685cff7825Smh27603 	 * all CPUs in the domain.
3695cff7825Smh27603 	 */
3705cff7825Smh27603 	cpupm_redefine_topspeed = ppm_redefine_topspeed;
3715cff7825Smh27603 #endif
3725cff7825Smh27603 
3735cff7825Smh27603 	ddi_report_dev(dip);
3745cff7825Smh27603 	return (DDI_SUCCESS);
3755cff7825Smh27603 
3765cff7825Smh27603 fail2:
3775cff7825Smh27603 	ddi_remove_minor_node(dip, "ddi_ppm");
3785cff7825Smh27603 	mutex_destroy(&unitp->lock);
3795cff7825Smh27603 fail1:
3805cff7825Smh27603 	ddi_soft_state_free(ppm_statep, ppm_inst);
3815cff7825Smh27603 	ppm_inst = -1;
3825cff7825Smh27603 	return (DDI_FAILURE);
3835cff7825Smh27603 }
3845cff7825Smh27603 
3855cff7825Smh27603 
3865cff7825Smh27603 /* ARGSUSED */
3875cff7825Smh27603 static int
ppm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3885cff7825Smh27603 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3895cff7825Smh27603 {
3905cff7825Smh27603 	ppm_unit_t *unitp;
3915cff7825Smh27603 #ifdef	DEBUG
3925cff7825Smh27603 	char *str = "ppm_detach";
3935cff7825Smh27603 #endif
3945cff7825Smh27603 
3955cff7825Smh27603 	switch (cmd) {
3965cff7825Smh27603 	case DDI_DETACH:
3975cff7825Smh27603 		PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
3985cff7825Smh27603 		return (DDI_FAILURE);
3995cff7825Smh27603 
4005cff7825Smh27603 	case DDI_SUSPEND:
4015cff7825Smh27603 		PPMD(D_DETACH, ("%s: suspending ...\n", str))
4025cff7825Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
4035cff7825Smh27603 		mutex_enter(&unitp->lock);
4045cff7825Smh27603 		unitp->states |= PPM_STATE_SUSPENDED;
4055cff7825Smh27603 		mutex_exit(&unitp->lock);
4065cff7825Smh27603 
4075cff7825Smh27603 		/*
4085cff7825Smh27603 		 * Suspend requires that timeout callouts to be canceled.
4095cff7825Smh27603 		 * Turning off the LED blinking will cancel the timeout.
4105cff7825Smh27603 		 */
4115cff7825Smh27603 		ppm_manage_led(PPM_LED_SOLIDON);
4125cff7825Smh27603 		return (DDI_SUCCESS);
4135cff7825Smh27603 
4145cff7825Smh27603 	default:
4155cff7825Smh27603 		cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
4165cff7825Smh27603 		    cmd, (void *)dip);
4175cff7825Smh27603 		return (DDI_FAILURE);
4185cff7825Smh27603 	}
4195cff7825Smh27603 }
4205cff7825Smh27603 
4215cff7825Smh27603 
4225cff7825Smh27603 /* ARGSUSED */
4235cff7825Smh27603 int
ppm_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)4245cff7825Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
4255cff7825Smh27603 {
4265cff7825Smh27603 	if (otyp != OTYP_CHR)
4275cff7825Smh27603 		return (EINVAL);
4285cff7825Smh27603 	PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
4295cff7825Smh27603 	    (void *)devp, flag, otyp))
4305cff7825Smh27603 	return (0);
4315cff7825Smh27603 }
4325cff7825Smh27603 
4335cff7825Smh27603 
4345cff7825Smh27603 /* ARGSUSED */
4355cff7825Smh27603 int
ppm_close(dev_t dev,int flag,int otyp,cred_t * credp)4365cff7825Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
4375cff7825Smh27603 {
4385cff7825Smh27603 	PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
4395cff7825Smh27603 	    dev, flag, otyp))
4405cff7825Smh27603 	return (0);
4415cff7825Smh27603 }
4425cff7825Smh27603 
4435cff7825Smh27603 
4445cff7825Smh27603 /* ARGSUSED */
4455cff7825Smh27603 int
ppm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)4465cff7825Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
4475cff7825Smh27603     int *rval_p)
4485cff7825Smh27603 {
4495cff7825Smh27603 #ifdef DEBUG
4505cff7825Smh27603 	char *str = "ppm_ioctl";
4515cff7825Smh27603 #endif
4525cff7825Smh27603 	ppm_domain_t *domp = NULL;
4535cff7825Smh27603 	uint8_t level, lvl;
4545cff7825Smh27603 	int ret = 0;
4555cff7825Smh27603 
4565cff7825Smh27603 	PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
4575cff7825Smh27603 	    str, dev, cmd, mode))
4585cff7825Smh27603 
4595cff7825Smh27603 	switch (cmd) {
4605cff7825Smh27603 	case PPMGET_DPWR:
4615cff7825Smh27603 	{
4625cff7825Smh27603 		STRUCT_DECL(ppm_dpwr, dpwr);
4635cff7825Smh27603 		struct ppm_unit *unitp;
4645cff7825Smh27603 		char *domain;
4655cff7825Smh27603 
4665cff7825Smh27603 		STRUCT_INIT(dpwr, mode);
4675cff7825Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
4685cff7825Smh27603 		    STRUCT_SIZE(dpwr), mode);
4695cff7825Smh27603 		if (ret != 0)
4705cff7825Smh27603 			return (EFAULT);
4715cff7825Smh27603 
4725cff7825Smh27603 		/* copyin domain name */
4735cff7825Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
4745cff7825Smh27603 		ret = copyinstr(
4755cff7825Smh27603 		    STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
4765cff7825Smh27603 		if (ret != 0) {
4775cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
4785cff7825Smh27603 			    str, __LINE__))
4795cff7825Smh27603 			ret = EFAULT;
4805cff7825Smh27603 			goto err_dpwr;
4815cff7825Smh27603 		}
4825cff7825Smh27603 
4835cff7825Smh27603 		/* locate domain */
4845cff7825Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
4855cff7825Smh27603 			PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
4865cff7825Smh27603 			ret = ENODEV;
4875cff7825Smh27603 			goto err_dpwr;
4885cff7825Smh27603 		}
4895cff7825Smh27603 
4905cff7825Smh27603 		switch (domp->model) {
4915cff7825Smh27603 		case PPMD_FET:	/* report power fet ON or OFF */
4925cff7825Smh27603 			if ((ret = ppm_fetget(domp, &lvl)) != 0) {
4935cff7825Smh27603 				ret = EIO;
4945cff7825Smh27603 				goto err_dpwr;
4955cff7825Smh27603 			}
4965cff7825Smh27603 			level = (lvl == PPMD_ON) ?
4975cff7825Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
4985cff7825Smh27603 			break;
4995cff7825Smh27603 
5005cff7825Smh27603 		case PPMD_PCI:	/* report pci slot clock ON or OFF */
5015cff7825Smh27603 		case PPMD_PCI_PROP:
5025cff7825Smh27603 		case PPMD_PCIE:
5035cff7825Smh27603 			level = (domp->status == PPMD_ON) ?
5045cff7825Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
5055cff7825Smh27603 			break;
5065cff7825Smh27603 
5075cff7825Smh27603 		case PPMD_LED:	/* report LED blinking or solid on */
5085cff7825Smh27603 
5095cff7825Smh27603 			unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
5105cff7825Smh27603 			if (unitp->led_tid == 0)
5115cff7825Smh27603 				level = PPMIO_LED_SOLIDON;
5125cff7825Smh27603 			else
5135cff7825Smh27603 				level = PPMIO_LED_BLINKING;
5145cff7825Smh27603 			break;
5155cff7825Smh27603 
5165cff7825Smh27603 		case PPMD_CPU:	/* report cpu speed divisor */
5175cff7825Smh27603 			level = domp->devlist->level;
5185cff7825Smh27603 			break;
5195cff7825Smh27603 
5205cff7825Smh27603 		default:
5215cff7825Smh27603 			ret = EINVAL;
5225cff7825Smh27603 			goto err_dpwr;
5235cff7825Smh27603 		}
5245cff7825Smh27603 
5255cff7825Smh27603 		STRUCT_FSET(dpwr, level, level);
5265cff7825Smh27603 		ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
5275cff7825Smh27603 		    STRUCT_SIZE(dpwr), mode);
5285cff7825Smh27603 		if (ret != 0) {
5295cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
5305cff7825Smh27603 			    str, __LINE__))
5315cff7825Smh27603 			ret = EFAULT;
5325cff7825Smh27603 		}
5335cff7825Smh27603 err_dpwr:
5345cff7825Smh27603 		kmem_free(domain, MAXNAMELEN);
5355cff7825Smh27603 
5365cff7825Smh27603 		break;
5375cff7825Smh27603 	}
5385cff7825Smh27603 
5395cff7825Smh27603 	case PPMGET_DOMBYDEV:
5405cff7825Smh27603 	{
5415cff7825Smh27603 		STRUCT_DECL(ppm_bydev, bydev);
5425cff7825Smh27603 		char *path = NULL;
5435cff7825Smh27603 		size_t   size, l;
5445cff7825Smh27603 
5455cff7825Smh27603 		STRUCT_INIT(bydev, mode);
5465cff7825Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
5475cff7825Smh27603 		    STRUCT_SIZE(bydev), mode);
5485cff7825Smh27603 		if (ret != 0)
5495cff7825Smh27603 			return (EFAULT);
5505cff7825Smh27603 
5515cff7825Smh27603 		/* copyin .path */
5525cff7825Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5535cff7825Smh27603 		ret = copyinstr(
5545cff7825Smh27603 		    STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
5555cff7825Smh27603 		if (ret != 0) {
5565cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
5575cff7825Smh27603 			    str, __LINE__))
5585cff7825Smh27603 			kmem_free(path, MAXPATHLEN);
5595cff7825Smh27603 			return (EFAULT);
5605cff7825Smh27603 		}
5615cff7825Smh27603 
5625cff7825Smh27603 		/* so far we have up to one domain for a given device */
5635cff7825Smh27603 		size = STRUCT_FGET(bydev, size);
5645cff7825Smh27603 		domp = ppm_get_domain_by_dev(path);
5655cff7825Smh27603 		kmem_free(path, MAXPATHLEN);
5665cff7825Smh27603 		if (domp != NULL) {
5675cff7825Smh27603 			l = strlen(domp->name) + 1;
5685cff7825Smh27603 			if (l > size) {
5695cff7825Smh27603 				PPMD(D_IOCTL, ("%s: buffer too small\n", str))
5705cff7825Smh27603 				return ((size == 0) ? EINVAL : EFAULT);
5715cff7825Smh27603 			}
5725cff7825Smh27603 		} else	/* no domain found to be associated with given device */
5735cff7825Smh27603 			return (ENODEV);
5745cff7825Smh27603 
5755cff7825Smh27603 		ret = copyoutstr(
5765cff7825Smh27603 		    domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
5775cff7825Smh27603 		if (ret != 0) {
5785cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
5795cff7825Smh27603 			    " \n", str, __LINE__))
5805cff7825Smh27603 			return (EFAULT);
5815cff7825Smh27603 		}
5825cff7825Smh27603 
5835cff7825Smh27603 		break;
5845cff7825Smh27603 	}
5855cff7825Smh27603 
5865cff7825Smh27603 
5875cff7825Smh27603 	case PPMGET_DEVBYDOM:
5885cff7825Smh27603 	{
5895cff7825Smh27603 		STRUCT_DECL(ppm_bydom, bydom);
5905cff7825Smh27603 		char *domain = NULL;
5915cff7825Smh27603 		char *devlist = NULL;
5925cff7825Smh27603 		ppm_dev_t *ppmd;
5935cff7825Smh27603 		dev_info_t *odip = NULL;
5945cff7825Smh27603 		char *s, *d;
5955cff7825Smh27603 		size_t  size, l;
5965cff7825Smh27603 
5975cff7825Smh27603 		STRUCT_INIT(bydom, mode);
5985cff7825Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
5995cff7825Smh27603 		    STRUCT_SIZE(bydom), mode);
6005cff7825Smh27603 		if (ret != 0)
6015cff7825Smh27603 			return (EFAULT);
6025cff7825Smh27603 
6035cff7825Smh27603 		/* copyin .domain */
6045cff7825Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
6055cff7825Smh27603 		ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
6065cff7825Smh27603 		    MAXNAMELEN, NULL);
6075cff7825Smh27603 		if (ret != 0) {
6085cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
6095cff7825Smh27603 			    str, __LINE__))
6105cff7825Smh27603 			ret = EFAULT;
6115cff7825Smh27603 			goto err_bydom;
6125cff7825Smh27603 		}
6135cff7825Smh27603 
6145cff7825Smh27603 		/* locate domain */
6155cff7825Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
6165cff7825Smh27603 			ret = ENODEV;
6175cff7825Smh27603 			goto err_bydom;
6185cff7825Smh27603 		}
6195cff7825Smh27603 
6205cff7825Smh27603 		l = 0;
6215cff7825Smh27603 		if ((size = STRUCT_FGET(bydom, size)) == 0)
6225cff7825Smh27603 			ret = EINVAL;
6235cff7825Smh27603 		else
6245cff7825Smh27603 			if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
6255cff7825Smh27603 				ret = EFAULT;
6265cff7825Smh27603 		if (ret != 0)
6275cff7825Smh27603 			goto err_bydom;
6285cff7825Smh27603 
6295cff7825Smh27603 		for (ppmd = domp->devlist; ppmd;
6305cff7825Smh27603 		    odip = ppmd->dip, ppmd = ppmd->next) {
6315cff7825Smh27603 
6325cff7825Smh27603 			if (ppmd->dip == odip)
6335cff7825Smh27603 				continue;
6345cff7825Smh27603 			if (ppmd != domp->devlist)
6355cff7825Smh27603 				*d++ = ' ';
6365cff7825Smh27603 
6375cff7825Smh27603 			l += strlen(ppmd->path) + 1;
6385cff7825Smh27603 			if (l > size) {
6395cff7825Smh27603 				PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
6405cff7825Smh27603 				ret = EFAULT;
6415cff7825Smh27603 				goto err_bydom;
6425cff7825Smh27603 			}
6435cff7825Smh27603 
6445cff7825Smh27603 			for (s = ppmd->path; *s != 0; )
6455cff7825Smh27603 				*d++ = *s++;
6465cff7825Smh27603 		}
6475cff7825Smh27603 		*d = 0;
6485cff7825Smh27603 
6495cff7825Smh27603 		if (*devlist == 0)
6505cff7825Smh27603 			goto err_bydom;
6515cff7825Smh27603 
6525cff7825Smh27603 		ret = copyoutstr(
6535cff7825Smh27603 		    devlist, STRUCT_FGETP(bydom, devlist), l, &l);
6545cff7825Smh27603 		if (ret != 0) {
6555cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
6565cff7825Smh27603 			    " \n", str, __LINE__))
6575cff7825Smh27603 			ret = EFAULT;
6585cff7825Smh27603 		}
6595cff7825Smh27603 
6605cff7825Smh27603 err_bydom:
6615cff7825Smh27603 		if (devlist)
6625cff7825Smh27603 			kmem_free(devlist, size);
6635cff7825Smh27603 		if (domain)
6645cff7825Smh27603 			kmem_free(domain, MAXNAMELEN);
6655cff7825Smh27603 
6665cff7825Smh27603 		break;
6675cff7825Smh27603 	}
6685cff7825Smh27603 
6695cff7825Smh27603 #if defined(__x86)
6705cff7825Smh27603 	/*
6715cff7825Smh27603 	 * Note that these two ioctls exist for test purposes only.
6725cff7825Smh27603 	 * Unfortunately, there really isn't any other good way of
6735cff7825Smh27603 	 * unit testing the dynamic redefinition of the top speed as it
6745cff7825Smh27603 	 * usually occurs due to environmental conditions.
6755cff7825Smh27603 	 */
6765cff7825Smh27603 	case PPMGET_NORMAL:
6775cff7825Smh27603 	case PPMSET_NORMAL:
6785cff7825Smh27603 	{
6795cff7825Smh27603 		STRUCT_DECL(ppm_norm, norm);
6805cff7825Smh27603 		char *path = NULL;
6815cff7825Smh27603 		struct pm_component *dcomps;
6825cff7825Smh27603 		struct pm_comp *pm_comp;
6835cff7825Smh27603 		ppm_dev_t *ppmd;
6845cff7825Smh27603 		int i;
6855cff7825Smh27603 
6865cff7825Smh27603 		STRUCT_INIT(norm, mode);
6875cff7825Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
6885cff7825Smh27603 		    STRUCT_SIZE(norm), mode);
6895cff7825Smh27603 		if (ret != 0)
6905cff7825Smh27603 			return (EFAULT);
6915cff7825Smh27603 
6925cff7825Smh27603 		/* copyin .path */
6935cff7825Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
6945cff7825Smh27603 		ret = copyinstr(
6955cff7825Smh27603 		    STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
6965cff7825Smh27603 		if (ret != 0) {
6975cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
6985cff7825Smh27603 			    str, __LINE__))
6995cff7825Smh27603 			kmem_free(path, MAXPATHLEN);
7005cff7825Smh27603 			return (EFAULT);
7015cff7825Smh27603 		}
7025cff7825Smh27603 
7035cff7825Smh27603 		domp = ppm_get_domain_by_dev(path);
7045cff7825Smh27603 		kmem_free(path, MAXPATHLEN);
7055cff7825Smh27603 
7065cff7825Smh27603 		if (domp == NULL)
7075cff7825Smh27603 			return (ENODEV);
7085cff7825Smh27603 
7095cff7825Smh27603 		ppmd = domp->devlist;
7105cff7825Smh27603 		if (cmd == PPMSET_NORMAL) {
7115cff7825Smh27603 			if (domp->model != PPMD_CPU)
7125cff7825Smh27603 				return (EINVAL);
7135cff7825Smh27603 			level = STRUCT_FGET(norm, norm);
7145cff7825Smh27603 			dcomps = DEVI(ppmd->dip)->devi_pm_components;
7155cff7825Smh27603 			pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
7165cff7825Smh27603 			for (i = pm_comp->pmc_numlevels; i > 0; i--) {
7175cff7825Smh27603 				if (pm_comp->pmc_lvals[i-1] == level)
7185cff7825Smh27603 					break;
7195cff7825Smh27603 			}
7205cff7825Smh27603 			if (i == 0)
7215cff7825Smh27603 				return (EINVAL);
7225cff7825Smh27603 
7235cff7825Smh27603 			ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
7245cff7825Smh27603 		}
7255cff7825Smh27603 
7265cff7825Smh27603 		level = pm_get_normal_power(ppmd->dip, 0);
7275cff7825Smh27603 
7285cff7825Smh27603 		STRUCT_FSET(norm, norm, level);
7295cff7825Smh27603 		ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
7305cff7825Smh27603 		    STRUCT_SIZE(norm), mode);
7315cff7825Smh27603 		if (ret != 0) {
7325cff7825Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
7335cff7825Smh27603 			    str, __LINE__))
7345cff7825Smh27603 			ret = EFAULT;
7355cff7825Smh27603 		}
7365cff7825Smh27603 		break;
7375cff7825Smh27603 	}
7385cff7825Smh27603 #endif
7395cff7825Smh27603 	default:
7405cff7825Smh27603 		PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
7415cff7825Smh27603 		return (EINVAL);
7425cff7825Smh27603 	}
7435cff7825Smh27603 
7445cff7825Smh27603 	return (ret);
7455cff7825Smh27603 }
7465cff7825Smh27603 
7475cff7825Smh27603 
748d67944fbSScott Rotondo static int	ppm_manage_sx(s3a_t *, int);
749d67944fbSScott Rotondo static int	ppm_search_list(pm_searchargs_t *);
750d67944fbSScott Rotondo 
7515cff7825Smh27603 /*
7525cff7825Smh27603  * interface between pm framework and ppm driver
7535cff7825Smh27603  */
7545cff7825Smh27603 /* ARGSUSED */
7555cff7825Smh27603 static int
ppm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)7565cff7825Smh27603 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
7575cff7825Smh27603     ddi_ctl_enum_t ctlop, void *arg, void *result)
7585cff7825Smh27603 {
7595cff7825Smh27603 	power_req_t	*reqp = (power_req_t *)arg;
7605cff7825Smh27603 	ppm_unit_t	*unitp;
7615cff7825Smh27603 	ppm_domain_t	*domp;
7625cff7825Smh27603 	ppm_dev_t	*ppmd;
7635cff7825Smh27603 	char		path[MAXNAMELEN];
7645cff7825Smh27603 	ppm_owned_t	*owned;
7655cff7825Smh27603 	int		mode;
7665cff7825Smh27603 	int		ret = DDI_SUCCESS;
7672df1fe9cSrandyf 	int		*res = (int *)result;
7682df1fe9cSrandyf 	s3a_t s3args;
7695cff7825Smh27603 
770*584b574aSToomas Soome 	domp = NULL;
7715cff7825Smh27603 #ifdef DEBUG
7725cff7825Smh27603 	char	*str = "ppm_ctlops";
7735cff7825Smh27603 	int	mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
7745cff7825Smh27603 	char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
7755cff7825Smh27603 	if (mask && ctlstr)
7765cff7825Smh27603 		PPMD(mask, ("%s: %s, %s\n",
7775cff7825Smh27603 		    str, ddi_binding_name(rdip), ctlstr))
7785cff7825Smh27603 #endif
7795cff7825Smh27603 
7802df1fe9cSrandyf 	if (ctlop != DDI_CTLOPS_POWER) {
7815cff7825Smh27603 		return (DDI_FAILURE);
7822df1fe9cSrandyf 	}
7835cff7825Smh27603 
7845cff7825Smh27603 	unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
7855cff7825Smh27603 
7865cff7825Smh27603 	switch (reqp->request_type) {
7875cff7825Smh27603 
7885cff7825Smh27603 	/* attempt to blink led if indeed all at lowest */
7895cff7825Smh27603 	case PMR_PPM_ALL_LOWEST:
7905cff7825Smh27603 		mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
7915cff7825Smh27603 		if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
7925cff7825Smh27603 			ppm_manage_led(PPM_LED_BLINKING);
7935cff7825Smh27603 		else
7945cff7825Smh27603 			ppm_manage_led(PPM_LED_SOLIDON);
7955cff7825Smh27603 		return (DDI_SUCCESS);
7965cff7825Smh27603 
7975cff7825Smh27603 	/* undo the claiming of 'rdip' at attach time */
7985cff7825Smh27603 	case PMR_PPM_POST_DETACH:
7995cff7825Smh27603 		ASSERT(reqp->req.ppm_set_power_req.who == rdip);
8005cff7825Smh27603 		mutex_enter(&unitp->lock);
8015cff7825Smh27603 		if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
8025cff7825Smh27603 		    (PPM_GET_PRIVATE(rdip) == NULL)) {
8035cff7825Smh27603 			mutex_exit(&unitp->lock);
8045cff7825Smh27603 			return (DDI_FAILURE);
8055cff7825Smh27603 		}
8065cff7825Smh27603 		mutex_exit(&unitp->lock);
8075cff7825Smh27603 		ppm_rem_dev(rdip);
8085cff7825Smh27603 		return (DDI_SUCCESS);
8095cff7825Smh27603 
8105cff7825Smh27603 	/* chance to adjust pwr_cnt if resume is about to power up rdip */
8115cff7825Smh27603 	case PMR_PPM_PRE_RESUME:
8125cff7825Smh27603 		ppm_svc_resume_ctlop(rdip, reqp);
8135cff7825Smh27603 		return (DDI_SUCCESS);
8145cff7825Smh27603 
8155cff7825Smh27603 	/*
8165cff7825Smh27603 	 * synchronizing, so that only the owner of the power lock is
8175cff7825Smh27603 	 * permitted to change device and component's power level.
8185cff7825Smh27603 	 */
8195cff7825Smh27603 	case PMR_PPM_UNLOCK_POWER:
8205cff7825Smh27603 	case PMR_PPM_TRY_LOCK_POWER:
8215cff7825Smh27603 	case PMR_PPM_LOCK_POWER:
8225cff7825Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
8235cff7825Smh27603 		if (ppmd)
8245cff7825Smh27603 			domp = ppmd->domp;
8255cff7825Smh27603 		else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
8265cff7825Smh27603 			domp = ppm_lookup_dev(rdip);
8275cff7825Smh27603 			ASSERT(domp);
8285cff7825Smh27603 			ppmd = ppm_get_dev(rdip, domp);
8295cff7825Smh27603 		}
8305cff7825Smh27603 
831*584b574aSToomas Soome 		if (domp == NULL)
832*584b574aSToomas Soome 			return (DDI_FAILURE);
833*584b574aSToomas Soome 
8345cff7825Smh27603 		PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
8355cff7825Smh27603 		    (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
8365cff7825Smh27603 		    ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
8375cff7825Smh27603 
8385cff7825Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
8395cff7825Smh27603 			ppm_lock_all(domp, reqp, result);
8405cff7825Smh27603 		else
8415cff7825Smh27603 			ppm_lock_one(ppmd, reqp, result);
8425cff7825Smh27603 		return (DDI_SUCCESS);
8435cff7825Smh27603 
8445cff7825Smh27603 	case PMR_PPM_POWER_LOCK_OWNER:
8455cff7825Smh27603 		ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
8465cff7825Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
847*584b574aSToomas Soome 		if (ppmd) {
8485cff7825Smh27603 			domp = ppmd->domp;
849*584b574aSToomas Soome 		} else {
8505cff7825Smh27603 			domp = ppm_lookup_dev(rdip);
8515cff7825Smh27603 			ASSERT(domp);
8525cff7825Smh27603 			ppmd = ppm_get_dev(rdip, domp);
8535cff7825Smh27603 		}
8545cff7825Smh27603 
855*584b574aSToomas Soome 		if (domp == NULL)
856*584b574aSToomas Soome 			return (DDI_FAILURE);
857*584b574aSToomas Soome 
8585cff7825Smh27603 		/*
8595cff7825Smh27603 		 * In case of LOCK_ALL, effective owner of the power lock
8605cff7825Smh27603 		 * is the owner of the domain lock. otherwise, it is the owner
8615cff7825Smh27603 		 * of the power lock.
8625cff7825Smh27603 		 */
8635cff7825Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
8645cff7825Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
8655cff7825Smh27603 			    mutex_owner(&domp->lock);
8665cff7825Smh27603 		else {
8675cff7825Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
8685cff7825Smh27603 			    DEVI(rdip)->devi_busy_thread;
8695cff7825Smh27603 		}
8705cff7825Smh27603 		return (DDI_SUCCESS);
8715cff7825Smh27603 
8725cff7825Smh27603 	case PMR_PPM_INIT_CHILD:
8735cff7825Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
8745cff7825Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
8755cff7825Smh27603 			return (DDI_SUCCESS);
8765cff7825Smh27603 
8775cff7825Smh27603 		/*
8785cff7825Smh27603 		 * We keep track of power-manageable devices starting with
8795cff7825Smh27603 		 * initialization process.  The initializing flag remains
8805cff7825Smh27603 		 * set until it is cleared by ppm_add_dev().  Power management
8815cff7825Smh27603 		 * policy for some domains are affected even during device
8825cff7825Smh27603 		 * initialization.  For example, PCI domains should leave
8835cff7825Smh27603 		 * their clock running meanwhile a device in that domain
8845cff7825Smh27603 		 * is initializing.
8855cff7825Smh27603 		 */
8865cff7825Smh27603 		mutex_enter(&domp->lock);
8875cff7825Smh27603 		owned = ppm_add_owned(rdip, domp);
8885cff7825Smh27603 		ASSERT(owned->initializing == 0);
8895cff7825Smh27603 		owned->initializing = 1;
8905cff7825Smh27603 
8915cff7825Smh27603 		if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
8925cff7825Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
8935cff7825Smh27603 			if (ret == DDI_SUCCESS)
8945cff7825Smh27603 				domp->dflags |= PPMD_INITCHILD_CLKON;
8955cff7825Smh27603 		}
8965cff7825Smh27603 		mutex_exit(&domp->lock);
8975cff7825Smh27603 		return (ret);
8985cff7825Smh27603 
8995cff7825Smh27603 	case PMR_PPM_POST_ATTACH:
9005cff7825Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9015cff7825Smh27603 		domp = ppm_lookup_dev(rdip);
9025cff7825Smh27603 		ASSERT(domp);
9035cff7825Smh27603 		ASSERT(domp->status == PPMD_ON);
9045cff7825Smh27603 		if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
9055cff7825Smh27603 			/*
9065cff7825Smh27603 			 * call ppm_get_dev, which will increment the
9075cff7825Smh27603 			 * domain power count by the right number.
9085cff7825Smh27603 			 * Undo the power count increment, done in PRE_PROBE.
9095cff7825Smh27603 			 */
9105cff7825Smh27603 			if (PM_GET_PM_INFO(rdip))
9115cff7825Smh27603 				ppmd = ppm_get_dev(rdip, domp);
9125cff7825Smh27603 			mutex_enter(&domp->lock);
9135cff7825Smh27603 			ASSERT(domp->pwr_cnt > 0);
9145cff7825Smh27603 			domp->pwr_cnt--;
9155cff7825Smh27603 			mutex_exit(&domp->lock);
9165cff7825Smh27603 			return (DDI_SUCCESS);
9175cff7825Smh27603 		}
9185cff7825Smh27603 
9195cff7825Smh27603 		ret = ppm_power_down_domain(rdip);
9205cff7825Smh27603 		/* FALLTHROUGH */
9215cff7825Smh27603 	case PMR_PPM_UNINIT_CHILD:
9225cff7825Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
9235cff7825Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
9245cff7825Smh27603 			return (DDI_SUCCESS);
9255cff7825Smh27603 
9265cff7825Smh27603 		(void) ddi_pathname(rdip, path);
9275cff7825Smh27603 		mutex_enter(&domp->lock);
9285cff7825Smh27603 		for (owned = domp->owned; owned; owned = owned->next)
9295cff7825Smh27603 			if (strcmp(owned->path, path) == 0)
9305cff7825Smh27603 				break;
9315cff7825Smh27603 
9325cff7825Smh27603 		/*
9335cff7825Smh27603 		 * In case we didn't go through a complete attach and detach,
9345cff7825Smh27603 		 * the initializing flag will still be set, so clear it.
9355cff7825Smh27603 		 */
9365cff7825Smh27603 		if ((owned != NULL) && (owned->initializing))
9375cff7825Smh27603 			owned->initializing = 0;
9385cff7825Smh27603 
9395cff7825Smh27603 		if (PPMD_IS_PCI(domp->model) &&
9405cff7825Smh27603 		    domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
9415cff7825Smh27603 		    (domp->dflags & PPMD_INITCHILD_CLKON) &&
9425cff7825Smh27603 		    ppm_none_else_holds_power(domp)) {
9435cff7825Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
9445cff7825Smh27603 			if (ret == DDI_SUCCESS)
9455cff7825Smh27603 				domp->dflags &= ~PPMD_INITCHILD_CLKON;
9465cff7825Smh27603 		}
9475cff7825Smh27603 		mutex_exit(&domp->lock);
9485cff7825Smh27603 		return (ret);
9495cff7825Smh27603 
9505cff7825Smh27603 	/* place holders */
9515cff7825Smh27603 	case PMR_PPM_UNMANAGE:
9525cff7825Smh27603 	case PMR_PPM_PRE_DETACH:
9535cff7825Smh27603 		return (DDI_SUCCESS);
9545cff7825Smh27603 
9555cff7825Smh27603 	case PMR_PPM_PRE_PROBE:
9565cff7825Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9575cff7825Smh27603 		return (ppm_power_up_domain(rdip));
9585cff7825Smh27603 
9595cff7825Smh27603 	case PMR_PPM_POST_PROBE:
9605cff7825Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9615cff7825Smh27603 		if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
9625cff7825Smh27603 		    reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
9635cff7825Smh27603 			return (DDI_SUCCESS);
9645cff7825Smh27603 
9655cff7825Smh27603 		/* Probe failed */
9665cff7825Smh27603 		PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
9675cff7825Smh27603 		    "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
9685cff7825Smh27603 		    reqp->req.ppm_config_req.result))
9695cff7825Smh27603 		return (ppm_power_down_domain(rdip));
9705cff7825Smh27603 
9715cff7825Smh27603 	case PMR_PPM_PRE_ATTACH:
9725cff7825Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
9735cff7825Smh27603 		/* Domain has already been powered up in PRE_PROBE */
9745cff7825Smh27603 		domp = ppm_lookup_dev(rdip);
9755cff7825Smh27603 		ASSERT(domp);
9765cff7825Smh27603 		ASSERT(domp->status == PPMD_ON);
9775cff7825Smh27603 		return (DDI_SUCCESS);
9785cff7825Smh27603 
9795cff7825Smh27603 	/* ppm intercepts power change process to the claimed devices */
9805cff7825Smh27603 	case PMR_PPM_SET_POWER:
9815cff7825Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
9825cff7825Smh27603 		if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
9835cff7825Smh27603 			domp = ppm_lookup_dev(rdip);
9845cff7825Smh27603 			ASSERT(domp);
9855cff7825Smh27603 			ppmd = ppm_get_dev(rdip, domp);
9865cff7825Smh27603 		}
9875cff7825Smh27603 		switch (ppmd->domp->model) {
9885cff7825Smh27603 		case PPMD_CPU:
9895cff7825Smh27603 			return (ppm_manage_cpus(rdip, reqp, result));
9905cff7825Smh27603 		case PPMD_FET:
9915cff7825Smh27603 			return (ppm_manage_fet(rdip, reqp, result));
9925cff7825Smh27603 		case PPMD_PCI:
9935cff7825Smh27603 		case PPMD_PCI_PROP:
9945cff7825Smh27603 			return (ppm_manage_pci(rdip, reqp, result));
9955cff7825Smh27603 		case PPMD_PCIE:
9965cff7825Smh27603 			return (ppm_manage_pcie(rdip, reqp, result));
9975cff7825Smh27603 		default:
9985cff7825Smh27603 			cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
9995cff7825Smh27603 			    " not support PMR_PPM_SET_POWER ctlop",
10005cff7825Smh27603 			    ppmd->domp->model);
10015cff7825Smh27603 			return (DDI_FAILURE);
10025cff7825Smh27603 		}
10035cff7825Smh27603 
10042df1fe9cSrandyf 	case PMR_PPM_ENTER_SX:
10052df1fe9cSrandyf 	case PMR_PPM_EXIT_SX:
10062df1fe9cSrandyf 		s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
10072df1fe9cSrandyf 		s3args.s3a_test_point =
10082df1fe9cSrandyf 		    reqp->req.ppm_power_enter_sx_req.test_point;
10092df1fe9cSrandyf 		s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
10102df1fe9cSrandyf 		s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
10112df1fe9cSrandyf 		ret = ppm_manage_sx(&s3args,
10122df1fe9cSrandyf 		    reqp->request_type == PMR_PPM_ENTER_SX);
10132df1fe9cSrandyf 		if (ret) {
10142df1fe9cSrandyf 			PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
10152df1fe9cSrandyf 			return (DDI_FAILURE);
10162df1fe9cSrandyf 		} else {
10172df1fe9cSrandyf 			return (DDI_SUCCESS);
10182df1fe9cSrandyf 		}
10192df1fe9cSrandyf 
10202df1fe9cSrandyf 	case PMR_PPM_SEARCH_LIST:
10212df1fe9cSrandyf 		ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
10222df1fe9cSrandyf 		reqp->req.ppm_search_list_req.result = ret;
10232df1fe9cSrandyf 		*res = ret;
10242df1fe9cSrandyf 		if (ret) {
10252df1fe9cSrandyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10262df1fe9cSrandyf 			return (DDI_FAILURE);
10272df1fe9cSrandyf 		} else {
10282df1fe9cSrandyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10292df1fe9cSrandyf 			return (DDI_SUCCESS);
10302df1fe9cSrandyf 		}
10312df1fe9cSrandyf 
10325cff7825Smh27603 	default:
10335cff7825Smh27603 		cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
10345cff7825Smh27603 		    reqp->request_type);
10355cff7825Smh27603 		return (DDI_FAILURE);
10365cff7825Smh27603 	}
10375cff7825Smh27603 }
10385cff7825Smh27603 
10395cff7825Smh27603 
10405cff7825Smh27603 /*
10415cff7825Smh27603  * Raise the power level of a subrange of cpus.  Used when cpu driver
10425cff7825Smh27603  * failed an attempt to lower the power of a cpu (probably because
10435cff7825Smh27603  * it got busy).  Need to revert the ones we already changed.
10445cff7825Smh27603  *
10455cff7825Smh27603  * ecpup = the ppm_dev_t for the cpu which failed to lower power
10465cff7825Smh27603  * level = power level to reset prior cpus to
10475cff7825Smh27603  */
10485cff7825Smh27603 int
ppm_revert_cpu_power(ppm_dev_t * ecpup,int level)10495cff7825Smh27603 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
10505cff7825Smh27603 {
10515cff7825Smh27603 	ppm_dev_t *cpup;
10525cff7825Smh27603 	int ret = DDI_SUCCESS;
10535cff7825Smh27603 
10545cff7825Smh27603 	for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
10555cff7825Smh27603 		PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
10565cff7825Smh27603 		    "level %d\n", cpup->path, level))
10575cff7825Smh27603 
10585cff7825Smh27603 		ret = pm_power(cpup->dip, 0, level);
10595cff7825Smh27603 		if (ret == DDI_SUCCESS) {
10605cff7825Smh27603 			cpup->level = level;
10615cff7825Smh27603 			cpup->rplvl = PM_LEVEL_UNKNOWN;
10625cff7825Smh27603 		}
10635cff7825Smh27603 	}
10645cff7825Smh27603 	return (ret);
10655cff7825Smh27603 }
10665cff7825Smh27603 
10675cff7825Smh27603 
10685cff7825Smh27603 /*
10695cff7825Smh27603  * ppm_manage_cpus - Process a request to change the power level of a cpu.
10705cff7825Smh27603  * If not all cpus want to be at the same level, OR if we are currently
10715cff7825Smh27603  * refusing slowdown requests due to thermal stress, we cache the request.
10725cff7825Smh27603  * Otherwise, set all cpus to the new power level.
10735cff7825Smh27603  */
10745cff7825Smh27603 /* ARGSUSED */
10755cff7825Smh27603 static int
ppm_manage_cpus(dev_info_t * dip,power_req_t * reqp,int * result)10765cff7825Smh27603 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
10775cff7825Smh27603 {
10785cff7825Smh27603 #ifdef	DEBUG
10795cff7825Smh27603 	char *str = "ppm_manage_cpus";
10805cff7825Smh27603 #endif
10815cff7825Smh27603 	int old, new, ret, kmflag;
10825cff7825Smh27603 	ppm_dev_t *ppmd, *cpup;
10835cff7825Smh27603 	int change_notify = 0;
10845cff7825Smh27603 	pm_ppm_devlist_t *devlist = NULL, *p;
10855cff7825Smh27603 	int		do_rescan = 0;
10865cff7825Smh27603 
10875cff7825Smh27603 	*result = DDI_SUCCESS;
10885cff7825Smh27603 
10895cff7825Smh27603 	switch (reqp->request_type) {
10905cff7825Smh27603 	case PMR_PPM_SET_POWER:
10915cff7825Smh27603 		break;
10925cff7825Smh27603 
10935cff7825Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
10945cff7825Smh27603 		change_notify = 1;
10955cff7825Smh27603 		break;
10965cff7825Smh27603 
10975cff7825Smh27603 	default:
10985cff7825Smh27603 		return (DDI_FAILURE);
10995cff7825Smh27603 	}
11005cff7825Smh27603 
11015cff7825Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
11025cff7825Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
11035cff7825Smh27603 	old = reqp->req.ppm_set_power_req.old_level;
11045cff7825Smh27603 	new = reqp->req.ppm_set_power_req.new_level;
11055cff7825Smh27603 
11065cff7825Smh27603 	if (change_notify) {
11075cff7825Smh27603 		ppmd->level = new;
11085cff7825Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
11095cff7825Smh27603 
11105cff7825Smh27603 		PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
11115cff7825Smh27603 		    "from %d to %d", str, (void *)dip, old, new))
11125cff7825Smh27603 		return (DDI_SUCCESS);
11135cff7825Smh27603 	}
11145cff7825Smh27603 
11155cff7825Smh27603 	if (ppm_manage_early_cpus(dip, new, result))
11165cff7825Smh27603 		return (*result);
11175cff7825Smh27603 
11185cff7825Smh27603 	if (new == ppmd->level) {
11195cff7825Smh27603 		PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
11205cff7825Smh27603 		return (DDI_SUCCESS);
11215cff7825Smh27603 	}
11225cff7825Smh27603 
11235cff7825Smh27603 	/*
11245cff7825Smh27603 	 * A request from lower to higher level transition is granted and
11255cff7825Smh27603 	 * made effective on all cpus. A request from higher to lower must
11265cff7825Smh27603 	 * be agreed upon by all cpus.
11275cff7825Smh27603 	 */
11285cff7825Smh27603 	ppmd->rplvl = new;
11295cff7825Smh27603 	for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11305cff7825Smh27603 		if (cpup->rplvl == new)
11315cff7825Smh27603 			continue;
11325cff7825Smh27603 
11335cff7825Smh27603 		if (new < old) {
11345cff7825Smh27603 			PPMD(D_SOME, ("%s: not all cpus wants to be at new "
11355cff7825Smh27603 			    "level %d yet.\n", str, new))
11365cff7825Smh27603 			return (DDI_SUCCESS);
11375cff7825Smh27603 		}
11385cff7825Smh27603 
11395cff7825Smh27603 		/*
11405cff7825Smh27603 		 * If a single cpu requests power up, honor the request
11415cff7825Smh27603 		 * powering up all cpus.
11425cff7825Smh27603 		 */
11435cff7825Smh27603 		if (new > old) {
11445cff7825Smh27603 			PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
11455cff7825Smh27603 			    "because of request from dip(%s@%s, %p), "
11465cff7825Smh27603 			    "need pm_rescan\n", str, PM_NAME(cpup->dip),
11475cff7825Smh27603 			    PM_ADDR(cpup->dip), (void *)cpup->dip,
11485cff7825Smh27603 			    PM_NAME(dip), PM_ADDR(dip), (void *)dip))
11495cff7825Smh27603 			do_rescan++;
11505cff7825Smh27603 		}
11515cff7825Smh27603 	}
11525cff7825Smh27603 
11535cff7825Smh27603 	PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
11545cff7825Smh27603 	    str, ppmd->path, ppmd->level, new))
11555cff7825Smh27603 	ret = ppm_change_cpu_power(ppmd, new);
11565cff7825Smh27603 	*result = ret;
11575cff7825Smh27603 
11585cff7825Smh27603 	if (ret == DDI_SUCCESS) {
11595cff7825Smh27603 		if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
11605cff7825Smh27603 			kmflag = KM_SLEEP;
11615cff7825Smh27603 		else
11625cff7825Smh27603 			kmflag = KM_NOSLEEP;
11635cff7825Smh27603 
11645cff7825Smh27603 		for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11655cff7825Smh27603 			if (cpup->dip == dip)
11665cff7825Smh27603 				continue;
11675cff7825Smh27603 
11685cff7825Smh27603 			if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
11695cff7825Smh27603 			    kmflag)) == NULL) {
11705cff7825Smh27603 				break;
11715cff7825Smh27603 			}
11725cff7825Smh27603 			p->ppd_who = cpup->dip;
11735cff7825Smh27603 			p->ppd_cmpt = cpup->cmpt;
11745cff7825Smh27603 			p->ppd_old_level = old;
11755cff7825Smh27603 			p->ppd_new_level = new;
11765cff7825Smh27603 			p->ppd_next = devlist;
11775cff7825Smh27603 
11785cff7825Smh27603 			PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
11795cff7825Smh27603 			    str, cpup->path, old, new))
11805cff7825Smh27603 
11815cff7825Smh27603 			devlist = p;
11825cff7825Smh27603 		}
11835cff7825Smh27603 		reqp->req.ppm_set_power_req.cookie = (void *) devlist;
11845cff7825Smh27603 
11855cff7825Smh27603 		if (do_rescan > 0) {
11865cff7825Smh27603 			for (cpup = ppmd->domp->devlist; cpup;
11875cff7825Smh27603 			    cpup = cpup->next) {
11885cff7825Smh27603 				if (cpup->dip == dip)
11895cff7825Smh27603 					continue;
11905cff7825Smh27603 				pm_rescan(cpup->dip);
11915cff7825Smh27603 			}
11925cff7825Smh27603 		}
11935cff7825Smh27603 	}
11945cff7825Smh27603 
11955cff7825Smh27603 	return (ret);
11965cff7825Smh27603 }
11975cff7825Smh27603 
11985cff7825Smh27603 
11995cff7825Smh27603 /*
12005cff7825Smh27603  * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
12015cff7825Smh27603  * increments its FET domain power count, in anticipation of that
12025cff7825Smh27603  * the indicated device(dip) would be powered up by its driver as
12035cff7825Smh27603  * a result of cpr resuming.
12045cff7825Smh27603  */
12055cff7825Smh27603 /* ARGSUSED */
12065cff7825Smh27603 static void
ppm_svc_resume_ctlop(dev_info_t * dip,power_req_t * reqp)12075cff7825Smh27603 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
12085cff7825Smh27603 {
12095cff7825Smh27603 	ppm_domain_t *domp;
12105cff7825Smh27603 	ppm_dev_t *ppmd;
12115cff7825Smh27603 	int powered;	/* power up count per dip */
12125cff7825Smh27603 
12135cff7825Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
12145cff7825Smh27603 	if (ppmd == NULL)
12155cff7825Smh27603 		return;
12165cff7825Smh27603 
12175cff7825Smh27603 	/*
12185cff7825Smh27603 	 * Maintain correct powered count for domain which cares
12195cff7825Smh27603 	 */
12205cff7825Smh27603 	powered = 0;
12215cff7825Smh27603 	domp = ppmd->domp;
12225cff7825Smh27603 	mutex_enter(&domp->lock);
12235cff7825Smh27603 	if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
12245cff7825Smh27603 	    (domp->model == PPMD_PCIE)) {
12255cff7825Smh27603 		for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12265cff7825Smh27603 			if (ppmd->dip == dip && ppmd->level)
12275cff7825Smh27603 				powered++;
12285cff7825Smh27603 		}
12295cff7825Smh27603 
12305cff7825Smh27603 		/*
12315cff7825Smh27603 		 * All fets and clocks are held on during suspend -
12325cff7825Smh27603 		 * resume window regardless their domain devices' power
12335cff7825Smh27603 		 * level.
12345cff7825Smh27603 		 */
12355cff7825Smh27603 		ASSERT(domp->status == PPMD_ON);
12365cff7825Smh27603 
12375cff7825Smh27603 		/*
12385cff7825Smh27603 		 * The difference indicates the number of components
12395cff7825Smh27603 		 * being off prior to suspend operation, that is the
12405cff7825Smh27603 		 * amount needs to be compensated in order to sync up
12415cff7825Smh27603 		 * bookkeeping with reality, for PROM reset would have
12425cff7825Smh27603 		 * brought up all devices.
12435cff7825Smh27603 		 */
12445cff7825Smh27603 		if (powered < PM_NUMCMPTS(dip))
12455cff7825Smh27603 			domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
12465cff7825Smh27603 	}
12475cff7825Smh27603 	for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12485cff7825Smh27603 		if (ppmd->dip == dip)
12495cff7825Smh27603 			ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
12505cff7825Smh27603 	}
12515cff7825Smh27603 	mutex_exit(&domp->lock);
12525cff7825Smh27603 }
12535cff7825Smh27603 
12545cff7825Smh27603 #ifdef	DEBUG
12555cff7825Smh27603 static int ppmbringup = 0;
12565cff7825Smh27603 #endif
12575cff7825Smh27603 
12585cff7825Smh27603 int
ppm_bringup_domains()12595cff7825Smh27603 ppm_bringup_domains()
12605cff7825Smh27603 {
12615cff7825Smh27603 #ifdef DEBUG
12625cff7825Smh27603 	char *str = "ppm_bringup_domains";
12635cff7825Smh27603 #endif
12645cff7825Smh27603 	ppm_domain_t	*domp;
12655cff7825Smh27603 	int	ret = DDI_SUCCESS;
12665cff7825Smh27603 
12675cff7825Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
12685cff7825Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
12695cff7825Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
12705cff7825Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
12715cff7825Smh27603 			continue;
12725cff7825Smh27603 
12735cff7825Smh27603 		mutex_enter(&domp->lock);
12745cff7825Smh27603 		if (domp->status == PPMD_ON) {
12755cff7825Smh27603 			mutex_exit(&domp->lock);
12765cff7825Smh27603 			continue;
12775cff7825Smh27603 		}
12785cff7825Smh27603 		switch (domp->model) {
12795cff7825Smh27603 		case PPMD_FET:
12805cff7825Smh27603 			ret = ppm_fetset(domp, PPMD_ON);
12815cff7825Smh27603 			break;
12825cff7825Smh27603 		case PPMD_PCI:
12835cff7825Smh27603 		case PPMD_PCI_PROP:
12845cff7825Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
12855cff7825Smh27603 			break;
12865cff7825Smh27603 		case PPMD_PCIE:
12875cff7825Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_ON);
12885cff7825Smh27603 			break;
12895cff7825Smh27603 		default:
12905cff7825Smh27603 			break;
12915cff7825Smh27603 		}
12925cff7825Smh27603 		mutex_exit(&domp->lock);
12935cff7825Smh27603 	}
12942df1fe9cSrandyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
12955cff7825Smh27603 
12965cff7825Smh27603 	return (ret);
12975cff7825Smh27603 }
12985cff7825Smh27603 
12995cff7825Smh27603 #ifdef	DEBUG
13005cff7825Smh27603 static int ppmsyncbp = 0;
13015cff7825Smh27603 #endif
13025cff7825Smh27603 
13035cff7825Smh27603 int
ppm_sync_bookkeeping()13045cff7825Smh27603 ppm_sync_bookkeeping()
13055cff7825Smh27603 {
13065cff7825Smh27603 #ifdef DEBUG
13075cff7825Smh27603 	char *str = "ppm_sync_bookkeeping";
13085cff7825Smh27603 #endif
13095cff7825Smh27603 	ppm_domain_t	*domp;
13105cff7825Smh27603 	int	ret = DDI_SUCCESS;
13115cff7825Smh27603 
13125cff7825Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
13135cff7825Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
13145cff7825Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
13155cff7825Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
13165cff7825Smh27603 			continue;
13175cff7825Smh27603 
13185cff7825Smh27603 		mutex_enter(&domp->lock);
13195cff7825Smh27603 		if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
13205cff7825Smh27603 			mutex_exit(&domp->lock);
13215cff7825Smh27603 			continue;
13225cff7825Smh27603 		}
13232df1fe9cSrandyf 
13242df1fe9cSrandyf 		/*
13252df1fe9cSrandyf 		 * skip NULL .devlist slot, for some may host pci device
13262df1fe9cSrandyf 		 * that can not tolerate clock off or not even participate
13272df1fe9cSrandyf 		 * in PM.
13282df1fe9cSrandyf 		 */
13292df1fe9cSrandyf 		if (domp->devlist == NULL)
13302df1fe9cSrandyf 			continue;
13312df1fe9cSrandyf 
13325cff7825Smh27603 		switch (domp->model) {
13335cff7825Smh27603 		case PPMD_FET:
13345cff7825Smh27603 			ret = ppm_fetset(domp, PPMD_OFF);
13355cff7825Smh27603 			break;
13365cff7825Smh27603 		case PPMD_PCI:
13375cff7825Smh27603 		case PPMD_PCI_PROP:
13385cff7825Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
13395cff7825Smh27603 			break;
13405cff7825Smh27603 		case PPMD_PCIE:
13415cff7825Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_OFF);
13425cff7825Smh27603 			break;
13435cff7825Smh27603 		default:
13445cff7825Smh27603 			break;
13455cff7825Smh27603 		}
13465cff7825Smh27603 		mutex_exit(&domp->lock);
13475cff7825Smh27603 	}
13482df1fe9cSrandyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
13495cff7825Smh27603 
13505cff7825Smh27603 	return (ret);
13515cff7825Smh27603 }
13525cff7825Smh27603 
13535cff7825Smh27603 
13545cff7825Smh27603 
13555cff7825Smh27603 /*
13565cff7825Smh27603  * pre-suspend window;
13575cff7825Smh27603  *
13585cff7825Smh27603  * power up every FET and PCI clock that are off;
13595cff7825Smh27603  *
13605cff7825Smh27603  * set ppm_cpr_window global flag to indicate
13615cff7825Smh27603  * that even though all pm_scan requested power transitions
13625cff7825Smh27603  * will be honored as usual but that until we're out
13635cff7825Smh27603  * of this window,  no FET or clock will be turned off
13645cff7825Smh27603  * for domains with pwr_cnt decremented down to 0.
13655cff7825Smh27603  * Such is to avoid accessing the orthogonal drivers that own
13665cff7825Smh27603  * the FET and clock registers that may not be resumed yet.
13675cff7825Smh27603  *
13685cff7825Smh27603  * at post-resume window, walk through each FET and PCI domains,
13695cff7825Smh27603  * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
13705cff7825Smh27603  * and noinvol check okays, power down the FET or PCI.  At last,
13715cff7825Smh27603  * clear the global flag ppm_cpr_window.
13725cff7825Smh27603  *
13735cff7825Smh27603  * ASSERT case 1, during cpr window, checks pwr_cnt against power
13745cff7825Smh27603  *	transitions;
13755cff7825Smh27603  * ASSERT case 2, out of cpr window, checks four things:
13765cff7825Smh27603  *	pwr_cnt <> power transition in/out of 0
13775cff7825Smh27603  *	<> status <> record of noinvol device detached
13785cff7825Smh27603  *
13795cff7825Smh27603  */
13805cff7825Smh27603 /* ARGSUSED */
13815cff7825Smh27603 static boolean_t
ppm_cpr_callb(void * arg,int code)13825cff7825Smh27603 ppm_cpr_callb(void *arg, int code)
13835cff7825Smh27603 {
13845cff7825Smh27603 	int	ret;
13855cff7825Smh27603 
13865cff7825Smh27603 	switch (code) {
13875cff7825Smh27603 	case CB_CODE_CPR_CHKPT:
13885cff7825Smh27603 
13895cff7825Smh27603 		/* pre-suspend: start of cpr window */
13905cff7825Smh27603 		mutex_enter(&ppm_cpr_window_lock);
13915cff7825Smh27603 		ASSERT(ppm_cpr_window_flag == B_FALSE);
13925cff7825Smh27603 		ppm_cpr_window_flag = B_TRUE;
13935cff7825Smh27603 		mutex_exit(&ppm_cpr_window_lock);
13945cff7825Smh27603 
13955cff7825Smh27603 		ret = ppm_bringup_domains();
13965cff7825Smh27603 
13975cff7825Smh27603 		break;
13985cff7825Smh27603 
13995cff7825Smh27603 	case CB_CODE_CPR_RESUME:
14005cff7825Smh27603 
14015cff7825Smh27603 		/* post-resume: end of cpr window */
14025cff7825Smh27603 		ret = ppm_sync_bookkeeping();
14035cff7825Smh27603 
14045cff7825Smh27603 		mutex_enter(&ppm_cpr_window_lock);
14055cff7825Smh27603 		ASSERT(ppm_cpr_window_flag == B_TRUE);
14065cff7825Smh27603 		ppm_cpr_window_flag = B_FALSE;
14075cff7825Smh27603 		mutex_exit(&ppm_cpr_window_lock);
14085cff7825Smh27603 
14095cff7825Smh27603 		break;
1410*584b574aSToomas Soome 	default:
1411*584b574aSToomas Soome 		ret = DDI_SUCCESS;
1412*584b574aSToomas Soome 		break;
14135cff7825Smh27603 	}
14145cff7825Smh27603 
14155cff7825Smh27603 	return (ret == DDI_SUCCESS);
14165cff7825Smh27603 }
14175cff7825Smh27603 
14185cff7825Smh27603 
14195cff7825Smh27603 /*
14205cff7825Smh27603  * Initialize our private version of real power level
14215cff7825Smh27603  * as well as lowest and highest levels the device supports;
14225cff7825Smh27603  * relate to ppm_add_dev
14235cff7825Smh27603  */
14245cff7825Smh27603 void
ppm_dev_init(ppm_dev_t * ppmd)14255cff7825Smh27603 ppm_dev_init(ppm_dev_t *ppmd)
14265cff7825Smh27603 {
14275cff7825Smh27603 	struct pm_component *dcomps;
14285cff7825Smh27603 	struct pm_comp *pm_comp;
14295cff7825Smh27603 	dev_info_t *dip;
14305cff7825Smh27603 	int maxi, i;
14315cff7825Smh27603 
14325cff7825Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14335cff7825Smh27603 	ppmd->level = PM_LEVEL_UNKNOWN;
14345cff7825Smh27603 	ppmd->rplvl = PM_LEVEL_UNKNOWN;
14355cff7825Smh27603 
14365cff7825Smh27603 	/* increment pwr_cnt per component */
14375cff7825Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
14385cff7825Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
14395cff7825Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
14405cff7825Smh27603 		ppmd->domp->pwr_cnt++;
14415cff7825Smh27603 
14425cff7825Smh27603 	dip = ppmd->dip;
14435cff7825Smh27603 
14445cff7825Smh27603 	/*
14455cff7825Smh27603 	 * ppm exists to handle power-manageable devices which require
14465cff7825Smh27603 	 * special handling on the current platform.  However, a
14475cff7825Smh27603 	 * driver for such a device may choose not to support power
14485cff7825Smh27603 	 * management on a particular load/attach.  In this case we
14495cff7825Smh27603 	 * we create a structure to represent a single-component device
14505cff7825Smh27603 	 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
14515cff7825Smh27603 	 * are effectively constant.
14525cff7825Smh27603 	 */
14535cff7825Smh27603 	if (PM_GET_PM_INFO(dip)) {
14545cff7825Smh27603 		dcomps = DEVI(dip)->devi_pm_components;
14555cff7825Smh27603 		pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
14565cff7825Smh27603 
14575cff7825Smh27603 		ppmd->lowest = pm_comp->pmc_lvals[0];
14585cff7825Smh27603 		ASSERT(ppmd->lowest >= 0);
14595cff7825Smh27603 		maxi = pm_comp->pmc_numlevels - 1;
14605cff7825Smh27603 		ppmd->highest = pm_comp->pmc_lvals[maxi];
14615cff7825Smh27603 
14625cff7825Smh27603 		/*
14635cff7825Smh27603 		 * If 66mhz PCI device on pci 66mhz bus supports D2 state
14645cff7825Smh27603 		 * (config reg PMC bit 10 set), ppm could turn off its bus
14655cff7825Smh27603 		 * clock once it is at D3hot.
14665cff7825Smh27603 		 */
14675cff7825Smh27603 		if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
14685cff7825Smh27603 			for (i = 0; i < maxi; i++)
14695cff7825Smh27603 				if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
14705cff7825Smh27603 					ppmd->flags |= PPMDEV_PCI66_D2;
14715cff7825Smh27603 					break;
14725cff7825Smh27603 				}
14735cff7825Smh27603 		}
14745cff7825Smh27603 	}
14755cff7825Smh27603 
14765cff7825Smh27603 	/*
14775cff7825Smh27603 	 * If device is in PCI_PROP domain and has exported the
14785cff7825Smh27603 	 * property listed in ppm.conf, its clock will be turned
14795cff7825Smh27603 	 * off when all pm'able devices in that domain are at D3.
14805cff7825Smh27603 	 */
14815cff7825Smh27603 	if ((ppmd->domp->model == PPMD_PCI_PROP) &&
14825cff7825Smh27603 	    (ppmd->domp->propname != NULL) &&
14835cff7825Smh27603 	    ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
14845cff7825Smh27603 	    ppmd->domp->propname))
14855cff7825Smh27603 		ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
14865cff7825Smh27603 }
14875cff7825Smh27603 
14885cff7825Smh27603 
14895cff7825Smh27603 /*
14905cff7825Smh27603  * relate to ppm_rem_dev
14915cff7825Smh27603  */
14925cff7825Smh27603 void
ppm_dev_fini(ppm_dev_t * ppmd)14935cff7825Smh27603 ppm_dev_fini(ppm_dev_t *ppmd)
14945cff7825Smh27603 {
14955cff7825Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14965cff7825Smh27603 
14975cff7825Smh27603 	/* decrement pwr_cnt per component */
14985cff7825Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
14995cff7825Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
15005cff7825Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
15015cff7825Smh27603 		if (ppmd->level != ppmd->lowest)
15025cff7825Smh27603 			ppmd->domp->pwr_cnt--;
15035cff7825Smh27603 }
15045cff7825Smh27603 
15055cff7825Smh27603 /*
15065cff7825Smh27603  * Each power fet controls the power of one or more platform
15075cff7825Smh27603  * device(s) within their domain.  Hence domain devices' power
15085cff7825Smh27603  * level change has been monitored, such that once all devices
15095cff7825Smh27603  * are powered off, the fet is turned off to save more power.
15105cff7825Smh27603  *
15115cff7825Smh27603  * To power on any domain device, the domain power fet
15125cff7825Smh27603  * needs to be turned on first. always one fet per domain.
15135cff7825Smh27603  */
15145cff7825Smh27603 static int
ppm_manage_fet(dev_info_t * dip,power_req_t * reqp,int * result)15155cff7825Smh27603 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
15165cff7825Smh27603 {
15175cff7825Smh27603 #ifdef DEBUG
15185cff7825Smh27603 	char *str = "ppm_manage_fet";
15195cff7825Smh27603 #endif
15205cff7825Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
15215cff7825Smh27603 	int		new, old, cmpt;
15225cff7825Smh27603 	ppm_dev_t	*ppmd;
15235cff7825Smh27603 	ppm_domain_t	*domp;
15245cff7825Smh27603 	int		incr = 0;
15255cff7825Smh27603 	int		dummy_ret;
15265cff7825Smh27603 
15275cff7825Smh27603 
15285cff7825Smh27603 	*result = DDI_SUCCESS;
15295cff7825Smh27603 	switch (reqp->request_type) {
15305cff7825Smh27603 	case PMR_PPM_SET_POWER:
15315cff7825Smh27603 		pwr_func = ppm_change_power_level;
15325cff7825Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
15335cff7825Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
15345cff7825Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
15355cff7825Smh27603 		break;
15365cff7825Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
15375cff7825Smh27603 		pwr_func = ppm_record_level_change;
15385cff7825Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
15395cff7825Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
15405cff7825Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
15415cff7825Smh27603 		break;
15425cff7825Smh27603 	default:
15435cff7825Smh27603 		*result = DDI_FAILURE;
15445cff7825Smh27603 		PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
15455cff7825Smh27603 		    str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
15465cff7825Smh27603 		return (DDI_FAILURE);
15475cff7825Smh27603 	}
15485cff7825Smh27603 
15495cff7825Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
15505cff7825Smh27603 		if (cmpt == ppmd->cmpt)
15515cff7825Smh27603 			break;
15525cff7825Smh27603 	if (!ppmd) {
15535cff7825Smh27603 		PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
15545cff7825Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
15555cff7825Smh27603 		*result = DDI_FAILURE;
15565cff7825Smh27603 		return (DDI_FAILURE);
15575cff7825Smh27603 	}
15585cff7825Smh27603 	domp = ppmd->domp;
15595cff7825Smh27603 	PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
15605cff7825Smh27603 	    "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
15615cff7825Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
15625cff7825Smh27603 	    ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
15635cff7825Smh27603 
15645cff7825Smh27603 
15655cff7825Smh27603 	ASSERT(old == ppmd->level);
15665cff7825Smh27603 
15675cff7825Smh27603 	if (new == ppmd->level) {
15685cff7825Smh27603 		PPMD(D_FET, ("nop\n"))
15695cff7825Smh27603 		return (DDI_SUCCESS);
15705cff7825Smh27603 	}
15715cff7825Smh27603 
15725cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
15735cff7825Smh27603 
15745cff7825Smh27603 	/*
15755cff7825Smh27603 	 * In general, a device's published lowest power level does not
15765cff7825Smh27603 	 * have to be 0 if power-off is not tolerated. i.e. a device
15775cff7825Smh27603 	 * instance may export its lowest level > 0.  It is reasonable to
15785cff7825Smh27603 	 * assume that level 0 indicates off state, positive level values
15795cff7825Smh27603 	 * indicate power states above off, include full power state.
15805cff7825Smh27603 	 */
15815cff7825Smh27603 	if (new > 0) { /* device powering up or to different positive level */
15825cff7825Smh27603 		if (domp->status == PPMD_OFF) {
15835cff7825Smh27603 
15845cff7825Smh27603 			/* can not be in (chpt, resume) window */
15855cff7825Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
15865cff7825Smh27603 
15875cff7825Smh27603 			ASSERT(old == 0 && domp->pwr_cnt == 0);
15885cff7825Smh27603 
15895cff7825Smh27603 			PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
15905cff7825Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
15915cff7825Smh27603 
15925cff7825Smh27603 			*result = ppm_fetset(domp, PPMD_ON);
15935cff7825Smh27603 			if (*result != DDI_SUCCESS) {
15945cff7825Smh27603 				PPMD(D_FET, ("\tCan't turn on power FET: "
15955cff7825Smh27603 				    "ret(%d)\n", *result))
15965cff7825Smh27603 				PPM_UNLOCK_DOMAIN(domp);
15975cff7825Smh27603 				return (DDI_FAILURE);
15985cff7825Smh27603 			}
15995cff7825Smh27603 		}
16005cff7825Smh27603 
16015cff7825Smh27603 		/*
16025cff7825Smh27603 		 * If powering up, pre-increment the count before
16035cff7825Smh27603 		 * calling pwr_func, because we are going to release
16045cff7825Smh27603 		 * the domain lock and another thread might turn off
16055cff7825Smh27603 		 * domain power otherwise.
16065cff7825Smh27603 		 */
16075cff7825Smh27603 		if (old == 0) {
16085cff7825Smh27603 			domp->pwr_cnt++;
16095cff7825Smh27603 			incr = 1;
16105cff7825Smh27603 		}
16115cff7825Smh27603 
16125cff7825Smh27603 		PPMD(D_FET, ("\t%s domain power count: %d\n",
16135cff7825Smh27603 		    domp->name, domp->pwr_cnt))
16145cff7825Smh27603 	}
16155cff7825Smh27603 
16165cff7825Smh27603 
16175cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
16185cff7825Smh27603 
16195cff7825Smh27603 	ASSERT(domp->pwr_cnt > 0);
16205cff7825Smh27603 
16215cff7825Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
16225cff7825Smh27603 		PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
16235cff7825Smh27603 		    ppmd->path, *result))
16245cff7825Smh27603 	}
16255cff7825Smh27603 
16265cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
16275cff7825Smh27603 
16285cff7825Smh27603 	/*
16295cff7825Smh27603 	 * Decr the power count in two cases:
16305cff7825Smh27603 	 *
16315cff7825Smh27603 	 *   1) request was to power device down and was successful
16325cff7825Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
16335cff7825Smh27603 	 */
16345cff7825Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
16355cff7825Smh27603 	    (*result != DDI_SUCCESS && incr)) {
16365cff7825Smh27603 		ASSERT(domp->pwr_cnt > 0);
16375cff7825Smh27603 		domp->pwr_cnt--;
16385cff7825Smh27603 	}
16395cff7825Smh27603 
16405cff7825Smh27603 	PPMD(D_FET, ("\t%s domain power count: %d\n",
16415cff7825Smh27603 	    domp->name, domp->pwr_cnt))
16425cff7825Smh27603 
16435cff7825Smh27603 	/*
16445cff7825Smh27603 	 * call to pwr_func will update ppm data structures, if it
16455cff7825Smh27603 	 * succeeds. ppm should return whatever is the return value
16465cff7825Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
16475cff7825Smh27603 	 * always in sync. Use dummy_ret from here for any further
16485cff7825Smh27603 	 * return values.
16495cff7825Smh27603 	 */
16505cff7825Smh27603 	if ((domp->pwr_cnt == 0) &&
16515cff7825Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
16525cff7825Smh27603 	    ppm_none_else_holds_power(domp)) {
16535cff7825Smh27603 
16545cff7825Smh27603 		PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
16555cff7825Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
16565cff7825Smh27603 
16575cff7825Smh27603 		dummy_ret = ppm_fetset(domp, PPMD_OFF);
16585cff7825Smh27603 		if (dummy_ret != DDI_SUCCESS) {
16595cff7825Smh27603 			PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
16605cff7825Smh27603 			    dummy_ret))
16615cff7825Smh27603 		}
16625cff7825Smh27603 	}
16635cff7825Smh27603 
16645cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
16655cff7825Smh27603 	ASSERT(domp->pwr_cnt >= 0);
16665cff7825Smh27603 	return (*result);
16675cff7825Smh27603 }
16685cff7825Smh27603 
16695cff7825Smh27603 
16705cff7825Smh27603 /*
16715cff7825Smh27603  * the actual code that turn on or off domain power fet and
16725cff7825Smh27603  * update domain status
16735cff7825Smh27603  */
16745cff7825Smh27603 static int
ppm_fetset(ppm_domain_t * domp,uint8_t value)16755cff7825Smh27603 ppm_fetset(ppm_domain_t *domp, uint8_t value)
16765cff7825Smh27603 {
16775cff7825Smh27603 	char	*str = "ppm_fetset";
16785cff7825Smh27603 	int	key;
16795cff7825Smh27603 	ppm_dc_t *dc;
16805cff7825Smh27603 	int	ret;
16815cff7825Smh27603 	clock_t	temp;
16825cff7825Smh27603 	clock_t delay = 0;
16835cff7825Smh27603 
16845cff7825Smh27603 	key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
16855cff7825Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
16865cff7825Smh27603 		if (dc->cmd == key)
16875cff7825Smh27603 			break;
16885cff7825Smh27603 	if (!dc || !dc->lh) {
16895cff7825Smh27603 		PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
16905cff7825Smh27603 		    str, domp->name))
16915cff7825Smh27603 		return (DDI_FAILURE);
16925cff7825Smh27603 	}
16935cff7825Smh27603 
16945cff7825Smh27603 	if (key == PPMDC_FET_ON) {
16955cff7825Smh27603 		PPM_GET_IO_DELAY(dc, delay);
16965cff7825Smh27603 		if (delay > 0 && domp->last_off_time > 0) {
16975cff7825Smh27603 			/*
16985cff7825Smh27603 			 * provide any delay required before turning on.
16995cff7825Smh27603 			 * some devices e.g. Samsung DVD require minimum
17005cff7825Smh27603 			 * of 1 sec between OFF->ON. no delay is required
17015cff7825Smh27603 			 * for the first time.
17025cff7825Smh27603 			 */
17035cff7825Smh27603 			temp = ddi_get_lbolt();
17045cff7825Smh27603 			temp -= domp->last_off_time;
17055cff7825Smh27603 			temp = drv_hztousec(temp);
17065cff7825Smh27603 
17075cff7825Smh27603 			if (temp < delay) {
17085cff7825Smh27603 				/*
17095cff7825Smh27603 				 * busy wait untill we meet the
17105cff7825Smh27603 				 * required delay. Since we maintain
17115cff7825Smh27603 				 * time stamps in terms of clock ticks
17125cff7825Smh27603 				 * we might wait for longer than required
17135cff7825Smh27603 				 */
17145cff7825Smh27603 				PPMD(D_FET, ("%s : waiting %lu micro seconds "
17155cff7825Smh27603 				    "before on\n", domp->name,
17162df1fe9cSrandyf 				    delay - temp));
17175cff7825Smh27603 				drv_usecwait(delay - temp);
17185cff7825Smh27603 			}
17195cff7825Smh27603 		}
17205cff7825Smh27603 	}
17215cff7825Smh27603 	switch (dc->method) {
17222df1fe9cSrandyf #ifdef sun4u
17235cff7825Smh27603 	case PPMDC_I2CKIO: {
17245cff7825Smh27603 		i2c_gpio_t i2c_req;
17255cff7825Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
17265cff7825Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
17275cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
17285cff7825Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
17295cff7825Smh27603 		break;
17305cff7825Smh27603 	}
17315cff7825Smh27603 #endif
17325cff7825Smh27603 
17335cff7825Smh27603 	case PPMDC_KIO:
17345cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
17355cff7825Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
17365cff7825Smh27603 		    NULL);
17375cff7825Smh27603 		break;
17385cff7825Smh27603 
17395cff7825Smh27603 	default:
17405cff7825Smh27603 		PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
17415cff7825Smh27603 		    str, domp->dc->method))
17425cff7825Smh27603 		return (DDI_FAILURE);
17435cff7825Smh27603 	}
17445cff7825Smh27603 
17455cff7825Smh27603 	PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
17465cff7825Smh27603 	    (ret == 0) ? "turned" : "failed to turn",
17475cff7825Smh27603 	    domp->name,
17485cff7825Smh27603 	    (domp->status == PPMD_ON) ? "ON" : "OFF",
17495cff7825Smh27603 	    (value == PPMD_ON) ? "ON" : "OFF"))
17505cff7825Smh27603 
17515cff7825Smh27603 	if (ret == DDI_SUCCESS) {
17525cff7825Smh27603 		domp->status = value;
17535cff7825Smh27603 
17545cff7825Smh27603 		if (key == PPMDC_FET_OFF)
17555cff7825Smh27603 			/*
17565cff7825Smh27603 			 * record the time, when it is off. time is recorded
17575cff7825Smh27603 			 * in clock ticks
17585cff7825Smh27603 			 */
17595cff7825Smh27603 			domp->last_off_time = ddi_get_lbolt();
17605cff7825Smh27603 
17615cff7825Smh27603 		/* implement any post op delay. */
17625cff7825Smh27603 		if (key == PPMDC_FET_ON) {
17631159df0bSmh27603 			PPM_GET_IO_POST_DELAY(dc, delay);
17645cff7825Smh27603 			PPMD(D_FET, ("%s : waiting %lu micro seconds "
17655cff7825Smh27603 			    "after on\n", domp->name, delay))
17665cff7825Smh27603 			if (delay > 0)
17675cff7825Smh27603 				drv_usecwait(delay);
17685cff7825Smh27603 		}
17695cff7825Smh27603 	}
17705cff7825Smh27603 
17715cff7825Smh27603 	return (ret);
17725cff7825Smh27603 }
17735cff7825Smh27603 
17745cff7825Smh27603 
17755cff7825Smh27603 /*
17765cff7825Smh27603  * read power fet status
17775cff7825Smh27603  */
17785cff7825Smh27603 static int
ppm_fetget(ppm_domain_t * domp,uint8_t * lvl)17795cff7825Smh27603 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
17805cff7825Smh27603 {
17815cff7825Smh27603 	char	*str = "ppm_fetget";
17825cff7825Smh27603 	ppm_dc_t *dc = domp->dc;
17835cff7825Smh27603 	uint_t	kio_val;
17845cff7825Smh27603 	int	off_val;
17855cff7825Smh27603 	int	ret;
17865cff7825Smh27603 
17875cff7825Smh27603 	if (!dc->lh) {
17885cff7825Smh27603 		PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
17895cff7825Smh27603 		    str, domp->name))
17905cff7825Smh27603 		return (DDI_FAILURE);
17915cff7825Smh27603 	}
17925cff7825Smh27603 	if (!dc->next) {
17935cff7825Smh27603 		cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
17945cff7825Smh27603 		    "defined, found only one in domain(%s)", str, domp->name);
17955cff7825Smh27603 		return (DDI_FAILURE);
17965cff7825Smh27603 	}
17975cff7825Smh27603 
17985cff7825Smh27603 	switch (dc->method) {
17992df1fe9cSrandyf #ifdef sun4u
18005cff7825Smh27603 	case PPMDC_I2CKIO: {
18015cff7825Smh27603 		i2c_gpio_t i2c_req;
18025cff7825Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
18035cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
18045cff7825Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
18055cff7825Smh27603 
18065cff7825Smh27603 		if (ret) {
18075cff7825Smh27603 			PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
18085cff7825Smh27603 			    str, ret))
18095cff7825Smh27603 			return (ret);
18105cff7825Smh27603 		}
18115cff7825Smh27603 
18125cff7825Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
18135cff7825Smh27603 		    dc->next->m_un.i2c.val;
18145cff7825Smh27603 		*lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
18155cff7825Smh27603 
18165cff7825Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18175cff7825Smh27603 		    (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
18185cff7825Smh27603 
18195cff7825Smh27603 		break;
18205cff7825Smh27603 	}
18215cff7825Smh27603 #endif
18225cff7825Smh27603 
18235cff7825Smh27603 	case PPMDC_KIO:
18245cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
18255cff7825Smh27603 		    (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
18265cff7825Smh27603 		if (ret) {
18275cff7825Smh27603 			PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
18285cff7825Smh27603 			    str, ret))
18295cff7825Smh27603 			return (ret);
18305cff7825Smh27603 		}
18315cff7825Smh27603 
18325cff7825Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
18335cff7825Smh27603 		    dc->next->m_un.kio.val;
18345cff7825Smh27603 		*lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
18355cff7825Smh27603 
18365cff7825Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18375cff7825Smh27603 		    (kio_val == off_val) ? "OFF" : "ON"))
18385cff7825Smh27603 
18395cff7825Smh27603 		break;
18405cff7825Smh27603 
18415cff7825Smh27603 	default:
18425cff7825Smh27603 		PPMD(D_FET, ("%s: unsupported domain control method %d\n",
18435cff7825Smh27603 		    str, domp->dc->method))
18445cff7825Smh27603 		return (DDI_FAILURE);
18455cff7825Smh27603 	}
18465cff7825Smh27603 
18475cff7825Smh27603 	return (DDI_SUCCESS);
18485cff7825Smh27603 }
18495cff7825Smh27603 
18505cff7825Smh27603 
18515cff7825Smh27603 /*
18525cff7825Smh27603  * the actual code that switches pci clock and update domain status
18535cff7825Smh27603  */
18545cff7825Smh27603 static int
ppm_switch_clock(ppm_domain_t * domp,int onoff)18555cff7825Smh27603 ppm_switch_clock(ppm_domain_t *domp, int onoff)
18565cff7825Smh27603 {
18575cff7825Smh27603 #ifdef DEBUG
18585cff7825Smh27603 	char *str = "ppm_switch_clock";
18595cff7825Smh27603 #endif
18605cff7825Smh27603 	int	cmd, pio_save;
18615cff7825Smh27603 	ppm_dc_t *dc;
18625cff7825Smh27603 	int ret;
18635cff7825Smh27603 	extern int do_polled_io;
18645cff7825Smh27603 	extern uint_t cfb_inuse;
18655cff7825Smh27603 	ppm_dev_t	*pdev;
18665cff7825Smh27603 
18675cff7825Smh27603 	cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
18685cff7825Smh27603 	dc = ppm_lookup_dc(domp, cmd);
18695cff7825Smh27603 	if (!dc) {
18705cff7825Smh27603 		PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
18715cff7825Smh27603 		    str, domp->name))
18725cff7825Smh27603 		return (DDI_FAILURE);
18735cff7825Smh27603 	}
18745cff7825Smh27603 
18755cff7825Smh27603 	switch (dc->method) {
18765cff7825Smh27603 	case PPMDC_KIO:
18775cff7825Smh27603 		/*
18785cff7825Smh27603 		 * If we're powering up cfb on a Stop-A, we only
18795cff7825Smh27603 		 * want to do polled i/o to turn ON the clock
18805cff7825Smh27603 		 */
18815cff7825Smh27603 		pio_save = do_polled_io;
18825cff7825Smh27603 		if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
18835cff7825Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
18845cff7825Smh27603 				if (pm_is_cfb(pdev->dip)) {
18855cff7825Smh27603 					do_polled_io = 1;
18865cff7825Smh27603 					break;
18875cff7825Smh27603 				}
18885cff7825Smh27603 			}
18895cff7825Smh27603 		}
18905cff7825Smh27603 
18915cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
18925cff7825Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
18935cff7825Smh27603 		    kcred, NULL);
18945cff7825Smh27603 
18955cff7825Smh27603 		do_polled_io = pio_save;
18965cff7825Smh27603 
18975cff7825Smh27603 		if (ret == 0) {
18985cff7825Smh27603 			if (cmd == PPMDC_CLK_ON) {
18995cff7825Smh27603 				domp->status = PPMD_ON;
19005cff7825Smh27603 
19015cff7825Smh27603 				/*
19025cff7825Smh27603 				 * PCI PM spec requires 50ms delay
19035cff7825Smh27603 				 */
19045cff7825Smh27603 				drv_usecwait(50000);
19055cff7825Smh27603 			} else
19065cff7825Smh27603 				domp->status = PPMD_OFF;
19075cff7825Smh27603 		}
19085cff7825Smh27603 
19095cff7825Smh27603 		PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
19105cff7825Smh27603 		    (ret == 0) ? "turned" : "failed to turn",
19115cff7825Smh27603 		    (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
19125cff7825Smh27603 		    domp->name))
19135cff7825Smh27603 
19145cff7825Smh27603 		break;
19155cff7825Smh27603 
19165cff7825Smh27603 	default:
19175cff7825Smh27603 		PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
19185cff7825Smh27603 		    str, dc->method))
19195cff7825Smh27603 		return (DDI_FAILURE);
19205cff7825Smh27603 	}
19215cff7825Smh27603 
19225cff7825Smh27603 	return (DDI_SUCCESS);
19235cff7825Smh27603 }
19245cff7825Smh27603 
19255cff7825Smh27603 
19265cff7825Smh27603 /*
19275cff7825Smh27603  * pci slot domain is formed of pci device(s) reside in a pci slot.
19285cff7825Smh27603  * This function monitors domain device's power level change, such
19295cff7825Smh27603  * that,
19305cff7825Smh27603  *   when all domain power count has gone to 0, it attempts to turn off
19315cff7825Smh27603  *        the pci slot's clock;
19325cff7825Smh27603  *   if any domain device is powering up, it'll turn on the pci slot's
19335cff7825Smh27603  *        clock as the first thing.
19345cff7825Smh27603  */
19355cff7825Smh27603 /* ARGUSED */
19365cff7825Smh27603 static int
ppm_manage_pci(dev_info_t * dip,power_req_t * reqp,int * result)19375cff7825Smh27603 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
19385cff7825Smh27603 {
19395cff7825Smh27603 #ifdef DEBUG
19405cff7825Smh27603 	char *str = "ppm_manage_pci";
19415cff7825Smh27603 #endif
19425cff7825Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
19435cff7825Smh27603 	int old, new, cmpt;
19445cff7825Smh27603 	ppm_dev_t *ppmd;
19455cff7825Smh27603 	ppm_domain_t *domp;
19465cff7825Smh27603 	int incr = 0;
19475cff7825Smh27603 	int dummy_ret;
19485cff7825Smh27603 
19495cff7825Smh27603 	*result = DDI_SUCCESS;
19505cff7825Smh27603 	switch (reqp->request_type) {
19515cff7825Smh27603 	case PMR_PPM_SET_POWER:
19525cff7825Smh27603 		pwr_func = ppm_change_power_level;
19535cff7825Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
19545cff7825Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
19555cff7825Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
19565cff7825Smh27603 		break;
19575cff7825Smh27603 
19585cff7825Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
19595cff7825Smh27603 		pwr_func = ppm_record_level_change;
19605cff7825Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
19615cff7825Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
19625cff7825Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
19635cff7825Smh27603 		break;
19645cff7825Smh27603 
19655cff7825Smh27603 	default:
19665cff7825Smh27603 		*result = DDI_FAILURE;
19675cff7825Smh27603 		return (DDI_FAILURE);
19685cff7825Smh27603 	}
19695cff7825Smh27603 
19705cff7825Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
19715cff7825Smh27603 		if (cmpt == ppmd->cmpt)
19725cff7825Smh27603 			break;
19735cff7825Smh27603 	if (!ppmd) {
19745cff7825Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
19755cff7825Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
19765cff7825Smh27603 		*result = DDI_FAILURE;
19775cff7825Smh27603 		return (DDI_FAILURE);
19785cff7825Smh27603 	}
19795cff7825Smh27603 	domp = ppmd->domp;
19805cff7825Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
19815cff7825Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
19825cff7825Smh27603 	    ppmd->path, cmpt, old, new))
19835cff7825Smh27603 
19845cff7825Smh27603 	ASSERT(old == ppmd->level);
19855cff7825Smh27603 	if (new == ppmd->level)
19865cff7825Smh27603 		return (DDI_SUCCESS);
19875cff7825Smh27603 
19885cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
19895cff7825Smh27603 
19905cff7825Smh27603 	if (new > 0) {		/* device powering up */
19915cff7825Smh27603 		if (domp->status == PPMD_OFF) {
19925cff7825Smh27603 
19935cff7825Smh27603 			/* cannot be off during (chpt, resume) window */
19945cff7825Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
19955cff7825Smh27603 
19965cff7825Smh27603 			/* either both OFF or both ON */
19975cff7825Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
19985cff7825Smh27603 
19995cff7825Smh27603 			PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
20005cff7825Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
20015cff7825Smh27603 
20025cff7825Smh27603 			*result = ppm_switch_clock(domp, PPMD_ON);
20035cff7825Smh27603 			if (*result != DDI_SUCCESS) {
20045cff7825Smh27603 				PPMD(D_PCI, ("\tcan't switch on pci clock: "
20055cff7825Smh27603 				    "ret(%d)\n", *result))
20065cff7825Smh27603 				PPM_UNLOCK_DOMAIN(domp);
20075cff7825Smh27603 				return (DDI_FAILURE);
20085cff7825Smh27603 			}
20095cff7825Smh27603 		}
20105cff7825Smh27603 
20115cff7825Smh27603 		if (old == 0) {
20125cff7825Smh27603 			domp->pwr_cnt++;
20135cff7825Smh27603 			incr = 1;
20145cff7825Smh27603 		}
20155cff7825Smh27603 
20165cff7825Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
20175cff7825Smh27603 		    domp->name, domp->pwr_cnt))
20185cff7825Smh27603 	}
20195cff7825Smh27603 
20205cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
20215cff7825Smh27603 
20225cff7825Smh27603 	ASSERT(domp->pwr_cnt > 0);
20235cff7825Smh27603 
20245cff7825Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
20255cff7825Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
20265cff7825Smh27603 		    ppmd->path, *result))
20275cff7825Smh27603 	}
20285cff7825Smh27603 
20295cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
20305cff7825Smh27603 
20315cff7825Smh27603 	/*
20325cff7825Smh27603 	 * Decr the power count in two cases:
20335cff7825Smh27603 	 *
20345cff7825Smh27603 	 *   1) request was to power device down and was successful
20355cff7825Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
20365cff7825Smh27603 	 */
20375cff7825Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
20385cff7825Smh27603 	    (*result != DDI_SUCCESS && incr)) {
20395cff7825Smh27603 		ASSERT(domp->pwr_cnt > 0);
20405cff7825Smh27603 		domp->pwr_cnt--;
20415cff7825Smh27603 	}
20425cff7825Smh27603 
20435cff7825Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
20445cff7825Smh27603 	    domp->name, domp->pwr_cnt))
20455cff7825Smh27603 
20465cff7825Smh27603 	/*
20475cff7825Smh27603 	 * call to pwr_func will update ppm data structures, if it
20485cff7825Smh27603 	 * succeeds. ppm should return whatever is the return value
20495cff7825Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
20505cff7825Smh27603 	 * always in sync. Use dummy_ret from here for any further
20515cff7825Smh27603 	 * return values.
20525cff7825Smh27603 	 */
20535cff7825Smh27603 	if ((domp->pwr_cnt == 0) &&
20545cff7825Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
20555cff7825Smh27603 	    ppm_none_else_holds_power(domp)) {
20565cff7825Smh27603 
20575cff7825Smh27603 		PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
20585cff7825Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
20595cff7825Smh27603 
20605cff7825Smh27603 		dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
20615cff7825Smh27603 		if (dummy_ret != DDI_SUCCESS) {
20625cff7825Smh27603 			PPMD(D_PCI, ("\tCan't switch clock off: "
20635cff7825Smh27603 			    "ret(%d)\n", dummy_ret))
20645cff7825Smh27603 		}
20655cff7825Smh27603 	}
20665cff7825Smh27603 
20675cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
20685cff7825Smh27603 	ASSERT(domp->pwr_cnt >= 0);
20695cff7825Smh27603 	return (*result);
20705cff7825Smh27603 }
20715cff7825Smh27603 
20725cff7825Smh27603 /*
20735cff7825Smh27603  * When the driver for the primary PCI-Express child has set the device to
20745cff7825Smh27603  * lowest power (D3hot), we come here to save even more power by transitioning
20755cff7825Smh27603  * the slot to D3cold.  Similarly, if the slot is in D3cold and we need to
20765cff7825Smh27603  * power up the child, we come here first to power up the slot.
20775cff7825Smh27603  */
20785cff7825Smh27603 /* ARGUSED */
20795cff7825Smh27603 static int
ppm_manage_pcie(dev_info_t * dip,power_req_t * reqp,int * result)20805cff7825Smh27603 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
20815cff7825Smh27603 {
20825cff7825Smh27603 #ifdef DEBUG
20835cff7825Smh27603 	char *str = "ppm_manage_pcie";
20845cff7825Smh27603 #endif
20855cff7825Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
20865cff7825Smh27603 	int old, new, cmpt;
20875cff7825Smh27603 	ppm_dev_t *ppmd;
20885cff7825Smh27603 	ppm_domain_t *domp;
20895cff7825Smh27603 	int incr = 0;
20905cff7825Smh27603 	int dummy_ret;
20915cff7825Smh27603 
20925cff7825Smh27603 	*result = DDI_SUCCESS;
20935cff7825Smh27603 	switch (reqp->request_type) {
20945cff7825Smh27603 	case PMR_PPM_SET_POWER:
20955cff7825Smh27603 		pwr_func = ppm_change_power_level;
20965cff7825Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
20975cff7825Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
20985cff7825Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
20995cff7825Smh27603 		break;
21005cff7825Smh27603 
21015cff7825Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
21025cff7825Smh27603 		pwr_func = ppm_record_level_change;
21035cff7825Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
21045cff7825Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
21055cff7825Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
21065cff7825Smh27603 		break;
21075cff7825Smh27603 
21085cff7825Smh27603 	default:
21095cff7825Smh27603 		*result = DDI_FAILURE;
21105cff7825Smh27603 		return (DDI_FAILURE);
21115cff7825Smh27603 	}
21125cff7825Smh27603 
21135cff7825Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
21145cff7825Smh27603 		if (cmpt == ppmd->cmpt)
21155cff7825Smh27603 			break;
21165cff7825Smh27603 	if (!ppmd) {
21175cff7825Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
21185cff7825Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
21195cff7825Smh27603 		*result = DDI_FAILURE;
21205cff7825Smh27603 		return (DDI_FAILURE);
21215cff7825Smh27603 	}
21225cff7825Smh27603 	domp = ppmd->domp;
21235cff7825Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
21245cff7825Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
21255cff7825Smh27603 	    ppmd->path, cmpt, old, new))
21265cff7825Smh27603 
21275cff7825Smh27603 	ASSERT(old == ppmd->level);
21285cff7825Smh27603 	if (new == ppmd->level)
21295cff7825Smh27603 		return (DDI_SUCCESS);
21305cff7825Smh27603 
21315cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
21325cff7825Smh27603 
21335cff7825Smh27603 	if (new > 0) {		/* device powering up */
21345cff7825Smh27603 		if (domp->status == PPMD_OFF) {
21355cff7825Smh27603 
21365cff7825Smh27603 			/* cannot be off during (chpt, resume) window */
21375cff7825Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
21385cff7825Smh27603 
21395cff7825Smh27603 			/* either both OFF or both ON */
21405cff7825Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
21415cff7825Smh27603 
21425cff7825Smh27603 			PPMD(D_PCI, ("About to turn on pcie slot for "
21435cff7825Smh27603 			    "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
21445cff7825Smh27603 
21455cff7825Smh27603 			*result = ppm_pcie_pwr(domp, PPMD_ON);
21465cff7825Smh27603 			if (*result != DDI_SUCCESS) {
21475cff7825Smh27603 				PPMD(D_PCI, ("\tcan't switch on pcie slot: "
21485cff7825Smh27603 				    "ret(%d)\n", *result))
21495cff7825Smh27603 				PPM_UNLOCK_DOMAIN(domp);
21505cff7825Smh27603 				return (DDI_FAILURE);
21515cff7825Smh27603 			}
21525cff7825Smh27603 		}
21535cff7825Smh27603 
21545cff7825Smh27603 		if (old == 0) {
21555cff7825Smh27603 			domp->pwr_cnt++;
21565cff7825Smh27603 			incr = 1;
21575cff7825Smh27603 		}
21585cff7825Smh27603 
21595cff7825Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
21605cff7825Smh27603 		    domp->name, domp->pwr_cnt))
21615cff7825Smh27603 	}
21625cff7825Smh27603 
21635cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
21645cff7825Smh27603 
21655cff7825Smh27603 	ASSERT(domp->pwr_cnt > 0);
21665cff7825Smh27603 
21675cff7825Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
21685cff7825Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
21695cff7825Smh27603 		    ppmd->path, *result))
21705cff7825Smh27603 	}
21715cff7825Smh27603 
21725cff7825Smh27603 	PPM_LOCK_DOMAIN(domp);
21735cff7825Smh27603 
21745cff7825Smh27603 	/*
21755cff7825Smh27603 	 * Decr the power count in two cases:
21765cff7825Smh27603 	 *
21775cff7825Smh27603 	 *   1) request was to power device down and was successful
21785cff7825Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
21795cff7825Smh27603 	 */
21805cff7825Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
21815cff7825Smh27603 	    (*result != DDI_SUCCESS && incr)) {
21825cff7825Smh27603 		ASSERT(domp->pwr_cnt > 0);
21835cff7825Smh27603 		domp->pwr_cnt--;
21845cff7825Smh27603 	}
21855cff7825Smh27603 
21865cff7825Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
21875cff7825Smh27603 	    domp->name, domp->pwr_cnt))
21885cff7825Smh27603 
21895cff7825Smh27603 	/*
21905cff7825Smh27603 	 * call to pwr_func will update ppm data structures, if it
21915cff7825Smh27603 	 * succeeds. ppm should return whatever is the return value
21925cff7825Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
21935cff7825Smh27603 	 * always in sync. Use dummy_ret from here for any further
21945cff7825Smh27603 	 * return values.
21955cff7825Smh27603 	 */
21965cff7825Smh27603 	if ((domp->pwr_cnt == 0) &&
21975cff7825Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
21985cff7825Smh27603 	    ppm_none_else_holds_power(domp)) {
21995cff7825Smh27603 
22005cff7825Smh27603 		PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
22015cff7825Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
22025cff7825Smh27603 
22035cff7825Smh27603 		dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
22045cff7825Smh27603 		if (dummy_ret != DDI_SUCCESS) {
22055cff7825Smh27603 			PPMD(D_PCI, ("\tCan't switch pcie slot off: "
22065cff7825Smh27603 			    "ret(%d)\n", dummy_ret))
22075cff7825Smh27603 		}
22085cff7825Smh27603 	}
22095cff7825Smh27603 
22105cff7825Smh27603 	PPM_UNLOCK_DOMAIN(domp);
22115cff7825Smh27603 	ASSERT(domp->pwr_cnt >= 0);
22125cff7825Smh27603 	return (*result);
22135cff7825Smh27603 
22145cff7825Smh27603 }
22155cff7825Smh27603 
22165cff7825Smh27603 /*
22175cff7825Smh27603  * Set or clear a bit on a GPIO device.  These bits are used for various device-
22185cff7825Smh27603  * specific purposes.
22195cff7825Smh27603  */
22205cff7825Smh27603 static int
ppm_gpioset(ppm_domain_t * domp,int key)22215cff7825Smh27603 ppm_gpioset(ppm_domain_t *domp, int key)
22225cff7825Smh27603 {
22235cff7825Smh27603 #ifdef DEBUG
22245cff7825Smh27603 	char	*str = "ppm_gpioset";
22255cff7825Smh27603 #endif
22265cff7825Smh27603 	ppm_dc_t *dc;
22275cff7825Smh27603 	int	ret;
22285cff7825Smh27603 	clock_t delay = 0;
22295cff7825Smh27603 
22305cff7825Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
22315cff7825Smh27603 		if (dc->cmd == key)
22325cff7825Smh27603 			break;
22335cff7825Smh27603 	if (!dc || !dc->lh) {
22345cff7825Smh27603 		PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
22355cff7825Smh27603 		    str, domp->name))
22365cff7825Smh27603 		return (DDI_FAILURE);
22375cff7825Smh27603 	}
22385cff7825Smh27603 
22395cff7825Smh27603 	PPM_GET_IO_DELAY(dc, delay);
22405cff7825Smh27603 	if (delay > 0) {
22415cff7825Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
22425cff7825Smh27603 		    "before change\n", domp->name, delay))
22435cff7825Smh27603 		drv_usecwait(delay);
22445cff7825Smh27603 	}
22455cff7825Smh27603 
22465cff7825Smh27603 	switch (dc->method) {
22472df1fe9cSrandyf #ifdef sun4u
22485cff7825Smh27603 	case PPMDC_I2CKIO: {
22495cff7825Smh27603 		i2c_gpio_t i2c_req;
22505cff7825Smh27603 		ppm_dev_t *pdev;
22515cff7825Smh27603 		int pio_save;
22525cff7825Smh27603 		extern int do_polled_io;
22535cff7825Smh27603 		extern uint_t cfb_inuse;
22545cff7825Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
22555cff7825Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
22565cff7825Smh27603 
22575cff7825Smh27603 		pio_save = do_polled_io;
22585cff7825Smh27603 		if (cfb_inuse) {
22595cff7825Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
22605cff7825Smh27603 				if (pm_is_cfb(pdev->dip)) {
22615cff7825Smh27603 					do_polled_io = 1;
22625cff7825Smh27603 					PPMD(D_GPIO, ("%s: cfb is in use, "
22635cff7825Smh27603 					    "i2c transaction is done in "
22645cff7825Smh27603 					    "poll-mode.\n", str))
22655cff7825Smh27603 					break;
22665cff7825Smh27603 				}
22675cff7825Smh27603 			}
22685cff7825Smh27603 		}
22695cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
22705cff7825Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
22715cff7825Smh27603 		do_polled_io = pio_save;
22725cff7825Smh27603 
22735cff7825Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22745cff7825Smh27603 		    "to gpio\n",
22755cff7825Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
22765cff7825Smh27603 		    domp->name,
22775cff7825Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
22785cff7825Smh27603 		    dc->m_un.i2c.val))
22795cff7825Smh27603 
22805cff7825Smh27603 		break;
22815cff7825Smh27603 	}
22825cff7825Smh27603 #endif
22832df1fe9cSrandyf 
22845cff7825Smh27603 	case PPMDC_KIO:
22855cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
22865cff7825Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
22875cff7825Smh27603 		    NULL);
22885cff7825Smh27603 
22895cff7825Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22905cff7825Smh27603 		    "to gpio\n",
22915cff7825Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
22925cff7825Smh27603 		    domp->name,
22935cff7825Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
22945cff7825Smh27603 		    dc->m_un.kio.val))
22955cff7825Smh27603 
22965cff7825Smh27603 		break;
22975cff7825Smh27603 
22985cff7825Smh27603 	default:
22995cff7825Smh27603 		PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
23005cff7825Smh27603 		    str, domp->dc->method))
23015cff7825Smh27603 		return (DDI_FAILURE);
23025cff7825Smh27603 	}
23035cff7825Smh27603 
23045cff7825Smh27603 	/* implement any post op delay. */
23051159df0bSmh27603 	PPM_GET_IO_POST_DELAY(dc, delay);
23065cff7825Smh27603 	if (delay > 0) {
23075cff7825Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23085cff7825Smh27603 		    "after change\n", domp->name, delay))
23095cff7825Smh27603 		drv_usecwait(delay);
23105cff7825Smh27603 	}
23115cff7825Smh27603 
23125cff7825Smh27603 	return (ret);
23135cff7825Smh27603 }
23145cff7825Smh27603 
23155cff7825Smh27603 static int
ppm_pcie_pwr(ppm_domain_t * domp,int onoff)23165cff7825Smh27603 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
23175cff7825Smh27603 {
23185cff7825Smh27603 #ifdef DEBUG
23195cff7825Smh27603 	char *str = "ppm_pcie_pwr";
23205cff7825Smh27603 #endif
23215cff7825Smh27603 	int ret = DDI_FAILURE;
23225cff7825Smh27603 	ppm_dc_t *dc;
23235cff7825Smh27603 	clock_t delay;
23245cff7825Smh27603 
23255cff7825Smh27603 	ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
23265cff7825Smh27603 
23275cff7825Smh27603 	dc = ppm_lookup_dc(domp,
23285cff7825Smh27603 	    onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
23295cff7825Smh27603 	if (dc) {
23305cff7825Smh27603 
23315cff7825Smh27603 		/*
23325cff7825Smh27603 		 * Invoke layered ioctl for pcie root complex nexus to
23335cff7825Smh27603 		 * transition the link
23345cff7825Smh27603 		 */
23355cff7825Smh27603 		ASSERT(dc->method == PPMDC_KIO);
23365cff7825Smh27603 		delay = dc->m_un.kio.delay;
23375cff7825Smh27603 		if (delay > 0) {
23385cff7825Smh27603 			PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23395cff7825Smh27603 			    "before change\n", domp->name, delay))
23405cff7825Smh27603 			drv_usecwait(delay);
23415cff7825Smh27603 		}
23425cff7825Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
23435cff7825Smh27603 		    (intptr_t)&(dc->m_un.kio.val),
23445cff7825Smh27603 		    FWRITE | FKIOCTL, kcred, NULL);
23455cff7825Smh27603 		if (ret == DDI_SUCCESS) {
23465cff7825Smh27603 			delay = dc->m_un.kio.post_delay;
23475cff7825Smh27603 			if (delay > 0) {
23485cff7825Smh27603 				PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23495cff7825Smh27603 				    "after change\n", domp->name, delay))
23505cff7825Smh27603 				drv_usecwait(delay);
23515cff7825Smh27603 			}
23525cff7825Smh27603 		} else {
23535cff7825Smh27603 			PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
23545cff7825Smh27603 			    str, domp->name))
23555cff7825Smh27603 			return (ret);
23565cff7825Smh27603 		}
23575cff7825Smh27603 	}
23585cff7825Smh27603 
23595cff7825Smh27603 	switch (onoff) {
23605cff7825Smh27603 	case PPMD_OFF:
23615cff7825Smh27603 		/* Turn off the clock for this slot. */
23625cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
23635cff7825Smh27603 			PPMD(D_GPIO,
23645cff7825Smh27603 			    ("%s: failed to turn off domain(%s) clock\n",
23655cff7825Smh27603 			    str, domp->name))
23665cff7825Smh27603 			return (ret);
23675cff7825Smh27603 		}
23685cff7825Smh27603 
23695cff7825Smh27603 		/* Turn off the power to this slot */
23705cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
23715cff7825Smh27603 			PPMD(D_GPIO,
23725cff7825Smh27603 			    ("%s: failed to turn off domain(%s) power\n",
23735cff7825Smh27603 			    str, domp->name))
23745cff7825Smh27603 			return (ret);
23755cff7825Smh27603 		}
23765cff7825Smh27603 		break;
23775cff7825Smh27603 	case PPMD_ON:
23785cff7825Smh27603 		/* Assert RESET for this slot. */
23795cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
23805cff7825Smh27603 			PPMD(D_GPIO,
23815cff7825Smh27603 			    ("%s: failed to assert reset for domain(%s)\n",
23825cff7825Smh27603 			    str, domp->name))
23835cff7825Smh27603 			return (ret);
23845cff7825Smh27603 		}
23855cff7825Smh27603 
23865cff7825Smh27603 		/* Turn on the power to this slot */
23875cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
23885cff7825Smh27603 			PPMD(D_GPIO,
23895cff7825Smh27603 			    ("%s: failed to turn on domain(%s) power\n",
23905cff7825Smh27603 			    str, domp->name))
23915cff7825Smh27603 			return (ret);
23925cff7825Smh27603 		}
23935cff7825Smh27603 
23945cff7825Smh27603 		/* Turn on the clock for this slot */
23955cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
23965cff7825Smh27603 			PPMD(D_GPIO,
23975cff7825Smh27603 			    ("%s: failed to turn on domain(%s) clock\n",
23985cff7825Smh27603 			    str, domp->name))
23995cff7825Smh27603 			return (ret);
24005cff7825Smh27603 		}
24015cff7825Smh27603 
24025cff7825Smh27603 		/* De-assert RESET for this slot. */
24035cff7825Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
24045cff7825Smh27603 			PPMD(D_GPIO,
24055cff7825Smh27603 			    ("%s: failed to de-assert reset for domain(%s)\n",
24065cff7825Smh27603 			    str, domp->name))
24075cff7825Smh27603 			return (ret);
24085cff7825Smh27603 		}
24095cff7825Smh27603 
24105cff7825Smh27603 		dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
24115cff7825Smh27603 		if (dc) {
24125cff7825Smh27603 			/*
24135cff7825Smh27603 			 * Invoke layered ioctl to PCIe root complex nexus
24145cff7825Smh27603 			 * to transition the link.
24155cff7825Smh27603 			 */
24165cff7825Smh27603 			ASSERT(dc->method == PPMDC_KIO);
24175cff7825Smh27603 			delay = dc->m_un.kio.delay;
24185cff7825Smh27603 			if (delay > 0) {
24195cff7825Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
24205cff7825Smh27603 				    "before change\n", domp->name, delay))
24215cff7825Smh27603 				drv_usecwait(delay);
24225cff7825Smh27603 			}
24235cff7825Smh27603 			ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
24245cff7825Smh27603 			    (intptr_t)&(dc->m_un.kio.val),
24255cff7825Smh27603 			    FWRITE | FKIOCTL, kcred, NULL);
24265cff7825Smh27603 
24275cff7825Smh27603 			if (ret != DDI_SUCCESS) {
24285cff7825Smh27603 				PPMD(D_PCI, ("%s: layered ioctl to PCIe"
24295cff7825Smh27603 				    "root complex nexus FAILed\n", str))
24305cff7825Smh27603 				return (ret);
24315cff7825Smh27603 			}
24325cff7825Smh27603 
24335cff7825Smh27603 			delay = dc->m_un.kio.post_delay;
24345cff7825Smh27603 			if (delay > 0) {
24355cff7825Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro "
24365cff7825Smh27603 				    "seconds after change\n",
24375cff7825Smh27603 				    domp->name, delay))
24385cff7825Smh27603 				drv_usecwait(delay);
24395cff7825Smh27603 			}
24405cff7825Smh27603 		}
24415cff7825Smh27603 		break;
24425cff7825Smh27603 	default:
24435cff7825Smh27603 		ASSERT(0);
24445cff7825Smh27603 	}
24455cff7825Smh27603 
24465cff7825Smh27603 	PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
24475cff7825Smh27603 	    str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
24485cff7825Smh27603 	    onoff == PPMD_ON ? "ON" : "OFF"))
24495cff7825Smh27603 
24505cff7825Smh27603 	domp->status = onoff;
24515cff7825Smh27603 	return (ret);
24525cff7825Smh27603 }
24535cff7825Smh27603 
24545cff7825Smh27603 
24555cff7825Smh27603 /*
24565cff7825Smh27603  * Change the power level for a component of a device.  If the change
24575cff7825Smh27603  * arg is true, we call the framework to actually change the device's
24585cff7825Smh27603  * power; otherwise, we just update our own copy of the power level.
24595cff7825Smh27603  */
24605cff7825Smh27603 static int
ppm_set_level(ppm_dev_t * ppmd,int cmpt,int level,boolean_t change)24615cff7825Smh27603 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
24625cff7825Smh27603 {
24635cff7825Smh27603 #ifdef DEBUG
24645cff7825Smh27603 	char *str = "ppm_set_level";
24655cff7825Smh27603 #endif
24665cff7825Smh27603 	int ret;
24675cff7825Smh27603 
24685cff7825Smh27603 	ret = DDI_SUCCESS;
24695cff7825Smh27603 	if (change)
24705cff7825Smh27603 		ret = pm_power(ppmd->dip, cmpt, level);
24715cff7825Smh27603 
24725cff7825Smh27603 	PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
24735cff7825Smh27603 	    str, ppmd->path, change, ppmd->level, level, ret))
24745cff7825Smh27603 
24755cff7825Smh27603 	if (ret == DDI_SUCCESS) {
24765cff7825Smh27603 		ppmd->level = level;
24775cff7825Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
24785cff7825Smh27603 	}
24795cff7825Smh27603 
24805cff7825Smh27603 	return (ret);
24815cff7825Smh27603 }
24825cff7825Smh27603 
24835cff7825Smh27603 
24845cff7825Smh27603 static int
ppm_change_power_level(ppm_dev_t * ppmd,int cmpt,int level)24855cff7825Smh27603 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
24865cff7825Smh27603 {
24875cff7825Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
24885cff7825Smh27603 }
24895cff7825Smh27603 
24905cff7825Smh27603 
24915cff7825Smh27603 static int
ppm_record_level_change(ppm_dev_t * ppmd,int cmpt,int level)24925cff7825Smh27603 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
24935cff7825Smh27603 {
24945cff7825Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
24955cff7825Smh27603 }
24965cff7825Smh27603 
24975cff7825Smh27603 
24985cff7825Smh27603 static void
ppm_manage_led(int action)24995cff7825Smh27603 ppm_manage_led(int action)
25005cff7825Smh27603 {
25015cff7825Smh27603 	ppm_domain_t *domp;
25025cff7825Smh27603 	ppm_unit_t *unitp;
25035cff7825Smh27603 	timeout_id_t	tid;
25045cff7825Smh27603 
25055cff7825Smh27603 
25065cff7825Smh27603 	PPMD(D_LED, ("ppm_manage_led: action: %s\n",
25075cff7825Smh27603 	    (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
25085cff7825Smh27603 	    "PPM_LED_SOLIDON"))
25095cff7825Smh27603 
25105cff7825Smh27603 	/*
25115cff7825Smh27603 	 * test whether led operation is practically supported,
25125cff7825Smh27603 	 * if not, we waive without pressing for reasons
25135cff7825Smh27603 	 */
25145cff7825Smh27603 	if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
25155cff7825Smh27603 		return;
25165cff7825Smh27603 
25175cff7825Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25185cff7825Smh27603 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
25195cff7825Smh27603 		domp = domp->next;
25205cff7825Smh27603 
25215cff7825Smh27603 	mutex_enter(&unitp->lock);
25225cff7825Smh27603 	if (action == PPM_LED_BLINKING) {
25235cff7825Smh27603 		ppm_set_led(domp, PPMD_OFF);
25245cff7825Smh27603 		unitp->led_tid = timeout(
25255cff7825Smh27603 		    ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
25265cff7825Smh27603 
25275cff7825Smh27603 	} else {	/* PPM_LED_SOLIDON */
25285cff7825Smh27603 		ASSERT(action == PPM_LED_SOLIDON);
25295cff7825Smh27603 		tid = unitp->led_tid;
25305cff7825Smh27603 		unitp->led_tid = 0;
25315cff7825Smh27603 
25325cff7825Smh27603 		mutex_exit(&unitp->lock);
25335cff7825Smh27603 		(void) untimeout(tid);
25345cff7825Smh27603 
25355cff7825Smh27603 		mutex_enter(&unitp->lock);
25365cff7825Smh27603 		ppm_set_led(domp, PPMD_ON);
25375cff7825Smh27603 	}
25385cff7825Smh27603 	mutex_exit(&unitp->lock);
25395cff7825Smh27603 }
25405cff7825Smh27603 
25415cff7825Smh27603 
25425cff7825Smh27603 static void
ppm_set_led(ppm_domain_t * domp,int val)25435cff7825Smh27603 ppm_set_led(ppm_domain_t *domp, int val)
25445cff7825Smh27603 {
25455cff7825Smh27603 	int ret;
25465cff7825Smh27603 
25475cff7825Smh27603 	ret = ppm_gpioset(domp,
25485cff7825Smh27603 	    (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
25495cff7825Smh27603 
25505cff7825Smh27603 	PPMD(D_LED, ("ppm_set_led:  %s LED from %s\n",
25515cff7825Smh27603 	    (ret == 0) ? "turned" : "FAILed to turn",
25525cff7825Smh27603 	    (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
25535cff7825Smh27603 
25545cff7825Smh27603 	if (ret == DDI_SUCCESS)
25555cff7825Smh27603 		domp->status = val;
25565cff7825Smh27603 }
25575cff7825Smh27603 
25585cff7825Smh27603 
25595cff7825Smh27603 static void
ppm_blink_led(void * arg)25605cff7825Smh27603 ppm_blink_led(void *arg)
25615cff7825Smh27603 {
25625cff7825Smh27603 	ppm_unit_t *unitp;
25635cff7825Smh27603 	clock_t intvl;
25645cff7825Smh27603 	ppm_domain_t *domp = arg;
25655cff7825Smh27603 
25665cff7825Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25675cff7825Smh27603 
25685cff7825Smh27603 	mutex_enter(&unitp->lock);
25695cff7825Smh27603 	if (unitp->led_tid == 0) {
25705cff7825Smh27603 		mutex_exit(&unitp->lock);
25715cff7825Smh27603 		return;
25725cff7825Smh27603 	}
25735cff7825Smh27603 
25745cff7825Smh27603 	if (domp->status == PPMD_ON) {
25755cff7825Smh27603 		ppm_set_led(domp, PPMD_OFF);
25765cff7825Smh27603 		intvl = PPM_LEDOFF_INTERVAL;
25775cff7825Smh27603 	} else {
25785cff7825Smh27603 		ppm_set_led(domp, PPMD_ON);
25795cff7825Smh27603 		intvl = PPM_LEDON_INTERVAL;
25805cff7825Smh27603 	}
25815cff7825Smh27603 
25825cff7825Smh27603 	unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
25835cff7825Smh27603 	mutex_exit(&unitp->lock);
25845cff7825Smh27603 }
25855cff7825Smh27603 
25865cff7825Smh27603 /*
25875cff7825Smh27603  * Function to power up a domain, if required. It also increments the
25885cff7825Smh27603  * domain pwr_cnt to prevent it from going down.
25895cff7825Smh27603  */
25905cff7825Smh27603 static int
ppm_power_up_domain(dev_info_t * dip)25915cff7825Smh27603 ppm_power_up_domain(dev_info_t *dip)
25925cff7825Smh27603 {
25935cff7825Smh27603 	int		ret = DDI_SUCCESS;
25945cff7825Smh27603 	ppm_domain_t	*domp;
25955cff7825Smh27603 	char		*str = "ppm_power_up_domain";
25965cff7825Smh27603 
25975cff7825Smh27603 	domp = ppm_lookup_dev(dip);
25985cff7825Smh27603 	ASSERT(domp);
25995cff7825Smh27603 	mutex_enter(&domp->lock);
26005cff7825Smh27603 	switch (domp->model) {
26015cff7825Smh27603 	case PPMD_FET:
26025cff7825Smh27603 		if (domp->status == PPMD_OFF) {
26035cff7825Smh27603 			if ((ret = ppm_fetset(domp,  PPMD_ON)) ==
26045cff7825Smh27603 			    DDI_SUCCESS) {
26055cff7825Smh27603 				PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
26065cff7825Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
26075cff7825Smh27603 			} else {
26085cff7825Smh27603 				PPMD(D_FET, ("%s: couldn't turn on fet "
26095cff7825Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26105cff7825Smh27603 				    PM_ADDR(dip)))
26115cff7825Smh27603 			}
26125cff7825Smh27603 		}
26135cff7825Smh27603 		break;
26145cff7825Smh27603 
26155cff7825Smh27603 	case PPMD_PCI:
26165cff7825Smh27603 	case PPMD_PCI_PROP:
26175cff7825Smh27603 		if (domp->status == PPMD_OFF) {
26185cff7825Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
26195cff7825Smh27603 			    DDI_SUCCESS) {
26205cff7825Smh27603 				PPMD(D_PCI, ("%s: turned on clock for "
26215cff7825Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
26225cff7825Smh27603 				    PM_ADDR(dip)))
26235cff7825Smh27603 			} else {
26245cff7825Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on clock "
26255cff7825Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26265cff7825Smh27603 				    PM_ADDR(dip)))
26275cff7825Smh27603 			}
26285cff7825Smh27603 		}
26295cff7825Smh27603 		break;
26305cff7825Smh27603 
26315cff7825Smh27603 	case PPMD_PCIE:
26325cff7825Smh27603 		if (domp->status == PPMD_OFF) {
26335cff7825Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
26345cff7825Smh27603 			    DDI_SUCCESS) {
26355cff7825Smh27603 				PPMD(D_PCI, ("%s: turned on link for "
26365cff7825Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
26375cff7825Smh27603 				    PM_ADDR(dip)))
26385cff7825Smh27603 			} else {
26395cff7825Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on link "
26405cff7825Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
26415cff7825Smh27603 				    PM_ADDR(dip)))
26425cff7825Smh27603 			}
26435cff7825Smh27603 		}
26445cff7825Smh27603 		break;
26455cff7825Smh27603 
26465cff7825Smh27603 	default:
26475cff7825Smh27603 		break;
26485cff7825Smh27603 	}
26495cff7825Smh27603 	if (ret == DDI_SUCCESS)
26505cff7825Smh27603 		domp->pwr_cnt++;
26515cff7825Smh27603 	mutex_exit(&domp->lock);
26525cff7825Smh27603 	return (ret);
26535cff7825Smh27603 }
26545cff7825Smh27603 
26555cff7825Smh27603 /*
26565cff7825Smh27603  * Decrements the domain pwr_cnt. if conditions to power down the domain
26575cff7825Smh27603  * are met, powers down the domain,.
26585cff7825Smh27603  */
26595cff7825Smh27603 static int
ppm_power_down_domain(dev_info_t * dip)26605cff7825Smh27603 ppm_power_down_domain(dev_info_t *dip)
26615cff7825Smh27603 {
26625cff7825Smh27603 	int		ret = DDI_SUCCESS;
26635cff7825Smh27603 	char		*str = "ppm_power_down_domain";
26645cff7825Smh27603 	ppm_domain_t	*domp;
26655cff7825Smh27603 
26665cff7825Smh27603 	domp = ppm_lookup_dev(dip);
26675cff7825Smh27603 	ASSERT(domp);
26685cff7825Smh27603 	mutex_enter(&domp->lock);
26695cff7825Smh27603 	ASSERT(domp->pwr_cnt > 0);
26705cff7825Smh27603 	domp->pwr_cnt--;
26715cff7825Smh27603 	switch (domp->model) {
26725cff7825Smh27603 	case PPMD_FET:
26735cff7825Smh27603 		if ((domp->pwr_cnt == 0) &&
26745cff7825Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
26755cff7825Smh27603 		    ppm_none_else_holds_power(domp)) {
26765cff7825Smh27603 			if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
26775cff7825Smh27603 			    DDI_SUCCESS) {
26785cff7825Smh27603 				PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
26795cff7825Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
26805cff7825Smh27603 			} else {
26815cff7825Smh27603 				PPMD(D_FET, ("%s: couldn't turn off FET for "
26825cff7825Smh27603 				    " %s@%s\n", str, PM_NAME(dip),
26835cff7825Smh27603 				    PM_ADDR(dip)))
26845cff7825Smh27603 			}
26855cff7825Smh27603 		}
26865cff7825Smh27603 		break;
26875cff7825Smh27603 
26885cff7825Smh27603 	case PPMD_PCI:
26895cff7825Smh27603 	case PPMD_PCI_PROP:
26905cff7825Smh27603 		if ((domp->pwr_cnt == 0) &&
26915cff7825Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
26925cff7825Smh27603 		    ppm_none_else_holds_power(domp)) {
26935cff7825Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
26945cff7825Smh27603 			    DDI_SUCCESS) {
26955cff7825Smh27603 				PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
26965cff7825Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
26975cff7825Smh27603 			} else {
26985cff7825Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off clock "
26995cff7825Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
27005cff7825Smh27603 				    PM_ADDR(dip)))
27015cff7825Smh27603 			}
27025cff7825Smh27603 		}
27035cff7825Smh27603 		break;
27045cff7825Smh27603 
27055cff7825Smh27603 	case PPMD_PCIE:
27065cff7825Smh27603 		if ((domp->pwr_cnt == 0) &&
27075cff7825Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
27085cff7825Smh27603 		    ppm_none_else_holds_power(domp)) {
27095cff7825Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
27105cff7825Smh27603 			    DDI_SUCCESS) {
27115cff7825Smh27603 				PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
27125cff7825Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
27135cff7825Smh27603 			} else {
27145cff7825Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off link "
27155cff7825Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
27165cff7825Smh27603 				    PM_ADDR(dip)))
27175cff7825Smh27603 			}
27185cff7825Smh27603 		}
27195cff7825Smh27603 		break;
27205cff7825Smh27603 
27215cff7825Smh27603 	default:
27225cff7825Smh27603 		break;
27235cff7825Smh27603 	}
27245cff7825Smh27603 	mutex_exit(&domp->lock);
27255cff7825Smh27603 	return (ret);
27265cff7825Smh27603 }
27272df1fe9cSrandyf 
27282df1fe9cSrandyf static int
ppm_manage_sx(s3a_t * s3ap,int enter)27292df1fe9cSrandyf ppm_manage_sx(s3a_t *s3ap, int enter)
27302df1fe9cSrandyf {
27312df1fe9cSrandyf 	ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
27322df1fe9cSrandyf 	ppm_dc_t *dc;
27332df1fe9cSrandyf 	int ret = 0;
27342df1fe9cSrandyf 
27352df1fe9cSrandyf 	if (domp == NULL) {
27362df1fe9cSrandyf 		PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
27372df1fe9cSrandyf 		return (ENODEV);
27382df1fe9cSrandyf 	}
27392df1fe9cSrandyf 	PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
27402df1fe9cSrandyf 	    enter))
27412df1fe9cSrandyf 	switch (s3ap->s3a_state) {
27422df1fe9cSrandyf 	case S3:
27432df1fe9cSrandyf 		if (enter) {
27442df1fe9cSrandyf 			dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
27452df1fe9cSrandyf 		} else {
27462df1fe9cSrandyf 			dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
27472df1fe9cSrandyf 		}
27482df1fe9cSrandyf 		ASSERT(dc && dc->method == PPMDC_KIO);
27492df1fe9cSrandyf 		PPMD(D_CPR,
27502df1fe9cSrandyf 		    ("ppm_manage_sx: calling acpi driver (handle %p)"
27512df1fe9cSrandyf 		    " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
27522df1fe9cSrandyf 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
27532df1fe9cSrandyf 		    (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
27542df1fe9cSrandyf 		break;
27552df1fe9cSrandyf 
27562df1fe9cSrandyf 	case S4:
27572df1fe9cSrandyf 		/* S4 is not supported yet */
27582df1fe9cSrandyf 		return (EINVAL);
27592df1fe9cSrandyf 	default:
27602df1fe9cSrandyf 		ASSERT(0);
27612df1fe9cSrandyf 	}
27622df1fe9cSrandyf 	return (ret);
27632df1fe9cSrandyf }
27642df1fe9cSrandyf 
27652df1fe9cSrandyf /*
27662df1fe9cSrandyf  * Search enable/disable lists, which are encoded in ppm.conf as an array
27672df1fe9cSrandyf  * of char strings.
27682df1fe9cSrandyf  */
27692df1fe9cSrandyf static int
ppm_search_list(pm_searchargs_t * sl)27702df1fe9cSrandyf ppm_search_list(pm_searchargs_t *sl)
27712df1fe9cSrandyf {
27722df1fe9cSrandyf 	int i;
27732df1fe9cSrandyf 	int flags = DDI_PROP_DONTPASS;
27742df1fe9cSrandyf 	ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
27752df1fe9cSrandyf 	char **pp;
27762df1fe9cSrandyf 	char *starp;
27772df1fe9cSrandyf 	uint_t nelements;
27782df1fe9cSrandyf 	char *manuf = sl->pms_manufacturer;
27792df1fe9cSrandyf 	char *prod = sl->pms_product;
27802df1fe9cSrandyf 
27812df1fe9cSrandyf 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
27822df1fe9cSrandyf 	    sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
27832df1fe9cSrandyf 		PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
27842df1fe9cSrandyf 		    sl->pms_listname))
27852df1fe9cSrandyf 		return (EINVAL);
27862df1fe9cSrandyf 	}
27872df1fe9cSrandyf 	ASSERT((nelements & 1) == 0);		/* must be even */
27882df1fe9cSrandyf 
27892df1fe9cSrandyf 	PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
27902df1fe9cSrandyf 
27912df1fe9cSrandyf 	for (i = 0; i < nelements; i += 2) {
27922df1fe9cSrandyf 		PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
27932df1fe9cSrandyf 		/* we support only a trailing '*' pattern match */
27942df1fe9cSrandyf 		if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
27952df1fe9cSrandyf 			/* LINTED - ptrdiff overflow */
27962df1fe9cSrandyf 			if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
27972df1fe9cSrandyf 				PPMD(D_CPR, (" no match %s with %s\n",
27982df1fe9cSrandyf 				    manuf, pp[i + 1]))
27992df1fe9cSrandyf 				continue;
28002df1fe9cSrandyf 			}
28012df1fe9cSrandyf 		}
28022df1fe9cSrandyf 		if ((starp = strchr(pp[i + 1], '*')) != NULL &&
28032df1fe9cSrandyf 		    *(starp + 1) == 0) {
28042df1fe9cSrandyf 			if (strncmp(prod,
28052df1fe9cSrandyf 			    /* LINTED - ptrdiff overflow */
28062df1fe9cSrandyf 			    pp[i + 1], (starp - pp[i + 1])) != 0) {
28072df1fe9cSrandyf 				PPMD(D_CPR, (" no match %s with %s\n",
28082df1fe9cSrandyf 				    prod, pp[i + 1]))
28092df1fe9cSrandyf 				continue;
28102df1fe9cSrandyf 			}
28112df1fe9cSrandyf 		}
28122df1fe9cSrandyf 		if (strcmp(manuf, pp[i]) == 0 &&
28132df1fe9cSrandyf 		    (strcmp(prod, pp[i + 1]) == 0)) {
28142df1fe9cSrandyf 			PPMD(D_CPR, (" match\n"))
28152df1fe9cSrandyf 			ddi_prop_free(pp);
28162df1fe9cSrandyf 			return (0);
28172df1fe9cSrandyf 		}
28182df1fe9cSrandyf 		PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
28192df1fe9cSrandyf 		    manuf, pp[i], prod, pp[i + 1]))
28202df1fe9cSrandyf 	}
28212df1fe9cSrandyf 	ddi_prop_free(pp);
28222df1fe9cSrandyf 	return (ENODEV);
28232df1fe9cSrandyf }
2824