Home | History | Annotate | Download | only in ppm
      1      0    stevel /*
      2      0    stevel  * CDDL HEADER START
      3      0    stevel  *
      4      0    stevel  * The contents of this file are subject to the terms of the
      5   1961       cth  * Common Development and Distribution License (the "License").
      6   1961       cth  * You may not use this file except in compliance with the License.
      7      0    stevel  *
      8      0    stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9      0    stevel  * or http://www.opensolaris.org/os/licensing.
     10      0    stevel  * See the License for the specific language governing permissions
     11      0    stevel  * and limitations under the License.
     12      0    stevel  *
     13      0    stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14      0    stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15      0    stevel  * If applicable, add the following below this CDDL HEADER, with the
     16      0    stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17      0    stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18      0    stevel  *
     19      0    stevel  * CDDL HEADER END
     20      0    stevel  */
     21      0    stevel /*
     22   9694     Scott  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23      0    stevel  * Use is subject to license terms.
     24  10488      Mark  */
     25  10488      Mark /*
     26  10488      Mark  * Copyright (c) 2009,  Intel Corporation.
     27  10488      Mark  * All Rights Reserved.
     28      0    stevel  */
     29      0    stevel 
     30      0    stevel 
     31      0    stevel /*
     32      0    stevel  * Platform Power Management master pseudo driver -
     33      0    stevel  *    - attaches only  when ppm.conf file is present, indicating a
     34      0    stevel  *      workstation (since Excalibur era ) that is designed to
     35      0    stevel  *      be MOU-3 EPA compliant and which uses platform-specific
     36      0    stevel  *	hardware to do so;
     37      0    stevel  *    - this pseudo driver uses a set of simple satellite
     38      0    stevel  *      device drivers responsible for accessing platform
     39      0    stevel  *      specific devices to modify the registers they own.
     40      0    stevel  *	ppm drivers tells these	satellite drivers what to do
     41      0    stevel  *	according to using command values taken from ppm.conf.
     42      0    stevel  */
     43      0    stevel #include <sys/conf.h>
     44      0    stevel #include <sys/stat.h>
     45      0    stevel #include <sys/file.h>
     46      0    stevel #include <sys/types.h>
     47      0    stevel #include <sys/param.h>
     48      0    stevel #include <sys/open.h>
     49      0    stevel #include <sys/callb.h>
     50      0    stevel #include <sys/va_list.h>
     51      0    stevel #include <sys/errno.h>
     52      0    stevel #include <sys/modctl.h>
     53      0    stevel #include <sys/sysmacros.h>
     54      0    stevel #include <sys/ddi_impldefs.h>
     55      0    stevel #include <sys/promif.h>
     56      0    stevel #include <sys/epm.h>
     57      0    stevel #include <sys/sunpm.h>
     58      0    stevel #include <sys/ppmio.h>
     59      0    stevel #include <sys/sunldi.h>
     60      0    stevel #include <sys/ppmvar.h>
     61      0    stevel #include <sys/ddi.h>
     62      0    stevel #include <sys/sunddi.h>
     63   4667   mh27603 #include <sys/ppm_plat.h>
     64      0    stevel 
     65      0    stevel /*
     66      0    stevel  * Note: When pm_power() is called (directly or indirectly) to change the
     67      0    stevel  * power level of a device and the call returns failure, DO NOT assume the
     68      0    stevel  * level is unchanged.  Doublecheck it against ppmd->level.
     69      0    stevel  */
     70      0    stevel 
     71      0    stevel /*
     72      0    stevel  * cb_ops
     73      0    stevel  */
     74      0    stevel static int	ppm_open(dev_t *, int, int, cred_t *);
     75      0    stevel static int	ppm_close(dev_t, int, int, cred_t *);
     76      0    stevel static int	ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
     77      0    stevel 
     78      0    stevel static struct cb_ops ppm_cb_ops = {
     79      0    stevel 	ppm_open,		/* open	*/
     80      0    stevel 	ppm_close,		/* close */
     81      0    stevel 	nodev,			/* strategy */
     82      0    stevel 	nodev,			/* print */
     83      0    stevel 	nodev,			/* dump */
     84      0    stevel 	nodev,			/* read */
     85      0    stevel 	nodev,			/* write */
     86      0    stevel 	ppm_ioctl,		/* ioctl */
     87      0    stevel 	nodev,			/* devmap */
     88      0    stevel 	nodev,			/* mmap */
     89      0    stevel 	nodev,			/* segmap */
     90      0    stevel 	nochpoll,		/* poll */
     91      0    stevel 	ddi_prop_op,		/* prop_op */
     92      0    stevel 	NULL,			/* streamtab */
     93      0    stevel 	D_MP | D_NEW,		/* driver compatibility flag */
     94      0    stevel 	CB_REV,			/* cb_ops revision */
     95      0    stevel 	nodev,			/* async read */
     96      0    stevel 	nodev			/* async write */
     97      0    stevel };
     98      0    stevel 
     99      0    stevel /*
    100      0    stevel  * bus_ops
    101      0    stevel  */
    102      0    stevel static int	ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
    103      0    stevel     void *);
    104      0    stevel 
    105      0    stevel static struct bus_ops ppm_bus_ops = {
    106      0    stevel 	BUSO_REV,		/* busops_rev		*/
    107      0    stevel 	0,			/* bus_map		*/
    108      0    stevel 	0,			/* bus_get_intrspec	*/
    109      0    stevel 	0,			/* bus_add_intrspec	*/
    110      0    stevel 	0,			/* bus_remove_intrspec	*/
    111      0    stevel 	0,			/* bus_map_fault	*/
    112      0    stevel 	ddi_no_dma_map,		/* bus_dma_map		*/
    113      0    stevel 	ddi_no_dma_allochdl,	/* bus_dma_allochdl	*/
    114      0    stevel 	NULL,			/* bus_dma_freehdl	*/
    115      0    stevel 	NULL,			/* bus_dma_bindhdl	*/
    116      0    stevel 	NULL,			/* bus_dma_unbindhdl	*/
    117      0    stevel 	NULL,			/* bus_dma_flush	*/
    118      0    stevel 	NULL,			/* bus_dma_win		*/
    119      0    stevel 	NULL,			/* bus_dma_ctl		*/
    120      0    stevel 	ppm_ctlops,		/* bus_ctl		*/
    121      0    stevel 	0,			/* bus_prop_op		*/
    122      0    stevel 	0,			/* bus_get_eventcookie	*/
    123      0    stevel 	0,			/* bus_add_eventcall	*/
    124      0    stevel 	0,			/* bus_remove_eventcall	*/
    125      0    stevel 	0,			/* bus_post_event	*/
    126      0    stevel 	0			/* bus_intr_ctl		*/
    127      0    stevel };
    128      0    stevel 
    129      0    stevel /*
    130      0    stevel  * dev_ops
    131      0    stevel  */
    132      0    stevel static int	ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
    133      0    stevel static int	ppm_attach(dev_info_t *, ddi_attach_cmd_t);
    134      0    stevel static int	ppm_detach(dev_info_t *, ddi_detach_cmd_t);
    135      0    stevel 
    136      0    stevel static struct dev_ops ppm_ops = {
    137      0    stevel 	DEVO_REV,		/* devo_rev */
    138      0    stevel 	0,			/* refcnt */
    139      0    stevel 	ppm_getinfo,		/* info */
    140      0    stevel 	nulldev,		/* identify */
    141      0    stevel 	nulldev,		/* probe */
    142      0    stevel 	ppm_attach,		/* attach */
    143      0    stevel 	ppm_detach,		/* detach */
    144      0    stevel 	nodev,			/* reset */
    145      0    stevel 	&ppm_cb_ops,		/* cb_ops */
    146      0    stevel 	&ppm_bus_ops,		/* bus_ops */
    147   7656    Sherry 	nulldev,		/* power */
    148   7656    Sherry 	ddi_quiesce_not_needed,		/* quiesce */
    149      0    stevel };
    150      0    stevel 
    151      0    stevel extern struct mod_ops mod_driverops;
    152      0    stevel 
    153      0    stevel static struct modldrv modldrv = {
    154      0    stevel 	&mod_driverops,
    155   7656    Sherry 	"platform pm driver",
    156      0    stevel 	&ppm_ops
    157      0    stevel };
    158      0    stevel 
    159      0    stevel static struct modlinkage modlinkage = {
    160      0    stevel 	MODREV_1,
    161      0    stevel 	&modldrv,
    162      0    stevel 	NULL
    163      0    stevel };
    164      0    stevel 
    165      0    stevel /*
    166      0    stevel  * Global data structure and variables
    167      0    stevel  */
    168      0    stevel int	ppm_inst = -1;
    169      0    stevel void	*ppm_statep;
    170      0    stevel ppm_domain_t *ppm_domain_p;
    171      0    stevel callb_id_t   *ppm_cprcb_id;
    172      0    stevel static kmutex_t ppm_cpr_window_lock;	/* guard ppm_cpr_window_flag */
    173      0    stevel static	boolean_t ppm_cpr_window_flag;	/* set indicating chpt-resume period */
    174      0    stevel 
    175      0    stevel /* LED actions */
    176      0    stevel #define	PPM_LED_SOLIDON		0
    177      0    stevel #define	PPM_LED_BLINKING	1
    178      0    stevel 
    179      0    stevel /*
    180      0    stevel  * Debug
    181      0    stevel  */
    182      0    stevel #ifdef	DEBUG
    183      0    stevel uint_t	ppm_debug = 0;
    184      0    stevel #endif
    185      0    stevel 
    186      0    stevel /*
    187      0    stevel  * Local function prototypes and data
    188      0    stevel  */
    189      0    stevel static boolean_t	ppm_cpr_callb(void *, int);
    190      0    stevel static int		ppm_fetset(ppm_domain_t *, uint8_t);
    191      0    stevel static int		ppm_fetget(ppm_domain_t *, uint8_t *);
    192     68  zx151605 static int		ppm_gpioset(ppm_domain_t *, int);
    193      0    stevel static int		ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
    194      0    stevel static int		ppm_manage_pci(dev_info_t *, power_req_t *, int *);
    195      0    stevel static int		ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
    196      0    stevel static int		ppm_manage_fet(dev_info_t *, power_req_t *, int *);
    197      0    stevel static void		ppm_manage_led(int);
    198      0    stevel static void		ppm_set_led(ppm_domain_t *, int);
    199      0    stevel static void		ppm_blink_led(void *);
    200      0    stevel static void		ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
    201      0    stevel static int		ppm_set_level(ppm_dev_t *, int, int, boolean_t);
    202      0    stevel static int		ppm_change_power_level(ppm_dev_t *, int, int);
    203      0    stevel static int		ppm_record_level_change(ppm_dev_t *, int, int);
    204      0    stevel static int		ppm_switch_clock(ppm_domain_t *, int);
    205      0    stevel static int		ppm_pcie_pwr(ppm_domain_t *, int);
    206      0    stevel static int		ppm_power_up_domain(dev_info_t *dip);
    207      0    stevel static int		ppm_power_down_domain(dev_info_t *dip);
    208      0    stevel 
    209      0    stevel int
    210      0    stevel _init(void)
    211      0    stevel {
    212      0    stevel 	if (ddi_soft_state_init(
    213   5295    randyf 	    &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
    214   5295    randyf 		PPMD(D_INIT, ("ppm: soft state init\n"))
    215      0    stevel 		return (DDI_FAILURE);
    216   5295    randyf 	}
    217      0    stevel 
    218      0    stevel 	if (mod_install(&modlinkage) != DDI_SUCCESS) {
    219      0    stevel 		ddi_soft_state_fini(&ppm_statep);
    220      0    stevel 		return (DDI_FAILURE);
    221      0    stevel 	}
    222      0    stevel 	return (DDI_SUCCESS);
    223      0    stevel }
    224      0    stevel 
    225      0    stevel 
    226      0    stevel int
    227      0    stevel _fini(void)
    228      0    stevel {
    229   5295    randyf 	int error;
    230   5295    randyf 
    231   5295    randyf 	if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
    232   5295    randyf 		ddi_soft_state_fini(&ppm_statep);
    233   5295    randyf 
    234   5295    randyf 	return (error);
    235      0    stevel }
    236      0    stevel 
    237      0    stevel 
    238      0    stevel int
    239      0    stevel _info(struct modinfo *modinfop)
    240      0    stevel {
    241      0    stevel 	return (mod_info(&modlinkage, modinfop));
    242      0    stevel }
    243      0    stevel 
    244      0    stevel 
    245      0    stevel /* ARGSUSED */
    246      0    stevel int
    247      0    stevel ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
    248      0    stevel {
    249      0    stevel 	struct ppm_unit *unitp;
    250      0    stevel 	dev_t	dev;
    251      0    stevel 	int	instance;
    252      0    stevel 	int	rval;
    253      0    stevel 
    254      0    stevel 	if (ppm_inst == -1)
    255      0    stevel 		return (DDI_FAILURE);
    256      0    stevel 
    257      0    stevel 	switch (cmd) {
    258      0    stevel 	case DDI_INFO_DEVT2DEVINFO:
    259      0    stevel 		if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
    260      0    stevel 			*resultp = unitp->dip;
    261      0    stevel 			rval = DDI_SUCCESS;
    262      0    stevel 		} else
    263      0    stevel 			rval = DDI_FAILURE;
    264      0    stevel 
    265      0    stevel 		return (rval);
    266      0    stevel 
    267      0    stevel 	case DDI_INFO_DEVT2INSTANCE:
    268      0    stevel 		dev = (dev_t)arg;
    269      0    stevel 		instance = getminor(dev);
    270      0    stevel 		*resultp = (void *)(uintptr_t)instance;
    271      0    stevel 		return (DDI_SUCCESS);
    272      0    stevel 
    273      0    stevel 	default:
    274      0    stevel 		return (DDI_FAILURE);
    275      0    stevel 	}
    276      0    stevel }
    277      0    stevel 
    278      0    stevel 
    279      0    stevel /*
    280      0    stevel  * attach(9E)
    281      0    stevel  */
    282      0    stevel static int
    283      0    stevel ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    284      0    stevel {
    285      0    stevel 	ppm_unit_t *unitp;
    286      0    stevel 	int ret;
    287      0    stevel #ifdef	DEBUG
    288      0    stevel 	char *str = "ppm_attach";
    289      0    stevel #endif
    290      0    stevel 
    291      0    stevel 
    292      0    stevel 	switch (cmd) {
    293      0    stevel 	case DDI_ATTACH:
    294      0    stevel 		PPMD(D_ATTACH, ("%s: attaching ...\n", str))
    295      0    stevel 		break;
    296      0    stevel 
    297      0    stevel 	case DDI_RESUME:
    298      0    stevel 		PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
    299      0    stevel 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
    300      0    stevel 		mutex_enter(&unitp->lock);
    301      0    stevel 		unitp->states &= ~PPM_STATE_SUSPENDED;
    302      0    stevel 		mutex_exit(&unitp->lock);
    303      0    stevel 		return (DDI_SUCCESS);
    304      0    stevel 
    305      0    stevel 	default:
    306      0    stevel 		cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
    307      0    stevel 		    cmd, (void *)dip);
    308      0    stevel 		return (DDI_FAILURE);
    309      0    stevel 	}
    310      0    stevel 
    311      0    stevel 	if (ppm_inst != -1) {
    312      0    stevel 		PPMD(D_ATTACH, ("%s: Already attached !", str))
    313      0    stevel 		return (DDI_FAILURE);
    314      0    stevel 	}
    315      0    stevel 
    316      0    stevel 	ppm_inst = ddi_get_instance(dip);
    317      0    stevel 	if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
    318      0    stevel 		PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
    319      0    stevel 		return (DDI_FAILURE);
    320      0    stevel 	}
    321      0    stevel 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
    322      0    stevel 
    323      0    stevel 	ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
    324      0    stevel 	    "ddi_ppm", 0);
    325      0    stevel 	if (ret != DDI_SUCCESS) {
    326      0    stevel 		PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
    327      0    stevel 		goto fail1;
    328      0    stevel 	}
    329      0    stevel 
    330      0    stevel 	unitp->dip = dip;
    331      0    stevel 	mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
    332      0    stevel 
    333      0    stevel 	/*
    334      0    stevel 	 * read ppm.conf, construct ppm_domain data structure and
    335      0    stevel 	 * their sub data structure.
    336      0    stevel 	 */
    337      0    stevel 	if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
    338      0    stevel 		goto fail2;
    339      0    stevel 
    340      0    stevel 	/*
    341      0    stevel 	 * walk down ppm domain control from each domain, initialize
    342      0    stevel 	 * domain control orthogonal function call handle
    343      0    stevel 	 */
    344      0    stevel 	ppm_init_cb(dip);
    345      0    stevel 
    346      0    stevel 	if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
    347      0    stevel 		cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
    348      0    stevel 		goto fail2;
    349      0    stevel 	}
    350      0    stevel 
    351      0    stevel 	mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
    352      0    stevel 	ppm_cpr_window_flag = B_FALSE;
    353      0    stevel 	ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
    354      0    stevel 	    CB_CL_CPR_PM, "ppm_cpr");
    355   4667   mh27603 
    356   4667   mh27603 #if defined(__x86)
    357   4667   mh27603 	/*
    358   4667   mh27603 	 * Register callback so that once CPUs have been added to
    359  10488      Mark 	 * the device tree, ppm CPU domains can be allocated using ACPI
    360   4667   mh27603 	 * data.
    361   4667   mh27603 	 */
    362  10488      Mark 	cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
    363  10488      Mark 	cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
    364   4667   mh27603 
    365   4667   mh27603 	/*
    366   4667   mh27603 	 * Register callback so that whenever max speed throttle requests
    367   4667   mh27603 	 * are received, ppm can redefine the high power level for
    368   4667   mh27603 	 * all CPUs in the domain.
    369   4667   mh27603 	 */
    370   4667   mh27603 	cpupm_redefine_topspeed = ppm_redefine_topspeed;
    371   4667   mh27603 #endif
    372      0    stevel 
    373      0    stevel 	ddi_report_dev(dip);
    374      0    stevel 	return (DDI_SUCCESS);
    375      0    stevel 
    376      0    stevel fail2:
    377      0    stevel 	ddi_remove_minor_node(dip, "ddi_ppm");
    378      0    stevel 	mutex_destroy(&unitp->lock);
    379      0    stevel fail1:
    380      0    stevel 	ddi_soft_state_free(ppm_statep, ppm_inst);
    381      0    stevel 	ppm_inst = -1;
    382      0    stevel 	return (DDI_FAILURE);
    383      0    stevel }
    384      0    stevel 
    385      0    stevel 
    386      0    stevel /* ARGSUSED */
    387      0    stevel static int
    388      0    stevel ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    389      0    stevel {
    390      0    stevel 	ppm_unit_t *unitp;
    391      0    stevel #ifdef	DEBUG
    392      0    stevel 	char *str = "ppm_detach";
    393      0    stevel #endif
    394      0    stevel 
    395      0    stevel 	switch (cmd) {
    396      0    stevel 	case DDI_DETACH:
    397      0    stevel 		PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
    398      0    stevel 		return (DDI_FAILURE);
    399      0    stevel 
    400      0    stevel 	case DDI_SUSPEND:
    401      0    stevel 		PPMD(D_DETACH, ("%s: suspending ...\n", str))
    402      0    stevel 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
    403      0    stevel 		mutex_enter(&unitp->lock);
    404      0    stevel 		unitp->states |= PPM_STATE_SUSPENDED;
    405      0    stevel 		mutex_exit(&unitp->lock);
    406      0    stevel 
    407      0    stevel 		/*
    408      0    stevel 		 * Suspend requires that timeout callouts to be canceled.
    409      0    stevel 		 * Turning off the LED blinking will cancel the timeout.
    410      0    stevel 		 */
    411      0    stevel 		ppm_manage_led(PPM_LED_SOLIDON);
    412      0    stevel 		return (DDI_SUCCESS);
    413      0    stevel 
    414      0    stevel 	default:
    415      0    stevel 		cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
    416      0    stevel 		    cmd, (void *)dip);
    417      0    stevel 		return (DDI_FAILURE);
    418      0    stevel 	}
    419      0    stevel }
    420      0    stevel 
    421      0    stevel 
    422      0    stevel /* ARGSUSED */
    423      0    stevel int
    424      0    stevel ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
    425      0    stevel {
    426      0    stevel 	if (otyp != OTYP_CHR)
    427      0    stevel 		return (EINVAL);
    428      0    stevel 	PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
    429      0    stevel 	    (void *)devp, flag, otyp))
    430      0    stevel 	return (0);
    431      0    stevel }
    432      0    stevel 
    433      0    stevel 
    434      0    stevel /* ARGSUSED */
    435      0    stevel int
    436      0    stevel ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
    437      0    stevel {
    438      0    stevel 	PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
    439      0    stevel 	    dev, flag, otyp))
    440      0    stevel 	return (0);
    441      0    stevel }
    442      0    stevel 
    443      0    stevel 
    444      0    stevel /* ARGSUSED */
    445      0    stevel int
    446      0    stevel ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
    447      0    stevel     int *rval_p)
    448      0    stevel {
    449      0    stevel #ifdef DEBUG
    450      0    stevel 	char *str = "ppm_ioctl";
    451      0    stevel #endif
    452      0    stevel 	ppm_domain_t *domp = NULL;
    453      0    stevel 	uint8_t level, lvl;
    454      0    stevel 	int ret = 0;
    455      0    stevel 
    456      0    stevel 	PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
    457      0    stevel 	    str, dev, cmd, mode))
    458      0    stevel 
    459      0    stevel 	switch (cmd) {
    460      0    stevel 	case PPMGET_DPWR:
    461      0    stevel 	{
    462      0    stevel 		STRUCT_DECL(ppm_dpwr, dpwr);
    463      0    stevel 		struct ppm_unit *unitp;
    464      0    stevel 		char *domain;
    465      0    stevel 
    466      0    stevel 		STRUCT_INIT(dpwr, mode);
    467      0    stevel 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
    468      0    stevel 		    STRUCT_SIZE(dpwr), mode);
    469      0    stevel 		if (ret != 0)
    470      0    stevel 			return (EFAULT);
    471      0    stevel 
    472      0    stevel 		/* copyin domain name */
    473      0    stevel 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
    474      0    stevel 		ret = copyinstr(
    475      0    stevel 		    STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
    476      0    stevel 		if (ret != 0) {
    477      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
    478      0    stevel 			    str, __LINE__))
    479      0    stevel 			ret = EFAULT;
    480      0    stevel 			goto err_dpwr;
    481      0    stevel 		}
    482      0    stevel 
    483      0    stevel 		/* locate domain */
    484      0    stevel 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
    485      0    stevel 			PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
    486      0    stevel 			ret = ENODEV;
    487      0    stevel 			goto err_dpwr;
    488      0    stevel 		}
    489      0    stevel 
    490      0    stevel 		switch (domp->model) {
    491      0    stevel 		case PPMD_FET:	/* report power fet ON or OFF */
    492      0    stevel 			if ((ret = ppm_fetget(domp, &lvl)) != 0) {
    493      0    stevel 				ret = EIO;
    494      0    stevel 				goto err_dpwr;
    495      0    stevel 			}
    496      0    stevel 			level = (lvl == PPMD_ON) ?
    497      0    stevel 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
    498      0    stevel 			break;
    499      0    stevel 
    500      0    stevel 		case PPMD_PCI:	/* report pci slot clock ON or OFF */
    501      0    stevel 		case PPMD_PCI_PROP:
    502     68  zx151605 		case PPMD_PCIE:
    503      0    stevel 			level = (domp->status == PPMD_ON) ?
    504      0    stevel 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
    505      0    stevel 			break;
    506      0    stevel 
    507      0    stevel 		case PPMD_LED:	/* report LED blinking or solid on */
    508      0    stevel 
    509      0    stevel 			unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
    510      0    stevel 			if (unitp->led_tid == 0)
    511      0    stevel 				level = PPMIO_LED_SOLIDON;
    512      0    stevel 			else
    513      0    stevel 				level = PPMIO_LED_BLINKING;
    514      0    stevel 			break;
    515      0    stevel 
    516      0    stevel 		case PPMD_CPU:	/* report cpu speed divisor */
    517      0    stevel 			level = domp->devlist->level;
    518      0    stevel 			break;
    519      0    stevel 
    520      0    stevel 		default:
    521      0    stevel 			ret = EINVAL;
    522      0    stevel 			goto err_dpwr;
    523      0    stevel 		}
    524      0    stevel 
    525      0    stevel 		STRUCT_FSET(dpwr, level, level);
    526      0    stevel 		ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
    527      0    stevel 		    STRUCT_SIZE(dpwr), mode);
    528      0    stevel 		if (ret != 0) {
    529      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
    530      0    stevel 			    str, __LINE__))
    531      0    stevel 			ret = EFAULT;
    532      0    stevel 		}
    533      0    stevel err_dpwr:
    534      0    stevel 		kmem_free(domain, MAXNAMELEN);
    535      0    stevel 
    536      0    stevel 		break;
    537      0    stevel 	}
    538      0    stevel 
    539      0    stevel 	case PPMGET_DOMBYDEV:
    540      0    stevel 	{
    541      0    stevel 		STRUCT_DECL(ppm_bydev, bydev);
    542      0    stevel 		char *path = NULL;
    543      0    stevel 		size_t   size, l;
    544      0    stevel 
    545      0    stevel 		STRUCT_INIT(bydev, mode);
    546      0    stevel 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
    547      0    stevel 		    STRUCT_SIZE(bydev), mode);
    548      0    stevel 		if (ret != 0)
    549      0    stevel 			return (EFAULT);
    550      0    stevel 
    551      0    stevel 		/* copyin .path */
    552      0    stevel 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
    553      0    stevel 		ret = copyinstr(
    554      0    stevel 		    STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
    555      0    stevel 		if (ret != 0) {
    556      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
    557      0    stevel 			    str, __LINE__))
    558      0    stevel 			kmem_free(path, MAXPATHLEN);
    559      0    stevel 			return (EFAULT);
    560      0    stevel 		}
    561      0    stevel 
    562      0    stevel 		/* so far we have up to one domain for a given device */
    563      0    stevel 		size = STRUCT_FGET(bydev, size);
    564      0    stevel 		domp = ppm_get_domain_by_dev(path);
    565      0    stevel 		kmem_free(path, MAXPATHLEN);
    566      0    stevel 		if (domp != NULL) {
    567      0    stevel 			l = strlen(domp->name) + 1;
    568      0    stevel 			if (l > size) {
    569      0    stevel 				PPMD(D_IOCTL, ("%s: buffer too small\n", str))
    570      0    stevel 				return ((size == 0) ? EINVAL : EFAULT);
    571      0    stevel 			}
    572      0    stevel 		} else	/* no domain found to be associated with given device */
    573      0    stevel 			return (ENODEV);
    574      0    stevel 
    575      0    stevel 		ret = copyoutstr(
    576      0    stevel 		    domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
    577      0    stevel 		if (ret != 0) {
    578      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
    579      0    stevel 			    " \n", str, __LINE__))
    580      0    stevel 			return (EFAULT);
    581      0    stevel 		}
    582      0    stevel 
    583      0    stevel 		break;
    584      0    stevel 	}
    585      0    stevel 
    586      0    stevel 
    587      0    stevel 	case PPMGET_DEVBYDOM:
    588      0    stevel 	{
    589      0    stevel 		STRUCT_DECL(ppm_bydom, bydom);
    590      0    stevel 		char *domain = NULL;
    591      0    stevel 		char *devlist = NULL;
    592      0    stevel 		ppm_dev_t *ppmd;
    593      0    stevel 		dev_info_t *odip = NULL;
    594      0    stevel 		char *s, *d;
    595      0    stevel 		size_t  size, l;
    596      0    stevel 
    597      0    stevel 		STRUCT_INIT(bydom, mode);
    598      0    stevel 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
    599      0    stevel 		    STRUCT_SIZE(bydom), mode);
    600      0    stevel 		if (ret != 0)
    601      0    stevel 			return (EFAULT);
    602      0    stevel 
    603      0    stevel 		/* copyin .domain */
    604      0    stevel 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
    605      0    stevel 		ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
    606      0    stevel 		    MAXNAMELEN, NULL);
    607      0    stevel 		if (ret != 0) {
    608      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
    609      0    stevel 			    str, __LINE__))
    610      0    stevel 			ret = EFAULT;
    611      0    stevel 			goto err_bydom;
    612      0    stevel 		}
    613      0    stevel 
    614      0    stevel 		/* locate domain */
    615      0    stevel 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
    616      0    stevel 			ret = ENODEV;
    617      0    stevel 			goto err_bydom;
    618      0    stevel 		}
    619      0    stevel 
    620      0    stevel 		l = 0;
    621      0    stevel 		if ((size = STRUCT_FGET(bydom, size)) == 0)
    622      0    stevel 			ret = EINVAL;
    623      0    stevel 		else
    624      0    stevel 			if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
    625      0    stevel 				ret = EFAULT;
    626      0    stevel 		if (ret != 0)
    627      0    stevel 			goto err_bydom;
    628      0    stevel 
    629      0    stevel 		for (ppmd = domp->devlist; ppmd;
    630      0    stevel 		    odip = ppmd->dip, ppmd = ppmd->next) {
    631      0    stevel 
    632      0    stevel 			if (ppmd->dip == odip)
    633      0    stevel 				continue;
    634      0    stevel 			if (ppmd != domp->devlist)
    635      0    stevel 				*d++ = ' ';
    636      0    stevel 
    637      0    stevel 			l += strlen(ppmd->path) + 1;
    638      0    stevel 			if (l > size) {
    639      0    stevel 				PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
    640      0    stevel 				ret = EFAULT;
    641      0    stevel 				goto err_bydom;
    642      0    stevel 			}
    643      0    stevel 
    644      0    stevel 			for (s = ppmd->path; *s != 0; )
    645      0    stevel 				*d++ = *s++;
    646      0    stevel 		}
    647      0    stevel 		*d = 0;
    648      0    stevel 
    649      0    stevel 		if (*devlist == 0)
    650      0    stevel 			goto err_bydom;
    651      0    stevel 
    652      0    stevel 		ret = copyoutstr(
    653      0    stevel 		    devlist, STRUCT_FGETP(bydom, devlist), l, &l);
    654      0    stevel 		if (ret != 0) {
    655      0    stevel 			PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
    656      0    stevel 			    " \n", str, __LINE__))
    657      0    stevel 			ret = EFAULT;
    658      0    stevel 		}
    659      0    stevel 
    660      0    stevel err_bydom:
    661      0    stevel 		if (devlist)
    662      0    stevel 			kmem_free(devlist, size);
    663      0    stevel 		if (domain)
    664      0    stevel 			kmem_free(domain, MAXNAMELEN);
    665      0    stevel 
    666      0    stevel 		break;
    667      0    stevel 	}
    668      0    stevel 
    669   4667   mh27603 #if defined(__x86)
    670   4667   mh27603 	/*
    671   4667   mh27603 	 * Note that these two ioctls exist for test purposes only.
    672   4667   mh27603 	 * Unfortunately, there really isn't any other good way of
    673   4667   mh27603 	 * unit testing the dynamic redefinition of the top speed as it
    674   4667   mh27603 	 * usually occurs due to environmental conditions.
    675   4667   mh27603 	 */
    676   4667   mh27603 	case PPMGET_NORMAL:
    677   4667   mh27603 	case PPMSET_NORMAL:
    678   4667   mh27603 	{
    679   4667   mh27603 		STRUCT_DECL(ppm_norm, norm);
    680   4667   mh27603 		char *path = NULL;
    681   4667   mh27603 		struct pm_component *dcomps;
    682   4667   mh27603 		struct pm_comp *pm_comp;
    683   4667   mh27603 		ppm_dev_t *ppmd;
    684   4667   mh27603 		int i;
    685   4667   mh27603 
    686   4667   mh27603 		STRUCT_INIT(norm, mode);
    687   4667   mh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
    688   5295    randyf 		    STRUCT_SIZE(norm), mode);
    689   4667   mh27603 		if (ret != 0)
    690   4667   mh27603 			return (EFAULT);
    691   4667   mh27603 
    692   4667   mh27603 		/* copyin .path */
    693   4667   mh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
    694   4667   mh27603 		ret = copyinstr(
    695   4667   mh27603 		    STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
    696   4667   mh27603 		if (ret != 0) {
    697   4667   mh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
    698   4667   mh27603 			    str, __LINE__))
    699   4667   mh27603 			kmem_free(path, MAXPATHLEN);
    700   4667   mh27603 			return (EFAULT);
    701   4667   mh27603 		}
    702   4667   mh27603 
    703   4667   mh27603 		domp = ppm_get_domain_by_dev(path);
    704   4667   mh27603 		kmem_free(path, MAXPATHLEN);
    705   4667   mh27603 
    706   4667   mh27603 		if (domp == NULL)
    707   4667   mh27603 			return (ENODEV);
    708   4667   mh27603 
    709   4667   mh27603 		ppmd = domp->devlist;
    710   4667   mh27603 		if (cmd == PPMSET_NORMAL) {
    711   4667   mh27603 			if (domp->model != PPMD_CPU)
    712   4667   mh27603 				return (EINVAL);
    713   4667   mh27603 			level = STRUCT_FGET(norm, norm);
    714   4667   mh27603 			dcomps = DEVI(ppmd->dip)->devi_pm_components;
    715   4667   mh27603 			pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
    716   4667   mh27603 			for (i = pm_comp->pmc_numlevels; i > 0; i--) {
    717   4667   mh27603 				if (pm_comp->pmc_lvals[i-1] == level)
    718   4667   mh27603 					break;
    719   4667   mh27603 			}
    720   4667   mh27603 			if (i == 0)
    721   4667   mh27603 				return (EINVAL);
    722   4667   mh27603 
    723   4667   mh27603 			ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
    724   4667   mh27603 		}
    725   4667   mh27603 
    726   4667   mh27603 		level = pm_get_normal_power(ppmd->dip, 0);
    727   4667   mh27603 
    728   4667   mh27603 		STRUCT_FSET(norm, norm, level);
    729   4667   mh27603 		ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
    730   4667   mh27603 		    STRUCT_SIZE(norm), mode);
    731   4667   mh27603 		if (ret != 0) {
    732   4667   mh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
    733   4667   mh27603 			    str, __LINE__))
    734   4667   mh27603 			ret = EFAULT;
    735   4667   mh27603 		}
    736   4667   mh27603 		break;
    737   4667   mh27603 	}
    738   4667   mh27603 #endif
    739      0    stevel 	default:
    740      0    stevel 		PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
    741      0    stevel 		return (EINVAL);
    742      0    stevel 	}
    743      0    stevel 
    744      0    stevel 	return (ret);
    745      0    stevel }
    746      0    stevel 
    747      0    stevel 
    748   9694     Scott static int	ppm_manage_sx(s3a_t *, int);
    749   9694     Scott static int	ppm_search_list(pm_searchargs_t *);
    750   9694     Scott 
    751      0    stevel /*
    752      0    stevel  * interface between pm framework and ppm driver
    753      0    stevel  */
    754      0    stevel /* ARGSUSED */
    755      0    stevel static int
    756      0    stevel ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
    757      0    stevel     ddi_ctl_enum_t ctlop, void *arg, void *result)
    758      0    stevel {
    759      0    stevel 	power_req_t	*reqp = (power_req_t *)arg;
    760      0    stevel 	ppm_unit_t	*unitp;
    761      0    stevel 	ppm_domain_t	*domp;
    762      0    stevel 	ppm_dev_t	*ppmd;
    763      0    stevel 	char		path[MAXNAMELEN];
    764      0    stevel 	ppm_owned_t	*owned;
    765      0    stevel 	int		mode;
    766      0    stevel 	int		ret = DDI_SUCCESS;
    767   5295    randyf 	int 		*res = (int *)result;
    768   5295    randyf 	s3a_t s3args;
    769      0    stevel 
    770      0    stevel #ifdef DEBUG
    771      0    stevel 	char	*str = "ppm_ctlops";
    772      0    stevel 	int	mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
    773      0    stevel 	char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
    774      0    stevel 	if (mask && ctlstr)
    775      0    stevel 		PPMD(mask, ("%s: %s, %s\n",
    776      0    stevel 		    str, ddi_binding_name(rdip), ctlstr))
    777      0    stevel #endif
    778      0    stevel 
    779   5295    randyf 	if (ctlop != DDI_CTLOPS_POWER) {
    780      0    stevel 		return (DDI_FAILURE);
    781   5295    randyf 	}
    782      0    stevel 
    783      0    stevel 	unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
    784      0    stevel 
    785      0    stevel 	switch (reqp->request_type) {
    786      0    stevel 
    787      0    stevel 	/* attempt to blink led if indeed all at lowest */
    788      0    stevel 	case PMR_PPM_ALL_LOWEST:
    789      0    stevel 		mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
    790      0    stevel 		if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
    791      0    stevel 			ppm_manage_led(PPM_LED_BLINKING);
    792      0    stevel 		else
    793      0    stevel 			ppm_manage_led(PPM_LED_SOLIDON);
    794      0    stevel 		return (DDI_SUCCESS);
    795      0    stevel 
    796      0    stevel 	/* undo the claiming of 'rdip' at attach time */
    797      0    stevel 	case PMR_PPM_POST_DETACH:
    798      0    stevel 		ASSERT(reqp->req.ppm_set_power_req.who == rdip);
    799      0    stevel 		mutex_enter(&unitp->lock);
    800      0    stevel 		if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
    801      0    stevel 		    (PPM_GET_PRIVATE(rdip) == NULL)) {
    802      0    stevel 			mutex_exit(&unitp->lock);
    803      0    stevel 			return (DDI_FAILURE);
    804      0    stevel 		}
    805      0    stevel 		mutex_exit(&unitp->lock);
    806      0    stevel 		ppm_rem_dev(rdip);
    807      0    stevel 		return (DDI_SUCCESS);
    808      0    stevel 
    809      0    stevel 	/* chance to adjust pwr_cnt if resume is about to power up rdip */
    810      0    stevel 	case PMR_PPM_PRE_RESUME:
    811      0    stevel 		ppm_svc_resume_ctlop(rdip, reqp);
    812      0    stevel 		return (DDI_SUCCESS);
    813      0    stevel 
    814      0    stevel 	/*
    815      0    stevel 	 * synchronizing, so that only the owner of the power lock is
    816      0    stevel 	 * permitted to change device and component's power level.
    817      0    stevel 	 */
    818      0    stevel 	case PMR_PPM_UNLOCK_POWER:
    819      0    stevel 	case PMR_PPM_TRY_LOCK_POWER:
    820      0    stevel 	case PMR_PPM_LOCK_POWER:
    821      0    stevel 		ppmd = PPM_GET_PRIVATE(rdip);
    822      0    stevel 		if (ppmd)
    823      0    stevel 			domp = ppmd->domp;
    824      0    stevel 		else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
    825      0    stevel 			domp = ppm_lookup_dev(rdip);
    826      0    stevel 			ASSERT(domp);
    827      0    stevel 			ppmd = ppm_get_dev(rdip, domp);
    828      0    stevel 		}
    829      0    stevel 
    830      0    stevel 		PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
    831      0    stevel 		    (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
    832      0    stevel 		    ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
    833      0    stevel 
    834      0    stevel 		if (domp->dflags & PPMD_LOCK_ALL)
    835      0    stevel 			ppm_lock_all(domp, reqp, result);
    836      0    stevel 		else
    837      0    stevel 			ppm_lock_one(ppmd, reqp, result);
    838      0    stevel 		return (DDI_SUCCESS);
    839      0    stevel 
    840      0    stevel 	case PMR_PPM_POWER_LOCK_OWNER:
    841      0    stevel 		ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
    842      0    stevel 		ppmd = PPM_GET_PRIVATE(rdip);
    843      0    stevel 		if (ppmd)
    844      0    stevel 			domp = ppmd->domp;
    845      0    stevel 		else {
    846      0    stevel 			domp = ppm_lookup_dev(rdip);
    847      0    stevel 			ASSERT(domp);
    848      0    stevel 			ppmd = ppm_get_dev(rdip, domp);
    849      0    stevel 		}
    850      0    stevel 
    851      0    stevel 		/*
    852      0    stevel 		 * In case of LOCK_ALL, effective owner of the power lock
    853      0    stevel 		 * is the owner of the domain lock. otherwise, it is the owner
    854      0    stevel 		 * of the power lock.
    855      0    stevel 		 */
    856      0    stevel 		if (domp->dflags & PPMD_LOCK_ALL)
    857      0    stevel 			reqp->req.ppm_power_lock_owner_req.owner =
    858      0    stevel 			    mutex_owner(&domp->lock);
    859      0    stevel 		else {
    860      0    stevel 			reqp->req.ppm_power_lock_owner_req.owner =
    861      0    stevel 			    DEVI(rdip)->devi_busy_thread;
    862      0    stevel 		}
    863      0    stevel 		return (DDI_SUCCESS);
    864      0    stevel 
    865      0    stevel 	case PMR_PPM_INIT_CHILD:
    866      0    stevel 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
    867      0    stevel 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
    868      0    stevel 			return (DDI_SUCCESS);
    869      0    stevel 
    870      0    stevel 		/*
    871      0    stevel 		 * We keep track of power-manageable devices starting with
    872      0    stevel 		 * initialization process.  The initializing flag remains
    873      0    stevel 		 * set until it is cleared by ppm_add_dev().  Power management
    874      0    stevel 		 * policy for some domains are affected even during device
    875      0    stevel 		 * initialization.  For example, PCI domains should leave
    876      0    stevel 		 * their clock running meanwhile a device in that domain
    877      0    stevel 		 * is initializing.
    878      0    stevel 		 */
    879      0    stevel 		mutex_enter(&domp->lock);
    880      0    stevel 		owned = ppm_add_owned(rdip, domp);
    881      0    stevel 		ASSERT(owned->initializing == 0);
    882      0    stevel 		owned->initializing = 1;
    883      0    stevel 
    884      0    stevel 		if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
    885      0    stevel 			ret = ppm_switch_clock(domp, PPMD_ON);
    886      0    stevel 			if (ret == DDI_SUCCESS)
    887      0    stevel 				domp->dflags |= PPMD_INITCHILD_CLKON;
    888      0    stevel 		}
    889      0    stevel 		mutex_exit(&domp->lock);
    890      0    stevel 		return (ret);
    891      0    stevel 
    892      0    stevel 	case PMR_PPM_POST_ATTACH:
    893      0    stevel 		ASSERT(reqp->req.ppm_config_req.who == rdip);
    894      0    stevel 		domp = ppm_lookup_dev(rdip);
    895      0    stevel 		ASSERT(domp);
    896      0    stevel 		ASSERT(domp->status == PPMD_ON);
    897      0    stevel 		if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
    898      0    stevel 			/*
    899      0    stevel 			 * call ppm_get_dev, which will increment the
    900      0    stevel 			 * domain power count by the right number.
    901      0    stevel 			 * Undo the power count increment, done in PRE_PROBE.
    902      0    stevel 			 */
    903      0    stevel 			if (PM_GET_PM_INFO(rdip))
    904      0    stevel 				ppmd = ppm_get_dev(rdip, domp);
    905      0    stevel 			mutex_enter(&domp->lock);
    906      0    stevel 			ASSERT(domp->pwr_cnt > 0);
    907      0    stevel 			domp->pwr_cnt--;
    908      0    stevel 			mutex_exit(&domp->lock);
    909      0    stevel 			return (DDI_SUCCESS);
    910      0    stevel 		}
    911      0    stevel 
    912      0    stevel 		ret = ppm_power_down_domain(rdip);
    913      0    stevel 		/* FALLTHROUGH */
    914      0    stevel 	case PMR_PPM_UNINIT_CHILD:
    915      0    stevel 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
    916      0    stevel 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
    917      0    stevel 			return (DDI_SUCCESS);
    918      0    stevel 
    919      0    stevel 		(void) ddi_pathname(rdip, path);
    920      0    stevel 		mutex_enter(&domp->lock);
    921      0    stevel 		for (owned = domp->owned; owned; owned = owned->next)
    922      0    stevel 			if (strcmp(owned->path, path) == 0)
    923      0    stevel 				break;
    924      0    stevel 
    925      0    stevel 		/*
    926      0    stevel 		 * In case we didn't go through a complete attach and detach,
    927      0    stevel 		 * the initializing flag will still be set, so clear it.
    928      0    stevel 		 */
    929      0    stevel 		if ((owned != NULL) && (owned->initializing))
    930      0    stevel 			owned->initializing = 0;
    931      0    stevel 
    932      0    stevel 		if (PPMD_IS_PCI(domp->model) &&
    933      0    stevel 		    domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
    934      0    stevel 		    (domp->dflags & PPMD_INITCHILD_CLKON) &&
    935      0    stevel 		    ppm_none_else_holds_power(domp)) {
    936      0    stevel 			ret = ppm_switch_clock(domp, PPMD_OFF);
    937      0    stevel 			if (ret == DDI_SUCCESS)
    938      0    stevel 				domp->dflags &= ~PPMD_INITCHILD_CLKON;
    939      0    stevel 		}
    940      0    stevel 		mutex_exit(&domp->lock);
    941      0    stevel 		return (ret);
    942      0    stevel 
    943      0    stevel 	/* place holders */
    944      0    stevel 	case PMR_PPM_UNMANAGE:
    945      0    stevel 	case PMR_PPM_PRE_DETACH:
    946      0    stevel 		return (DDI_SUCCESS);
    947      0    stevel 
    948      0    stevel 	case PMR_PPM_PRE_PROBE:
    949      0    stevel 		ASSERT(reqp->req.ppm_config_req.who == rdip);
    950      0    stevel 		return (ppm_power_up_domain(rdip));
    951      0    stevel 
    952      0    stevel 	case PMR_PPM_POST_PROBE:
    953      0    stevel 		ASSERT(reqp->req.ppm_config_req.who == rdip);
    954      0    stevel 		if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
    955      0    stevel 		    reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
    956      0    stevel 			return (DDI_SUCCESS);
    957      0    stevel 
    958      0    stevel 		/* Probe failed */
    959      0    stevel 		PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
    960      0    stevel 		    "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
    961      0    stevel 		    reqp->req.ppm_config_req.result))
    962      0    stevel 		return (ppm_power_down_domain(rdip));
    963      0    stevel 
    964      0    stevel 	case PMR_PPM_PRE_ATTACH:
    965      0    stevel 		ASSERT(reqp->req.ppm_config_req.who == rdip);
    966      0    stevel 		/* Domain has already been powered up in PRE_PROBE */
    967      0    stevel 		domp = ppm_lookup_dev(rdip);
    968      0    stevel 		ASSERT(domp);
    969      0    stevel 		ASSERT(domp->status == PPMD_ON);
    970      0    stevel 		return (DDI_SUCCESS);
    971      0    stevel 
    972      0    stevel 	/* ppm intercepts power change process to the claimed devices */
    973      0    stevel 	case PMR_PPM_SET_POWER:
    974      0    stevel 	case PMR_PPM_POWER_CHANGE_NOTIFY:
    975      0    stevel 		if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
    976      0    stevel 			domp = ppm_lookup_dev(rdip);
    977      0    stevel 			ASSERT(domp);
    978      0    stevel 			ppmd = ppm_get_dev(rdip, domp);
    979      0    stevel 		}
    980      0    stevel 		switch (ppmd->domp->model) {
    981      0    stevel 		case PPMD_CPU:
    982      0    stevel 			return (ppm_manage_cpus(rdip, reqp, result));
    983      0    stevel 		case PPMD_FET:
    984      0    stevel 			return (ppm_manage_fet(rdip, reqp, result));
    985      0    stevel 		case PPMD_PCI:
    986      0    stevel 		case PPMD_PCI_PROP:
    987      0    stevel 			return (ppm_manage_pci(rdip, reqp, result));
    988      0    stevel 		case PPMD_PCIE:
    989      0    stevel 			return (ppm_manage_pcie(rdip, reqp, result));
    990      0    stevel 		default:
    991      0    stevel 			cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
    992      0    stevel 			    " not support PMR_PPM_SET_POWER ctlop",
    993      0    stevel 			    ppmd->domp->model);
    994      0    stevel 			return (DDI_FAILURE);
    995      0    stevel 		}
    996      0    stevel 
    997   5295    randyf 	case PMR_PPM_ENTER_SX:
    998   5295    randyf 	case PMR_PPM_EXIT_SX:
    999   5295    randyf 		s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
   1000   5295    randyf 		s3args.s3a_test_point =
   1001   5295    randyf 		    reqp->req.ppm_power_enter_sx_req.test_point;
   1002   5295    randyf 		s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
   1003   5295    randyf 		s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
   1004   5295    randyf 		ret = ppm_manage_sx(&s3args,
   1005   5295    randyf 		    reqp->request_type == PMR_PPM_ENTER_SX);
   1006   5295    randyf 		if (ret) {
   1007   5295    randyf 			PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
   1008   5295    randyf 			return (DDI_FAILURE);
   1009   5295    randyf 		} else {
   1010   5295    randyf 			return (DDI_SUCCESS);
   1011   5295    randyf 		}
   1012   5295    randyf 
   1013   5295    randyf 	case PMR_PPM_SEARCH_LIST:
   1014   5295    randyf 		ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
   1015   5295    randyf 		reqp->req.ppm_search_list_req.result = ret;
   1016   5295    randyf 		*res = ret;
   1017   5295    randyf 		if (ret) {
   1018   5295    randyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
   1019   5295    randyf 			return (DDI_FAILURE);
   1020   5295    randyf 		} else {
   1021   5295    randyf 			PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
   1022   5295    randyf 			return (DDI_SUCCESS);
   1023   5295    randyf 		}
   1024   5295    randyf 
   1025      0    stevel 	default:
   1026      0    stevel 		cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
   1027   5295    randyf 		    reqp->request_type);
   1028      0    stevel 		return (DDI_FAILURE);
   1029      0    stevel 	}
   1030      0    stevel }
   1031      0    stevel 
   1032      0    stevel 
   1033      0    stevel /*
   1034      0    stevel  * Raise the power level of a subrange of cpus.  Used when cpu driver
   1035      0    stevel  * failed an attempt to lower the power of a cpu (probably because
   1036      0    stevel  * it got busy).  Need to revert the ones we already changed.
   1037      0    stevel  *
   1038      0    stevel  * ecpup = the ppm_dev_t for the cpu which failed to lower power
   1039      0    stevel  * level = power level to reset prior cpus to
   1040      0    stevel  */
   1041   4667   mh27603 int
   1042      0    stevel ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
   1043      0    stevel {
   1044      0    stevel 	ppm_dev_t *cpup;
   1045      0    stevel 	int ret = DDI_SUCCESS;
   1046      0    stevel 
   1047      0    stevel 	for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
   1048      0    stevel 		PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
   1049      0    stevel 		    "level %d\n", cpup->path, level))
   1050      0    stevel 
   1051      0    stevel 		ret = pm_power(cpup->dip, 0, level);
   1052      0    stevel 		if (ret == DDI_SUCCESS) {
   1053      0    stevel 			cpup->level = level;
   1054      0    stevel 			cpup->rplvl = PM_LEVEL_UNKNOWN;
   1055      0    stevel 		}
   1056      0    stevel 	}
   1057      0    stevel 	return (ret);
   1058      0    stevel }
   1059   4667   mh27603 
   1060      0    stevel 
   1061      0    stevel /*
   1062      0    stevel  * ppm_manage_cpus - Process a request to change the power level of a cpu.
   1063      0    stevel  * If not all cpus want to be at the same level, OR if we are currently
   1064      0    stevel  * refusing slowdown requests due to thermal stress, we cache the request.
   1065      0    stevel  * Otherwise, set all cpus to the new power level.
   1066      0    stevel  */
   1067      0    stevel /* ARGSUSED */
   1068      0    stevel static int
   1069      0    stevel ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
   1070      0    stevel {
   1071      0    stevel #ifdef	DEBUG
   1072      0    stevel 	char *str = "ppm_manage_cpus";
   1073      0    stevel #endif
   1074      0    stevel 	int old, new, ret, kmflag;
   1075      0    stevel 	ppm_dev_t *ppmd, *cpup;
   1076      0    stevel 	int change_notify = 0;
   1077      0    stevel 	pm_ppm_devlist_t *devlist = NULL, *p;
   1078      0    stevel 	int		do_rescan = 0;
   1079      0    stevel 
   1080      0    stevel 	*result = DDI_SUCCESS;
   1081      0    stevel 
   1082      0    stevel 	switch (reqp->request_type) {
   1083      0    stevel 	case PMR_PPM_SET_POWER:
   1084      0    stevel 		break;
   1085      0    stevel 
   1086      0    stevel 	case PMR_PPM_POWER_CHANGE_NOTIFY:
   1087      0    stevel 		change_notify = 1;
   1088      0    stevel 		break;
   1089      0    stevel 
   1090      0    stevel 	default:
   1091      0    stevel 		return (DDI_FAILURE);
   1092      0    stevel 	}
   1093      0    stevel 
   1094      0    stevel 	ppmd = PPM_GET_PRIVATE(dip);
   1095      0    stevel 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
   1096      0    stevel 	old = reqp->req.ppm_set_power_req.old_level;
   1097      0    stevel 	new = reqp->req.ppm_set_power_req.new_level;
   1098      0    stevel 
   1099      0    stevel 	if (change_notify) {
   1100      0    stevel 		ppmd->level = new;
   1101      0    stevel 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
   1102      0    stevel 
   1103      0    stevel 		PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
   1104      0    stevel 		    "from %d to %d", str, (void *)dip, old, new))
   1105      0    stevel 		return (DDI_SUCCESS);
   1106      0    stevel 	}
   1107      0    stevel 
   1108   4667   mh27603 	if (ppm_manage_early_cpus(dip, new, result))
   1109   4667   mh27603 		return (*result);
   1110      0    stevel 
   1111      0    stevel 	if (new == ppmd->level) {
   1112      0    stevel 		PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
   1113      0    stevel 		return (DDI_SUCCESS);
   1114      0    stevel 	}
   1115      0    stevel 
   1116      0    stevel 	/*
   1117      0    stevel 	 * A request from lower to higher level transition is granted and
   1118   4667   mh27603 	 * made effective on all cpus. A request from higher to lower must
   1119   4667   mh27603 	 * be agreed upon by all cpus.
   1120      0    stevel 	 */
   1121      0    stevel 	ppmd->rplvl = new;
   1122      0    stevel 	for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
   1123      0    stevel 		if (cpup->rplvl == new)
   1124      0    stevel 			continue;
   1125      0    stevel 
   1126      0    stevel 		if (new < old) {
   1127      0    stevel 			PPMD(D_SOME, ("%s: not all cpus wants to be at new "
   1128      0    stevel 			    "level %d yet.\n", str, new))
   1129      0    stevel 			return (DDI_SUCCESS);
   1130      0    stevel 		}
   1131      0    stevel 
   1132      0    stevel 		/*
   1133   4667   mh27603 		 * If a single cpu requests power up, honor the request
   1134   4667   mh27603 		 * powering up all cpus.
   1135      0    stevel 		 */
   1136      0    stevel 		if (new > old) {
   1137      0    stevel 			PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
   1138      0    stevel 			    "because of request from dip(%s@%s, %p), "
   1139      0    stevel 			    "need pm_rescan\n", str, PM_NAME(cpup->dip),
   1140      0    stevel 			    PM_ADDR(cpup->dip), (void *)cpup->dip,
   1141      0    stevel 			    PM_NAME(dip), PM_ADDR(dip), (void *)dip))
   1142      0    stevel 			do_rescan++;
   1143      0    stevel 		}
   1144      0    stevel 	}
   1145      0    stevel 
   1146      0    stevel 	PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
   1147      0    stevel 	    str, ppmd->path, ppmd->level, new))
   1148      0    stevel 	ret = ppm_change_cpu_power(ppmd, new);
   1149      0    stevel 	*result = ret;
   1150      0    stevel 
   1151      0    stevel 	if (ret == DDI_SUCCESS) {
   1152      0    stevel 		if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
   1153      0    stevel 			kmflag = KM_SLEEP;
   1154      0    stevel 		else
   1155      0    stevel 			kmflag = KM_NOSLEEP;
   1156      0    stevel 
   1157      0    stevel 		for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
   1158      0    stevel 			if (cpup->dip == dip)
   1159      0    stevel 				continue;
   1160      0    stevel 
   1161      0    stevel 			if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
   1162      0    stevel 			    kmflag)) == NULL) {
   1163      0    stevel 				break;
   1164      0    stevel 			}
   1165      0    stevel 			p->ppd_who = cpup->dip;
   1166      0    stevel 			p->ppd_cmpt = cpup->cmpt;
   1167      0    stevel 			p->ppd_old_level = old;
   1168      0    stevel 			p->ppd_new_level = new;
   1169      0    stevel 			p->ppd_next = devlist;
   1170      0    stevel 
   1171      0    stevel 			PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
   1172      0    stevel 			    str, cpup->path, old, new))
   1173      0    stevel 
   1174      0    stevel 			devlist = p;
   1175      0    stevel 		}
   1176      0    stevel 		reqp->req.ppm_set_power_req.cookie = (void *) devlist;
   1177      0    stevel 
   1178   4667   mh27603 		if (do_rescan > 0) {
   1179   4667   mh27603 			for (cpup = ppmd->domp->devlist; cpup;
   1180   4667   mh27603 			    cpup = cpup->next) {
   1181   4667   mh27603 				if (cpup->dip == dip)
   1182   4667   mh27603 					continue;
   1183   4667   mh27603 				pm_rescan(cpup->dip);
   1184   4667   mh27603 			}
   1185   4667   mh27603 		}
   1186      0    stevel 	}
   1187      0    stevel 
   1188      0    stevel 	return (ret);
   1189      0    stevel }
   1190      0    stevel 
   1191      0    stevel 
   1192      0    stevel /*
   1193      0    stevel  * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
   1194      0    stevel  * increments its FET domain power count, in anticipation of that
   1195      0    stevel  * the indicated device(dip) would be powered up by its driver as
   1196      0    stevel  * a result of cpr resuming.
   1197      0    stevel  */
   1198      0    stevel /* ARGSUSED */
   1199      0    stevel static void
   1200      0    stevel ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
   1201      0    stevel {
   1202      0    stevel 	ppm_domain_t *domp;
   1203      0    stevel 	ppm_dev_t *ppmd;
   1204      0    stevel 	int powered;	/* power up count per dip */
   1205      0    stevel 
   1206      0    stevel 	ppmd = PPM_GET_PRIVATE(dip);
   1207      0    stevel 	if (ppmd == NULL)
   1208      0    stevel 		return;
   1209      0    stevel 
   1210      0    stevel 	/*
   1211      0    stevel 	 * Maintain correct powered count for domain which cares
   1212      0    stevel 	 */
   1213      0    stevel 	powered = 0;
   1214      0    stevel 	domp = ppmd->domp;
   1215      0    stevel 	mutex_enter(&domp->lock);
   1216    686    osaeed 	if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
   1217    686    osaeed 	    (domp->model == PPMD_PCIE)) {
   1218      0    stevel 		for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
   1219      0    stevel 			if (ppmd->dip == dip && ppmd->level)
   1220      0    stevel 				powered++;
   1221      0    stevel 		}
   1222      0    stevel 
   1223      0    stevel 		/*
   1224      0    stevel 		 * All fets and clocks are held on during suspend -
   1225      0    stevel 		 * resume window regardless their domain devices' power
   1226      0    stevel 		 * level.
   1227      0    stevel 		 */
   1228      0    stevel 		ASSERT(domp->status == PPMD_ON);
   1229      0    stevel 
   1230      0    stevel 		/*
   1231      0    stevel 		 * The difference indicates the number of components
   1232      0    stevel 		 * being off prior to suspend operation, that is the
   1233      0    stevel 		 * amount needs to be compensated in order to sync up
   1234      0    stevel 		 * bookkeeping with reality, for PROM reset would have
   1235      0    stevel 		 * brought up all devices.
   1236      0    stevel 		 */
   1237      0    stevel 		if (powered < PM_NUMCMPTS(dip))
   1238      0    stevel 			domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
   1239      0    stevel 	}
   1240      0    stevel 	for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
   1241      0    stevel 		if (ppmd->dip == dip)
   1242      0    stevel 			ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
   1243      0    stevel 	}
   1244      0    stevel 	mutex_exit(&domp->lock);
   1245      0    stevel }
   1246      0    stevel 
   1247      0    stevel #ifdef	DEBUG
   1248      0    stevel static int ppmbringup = 0;
   1249      0    stevel #endif
   1250      0    stevel 
   1251      0    stevel int
   1252      0    stevel ppm_bringup_domains()
   1253      0    stevel {
   1254      0    stevel #ifdef DEBUG
   1255      0    stevel 	char *str = "ppm_bringup_domains";
   1256      0    stevel #endif
   1257      0    stevel 	ppm_domain_t	*domp;
   1258      0    stevel 	int	ret = DDI_SUCCESS;
   1259      0    stevel 
   1260      0    stevel 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
   1261      0    stevel 	for (domp = ppm_domain_p; domp; domp = domp->next) {
   1262    686    osaeed 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
   1263    686    osaeed 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
   1264      0    stevel 			continue;
   1265      0    stevel 
   1266      0    stevel 		mutex_enter(&domp->lock);
   1267      0    stevel 		if (domp->status == PPMD_ON) {
   1268      0    stevel 			mutex_exit(&domp->lock);
   1269      0    stevel 			continue;
   1270      0    stevel 		}
   1271    686    osaeed 		switch (domp->model) {
   1272    686    osaeed 		case PPMD_FET:
   1273    686    osaeed 			ret = ppm_fetset(domp, PPMD_ON);
   1274    686    osaeed 			break;
   1275    686    osaeed 		case PPMD_PCI:
   1276    686    osaeed 		case PPMD_PCI_PROP:
   1277      0    stevel 			ret = ppm_switch_clock(domp, PPMD_ON);
   1278    686    osaeed 			break;
   1279    686    osaeed 		case PPMD_PCIE:
   1280    686    osaeed 			ret = ppm_pcie_pwr(domp, PPMD_ON);
   1281    686    osaeed 			break;
   1282    686    osaeed 		default:
   1283    686    osaeed 			break;
   1284    686    osaeed 		}
   1285      0    stevel 		mutex_exit(&domp->lock);
   1286      0    stevel 	}
   1287   5295    randyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
   1288      0    stevel 
   1289      0    stevel 	return (ret);
   1290      0    stevel }
   1291      0    stevel 
   1292      0    stevel #ifdef	DEBUG
   1293      0    stevel static int ppmsyncbp = 0;
   1294      0    stevel #endif
   1295      0    stevel 
   1296      0    stevel int
   1297      0    stevel ppm_sync_bookkeeping()
   1298      0    stevel {
   1299      0    stevel #ifdef DEBUG
   1300      0    stevel 	char *str = "ppm_sync_bookkeeping";
   1301      0    stevel #endif
   1302      0    stevel 	ppm_domain_t	*domp;
   1303      0    stevel 	int	ret = DDI_SUCCESS;
   1304      0    stevel 
   1305      0    stevel 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
   1306      0    stevel 	for (domp = ppm_domain_p; domp; domp = domp->next) {
   1307    686    osaeed 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
   1308    686    osaeed 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
   1309      0    stevel 			continue;
   1310      0    stevel 
   1311      0    stevel 		mutex_enter(&domp->lock);
   1312      0    stevel 		if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
   1313      0    stevel 			mutex_exit(&domp->lock);
   1314      0    stevel 			continue;
   1315      0    stevel 		}
   1316   5295    randyf 
   1317   5295    randyf 		/*
   1318   5295    randyf 		 * skip NULL .devlist slot, for some may host pci device
   1319   5295    randyf 		 * that can not tolerate clock off or not even participate
   1320   5295    randyf 		 * in PM.
   1321   5295    randyf 		 */
   1322   5295    randyf 		if (domp->devlist == NULL)
   1323   5295    randyf 			continue;
   1324   5295    randyf 
   1325    686    osaeed 		switch (domp->model) {
   1326    686    osaeed 		case PPMD_FET:
   1327      0    stevel 			ret = ppm_fetset(domp, PPMD_OFF);
   1328    686    osaeed 			break;
   1329    686    osaeed 		case PPMD_PCI:
   1330    686    osaeed 		case PPMD_PCI_PROP:
   1331      0    stevel 			ret = ppm_switch_clock(domp, PPMD_OFF);
   1332    686    osaeed 			break;
   1333    686    osaeed 		case PPMD_PCIE:
   1334    686    osaeed 			ret = ppm_pcie_pwr(domp, PPMD_OFF);
   1335    686    osaeed 			break;
   1336    686    osaeed 		default:
   1337    686    osaeed 			break;
   1338    686    osaeed 		}
   1339      0    stevel 		mutex_exit(&domp->lock);
   1340      0    stevel 	}
   1341   5295    randyf 	PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
   1342      0    stevel 
   1343      0    stevel 	return (ret);
   1344      0    stevel }
   1345      0    stevel 
   1346      0    stevel 
   1347      0    stevel 
   1348      0    stevel /*
   1349      0    stevel  * pre-suspend window;
   1350      0    stevel  *
   1351      0    stevel  * power up every FET and PCI clock that are off;
   1352      0    stevel  *
   1353      0    stevel  * set ppm_cpr_window global flag to indicate
   1354      0    stevel  * that even though all pm_scan requested power transitions
   1355      0    stevel  * will be honored as usual but that until we're out
   1356      0    stevel  * of this window,  no FET or clock will be turned off
   1357      0    stevel  * for domains with pwr_cnt decremented down to 0.
   1358      0    stevel  * Such is to avoid accessing the orthogonal drivers that own
   1359      0    stevel  * the FET and clock registers that may not be resumed yet.
   1360      0    stevel  *
   1361      0    stevel  * at post-resume window, walk through each FET and PCI domains,
   1362      0    stevel  * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
   1363      0    stevel  * and noinvol check okays, power down the FET or PCI.  At last,
   1364      0    stevel  * clear the global flag ppm_cpr_window.
   1365      0    stevel  *
   1366      0    stevel  * ASSERT case 1, during cpr window, checks pwr_cnt against power
   1367      0    stevel  *	transitions;
   1368      0    stevel  * ASSERT case 2, out of cpr window, checks four things:
   1369      0    stevel  *	pwr_cnt <> power transition in/out of 0
   1370      0    stevel  *	<> status <> record of noinvol device detached
   1371      0    stevel  *
   1372      0    stevel  */
   1373      0    stevel /* ARGSUSED */
   1374      0    stevel static boolean_t
   1375      0    stevel ppm_cpr_callb(void *arg, int code)
   1376      0    stevel {
   1377      0    stevel 	int	ret;
   1378      0    stevel 
   1379      0    stevel 	switch (code) {
   1380      0    stevel 	case CB_CODE_CPR_CHKPT:
   1381      0    stevel 
   1382      0    stevel 		/* pre-suspend: start of cpr window */
   1383      0    stevel 		mutex_enter(&ppm_cpr_window_lock);
   1384      0    stevel 		ASSERT(ppm_cpr_window_flag == B_FALSE);
   1385      0    stevel 		ppm_cpr_window_flag = B_TRUE;
   1386      0    stevel 		mutex_exit(&ppm_cpr_window_lock);
   1387      0    stevel 
   1388      0    stevel 		ret = ppm_bringup_domains();
   1389      0    stevel 
   1390      0    stevel 		break;
   1391      0    stevel 
   1392      0    stevel 	case CB_CODE_CPR_RESUME:
   1393      0    stevel 
   1394      0    stevel 		/* post-resume: end of cpr window */
   1395      0    stevel 		ret = ppm_sync_bookkeeping();
   1396      0    stevel 
   1397      0    stevel 		mutex_enter(&ppm_cpr_window_lock);
   1398      0    stevel 		ASSERT(ppm_cpr_window_flag == B_TRUE);
   1399      0    stevel 		ppm_cpr_window_flag = B_FALSE;
   1400      0    stevel 		mutex_exit(&ppm_cpr_window_lock);
   1401      0    stevel 
   1402      0    stevel 		break;
   1403      0    stevel 	}
   1404      0    stevel 
   1405      0    stevel 	return (ret == DDI_SUCCESS);
   1406      0    stevel }
   1407      0    stevel 
   1408      0    stevel 
   1409      0    stevel /*
   1410      0    stevel  * Initialize our private version of real power level
   1411      0    stevel  * as well as lowest and highest levels the device supports;
   1412      0    stevel  * relate to ppm_add_dev
   1413      0    stevel  */
   1414      0    stevel void
   1415      0    stevel ppm_dev_init(ppm_dev_t *ppmd)
   1416      0    stevel {
   1417      0    stevel 	struct pm_component *dcomps;
   1418      0    stevel 	struct pm_comp *pm_comp;
   1419      0    stevel 	dev_info_t *dip;
   1420      0    stevel 	int maxi, i;
   1421      0    stevel 
   1422      0    stevel 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
   1423      0    stevel 	ppmd->level = PM_LEVEL_UNKNOWN;
   1424      0    stevel 	ppmd->rplvl = PM_LEVEL_UNKNOWN;
   1425      0    stevel 
   1426      0    stevel 	/* increment pwr_cnt per component */
   1427      0    stevel 	if ((ppmd->domp->model == PPMD_FET) ||
   1428      0    stevel 	    PPMD_IS_PCI(ppmd->domp->model) ||
   1429      0    stevel 	    (ppmd->domp->model == PPMD_PCIE))
   1430      0    stevel 		ppmd->domp->pwr_cnt++;
   1431      0    stevel 
   1432      0    stevel 	dip = ppmd->dip;
   1433      0    stevel 
   1434      0    stevel 	/*
   1435   2155       cth 	 * ppm exists to handle power-manageable devices which require
   1436   2155       cth 	 * special handling on the current platform.  However, a
   1437   2155       cth 	 * driver for such a device may choose not to support power
   1438   2155       cth 	 * management on a particular load/attach.  In this case we
   1439   2155       cth 	 * we create a structure to represent a single-component device
   1440   2155       cth 	 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
   1441   2155       cth 	 * are effectively constant.
   1442      0    stevel 	 */
   1443   2155       cth 	if (PM_GET_PM_INFO(dip)) {
   1444   2155       cth 		dcomps = DEVI(dip)->devi_pm_components;
   1445   2155       cth 		pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
   1446   2155       cth 
   1447   2155       cth 		ppmd->lowest = pm_comp->pmc_lvals[0];
   1448   2155       cth 		ASSERT(ppmd->lowest >= 0);
   1449   2155       cth 		maxi = pm_comp->pmc_numlevels - 1;
   1450   2155       cth 		ppmd->highest = pm_comp->pmc_lvals[maxi];
   1451   2155       cth 
   1452   2155       cth 		/*
   1453   2155       cth 		 * If 66mhz PCI device on pci 66mhz bus supports D2 state
   1454   2155       cth 		 * (config reg PMC bit 10 set), ppm could turn off its bus
   1455   2155       cth 		 * clock once it is at D3hot.
   1456   2155       cth 		 */
   1457   2155       cth 		if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
   1458   2155       cth 			for (i = 0; i < maxi; i++)
   1459   2155       cth 				if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
   1460   2155       cth 					ppmd->flags |= PPMDEV_PCI66_D2;
   1461   2155       cth 					break;
   1462   2155       cth 				}
   1463   2155       cth 		}
   1464      0    stevel 	}
   1465      0    stevel 
   1466      0    stevel 	/*
   1467      0    stevel 	 * If device is in PCI_PROP domain and has exported the
   1468      0    stevel 	 * property listed in ppm.conf, its clock will be turned
   1469      0    stevel 	 * off when all pm'able devices in that domain are at D3.
   1470      0    stevel 	 */
   1471      0    stevel 	if ((ppmd->domp->model == PPMD_PCI_PROP) &&
   1472      0    stevel 	    (ppmd->domp->propname != NULL) &&
   1473      0    stevel 	    ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
   1474      0    stevel 	    ppmd->domp->propname))
   1475      0    stevel 		ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
   1476      0    stevel }
   1477      0    stevel 
   1478      0    stevel 
   1479      0    stevel /*
   1480      0    stevel  * relate to ppm_rem_dev
   1481      0    stevel  */
   1482      0    stevel void
   1483      0    stevel ppm_dev_fini(ppm_dev_t *ppmd)
   1484      0    stevel {
   1485      0    stevel 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
   1486      0    stevel 
   1487      0    stevel 	/* decrement pwr_cnt per component */
   1488      0    stevel 	if ((ppmd->domp->model == PPMD_FET) ||
   1489      0    stevel 	    PPMD_IS_PCI(ppmd->domp->model) ||
   1490      0    stevel 	    (ppmd->domp->model == PPMD_PCIE))
   1491      0    stevel 		if (ppmd->level != ppmd->lowest)
   1492      0    stevel 			ppmd->domp->pwr_cnt--;
   1493      0    stevel }
   1494      0    stevel 
   1495      0    stevel /*
   1496      0    stevel  * Each power fet controls the power of one or more platform
   1497      0    stevel  * device(s) within their domain.  Hence domain devices' power
   1498      0    stevel  * level change has been monitored, such that once all devices
   1499      0    stevel  * are powered off, the fet is turned off to save more power.
   1500      0    stevel  *
   1501      0    stevel  * To power on any domain device, the domain power fet
   1502      0    stevel  * needs to be turned on first. always one fet per domain.
   1503      0    stevel  */
   1504      0    stevel static int
   1505      0    stevel ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
   1506      0    stevel {
   1507      0    stevel #ifdef DEBUG
   1508      0    stevel 	char *str = "ppm_manage_fet";
   1509      0    stevel #endif
   1510      0    stevel 	int (*pwr_func)(ppm_dev_t *, int, int);
   1511      0    stevel 	int		new, old, cmpt;
   1512      0    stevel 	ppm_dev_t	*ppmd;
   1513      0    stevel 	ppm_domain_t	*domp;
   1514      0    stevel 	int		incr = 0;
   1515      0    stevel 	int		dummy_ret;
   1516      0    stevel 
   1517      0    stevel 
   1518      0    stevel 	*result = DDI_SUCCESS;
   1519      0    stevel 	switch (reqp->request_type) {
   1520      0    stevel 	case PMR_PPM_SET_POWER:
   1521      0    stevel 		pwr_func = ppm_change_power_level;
   1522      0    stevel 		old = reqp->req.ppm_set_power_req.old_level;
   1523      0    stevel 		new = reqp->req.ppm_set_power_req.new_level;
   1524      0    stevel 		cmpt = reqp->req.ppm_set_power_req.cmpt;
   1525      0    stevel 		break;
   1526      0    stevel 	case PMR_PPM_POWER_CHANGE_NOTIFY:
   1527      0    stevel 		pwr_func = ppm_record_level_change;
   1528      0    stevel 		old = reqp->req.ppm_notify_level_req.old_level;
   1529      0    stevel 		new = reqp->req.ppm_notify_level_req.new_level;
   1530      0    stevel 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
   1531      0    stevel 		break;
   1532      0    stevel 	default:
   1533      0    stevel 		*result = DDI_FAILURE;
   1534      0    stevel 		PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
   1535      0    stevel 		    str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
   1536      0    stevel 		return (DDI_FAILURE);
   1537      0    stevel 	}
   1538      0    stevel 
   1539      0    stevel 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
   1540      0    stevel 		if (cmpt == ppmd->cmpt)
   1541      0    stevel 			break;
   1542      0    stevel 	if (!ppmd) {
   1543      0    stevel 		PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
   1544      0    stevel 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
   1545      0    stevel 		*result = DDI_FAILURE;
   1546      0    stevel 		return (DDI_FAILURE);
   1547      0    stevel 	}
   1548      0    stevel 	domp = ppmd->domp;
   1549      0    stevel 	PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
   1550      0    stevel 	    "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
   1551      0    stevel 	    ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
   1552      0    stevel 	    ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
   1553      0    stevel 
   1554      0    stevel 
   1555      0    stevel 	ASSERT(old == ppmd->level);
   1556      0    stevel 
   1557      0    stevel 	if (new == ppmd->level) {
   1558      0    stevel 		PPMD(D_FET, ("nop\n"))
   1559      0    stevel 		return (DDI_SUCCESS);
   1560      0    stevel 	}
   1561      0    stevel 
   1562      0    stevel 	PPM_LOCK_DOMAIN(domp);
   1563      0    stevel 
   1564      0    stevel 	/*
   1565      0    stevel 	 * In general, a device's published lowest power level does not
   1566      0    stevel 	 * have to be 0 if power-off is not tolerated. i.e. a device
   1567      0    stevel 	 * instance may export its lowest level > 0.  It is reasonable to
   1568      0    stevel 	 * assume that level 0 indicates off state, positive level values
   1569      0    stevel 	 * indicate power states above off, include full power state.
   1570      0    stevel 	 */
   1571      0    stevel 	if (new > 0) { /* device powering up or to different positive level */
   1572      0    stevel 		if (domp->status == PPMD_OFF) {
   1573      0    stevel 
   1574      0    stevel 			/* can not be in (chpt, resume) window */
   1575      0    stevel 			ASSERT(ppm_cpr_window_flag == B_FALSE);
   1576      0    stevel 
   1577      0    stevel 			ASSERT(old == 0 && domp->pwr_cnt == 0);
   1578      0    stevel 
   1579      0    stevel 			PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
   1580      0    stevel 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
   1581      0    stevel 
   1582      0    stevel 			*result = ppm_fetset(domp, PPMD_ON);
   1583      0    stevel 			if (*result != DDI_SUCCESS) {
   1584      0    stevel 				PPMD(D_FET, ("\tCan't turn on power FET: "
   1585      0    stevel 				    "ret(%d)\n", *result))
   1586      0    stevel 				PPM_UNLOCK_DOMAIN(domp);
   1587      0    stevel 				return (DDI_FAILURE);
   1588      0    stevel 			}
   1589      0    stevel 		}
   1590      0    stevel 
   1591      0    stevel 		/*
   1592      0    stevel 		 * If powering up, pre-increment the count before
   1593      0    stevel 		 * calling pwr_func, because we are going to release
   1594      0    stevel 		 * the domain lock and another thread might turn off
   1595      0    stevel 		 * domain power otherwise.
   1596      0    stevel 		 */
   1597      0    stevel 		if (old == 0) {
   1598      0    stevel 			domp->pwr_cnt++;
   1599      0    stevel 			incr = 1;
   1600      0    stevel 		}
   1601      0    stevel 
   1602      0    stevel 		PPMD(D_FET, ("\t%s domain power count: %d\n",
   1603      0    stevel 		    domp->name, domp->pwr_cnt))
   1604      0    stevel 	}
   1605      0    stevel 
   1606      0    stevel 
   1607      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   1608      0    stevel 
   1609      0    stevel 	ASSERT(domp->pwr_cnt > 0);
   1610      0    stevel 
   1611      0    stevel 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
   1612      0    stevel 		PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
   1613      0    stevel 		    ppmd->path, *result))
   1614      0    stevel 	}
   1615      0    stevel 
   1616      0    stevel 	PPM_LOCK_DOMAIN(domp);
   1617      0    stevel 
   1618      0    stevel 	/*
   1619      0    stevel 	 * Decr the power count in two cases:
   1620      0    stevel 	 *
   1621      0    stevel 	 *   1) request was to power device down and was successful
   1622      0    stevel 	 *   2) request was to power up (we pre-incremented count), but failed.
   1623      0    stevel 	 */
   1624      0    stevel 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
   1625      0    stevel 	    (*result != DDI_SUCCESS && incr)) {
   1626      0    stevel 		ASSERT(domp->pwr_cnt > 0);
   1627      0    stevel 		domp->pwr_cnt--;
   1628      0    stevel 	}
   1629      0    stevel 
   1630      0    stevel 	PPMD(D_FET, ("\t%s domain power count: %d\n",
   1631      0    stevel 	    domp->name, domp->pwr_cnt))
   1632      0    stevel 
   1633      0    stevel 	/*
   1634      0    stevel 	 * call to pwr_func will update ppm data structures, if it
   1635      0    stevel 	 * succeeds. ppm should return whatever is the return value
   1636      0    stevel 	 * from call to pwr_func. This way pm and ppm data structures
   1637      0    stevel 	 * always in sync. Use dummy_ret from here for any further
   1638      0    stevel 	 * return values.
   1639      0    stevel 	 */
   1640      0    stevel 	if ((domp->pwr_cnt == 0) &&
   1641      0    stevel 	    (ppm_cpr_window_flag == B_FALSE) &&
   1642      0    stevel 	    ppm_none_else_holds_power(domp)) {
   1643      0    stevel 
   1644      0    stevel 		PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
   1645      0    stevel 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
   1646      0    stevel 
   1647      0    stevel 		dummy_ret = ppm_fetset(domp, PPMD_OFF);
   1648      0    stevel 		if (dummy_ret != DDI_SUCCESS) {
   1649      0    stevel 			PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
   1650      0    stevel 			    dummy_ret))
   1651      0    stevel 		}
   1652      0    stevel 	}
   1653      0    stevel 
   1654      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   1655      0    stevel 	ASSERT(domp->pwr_cnt >= 0);
   1656      0    stevel 	return (*result);
   1657      0    stevel }
   1658      0    stevel 
   1659      0    stevel 
   1660      0    stevel /*
   1661      0    stevel  * the actual code that turn on or off domain power fet and
   1662      0    stevel  * update domain status
   1663      0    stevel  */
   1664      0    stevel static int
   1665      0    stevel ppm_fetset(ppm_domain_t *domp, uint8_t value)
   1666      0    stevel {
   1667      0    stevel 	char	*str = "ppm_fetset";
   1668      0    stevel 	int	key;
   1669      0    stevel 	ppm_dc_t *dc;
   1670      0    stevel 	int	ret;
   1671      0    stevel 	clock_t	temp;
   1672      0    stevel 	clock_t delay = 0;
   1673      0    stevel 
   1674      0    stevel 	key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
   1675      0    stevel 	for (dc = domp->dc; dc; dc = dc->next)
   1676      0    stevel 		if (dc->cmd == key)
   1677      0    stevel 			break;
   1678      0    stevel 	if (!dc || !dc->lh) {
   1679      0    stevel 		PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
   1680     68  zx151605 		    str, domp->name))
   1681      0    stevel 		return (DDI_FAILURE);
   1682      0    stevel 	}
   1683      0    stevel 
   1684      0    stevel 	if (key == PPMDC_FET_ON) {
   1685   4667   mh27603 		PPM_GET_IO_DELAY(dc, delay);
   1686      0    stevel 		if (delay > 0 && domp->last_off_time > 0) {
   1687      0    stevel 			/*
   1688      0    stevel 			 * provide any delay required before turning on.
   1689      0    stevel 			 * some devices e.g. Samsung DVD require minimum
   1690      0    stevel 			 * of 1 sec between OFF->ON. no delay is required
   1691      0    stevel 			 * for the first time.
   1692      0    stevel 			 */
   1693      0    stevel 			temp = ddi_get_lbolt();
   1694      0    stevel 			temp -= domp->last_off_time;
   1695      0    stevel 			temp = drv_hztousec(temp);
   1696      0    stevel 
   1697      0    stevel 			if (temp < delay) {
   1698      0    stevel 				/*
   1699      0    stevel 				 * busy wait untill we meet the
   1700      0    stevel 				 * required delay. Since we maintain
   1701      0    stevel 				 * time stamps in terms of clock ticks
   1702      0    stevel 				 * we might wait for longer than required
   1703      0    stevel 				 */
   1704      0    stevel 				PPMD(D_FET, ("%s : waiting %lu micro seconds "
   1705   5295    randyf 				    "before on\n", domp->name,
   1706   5295    randyf 				    delay - temp));
   1707      0    stevel 				drv_usecwait(delay - temp);
   1708      0    stevel 			}
   1709      0    stevel 		}
   1710      0    stevel 	}
   1711      0    stevel 	switch (dc->method) {
   1712   5295    randyf #ifdef sun4u
   1713   4667   mh27603 	case PPMDC_I2CKIO: {
   1714   4667   mh27603 		i2c_gpio_t i2c_req;
   1715      0    stevel 		i2c_req.reg_mask = dc->m_un.i2c.mask;
   1716      0    stevel 		i2c_req.reg_val = dc->m_un.i2c.val;
   1717      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
   1718      0    stevel 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
   1719      0    stevel 		break;
   1720   4667   mh27603 	}
   1721   4667   mh27603 #endif
   1722      0    stevel 
   1723      0    stevel 	case PPMDC_KIO:
   1724      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   1725      0    stevel 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
   1726      0    stevel 		    NULL);
   1727      0    stevel 		break;
   1728      0    stevel 
   1729      0    stevel 	default:
   1730      0    stevel 		PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
   1731      0    stevel 		    str, domp->dc->method))
   1732      0    stevel 		return (DDI_FAILURE);
   1733      0    stevel 	}
   1734      0    stevel 
   1735      0    stevel 	PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
   1736      0    stevel 	    (ret == 0) ? "turned" : "failed to turn",
   1737      0    stevel 	    domp->name,
   1738      0    stevel 	    (domp->status == PPMD_ON) ? "ON" : "OFF",
   1739      0    stevel 	    (value == PPMD_ON) ? "ON" : "OFF"))
   1740      0    stevel 
   1741      0    stevel 	if (ret == DDI_SUCCESS) {
   1742      0    stevel 		domp->status = value;
   1743      0    stevel 
   1744      0    stevel 		if (key == PPMDC_FET_OFF)
   1745      0    stevel 			/*
   1746      0    stevel 			 * record the time, when it is off. time is recorded
   1747      0    stevel 			 * in clock ticks
   1748      0    stevel 			 */
   1749      0    stevel 			domp->last_off_time = ddi_get_lbolt();
   1750      0    stevel 
   1751      0    stevel 		/* implement any post op delay. */
   1752      0    stevel 		if (key == PPMDC_FET_ON) {
   1753   6464   mh27603 			PPM_GET_IO_POST_DELAY(dc, delay);
   1754      0    stevel 			PPMD(D_FET, ("%s : waiting %lu micro seconds "
   1755     68  zx151605 			    "after on\n", domp->name, delay))
   1756      0    stevel 			if (delay > 0)
   1757      0    stevel 				drv_usecwait(delay);
   1758      0    stevel 		}
   1759      0    stevel 	}
   1760      0    stevel 
   1761      0    stevel 	return (ret);
   1762      0    stevel }
   1763      0    stevel 
   1764      0    stevel 
   1765      0    stevel /*
   1766      0    stevel  * read power fet status
   1767      0    stevel  */
   1768      0    stevel static int
   1769      0    stevel ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
   1770      0    stevel {
   1771      0    stevel 	char	*str = "ppm_fetget";
   1772      0    stevel 	ppm_dc_t *dc = domp->dc;
   1773      0    stevel 	uint_t	kio_val;
   1774      0    stevel 	int	off_val;
   1775      0    stevel 	int	ret;
   1776      0    stevel 
   1777      0    stevel 	if (!dc->lh) {
   1778      0    stevel 		PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
   1779      0    stevel 		    str, domp->name))
   1780      0    stevel 		return (DDI_FAILURE);
   1781      0    stevel 	}
   1782      0    stevel 	if (!dc->next) {
   1783      0    stevel 		cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
   1784      0    stevel 		    "defined, found only one in domain(%s)", str, domp->name);
   1785      0    stevel 		return (DDI_FAILURE);
   1786      0    stevel 	}
   1787      0    stevel 
   1788      0    stevel 	switch (dc->method) {
   1789   5295    randyf #ifdef sun4u
   1790   4667   mh27603 	case PPMDC_I2CKIO: {
   1791   4667   mh27603 		i2c_gpio_t i2c_req;
   1792      0    stevel 		i2c_req.reg_mask = dc->m_un.i2c.mask;
   1793      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
   1794      0    stevel 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
   1795      0    stevel 
   1796      0    stevel 		if (ret) {
   1797      0    stevel 			PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
   1798      0    stevel 			    str, ret))
   1799      0    stevel 			return (ret);
   1800      0    stevel 		}
   1801      0    stevel 
   1802      0    stevel 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
   1803      0    stevel 		    dc->next->m_un.i2c.val;
   1804      0    stevel 		*lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
   1805      0    stevel 
   1806      0    stevel 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
   1807      0    stevel 		    (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
   1808      0    stevel 
   1809      0    stevel 		break;
   1810   4667   mh27603 	}
   1811   4667   mh27603 #endif
   1812      0    stevel 
   1813      0    stevel 	case PPMDC_KIO:
   1814      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
   1815      0    stevel 		    (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
   1816      0    stevel 		if (ret) {
   1817      0    stevel 			PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
   1818      0    stevel 			    str, ret))
   1819      0    stevel 			return (ret);
   1820      0    stevel 		}
   1821      0    stevel 
   1822      0    stevel 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
   1823   5295    randyf 		    dc->next->m_un.kio.val;
   1824      0    stevel 		*lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
   1825      0    stevel 
   1826      0    stevel 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
   1827      0    stevel 		    (kio_val == off_val) ? "OFF" : "ON"))
   1828      0    stevel 
   1829      0    stevel 		break;
   1830      0    stevel 
   1831      0    stevel 	default:
   1832      0    stevel 		PPMD(D_FET, ("%s: unsupported domain control method %d\n",
   1833      0    stevel 		    str, domp->dc->method))
   1834      0    stevel 		return (DDI_FAILURE);
   1835      0    stevel 	}
   1836      0    stevel 
   1837      0    stevel 	return (DDI_SUCCESS);
   1838      0    stevel }
   1839      0    stevel 
   1840      0    stevel 
   1841      0    stevel /*
   1842      0    stevel  * the actual code that switches pci clock and update domain status
   1843      0    stevel  */
   1844      0    stevel static int
   1845      0    stevel ppm_switch_clock(ppm_domain_t *domp, int onoff)
   1846      0    stevel {
   1847      0    stevel #ifdef DEBUG
   1848      0    stevel 	char *str = "ppm_switch_clock";
   1849      0    stevel #endif
   1850      0    stevel 	int	cmd, pio_save;
   1851      0    stevel 	ppm_dc_t *dc;
   1852      0    stevel 	int ret;
   1853      0    stevel 	extern int do_polled_io;
   1854      0    stevel 	extern uint_t cfb_inuse;
   1855      0    stevel 	ppm_dev_t	*pdev;
   1856      0    stevel 
   1857      0    stevel 	cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
   1858      0    stevel 	dc = ppm_lookup_dc(domp, cmd);
   1859      0    stevel 	if (!dc) {
   1860      0    stevel 		PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
   1861      0    stevel 		    str, domp->name))
   1862      0    stevel 		return (DDI_FAILURE);
   1863      0    stevel 	}
   1864      0    stevel 
   1865      0    stevel 	switch (dc->method) {
   1866      0    stevel 	case PPMDC_KIO:
   1867      0    stevel 		/*
   1868      0    stevel 		 * If we're powering up cfb on a Stop-A, we only
   1869      0    stevel 		 * want to do polled i/o to turn ON the clock
   1870      0    stevel 		 */
   1871      0    stevel 		pio_save = do_polled_io;
   1872      0    stevel 		if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
   1873      0    stevel 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
   1874      0    stevel 				if (pm_is_cfb(pdev->dip)) {
   1875      0    stevel 					do_polled_io = 1;
   1876      0    stevel 					break;
   1877      0    stevel 				}
   1878      0    stevel 			}
   1879      0    stevel 		}
   1880      0    stevel 
   1881      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   1882      0    stevel 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
   1883      0    stevel 		    kcred, NULL);
   1884      0    stevel 
   1885      0    stevel 		do_polled_io = pio_save;
   1886      0    stevel 
   1887      0    stevel 		if (ret == 0) {
   1888      0    stevel 			if (cmd == PPMDC_CLK_ON) {
   1889      0    stevel 				domp->status = PPMD_ON;
   1890      0    stevel 
   1891      0    stevel 				/*
   1892      0    stevel 				 * PCI PM spec requires 50ms delay
   1893      0    stevel 				 */
   1894      0    stevel 				drv_usecwait(50000);
   1895      0    stevel 			} else
   1896      0    stevel 				domp->status = PPMD_OFF;
   1897      0    stevel 		}
   1898      0    stevel 
   1899      0    stevel 		PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
   1900      0    stevel 		    (ret == 0) ? "turned" : "failed to turn",
   1901      0    stevel 		    (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
   1902      0    stevel 		    domp->name))
   1903      0    stevel 
   1904      0    stevel 		break;
   1905      0    stevel 
   1906      0    stevel 	default:
   1907      0    stevel 		PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
   1908      0    stevel 		    str, dc->method))
   1909      0    stevel 		return (DDI_FAILURE);
   1910      0    stevel 	}
   1911      0    stevel 
   1912      0    stevel 	return (DDI_SUCCESS);
   1913      0    stevel }
   1914      0    stevel 
   1915      0    stevel 
   1916      0    stevel /*
   1917      0    stevel  * pci slot domain is formed of pci device(s) reside in a pci slot.
   1918      0    stevel  * This function monitors domain device's power level change, such
   1919      0    stevel  * that,
   1920      0    stevel  *   when all domain power count has gone to 0, it attempts to turn off
   1921      0    stevel  *        the pci slot's clock;
   1922      0    stevel  *   if any domain device is powering up, it'll turn on the pci slot's
   1923      0    stevel  *        clock as the first thing.
   1924      0    stevel  */
   1925      0    stevel /* ARGUSED */
   1926      0    stevel static int
   1927      0    stevel ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
   1928      0    stevel {
   1929      0    stevel #ifdef DEBUG
   1930      0    stevel 	char *str = "ppm_manage_pci";
   1931      0    stevel #endif
   1932      0    stevel 	int (*pwr_func)(ppm_dev_t *, int, int);
   1933      0    stevel 	int old, new, cmpt;
   1934      0    stevel 	ppm_dev_t *ppmd;
   1935      0    stevel 	ppm_domain_t *domp;
   1936      0    stevel 	int incr = 0;
   1937      0    stevel 	int dummy_ret;
   1938      0    stevel 
   1939      0    stevel 	*result = DDI_SUCCESS;
   1940      0    stevel 	switch (reqp->request_type) {
   1941      0    stevel 	case PMR_PPM_SET_POWER:
   1942      0    stevel 		pwr_func = ppm_change_power_level;
   1943      0    stevel 		old = reqp->req.ppm_set_power_req.old_level;
   1944      0    stevel 		new = reqp->req.ppm_set_power_req.new_level;
   1945      0    stevel 		cmpt = reqp->req.ppm_set_power_req.cmpt;
   1946      0    stevel 		break;
   1947      0    stevel 
   1948      0    stevel 	case PMR_PPM_POWER_CHANGE_NOTIFY:
   1949      0    stevel 		pwr_func = ppm_record_level_change;
   1950      0    stevel 		old = reqp->req.ppm_notify_level_req.old_level;
   1951      0    stevel 		new = reqp->req.ppm_notify_level_req.new_level;
   1952      0    stevel 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
   1953      0    stevel 		break;
   1954      0    stevel 
   1955      0    stevel 	default:
   1956      0    stevel 		*result = DDI_FAILURE;
   1957      0    stevel 		return (DDI_FAILURE);
   1958      0    stevel 	}
   1959      0    stevel 
   1960      0    stevel 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
   1961      0    stevel 		if (cmpt == ppmd->cmpt)
   1962      0    stevel 			break;
   1963      0    stevel 	if (!ppmd) {
   1964      0    stevel 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
   1965      0    stevel 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
   1966      0    stevel 		*result = DDI_FAILURE;
   1967      0    stevel 		return (DDI_FAILURE);
   1968      0    stevel 	}
   1969      0    stevel 	domp = ppmd->domp;
   1970      0    stevel 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
   1971      0    stevel 	    ppm_get_ctlstr(reqp->request_type, ~0),
   1972      0    stevel 	    ppmd->path, cmpt, old, new))
   1973      0    stevel 
   1974      0    stevel 	ASSERT(old == ppmd->level);
   1975      0    stevel 	if (new == ppmd->level)
   1976      0    stevel 		return (DDI_SUCCESS);
   1977      0    stevel 
   1978      0    stevel 	PPM_LOCK_DOMAIN(domp);
   1979      0    stevel 
   1980      0    stevel 	if (new > 0) {		/* device powering up */
   1981      0    stevel 		if (domp->status == PPMD_OFF) {
   1982      0    stevel 
   1983      0    stevel 			/* cannot be off during (chpt, resume) window */
   1984      0    stevel 			ASSERT(ppm_cpr_window_flag == B_FALSE);
   1985      0    stevel 
   1986      0    stevel 			/* either both OFF or both ON */
   1987      0    stevel 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
   1988      0    stevel 
   1989      0    stevel 			PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
   1990      0    stevel 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
   1991      0    stevel 
   1992      0    stevel 			*result = ppm_switch_clock(domp, PPMD_ON);
   1993      0    stevel 			if (*result != DDI_SUCCESS) {
   1994      0    stevel 				PPMD(D_PCI, ("\tcan't switch on pci clock: "
   1995      0    stevel 				    "ret(%d)\n", *result))
   1996      0    stevel 				PPM_UNLOCK_DOMAIN(domp);
   1997      0    stevel 				return (DDI_FAILURE);
   1998      0    stevel 			}
   1999      0    stevel 		}
   2000      0    stevel 
   2001      0    stevel 		if (old == 0) {
   2002      0    stevel 			domp->pwr_cnt++;
   2003      0    stevel 			incr = 1;
   2004      0    stevel 		}
   2005      0    stevel 
   2006      0    stevel 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
   2007      0    stevel 		    domp->name, domp->pwr_cnt))
   2008      0    stevel 	}
   2009      0    stevel 
   2010      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   2011      0    stevel 
   2012      0    stevel 	ASSERT(domp->pwr_cnt > 0);
   2013      0    stevel 
   2014      0    stevel 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
   2015      0    stevel 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
   2016      0    stevel 		    ppmd->path, *result))
   2017      0    stevel 	}
   2018      0    stevel 
   2019      0    stevel 	PPM_LOCK_DOMAIN(domp);
   2020      0    stevel 
   2021      0    stevel 	/*
   2022      0    stevel 	 * Decr the power count in two cases:
   2023      0    stevel 	 *
   2024      0    stevel 	 *   1) request was to power device down and was successful
   2025      0    stevel 	 *   2) request was to power up (we pre-incremented count), but failed.
   2026      0    stevel 	 */
   2027      0    stevel 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
   2028      0    stevel 	    (*result != DDI_SUCCESS && incr)) {
   2029      0    stevel 		ASSERT(domp->pwr_cnt > 0);
   2030      0    stevel 		domp->pwr_cnt--;
   2031      0    stevel 	}
   2032      0    stevel 
   2033      0    stevel 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
   2034      0    stevel 	    domp->name, domp->pwr_cnt))
   2035      0    stevel 
   2036      0    stevel 	/*
   2037      0    stevel 	 * call to pwr_func will update ppm data structures, if it
   2038      0    stevel 	 * succeeds. ppm should return whatever is the return value
   2039      0    stevel 	 * from call to pwr_func. This way pm and ppm data structures
   2040      0    stevel 	 * always in sync. Use dummy_ret from here for any further
   2041      0    stevel 	 * return values.
   2042      0    stevel 	 */
   2043      0    stevel 	if ((domp->pwr_cnt == 0) &&
   2044      0    stevel 	    (ppm_cpr_window_flag == B_FALSE) &&
   2045      0    stevel 	    ppm_none_else_holds_power(domp)) {
   2046      0    stevel 
   2047      0    stevel 		PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
   2048      0    stevel 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
   2049      0    stevel 
   2050      0    stevel 		dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
   2051      0    stevel 		if (dummy_ret != DDI_SUCCESS) {
   2052      0    stevel 			PPMD(D_PCI, ("\tCan't switch clock off: "
   2053      0    stevel 			    "ret(%d)\n", dummy_ret))
   2054      0    stevel 		}
   2055      0    stevel 	}
   2056      0    stevel 
   2057      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   2058      0    stevel 	ASSERT(domp->pwr_cnt >= 0);
   2059      0    stevel 	return (*result);
   2060      0    stevel }
   2061      0    stevel 
   2062      0    stevel /*
   2063      0    stevel  * When the driver for the primary PCI-Express child has set the device to
   2064      0    stevel  * lowest power (D3hot), we come here to save even more power by transitioning
   2065      0    stevel  * the slot to D3cold.  Similarly, if the slot is in D3cold and we need to
   2066      0    stevel  * power up the child, we come here first to power up the slot.
   2067      0    stevel  */
   2068      0    stevel /* ARGUSED */
   2069      0    stevel static int
   2070      0    stevel ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
   2071      0    stevel {
   2072      0    stevel #ifdef DEBUG
   2073      0    stevel 	char *str = "ppm_manage_pcie";
   2074      0    stevel #endif
   2075      0    stevel 	int (*pwr_func)(ppm_dev_t *, int, int);
   2076      0    stevel 	int old, new, cmpt;
   2077      0    stevel 	ppm_dev_t *ppmd;
   2078      0    stevel 	ppm_domain_t *domp;
   2079      0    stevel 	int incr = 0;
   2080      0    stevel 	int dummy_ret;
   2081      0    stevel 
   2082      0    stevel 	*result = DDI_SUCCESS;
   2083      0    stevel 	switch (reqp->request_type) {
   2084      0    stevel 	case PMR_PPM_SET_POWER:
   2085      0    stevel 		pwr_func = ppm_change_power_level;
   2086      0    stevel 		old = reqp->req.ppm_set_power_req.old_level;
   2087      0    stevel 		new = reqp->req.ppm_set_power_req.new_level;
   2088      0    stevel 		cmpt = reqp->req.ppm_set_power_req.cmpt;
   2089      0    stevel 		break;
   2090      0    stevel 
   2091      0    stevel 	case PMR_PPM_POWER_CHANGE_NOTIFY:
   2092      0    stevel 		pwr_func = ppm_record_level_change;
   2093      0    stevel 		old = reqp->req.ppm_notify_level_req.old_level;
   2094      0    stevel 		new = reqp->req.ppm_notify_level_req.new_level;
   2095      0    stevel 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
   2096      0    stevel 		break;
   2097      0    stevel 
   2098      0    stevel 	default:
   2099      0    stevel 		*result = DDI_FAILURE;
   2100      0    stevel 		return (DDI_FAILURE);
   2101      0    stevel 	}
   2102      0    stevel 
   2103      0    stevel 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
   2104      0    stevel 		if (cmpt == ppmd->cmpt)
   2105      0    stevel 			break;
   2106      0    stevel 	if (!ppmd) {
   2107      0    stevel 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
   2108      0    stevel 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
   2109      0    stevel 		*result = DDI_FAILURE;
   2110      0    stevel 		return (DDI_FAILURE);
   2111      0    stevel 	}
   2112      0    stevel 	domp = ppmd->domp;
   2113      0    stevel 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
   2114      0    stevel 	    ppm_get_ctlstr(reqp->request_type, ~0),
   2115      0    stevel 	    ppmd->path, cmpt, old, new))
   2116      0    stevel 
   2117      0    stevel 	ASSERT(old == ppmd->level);
   2118      0    stevel 	if (new == ppmd->level)
   2119      0    stevel 		return (DDI_SUCCESS);
   2120      0    stevel 
   2121      0    stevel 	PPM_LOCK_DOMAIN(domp);
   2122      0    stevel 
   2123      0    stevel 	if (new > 0) {		/* device powering up */
   2124      0    stevel 		if (domp->status == PPMD_OFF) {
   2125      0    stevel 
   2126      0    stevel 			/* cannot be off during (chpt, resume) window */
   2127      0    stevel 			ASSERT(ppm_cpr_window_flag == B_FALSE);
   2128      0    stevel 
   2129      0    stevel 			/* either both OFF or both ON */
   2130      0    stevel 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
   2131      0    stevel 
   2132      0    stevel 			PPMD(D_PCI, ("About to turn on pcie slot for "
   2133      0    stevel 			    "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
   2134      0    stevel 
   2135      0    stevel 			*result = ppm_pcie_pwr(domp, PPMD_ON);
   2136      0    stevel 			if (*result != DDI_SUCCESS) {
   2137      0    stevel 				PPMD(D_PCI, ("\tcan't switch on pcie slot: "
   2138      0    stevel 				    "ret(%d)\n", *result))
   2139      0    stevel 				PPM_UNLOCK_DOMAIN(domp);
   2140      0    stevel 				return (DDI_FAILURE);
   2141      0    stevel 			}
   2142      0    stevel 		}
   2143      0    stevel 
   2144      0    stevel 		if (old == 0) {
   2145      0    stevel 			domp->pwr_cnt++;
   2146      0    stevel 			incr = 1;
   2147      0    stevel 		}
   2148      0    stevel 
   2149      0    stevel 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
   2150      0    stevel 		    domp->name, domp->pwr_cnt))
   2151      0    stevel 	}
   2152      0    stevel 
   2153      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   2154      0    stevel 
   2155      0    stevel 	ASSERT(domp->pwr_cnt > 0);
   2156      0    stevel 
   2157      0    stevel 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
   2158      0    stevel 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
   2159      0    stevel 		    ppmd->path, *result))
   2160      0    stevel 	}
   2161      0    stevel 
   2162      0    stevel 	PPM_LOCK_DOMAIN(domp);
   2163      0    stevel 
   2164      0    stevel 	/*
   2165      0    stevel 	 * Decr the power count in two cases:
   2166      0    stevel 	 *
   2167      0    stevel 	 *   1) request was to power device down and was successful
   2168      0    stevel 	 *   2) request was to power up (we pre-incremented count), but failed.
   2169      0    stevel 	 */
   2170      0    stevel 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
   2171      0    stevel 	    (*result != DDI_SUCCESS && incr)) {
   2172      0    stevel 		ASSERT(domp->pwr_cnt > 0);
   2173      0    stevel 		domp->pwr_cnt--;
   2174      0    stevel 	}
   2175      0    stevel 
   2176      0    stevel 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
   2177      0    stevel 	    domp->name, domp->pwr_cnt))
   2178      0    stevel 
   2179      0    stevel 	/*
   2180      0    stevel 	 * call to pwr_func will update ppm data structures, if it
   2181      0    stevel 	 * succeeds. ppm should return whatever is the return value
   2182      0    stevel 	 * from call to pwr_func. This way pm and ppm data structures
   2183      0    stevel 	 * always in sync. Use dummy_ret from here for any further
   2184      0    stevel 	 * return values.
   2185      0    stevel 	 */
   2186      0    stevel 	if ((domp->pwr_cnt == 0) &&
   2187      0    stevel 	    (ppm_cpr_window_flag == B_FALSE) &&
   2188      0    stevel 	    ppm_none_else_holds_power(domp)) {
   2189      0    stevel 
   2190      0    stevel 		PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
   2191      0    stevel 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
   2192      0    stevel 
   2193      0    stevel 		dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
   2194      0    stevel 		if (dummy_ret != DDI_SUCCESS) {
   2195      0    stevel 			PPMD(D_PCI, ("\tCan't switch pcie slot off: "
   2196      0    stevel 			    "ret(%d)\n", dummy_ret))
   2197      0    stevel 		}
   2198      0    stevel 	}
   2199      0    stevel 
   2200      0    stevel 	PPM_UNLOCK_DOMAIN(domp);
   2201      0    stevel 	ASSERT(domp->pwr_cnt >= 0);
   2202      0    stevel 	return (*result);
   2203      0    stevel 
   2204      0    stevel }
   2205      0    stevel 
   2206      0    stevel /*
   2207      0    stevel  * Set or clear a bit on a GPIO device.  These bits are used for various device-
   2208      0    stevel  * specific purposes.
   2209      0    stevel  */
   2210      0    stevel static int
   2211     68  zx151605 ppm_gpioset(ppm_domain_t *domp, int key)
   2212      0    stevel {
   2213      0    stevel #ifdef DEBUG
   2214      0    stevel 	char	*str = "ppm_gpioset";
   2215      0    stevel #endif
   2216      0    stevel 	ppm_dc_t *dc;
   2217   4667   mh27603 	int	ret;
   2218      0    stevel 	clock_t delay = 0;
   2219      0    stevel 
   2220      0    stevel 	for (dc = domp->dc; dc; dc = dc->next)
   2221      0    stevel 		if (dc->cmd == key)
   2222      0    stevel 			break;
   2223      0    stevel 	if (!dc || !dc->lh) {
   2224      0    stevel 		PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
   2225     68  zx151605 		    str, domp->name))
   2226      0    stevel 		return (DDI_FAILURE);
   2227      0    stevel 	}
   2228      0    stevel 
   2229   4667   mh27603 	PPM_GET_IO_DELAY(dc, delay);
   2230      0    stevel 	if (delay > 0) {
   2231      0    stevel 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
   2232      0    stevel 		    "before change\n", domp->name, delay))
   2233      0    stevel 		drv_usecwait(delay);
   2234      0    stevel 	}
   2235      0    stevel 
   2236      0    stevel 	switch (dc->method) {
   2237   5295    randyf #ifdef sun4u
   2238   4667   mh27603 	case PPMDC_I2CKIO: {
   2239   4667   mh27603 		i2c_gpio_t i2c_req;
   2240   4667   mh27603 		ppm_dev_t *pdev;
   2241   4667   mh27603 		int pio_save;
   2242   4667   mh27603 		extern int do_polled_io;
   2243   4667   mh27603 		extern uint_t cfb_inuse;
   2244      0    stevel 		i2c_req.reg_mask = dc->m_un.i2c.mask;
   2245      0    stevel 		i2c_req.reg_val = dc->m_un.i2c.val;
   2246   1147      jchu 
   2247   1147      jchu 		pio_save = do_polled_io;
   2248   1147      jchu 		if (cfb_inuse) {
   2249   1147      jchu 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
   2250   1147      jchu 				if (pm_is_cfb(pdev->dip)) {
   2251   1147      jchu 					do_polled_io = 1;
   2252   1147      jchu 					PPMD(D_GPIO, ("%s: cfb is in use, "
   2253   1147      jchu 					    "i2c transaction is done in "
   2254   1147      jchu 					    "poll-mode.\n", str))
   2255   1147      jchu 					break;
   2256   1147      jchu 				}
   2257   1147      jchu 			}
   2258   1147      jchu 		}
   2259      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
   2260      0    stevel 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
   2261   1147      jchu 		do_polled_io = pio_save;
   2262     68  zx151605 
   2263     68  zx151605 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
   2264     68  zx151605 		    "to gpio\n",
   2265     68  zx151605 		    str, (ret == 0) ? "turned" : "FAILed to turn",
   2266     68  zx151605 		    domp->name,
   2267     68  zx151605 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
   2268     68  zx151605 		    dc->m_un.i2c.val))
   2269     68  zx151605 
   2270      0    stevel 		break;
   2271   4667   mh27603 	}
   2272   4667   mh27603 #endif
   2273   5295    randyf 
   2274      0    stevel 	case PPMDC_KIO:
   2275      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   2276      0    stevel 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
   2277      0    stevel 		    NULL);
   2278     68  zx151605 
   2279     68  zx151605 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
   2280     68  zx151605 		    "to gpio\n",
   2281     68  zx151605 		    str, (ret == 0) ? "turned" : "FAILed to turn",
   2282     68  zx151605 		    domp->name,
   2283     68  zx151605 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
   2284     68  zx151605 		    dc->m_un.kio.val))
   2285     68  zx151605 
   2286      0    stevel 		break;
   2287      0    stevel 
   2288      0    stevel 	default:
   2289      0    stevel 		PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
   2290      0    stevel 		    str, domp->dc->method))
   2291      0    stevel 		return (DDI_FAILURE);
   2292      0    stevel 	}
   2293      0    stevel 
   2294     68  zx151605 	/* implement any post op delay. */
   2295   6464   mh27603 	PPM_GET_IO_POST_DELAY(dc, delay);
   2296     68  zx151605 	if (delay > 0) {
   2297     68  zx151605 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
   2298     68  zx151605 		    "after change\n", domp->name, delay))
   2299     68  zx151605 		drv_usecwait(delay);
   2300      0    stevel 	}
   2301      0    stevel 
   2302      0    stevel 	return (ret);
   2303      0    stevel }
   2304      0    stevel 
   2305      0    stevel static int
   2306      0    stevel ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
   2307      0    stevel {
   2308      0    stevel #ifdef DEBUG
   2309      0    stevel 	char *str = "ppm_pcie_pwr";
   2310      0    stevel #endif
   2311      0    stevel 	int ret = DDI_FAILURE;
   2312      0    stevel 	ppm_dc_t *dc;
   2313      0    stevel 	clock_t delay;
   2314      0    stevel 
   2315      0    stevel 	ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
   2316      0    stevel 
   2317      0    stevel 	dc = ppm_lookup_dc(domp,
   2318      0    stevel 	    onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
   2319      0    stevel 	if (dc) {
   2320      0    stevel 
   2321      0    stevel 		/*
   2322      0    stevel 		 * Invoke layered ioctl for pcie root complex nexus to
   2323      0    stevel 		 * transition the link
   2324      0    stevel 		 */
   2325      0    stevel 		ASSERT(dc->method == PPMDC_KIO);
   2326      0    stevel 		delay = dc->m_un.kio.delay;
   2327      0    stevel 		if (delay > 0) {
   2328      0    stevel 			PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
   2329      0    stevel 			    "before change\n", domp->name, delay))
   2330      0    stevel 			drv_usecwait(delay);
   2331      0    stevel 		}
   2332      0    stevel 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   2333      0    stevel 		    (intptr_t)&(dc->m_un.kio.val),
   2334      0    stevel 		    FWRITE | FKIOCTL, kcred, NULL);
   2335      0    stevel 		if (ret == DDI_SUCCESS) {
   2336      0    stevel 			delay = dc->m_un.kio.post_delay;
   2337      0    stevel 			if (delay > 0) {
   2338      0    stevel 				PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
   2339      0    stevel 				    "after change\n", domp->name, delay))
   2340      0    stevel 				drv_usecwait(delay);
   2341      0    stevel 			}
   2342     68  zx151605 		} else {
   2343     68  zx151605 			PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
   2344     68  zx151605 			    str, domp->name))
   2345      0    stevel 			return (ret);
   2346     68  zx151605 		}
   2347      0    stevel 	}
   2348      0    stevel 
   2349      0    stevel 	switch (onoff) {
   2350      0    stevel 	case PPMD_OFF:
   2351      0    stevel 		/* Turn off the clock for this slot. */
   2352     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
   2353      0    stevel 			PPMD(D_GPIO,
   2354      0    stevel 			    ("%s: failed to turn off domain(%s) clock\n",
   2355     68  zx151605 			    str, domp->name))
   2356      0    stevel 			return (ret);
   2357      0    stevel 		}
   2358      0    stevel 
   2359      0    stevel 		/* Turn off the power to this slot */
   2360     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
   2361     68  zx151605 			PPMD(D_GPIO,
   2362     68  zx151605 			    ("%s: failed to turn off domain(%s) power\n",
   2363     68  zx151605 			    str, domp->name))
   2364     68  zx151605 			return (ret);
   2365      0    stevel 		}
   2366      0    stevel 		break;
   2367      0    stevel 	case PPMD_ON:
   2368      0    stevel 		/* Assert RESET for this slot. */
   2369     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
   2370      0    stevel 			PPMD(D_GPIO,
   2371      0    stevel 			    ("%s: failed to assert reset for domain(%s)\n",
   2372     68  zx151605 			    str, domp->name))
   2373      0    stevel 			return (ret);
   2374      0    stevel 		}
   2375      0    stevel 
   2376      0    stevel 		/* Turn on the power to this slot */
   2377     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
   2378      0    stevel 			PPMD(D_GPIO,
   2379     68  zx151605 			    ("%s: failed to turn on domain(%s) power\n",
   2380     68  zx151605 			    str, domp->name))
   2381      0    stevel 			return (ret);
   2382      0    stevel 		}
   2383      0    stevel 
   2384      0    stevel 		/* Turn on the clock for this slot */
   2385     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
   2386      0    stevel 			PPMD(D_GPIO,
   2387     68  zx151605 			    ("%s: failed to turn on domain(%s) clock\n",
   2388     68  zx151605 			    str, domp->name))
   2389      0    stevel 			return (ret);
   2390      0    stevel 		}
   2391      0    stevel 
   2392      0    stevel 		/* De-assert RESET for this slot. */
   2393     68  zx151605 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
   2394      0    stevel 			PPMD(D_GPIO,
   2395      0    stevel 			    ("%s: failed to de-assert reset for domain(%s)\n",
   2396     68  zx151605 			    str, domp->name))
   2397      0    stevel 			return (ret);
   2398      0    stevel 		}
   2399      0    stevel 
   2400      0    stevel 		dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
   2401      0    stevel 		if (dc) {
   2402      0    stevel 			/*
   2403      0    stevel 			 * Invoke layered ioctl to PCIe root complex nexus
   2404      0    stevel 			 * to transition the link.
   2405      0    stevel 			 */
   2406      0    stevel 			ASSERT(dc->method == PPMDC_KIO);
   2407      0    stevel 			delay = dc->m_un.kio.delay;
   2408      0    stevel 			if (delay > 0) {
   2409      0    stevel 				PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
   2410      0    stevel 				    "before change\n", domp->name, delay))
   2411      0    stevel 				drv_usecwait(delay);
   2412      0    stevel 			}
   2413      0    stevel 			ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   2414      0    stevel 			    (intptr_t)&(dc->m_un.kio.val),
   2415      0    stevel 			    FWRITE | FKIOCTL, kcred, NULL);
   2416     68  zx151605 
   2417     68  zx151605 			if (ret != DDI_SUCCESS) {
   2418     68  zx151605 				PPMD(D_PCI, ("%s: layered ioctl to PCIe"
   2419     68  zx151605 				    "root complex nexus FAILed\n", str))
   2420     68  zx151605 				return (ret);
   2421     68  zx151605 			}
   2422     68  zx151605 
   2423     68  zx151605 			delay = dc->m_un.kio.post_delay;
   2424     68  zx151605 			if (delay > 0) {
   2425     68  zx151605 				PPMD(D_GPIO, ("%s: waiting %lu micro "
   2426     68  zx151605 				    "seconds after change\n",
   2427     68  zx151605 				    domp->name, delay))
   2428    686    osaeed 				drv_usecwait(delay);
   2429      0    stevel 			}
   2430      0    stevel 		}
   2431      0    stevel 		break;
   2432      0    stevel 	default:
   2433      0    stevel 		ASSERT(0);
   2434      0    stevel 	}
   2435      0    stevel 
   2436     68  zx151605 	PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
   2437     68  zx151605 	    str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
   2438     68  zx151605 	    onoff == PPMD_ON ? "ON" : "OFF"))
   2439     68  zx151605 
   2440     68  zx151605 	domp->status = onoff;
   2441      0    stevel 	return (ret);
   2442      0    stevel }
   2443      0    stevel 
   2444      0    stevel 
   2445      0    stevel /*
   2446      0    stevel  * Change the power level for a component of a device.  If the change
   2447      0    stevel  * arg is true, we call the framework to actually change the device's
   2448      0    stevel  * power; otherwise, we just update our own copy of the power level.
   2449      0    stevel  */
   2450      0    stevel static int
   2451      0    stevel ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
   2452      0    stevel {
   2453      0    stevel #ifdef DEBUG
   2454      0    stevel 	char *str = "ppm_set_level";
   2455      0    stevel #endif
   2456      0    stevel 	int ret;
   2457      0    stevel 
   2458      0    stevel 	ret = DDI_SUCCESS;
   2459      0    stevel 	if (change)
   2460      0    stevel 		ret = pm_power(ppmd->dip, cmpt, level);
   2461      0    stevel 
   2462      0    stevel 	PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
   2463      0    stevel 	    str, ppmd->path, change, ppmd->level, level, ret))
   2464      0    stevel 
   2465      0    stevel 	if (ret == DDI_SUCCESS) {
   2466      0    stevel 		ppmd->level = level;
   2467      0    stevel 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
   2468      0    stevel 	}
   2469      0    stevel 
   2470      0    stevel 	return (ret);
   2471      0    stevel }
   2472      0    stevel 
   2473      0    stevel 
   2474      0    stevel static int
   2475      0    stevel ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
   2476      0    stevel {
   2477      0    stevel 	return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
   2478      0    stevel }
   2479      0    stevel 
   2480      0    stevel 
   2481      0    stevel static int
   2482      0    stevel ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
   2483      0    stevel {
   2484      0    stevel 	return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
   2485      0    stevel }
   2486      0    stevel 
   2487      0    stevel 
   2488      0    stevel static void
   2489      0    stevel ppm_manage_led(int action)
   2490      0    stevel {
   2491      0    stevel 	ppm_domain_t *domp;
   2492      0    stevel 	ppm_unit_t *unitp;
   2493      0    stevel 	timeout_id_t	tid;
   2494      0    stevel 
   2495      0    stevel 
   2496      0    stevel 	PPMD(D_LED, ("ppm_manage_led: action: %s\n",
   2497      0    stevel 	    (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
   2498      0    stevel 	    "PPM_LED_SOLIDON"))
   2499      0    stevel 
   2500      0    stevel 	/*
   2501      0    stevel 	 * test whether led operation is practically supported,
   2502      0    stevel 	 * if not, we waive without pressing for reasons
   2503      0    stevel 	 */
   2504      0    stevel 	if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
   2505      0    stevel 		return;
   2506      0    stevel 
   2507      0    stevel 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
   2508      0    stevel 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
   2509      0    stevel 		domp = domp->next;
   2510      0    stevel 
   2511      0    stevel 	mutex_enter(&unitp->lock);
   2512      0    stevel 	if (action == PPM_LED_BLINKING) {
   2513      0    stevel 		ppm_set_led(domp, PPMD_OFF);
   2514      0    stevel 		unitp->led_tid = timeout(
   2515      0    stevel 		    ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
   2516      0    stevel 
   2517      0    stevel 	} else {	/* PPM_LED_SOLIDON */
   2518      0    stevel 		ASSERT(action == PPM_LED_SOLIDON);
   2519      0    stevel 		tid = unitp->led_tid;
   2520      0    stevel 		unitp->led_tid = 0;
   2521      0    stevel 
   2522      0    stevel 		mutex_exit(&unitp->lock);
   2523      0    stevel 		(void) untimeout(tid);
   2524      0    stevel 
   2525      0    stevel 		mutex_enter(&unitp->lock);
   2526      0    stevel 		ppm_set_led(domp, PPMD_ON);
   2527      0    stevel 	}
   2528      0    stevel 	mutex_exit(&unitp->lock);
   2529      0    stevel }
   2530      0    stevel 
   2531      0    stevel 
   2532      0    stevel static void
   2533      0    stevel ppm_set_led(ppm_domain_t *domp, int val)
   2534      0    stevel {
   2535     68  zx151605 	int ret;
   2536     68  zx151605 
   2537     68  zx151605 	ret = ppm_gpioset(domp,
   2538     68  zx151605 	    (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
   2539     68  zx151605 
   2540     68  zx151605 	PPMD(D_LED, ("ppm_set_led:  %s LED from %s\n",
   2541     68  zx151605 	    (ret == 0) ? "turned" : "FAILed to turn",
   2542     68  zx151605 	    (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
   2543     68  zx151605 
   2544     68  zx151605 	if (ret == DDI_SUCCESS)
   2545     68  zx151605 		domp->status = val;
   2546      0    stevel }
   2547      0    stevel 
   2548      0    stevel 
   2549      0    stevel static void
   2550      0    stevel ppm_blink_led(void *arg)
   2551      0    stevel {
   2552      0    stevel 	ppm_unit_t *unitp;
   2553      0    stevel 	clock_t intvl;
   2554      0    stevel 	ppm_domain_t *domp = arg;
   2555      0    stevel 
   2556      0    stevel 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
   2557      0    stevel 
   2558      0    stevel 	mutex_enter(&unitp->lock);
   2559      0    stevel 	if (unitp->led_tid == 0) {
   2560      0    stevel 		mutex_exit(&unitp->lock);
   2561      0    stevel 		return;
   2562      0    stevel 	}
   2563      0    stevel 
   2564      0    stevel 	if (domp->status == PPMD_ON) {
   2565      0    stevel 		ppm_set_led(domp, PPMD_OFF);
   2566      0    stevel 		intvl = PPM_LEDOFF_INTERVAL;
   2567      0    stevel 	} else {
   2568      0    stevel 		ppm_set_led(domp, PPMD_ON);
   2569      0    stevel 		intvl = PPM_LEDON_INTERVAL;
   2570      0    stevel 	}
   2571      0    stevel 
   2572      0    stevel 	unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
   2573      0    stevel 	mutex_exit(&unitp->lock);
   2574      0    stevel }
   2575      0    stevel 
   2576      0    stevel /*
   2577      0    stevel  * Function to power up a domain, if required. It also increments the
   2578      0    stevel  * domain pwr_cnt to prevent it from going down.
   2579      0    stevel  */
   2580      0    stevel static int
   2581      0    stevel ppm_power_up_domain(dev_info_t *dip)
   2582      0    stevel {
   2583      0    stevel 	int		ret = DDI_SUCCESS;
   2584      0    stevel 	ppm_domain_t	*domp;
   2585      0    stevel 	char		*str = "ppm_power_up_domain";
   2586      0    stevel 
   2587      0    stevel 	domp = ppm_lookup_dev(dip);
   2588      0    stevel 	ASSERT(domp);
   2589      0    stevel 	mutex_enter(&domp->lock);
   2590      0    stevel 	switch (domp->model) {
   2591      0    stevel 	case PPMD_FET:
   2592      0    stevel 		if (domp->status == PPMD_OFF) {
   2593      0    stevel 			if ((ret = ppm_fetset(domp,  PPMD_ON)) ==
   2594      0    stevel 			    DDI_SUCCESS) {
   2595      0    stevel 				PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
   2596      0    stevel 				    str, PM_NAME(dip), PM_ADDR(dip)))
   2597      0    stevel 			} else {
   2598    686    osaeed 				PPMD(D_FET, ("%s: couldn't turn on fet "
   2599      0    stevel 				    "for %s@%s\n", str, PM_NAME(dip),
   2600      0    stevel 				    PM_ADDR(dip)))
   2601      0    stevel 			}
   2602      0    stevel 		}
   2603      0    stevel 		break;
   2604      0    stevel 
   2605      0    stevel 	case PPMD_PCI:
   2606      0    stevel 	case PPMD_PCI_PROP:
   2607      0    stevel 		if (domp->status == PPMD_OFF) {
   2608      0    stevel 			if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
   2609      0    stevel 			    DDI_SUCCESS) {
   2610      0    stevel 				PPMD(D_PCI, ("%s: turned on clock for "
   2611      0    stevel 				    "%s@%s\n", str, PM_NAME(dip),
   2612      0    stevel 				    PM_ADDR(dip)))
   2613      0    stevel 			} else {
   2614    686    osaeed 				PPMD(D_PCI, ("%s: couldn't turn on clock "
   2615    686    osaeed 				    "for %s@%s\n", str, PM_NAME(dip),
   2616    686    osaeed 				    PM_ADDR(dip)))
   2617    686    osaeed 			}
   2618    686    osaeed 		}
   2619    686    osaeed 		break;
   2620    686    osaeed 
   2621    686    osaeed 	case PPMD_PCIE:
   2622    686    osaeed 		if (domp->status == PPMD_OFF) {
   2623    686    osaeed 			if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
   2624    686    osaeed 			    DDI_SUCCESS) {
   2625    686    osaeed 				PPMD(D_PCI, ("%s: turned on link for "
   2626    686    osaeed 				    "%s@%s\n", str, PM_NAME(dip),
   2627    686    osaeed 				    PM_ADDR(dip)))
   2628    686    osaeed 			} else {
   2629    686    osaeed 				PPMD(D_PCI, ("%s: couldn't turn on link "
   2630      0    stevel 				    "for %s@%s\n", str, PM_NAME(dip),
   2631      0    stevel 				    PM_ADDR(dip)))
   2632      0    stevel 			}
   2633      0    stevel 		}
   2634      0    stevel 		break;
   2635      0    stevel 
   2636      0    stevel 	default:
   2637      0    stevel 		break;
   2638      0    stevel 	}
   2639      0    stevel 	if (ret == DDI_SUCCESS)
   2640      0    stevel 		domp->pwr_cnt++;
   2641      0    stevel 	mutex_exit(&domp->lock);
   2642      0    stevel 	return (ret);
   2643      0    stevel }
   2644      0    stevel 
   2645      0    stevel /*
   2646      0    stevel  * Decrements the domain pwr_cnt. if conditions to power down the domain
   2647      0    stevel  * are met, powers down the domain,.
   2648      0    stevel  */
   2649      0    stevel static int
   2650      0    stevel ppm_power_down_domain(dev_info_t *dip)
   2651      0    stevel {
   2652      0    stevel 	int		ret = DDI_SUCCESS;
   2653      0    stevel 	char		*str = "ppm_power_down_domain";
   2654      0    stevel 	ppm_domain_t	*domp;
   2655      0    stevel 
   2656      0    stevel 	domp = ppm_lookup_dev(dip);
   2657      0    stevel 	ASSERT(domp);
   2658      0    stevel 	mutex_enter(&domp->lock);
   2659      0    stevel 	ASSERT(domp->pwr_cnt > 0);
   2660      0    stevel 	domp->pwr_cnt--;
   2661      0    stevel 	switch (domp->model) {
   2662      0    stevel 	case PPMD_FET:
   2663      0    stevel 		if ((domp->pwr_cnt == 0) &&
   2664      0    stevel 		    (ppm_cpr_window_flag == B_FALSE) &&
   2665      0    stevel 		    ppm_none_else_holds_power(domp)) {
   2666      0    stevel 			if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
   2667      0    stevel 			    DDI_SUCCESS) {
   2668      0    stevel 				PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
   2669      0    stevel 				    str, PM_NAME(dip), PM_ADDR(dip)))
   2670      0    stevel 			} else {
   2671      0    stevel 				PPMD(D_FET, ("%s: couldn't turn off FET for "
   2672      0    stevel 				    " %s@%s\n", str, PM_NAME(dip),
   2673      0    stevel 				    PM_ADDR(dip)))
   2674      0    stevel 			}
   2675      0    stevel 		}
   2676      0    stevel 		break;
   2677      0    stevel 
   2678      0    stevel 	case PPMD_PCI:
   2679      0    stevel 	case PPMD_PCI_PROP:
   2680      0    stevel 		if ((domp->pwr_cnt == 0) &&
   2681      0    stevel 		    (ppm_cpr_window_flag == B_FALSE) &&
   2682      0    stevel 		    ppm_none_else_holds_power(domp)) {
   2683      0    stevel 			if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
   2684      0    stevel 			    DDI_SUCCESS) {
   2685      0    stevel 				PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
   2686      0    stevel 				    str, PM_NAME(dip), PM_ADDR(dip)))
   2687      0    stevel 			} else {
   2688      0    stevel 				PPMD(D_PCI, ("%s: couldn't turn off clock "
   2689      0    stevel 				    "for %s@%s\n", str, PM_NAME(dip),
   2690      0    stevel 				    PM_ADDR(dip)))
   2691      0    stevel 			}
   2692      0    stevel 		}
   2693      0    stevel 		break;
   2694      0    stevel 
   2695    686    osaeed 	case PPMD_PCIE:
   2696    686    osaeed 		if ((domp->pwr_cnt == 0) &&
   2697    686    osaeed 		    (ppm_cpr_window_flag == B_FALSE) &&
   2698    686    osaeed 		    ppm_none_else_holds_power(domp)) {
   2699    686    osaeed 			if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
   2700    686    osaeed 			    DDI_SUCCESS) {
   2701    686    osaeed 				PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
   2702    686    osaeed 				    str, PM_NAME(dip), PM_ADDR(dip)))
   2703    686    osaeed 			} else {
   2704    686    osaeed 				PPMD(D_PCI, ("%s: couldn't turn off link "
   2705    686    osaeed 				    "for %s@%s\n", str, PM_NAME(dip),
   2706    686    osaeed 				    PM_ADDR(dip)))
   2707    686    osaeed 			}
   2708    686    osaeed 		}
   2709    686    osaeed 		break;
   2710    686    osaeed 
   2711      0    stevel 	default:
   2712      0    stevel 		break;
   2713      0    stevel 	}
   2714      0    stevel 	mutex_exit(&domp->lock);
   2715      0    stevel 	return (ret);
   2716      0    stevel }
   2717   5295    randyf 
   2718   5295    randyf static int
   2719   5295    randyf ppm_manage_sx(s3a_t *s3ap, int enter)
   2720   5295    randyf {
   2721   5295    randyf 	ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
   2722   5295    randyf 	ppm_dc_t *dc;
   2723   5295    randyf 	int ret = 0;
   2724   5295    randyf 
   2725   5295    randyf 	if (domp == NULL) {
   2726   5295    randyf 		PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
   2727   5295    randyf 		return (ENODEV);
   2728   5295    randyf 	}
   2729   5295    randyf 	PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
   2730   5295    randyf 	    enter))
   2731   5295    randyf 	switch (s3ap->s3a_state) {
   2732   5295    randyf 	case S3:
   2733   5295    randyf 		if (enter) {
   2734   5295    randyf 			dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
   2735   5295    randyf 		} else {
   2736   5295    randyf 			dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
   2737   5295    randyf 		}
   2738   5295    randyf 		ASSERT(dc && dc->method == PPMDC_KIO);
   2739   5295    randyf 		PPMD(D_CPR,
   2740   5295    randyf 		    ("ppm_manage_sx: calling acpi driver (handle %p)"
   2741   5295    randyf 		    " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
   2742   5295    randyf 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
   2743   5295    randyf 		    (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
   2744   5295    randyf 		break;
   2745   5295    randyf 
   2746   5295    randyf 	case S4:
   2747   5295    randyf 		/* S4 is not supported yet */
   2748   5295    randyf 		return (EINVAL);
   2749   5295    randyf 	default:
   2750   5295    randyf 		ASSERT(0);
   2751   5295    randyf 	}
   2752   5295    randyf 	return (ret);
   2753   5295    randyf }
   2754   5295    randyf 
   2755   5295    randyf /*
   2756   5295    randyf  * Search enable/disable lists, which are encoded in ppm.conf as an array
   2757   5295    randyf  * of char strings.
   2758   5295    randyf  */
   2759   5295    randyf static int
   2760   5295    randyf ppm_search_list(pm_searchargs_t *sl)
   2761   5295    randyf {
   2762   5295    randyf 	int i;
   2763   5295    randyf 	int flags = DDI_PROP_DONTPASS;
   2764   5295    randyf 	ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
   2765   5295    randyf 	char **pp;
   2766   5295    randyf 	char *starp;
   2767   5295    randyf 	uint_t nelements;
   2768   5295    randyf 	char *manuf = sl->pms_manufacturer;
   2769   5295    randyf 	char *prod = sl->pms_product;
   2770   5295    randyf 
   2771   5295    randyf 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
   2772   5295    randyf 	    sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
   2773   5295    randyf 		PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
   2774   5295    randyf 		    sl->pms_listname))
   2775   5295    randyf 		return (EINVAL);
   2776   5295    randyf 	}
   2777   5295    randyf 	ASSERT((nelements & 1) == 0);		/* must be even */
   2778   5295    randyf 
   2779   5295    randyf 	PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
   2780   5295    randyf 
   2781   5295    randyf 	for (i = 0; i < nelements; i += 2) {
   2782   5295    randyf 		PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
   2783   5295    randyf 		/* we support only a trailing '*' pattern match */
   2784   5295    randyf 		if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
   2785   5295    randyf 			/* LINTED - ptrdiff overflow */
   2786   5295    randyf 			if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
   2787   5295    randyf 				PPMD(D_CPR, (" no match %s with %s\n",
   2788   5295    randyf 				    manuf, pp[i + 1]))
   2789   5295    randyf 				continue;
   2790   5295    randyf 			}
   2791   5295    randyf 		}
   2792   5295    randyf 		if ((starp = strchr(pp[i + 1], '*')) != NULL &&
   2793   5295    randyf 		    *(starp + 1) == 0) {
   2794   5295    randyf 			if (strncmp(prod,
   2795   5295    randyf 			    /* LINTED - ptrdiff overflow */
   2796   5295    randyf 			    pp[i + 1], (starp - pp[i + 1])) != 0) {
   2797   5295    randyf 				PPMD(D_CPR, (" no match %s with %s\n",
   2798   5295    randyf 				    prod, pp[i + 1]))
   2799   5295    randyf 				continue;
   2800   5295    randyf 			}
   2801   5295    randyf 		}
   2802   5295    randyf 		if (strcmp(manuf, pp[i]) == 0 &&
   2803   5295    randyf 		    (strcmp(prod, pp[i + 1]) == 0)) {
   2804   5295    randyf 			PPMD(D_CPR, (" match\n"))
   2805   5295    randyf 			ddi_prop_free(pp);
   2806   5295    randyf 			return (0);
   2807   5295    randyf 		}
   2808   5295    randyf 		PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
   2809   5295    randyf 		    manuf, pp[i], prod, pp[i + 1]))
   2810   5295    randyf 	}
   2811   5295    randyf 	ddi_prop_free(pp);
   2812   5295    randyf 	return (ENODEV);
   2813   5295    randyf }
   2814