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