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