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