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