xref: /illumos-gate/usr/src/uts/sun4u/io/pci/pci_pwr.c (revision f47a9c50)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*f47a9c50Smathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
317c478bd9Sstevel@tonic-gate #include <sys/async.h>
327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
337c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
347c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
357c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
367c478bd9Sstevel@tonic-gate #include <sys/ddi_implfuncs.h>
377c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
387c478bd9Sstevel@tonic-gate #include <sys/pci/pci_pwr.h>
397c478bd9Sstevel@tonic-gate #include <sys/pci.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate static void pci_pwr_update_comp(pci_pwr_t *pwr_p, pci_pwr_chld_t *p, int comp,
427c478bd9Sstevel@tonic-gate 	int lvl);
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate #ifdef DEBUG
457c478bd9Sstevel@tonic-gate static char *pci_pwr_bus_label[] = {"PM_LEVEL_B3", "PM_LEVEL_B2", \
467c478bd9Sstevel@tonic-gate 	"PM_LEVEL_B1", "PM_LEVEL_B0"};
477c478bd9Sstevel@tonic-gate #endif
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * Retreive the pci_pwr_chld_t structure for a given devinfo node.
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate pci_pwr_chld_t *
pci_pwr_get_info(pci_pwr_t * pwr_p,dev_info_t * dip)557c478bd9Sstevel@tonic-gate pci_pwr_get_info(pci_pwr_t *pwr_p, dev_info_t *dip)
567c478bd9Sstevel@tonic-gate {
577c478bd9Sstevel@tonic-gate 	pci_pwr_chld_t *p;
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 	ASSERT(PM_CAPABLE(pwr_p));
607c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	for (p = pwr_p->pwr_info; p != NULL; p = p->next) {
637c478bd9Sstevel@tonic-gate 		if (p->dip == dip) {
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 			return (p);
667c478bd9Sstevel@tonic-gate 		}
677c478bd9Sstevel@tonic-gate 	}
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate 	cmn_err(CE_PANIC, "unable to find pwr info data for %s@%s",
707c478bd9Sstevel@tonic-gate 	    ddi_node_name(dip), ddi_get_name_addr(dip));
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
73*f47a9c50Smathue 	return (NULL);
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * Create a pci_pwr_chld_t structure for a given devinfo node.
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate void
pci_pwr_create_info(pci_pwr_t * pwr_p,dev_info_t * dip)807c478bd9Sstevel@tonic-gate pci_pwr_create_info(pci_pwr_t *pwr_p, dev_info_t *dip)
817c478bd9Sstevel@tonic-gate {
827c478bd9Sstevel@tonic-gate 	pci_pwr_chld_t *p;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	ASSERT(PM_CAPABLE(pwr_p));
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_PWR, ddi_get_parent(dip), "ADDING NEW PWR_INFO %s@%s\n",
877c478bd9Sstevel@tonic-gate 	    ddi_node_name(dip), ddi_get_name_addr(dip));
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	p = kmem_zalloc(sizeof (struct pci_pwr_chld), KM_SLEEP);
907c478bd9Sstevel@tonic-gate 	p->dip = dip;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	mutex_enter(&pwr_p->pwr_mutex);
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 	/*
957c478bd9Sstevel@tonic-gate 	 * Until components are created for this device, bus
967c478bd9Sstevel@tonic-gate 	 * should be at full power since power of child device
977c478bd9Sstevel@tonic-gate 	 * is unknown.  Increment # children requiring "full power"
987c478bd9Sstevel@tonic-gate 	 */
997c478bd9Sstevel@tonic-gate 	p->flags |= PWR_FP_HOLD;
1007c478bd9Sstevel@tonic-gate 	pwr_p->pwr_fp++;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	p->next =  pwr_p->pwr_info;
1037c478bd9Sstevel@tonic-gate 	pwr_p->pwr_info = p;
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	pci_pwr_change(pwr_p, pwr_p->current_lvl, pci_pwr_new_lvl(pwr_p));
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	mutex_exit(&pwr_p->pwr_mutex);
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate void
pci_pwr_rm_info(pci_pwr_t * pwr_p,dev_info_t * cdip)1117c478bd9Sstevel@tonic-gate pci_pwr_rm_info(pci_pwr_t *pwr_p, dev_info_t *cdip)
1127c478bd9Sstevel@tonic-gate {
1137c478bd9Sstevel@tonic-gate 	pci_pwr_chld_t **prev_infop;
1147c478bd9Sstevel@tonic-gate 	pci_pwr_chld_t *infop = NULL;
1157c478bd9Sstevel@tonic-gate 	int i;
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	ASSERT(PM_CAPABLE(pwr_p));
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	mutex_enter(&pwr_p->pwr_mutex);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	for (prev_infop = &pwr_p->pwr_info; *prev_infop != NULL;
1227c478bd9Sstevel@tonic-gate 	    prev_infop = &((*prev_infop)->next)) {
1237c478bd9Sstevel@tonic-gate 		if ((*prev_infop)->dip == cdip) {
1247c478bd9Sstevel@tonic-gate 			infop = *prev_infop;
1257c478bd9Sstevel@tonic-gate 			break;
1267c478bd9Sstevel@tonic-gate 		}
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	if (infop == NULL) {
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 		mutex_exit(&pwr_p->pwr_mutex);
1327c478bd9Sstevel@tonic-gate 		return;
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	*prev_infop =  infop->next;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	/*
1387c478bd9Sstevel@tonic-gate 	 * Remove any reference counts for this child.
1397c478bd9Sstevel@tonic-gate 	 */
1407c478bd9Sstevel@tonic-gate 	if (infop->comp_pwr != NULL) {
1417c478bd9Sstevel@tonic-gate 		for (i = 0; i < infop->num_comps; i++) {
1427c478bd9Sstevel@tonic-gate 			pci_pwr_update_comp(pwr_p, infop, i, PM_LEVEL_NOLEVEL);
1437c478bd9Sstevel@tonic-gate 		}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 		kmem_free(infop->comp_pwr, sizeof (int) * infop->num_comps);
1467c478bd9Sstevel@tonic-gate 	}
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	if (infop->flags & PWR_FP_HOLD) {
1497c478bd9Sstevel@tonic-gate 		pwr_p->pwr_fp--;
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	pci_pwr_change(pwr_p, pwr_p->current_lvl, pci_pwr_new_lvl(pwr_p));
1537c478bd9Sstevel@tonic-gate 	mutex_exit(&pwr_p->pwr_mutex);
1547c478bd9Sstevel@tonic-gate 	kmem_free(infop, sizeof (struct pci_pwr_chld));
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * Allocate space for component state information in pci_pwr_chld_t
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate void
pci_pwr_add_components(pci_pwr_t * pwr_p,dev_info_t * cdip,pci_pwr_chld_t * p)1617c478bd9Sstevel@tonic-gate pci_pwr_add_components(pci_pwr_t *pwr_p, dev_info_t *cdip, pci_pwr_chld_t *p)
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	int num_comps = PM_NUMCMPTS(cdip);
1647c478bd9Sstevel@tonic-gate 	int i;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
1677c478bd9Sstevel@tonic-gate 	/*
1687c478bd9Sstevel@tonic-gate 	 * Assume the power level of a component is UNKNOWN until
1697c478bd9Sstevel@tonic-gate 	 * notified otherwise.
1707c478bd9Sstevel@tonic-gate 	 */
1717c478bd9Sstevel@tonic-gate 	if (num_comps > 0) {
1727c478bd9Sstevel@tonic-gate 		p->comp_pwr =
1737c478bd9Sstevel@tonic-gate 		    kmem_alloc(sizeof (int) * num_comps, KM_SLEEP);
1747c478bd9Sstevel@tonic-gate 		p->num_comps = num_comps;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 		DEBUG3(DBG_PWR, ddi_get_parent(cdip),
1777c478bd9Sstevel@tonic-gate 		    "ADDING %d COMPONENTS FOR %s@%s\n", num_comps,
1787c478bd9Sstevel@tonic-gate 		    ddi_node_name(cdip), ddi_get_name_addr(cdip));
1797c478bd9Sstevel@tonic-gate 	} else {
1807c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d device has %d components",
1817c478bd9Sstevel@tonic-gate 		    ddi_driver_name(cdip), ddi_get_instance(cdip),
1827c478bd9Sstevel@tonic-gate 		    num_comps);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		return;
1857c478bd9Sstevel@tonic-gate 	}
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	/*
1887c478bd9Sstevel@tonic-gate 	 * Release the fp hold that was made when the device
1897c478bd9Sstevel@tonic-gate 	 * was created.
1907c478bd9Sstevel@tonic-gate 	 */
1917c478bd9Sstevel@tonic-gate 	ASSERT((p->flags & PWR_FP_HOLD) == PWR_FP_HOLD);
1927c478bd9Sstevel@tonic-gate 	p->flags &= ~PWR_FP_HOLD;
1937c478bd9Sstevel@tonic-gate 	pwr_p->pwr_fp--;
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_comps; i++) {
1967c478bd9Sstevel@tonic-gate 		/*
1977c478bd9Sstevel@tonic-gate 		 * Initialize the component lvl so that the
1987c478bd9Sstevel@tonic-gate 		 * state reference counts will be updated correctly.
1997c478bd9Sstevel@tonic-gate 		 */
2007c478bd9Sstevel@tonic-gate 		p->comp_pwr[i] = PM_LEVEL_NOLEVEL;
2017c478bd9Sstevel@tonic-gate 		pci_pwr_update_comp(pwr_p, p, i, PM_LEVEL_UNKNOWN);
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate }
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate  * Update the current power level for component.  Then adjust the
2077c478bd9Sstevel@tonic-gate  * bus reference counter for given state.
2087c478bd9Sstevel@tonic-gate  */
2097c478bd9Sstevel@tonic-gate static void
pci_pwr_update_comp(pci_pwr_t * pwr_p,pci_pwr_chld_t * p,int comp,int lvl)2107c478bd9Sstevel@tonic-gate pci_pwr_update_comp(pci_pwr_t *pwr_p, pci_pwr_chld_t *p, int comp,
2117c478bd9Sstevel@tonic-gate 			int lvl)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	/*
2167c478bd9Sstevel@tonic-gate 	 * Remove old pwr state count for old PM level.
2177c478bd9Sstevel@tonic-gate 	 */
2187c478bd9Sstevel@tonic-gate 	switch (p->comp_pwr[comp]) {
2197c478bd9Sstevel@tonic-gate 	case PM_LEVEL_UNKNOWN:
2207c478bd9Sstevel@tonic-gate 		pwr_p->pwr_uk--;
2217c478bd9Sstevel@tonic-gate 		p->u01--;
2227c478bd9Sstevel@tonic-gate 		ASSERT(pwr_p->pwr_uk >= 0);
2237c478bd9Sstevel@tonic-gate 		break;
2247c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D0:
2257c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d0--;
2267c478bd9Sstevel@tonic-gate 		p->u01--;
2277c478bd9Sstevel@tonic-gate 		ASSERT(pwr_p->pwr_d0 >= 0);
2287c478bd9Sstevel@tonic-gate 		break;
2297c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D1:
2307c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d1--;
2317c478bd9Sstevel@tonic-gate 		p->u01--;
2327c478bd9Sstevel@tonic-gate 		ASSERT(pwr_p->pwr_d1 >= 0);
2337c478bd9Sstevel@tonic-gate 		break;
2347c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D2:
2357c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d2--;
2367c478bd9Sstevel@tonic-gate 		ASSERT(pwr_p->pwr_d2 >= 0);
2377c478bd9Sstevel@tonic-gate 		break;
2387c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D3:
2397c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d3--;
2407c478bd9Sstevel@tonic-gate 		ASSERT(pwr_p->pwr_d3 >= 0);
2417c478bd9Sstevel@tonic-gate 		break;
2427c478bd9Sstevel@tonic-gate 	default:
2437c478bd9Sstevel@tonic-gate 		break;
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	p->comp_pwr[comp] = lvl;
2477c478bd9Sstevel@tonic-gate 	/*
2487c478bd9Sstevel@tonic-gate 	 * Add new pwr state count for the new PM level.
2497c478bd9Sstevel@tonic-gate 	 */
2507c478bd9Sstevel@tonic-gate 	switch (lvl) {
2517c478bd9Sstevel@tonic-gate 	case PM_LEVEL_UNKNOWN:
2527c478bd9Sstevel@tonic-gate 		pwr_p->pwr_uk++;
2537c478bd9Sstevel@tonic-gate 		p->u01++;
2547c478bd9Sstevel@tonic-gate 		break;
2557c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D0:
2567c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d0++;
2577c478bd9Sstevel@tonic-gate 		p->u01++;
2587c478bd9Sstevel@tonic-gate 		break;
2597c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D1:
2607c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d1++;
2617c478bd9Sstevel@tonic-gate 		p->u01++;
2627c478bd9Sstevel@tonic-gate 		break;
2637c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D2:
2647c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d2++;
2657c478bd9Sstevel@tonic-gate 		break;
2667c478bd9Sstevel@tonic-gate 	case PM_LEVEL_D3:
2677c478bd9Sstevel@tonic-gate 		pwr_p->pwr_d3++;
2687c478bd9Sstevel@tonic-gate 		break;
2697c478bd9Sstevel@tonic-gate 	default:
2707c478bd9Sstevel@tonic-gate 		break;
2717c478bd9Sstevel@tonic-gate 	}
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate  * Knowing the current state of all devices on the bus, return the
2777c478bd9Sstevel@tonic-gate  * appropriate supported bus speed.
2787c478bd9Sstevel@tonic-gate  */
2797c478bd9Sstevel@tonic-gate int
pci_pwr_new_lvl(pci_pwr_t * pwr_p)2807c478bd9Sstevel@tonic-gate pci_pwr_new_lvl(pci_pwr_t *pwr_p)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	int b_lvl;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	if (pwr_p->pwr_fp > 0) {
2877c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip, "new_lvl: "
2887c478bd9Sstevel@tonic-gate 		    "returning PM_LEVEL_B0 pwr_fp = %d\n", pwr_p->pwr_fp);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 		return (PM_LEVEL_B0);
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	/*
2947c478bd9Sstevel@tonic-gate 	 * If any components are at unknown power levels, the
2957c478bd9Sstevel@tonic-gate 	 * highest power level has to be assumed for the device (D0).
2967c478bd9Sstevel@tonic-gate 	 */
2977c478bd9Sstevel@tonic-gate 	if (pwr_p->pwr_uk > 0) {
2987c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip, "new_lvl: unknown "
2997c478bd9Sstevel@tonic-gate 		    "count is %d. returning PM_LEVEL_B0\n", pwr_p->pwr_uk);
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 		return (PM_LEVEL_B0);
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	/*
3057c478bd9Sstevel@tonic-gate 	 * Find the lowest theoretical level
3067c478bd9Sstevel@tonic-gate 	 * the bus can operate at.
3077c478bd9Sstevel@tonic-gate 	 */
3087c478bd9Sstevel@tonic-gate 	if (pwr_p->pwr_d0 > 0) {
3097c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B0;
3107c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
3117c478bd9Sstevel@tonic-gate 		    "new_lvl: PM_LEVEL_B0 d0 count = %d\n",
3127c478bd9Sstevel@tonic-gate 		    pwr_p->pwr_d0);
3137c478bd9Sstevel@tonic-gate 	} else if (pwr_p->pwr_d1 > 0) {
3147c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B1;
3157c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
3167c478bd9Sstevel@tonic-gate 		    "new_lvl: PM_LEVEL_B1 d1 count = %d\n",
3177c478bd9Sstevel@tonic-gate 		    pwr_p->pwr_d1);
3187c478bd9Sstevel@tonic-gate 	} else if (pwr_p->pwr_d2 > 0) {
3197c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B2;
3207c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
3217c478bd9Sstevel@tonic-gate 		    "new_lvl: PM_LEVEL_B2 d2 count = %d\n",
3227c478bd9Sstevel@tonic-gate 		    pwr_p->pwr_d2);
3237c478bd9Sstevel@tonic-gate 	} else if (pwr_p->pwr_d3 > 0) {
3247c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B3;
3257c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
3267c478bd9Sstevel@tonic-gate 		    "new_lvl: PM_LEVEL_B3 d3 count = %d\n",
3277c478bd9Sstevel@tonic-gate 		    pwr_p->pwr_d3);
3287c478bd9Sstevel@tonic-gate 	} else {
3297c478bd9Sstevel@tonic-gate 		DEBUG0(DBG_PWR, pwr_p->pwr_dip,
3307c478bd9Sstevel@tonic-gate 		    "new_lvl: PM_LEVEL_B3: all counts are 0\n");
3317c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B3;
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/*
3357c478bd9Sstevel@tonic-gate 	 * Now find the closest supported level available.
3367c478bd9Sstevel@tonic-gate 	 * If the level isn't available, have to find the
3377c478bd9Sstevel@tonic-gate 	 * next highest power level (or lowest in B# terms).
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	switch (b_lvl) {
3407c478bd9Sstevel@tonic-gate 	case PM_LEVEL_B3:
3417c478bd9Sstevel@tonic-gate 		if (pwr_p->pwr_flags & PCI_PWR_B3_CAPABLE) {
3427c478bd9Sstevel@tonic-gate 			break;
3437c478bd9Sstevel@tonic-gate 		}
3447c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
3457c478bd9Sstevel@tonic-gate 	case PM_LEVEL_B2:
3467c478bd9Sstevel@tonic-gate 		if (pwr_p->pwr_flags & PCI_PWR_B2_CAPABLE) {
3477c478bd9Sstevel@tonic-gate 			b_lvl = PM_LEVEL_B2;
3487c478bd9Sstevel@tonic-gate 			break;
3497c478bd9Sstevel@tonic-gate 		}
3507c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
3517c478bd9Sstevel@tonic-gate 	case PM_LEVEL_B1:
3527c478bd9Sstevel@tonic-gate 		if (pwr_p->pwr_flags & PCI_PWR_B1_CAPABLE) {
3537c478bd9Sstevel@tonic-gate 			b_lvl = PM_LEVEL_B1;
3547c478bd9Sstevel@tonic-gate 			break;
3557c478bd9Sstevel@tonic-gate 		}
3567c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
3577c478bd9Sstevel@tonic-gate 	case PM_LEVEL_B0:
3587c478bd9Sstevel@tonic-gate 		/*
3597c478bd9Sstevel@tonic-gate 		 * This level always supported
3607c478bd9Sstevel@tonic-gate 		 */
3617c478bd9Sstevel@tonic-gate 		b_lvl = PM_LEVEL_B0;
3627c478bd9Sstevel@tonic-gate 		break;
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 	DEBUG1(DBG_PWR, pwr_p->pwr_dip,
3657c478bd9Sstevel@tonic-gate 	    "new_lvl: Adjusted Level is %s\n",
3667c478bd9Sstevel@tonic-gate 	    pci_pwr_bus_label[b_lvl]);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	return (b_lvl);
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate }
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate int
pci_raise_power(pci_pwr_t * pwr_p,int current,int new,void * impl_arg,pm_bp_nexus_pwrup_t bpn)3737c478bd9Sstevel@tonic-gate pci_raise_power(pci_pwr_t *pwr_p, int current, int new, void *impl_arg,
3747c478bd9Sstevel@tonic-gate     pm_bp_nexus_pwrup_t bpn)
3757c478bd9Sstevel@tonic-gate {
3767c478bd9Sstevel@tonic-gate 	int ret = DDI_SUCCESS, pwrup_res;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	pci_pwr_component_busy(pwr_p);
3817c478bd9Sstevel@tonic-gate 	mutex_exit(&pwr_p->pwr_mutex);
3827c478bd9Sstevel@tonic-gate 	ret = pm_busop_bus_power(pwr_p->pwr_dip, impl_arg,
3837c478bd9Sstevel@tonic-gate 	    BUS_POWER_NEXUS_PWRUP, (void *) &bpn,
3847c478bd9Sstevel@tonic-gate 	    (void *) &pwrup_res);
3857c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
3867c478bd9Sstevel@tonic-gate 		mutex_enter(&pwr_p->pwr_mutex);
3877c478bd9Sstevel@tonic-gate 		pci_pwr_component_idle(pwr_p);
3887c478bd9Sstevel@tonic-gate 		mutex_exit(&pwr_p->pwr_mutex);
3897c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d pci_raise_power failed",
3907c478bd9Sstevel@tonic-gate 		    ddi_driver_name(pwr_p->pwr_dip),
3917c478bd9Sstevel@tonic-gate 		    ddi_get_instance(pwr_p->pwr_dip));
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	return (ret);
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate int
pci_pwr_ops(pci_pwr_t * pwr_p,dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)3987c478bd9Sstevel@tonic-gate pci_pwr_ops(pci_pwr_t *pwr_p, dev_info_t *dip, void *impl_arg,
3997c478bd9Sstevel@tonic-gate     pm_bus_power_op_t op, void *arg, void *result)
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 	pci_pwr_chld_t *p_chld;
4027c478bd9Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t bpn;
4037c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
4047c478bd9Sstevel@tonic-gate 	dev_info_t *rdip = bpc->bpc_dip;
4057c478bd9Sstevel@tonic-gate 	int new_level, *res = (int *)result, ret = DDI_SUCCESS;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	mutex_enter(&pwr_p->pwr_mutex);
4087c478bd9Sstevel@tonic-gate 	switch (op) {
4097c478bd9Sstevel@tonic-gate 	case BUS_POWER_HAS_CHANGED:
4107c478bd9Sstevel@tonic-gate 		p_chld = pci_pwr_get_info(pwr_p, rdip);
4117c478bd9Sstevel@tonic-gate 		DEBUG5(DBG_PWR, dip, "%s@%s CHANGED_POWER cmp = %d "
4127c478bd9Sstevel@tonic-gate 		    "old = %d new = %d\n",
4137c478bd9Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip),
4147c478bd9Sstevel@tonic-gate 		    bpc->bpc_comp, bpc->bpc_olevel, bpc->bpc_nlevel);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 		if (*res == DDI_FAILURE) {
4177c478bd9Sstevel@tonic-gate 			DEBUG0(DBG_PWR, rdip, "changed_power_req FAILED\n");
4187c478bd9Sstevel@tonic-gate 			break;
4197c478bd9Sstevel@tonic-gate 		} else {
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 			/*
4227c478bd9Sstevel@tonic-gate 			 * pci_pwr_add_components must be called here if
4237c478bd9Sstevel@tonic-gate 			 * comp_pwr hasn't been set up yet.  It has to be done
4247c478bd9Sstevel@tonic-gate 			 * here rather than in post-attach, since it is possible
4257c478bd9Sstevel@tonic-gate 			 * for power() of child to get called before attach
4267c478bd9Sstevel@tonic-gate 			 * completes.
4277c478bd9Sstevel@tonic-gate 			 */
4287c478bd9Sstevel@tonic-gate 			if (p_chld->comp_pwr == NULL)
4297c478bd9Sstevel@tonic-gate 				pci_pwr_add_components(pwr_p, rdip, p_chld);
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 			pci_pwr_update_comp(pwr_p, p_chld,
4327c478bd9Sstevel@tonic-gate 			    bpc->bpc_comp, bpc->bpc_nlevel);
4337c478bd9Sstevel@tonic-gate 		}
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		new_level = pci_pwr_new_lvl(pwr_p);
4367c478bd9Sstevel@tonic-gate 		bpn.bpn_dip = pwr_p->pwr_dip;
4377c478bd9Sstevel@tonic-gate 		bpn.bpn_comp = PCI_PM_COMP_0;
4387c478bd9Sstevel@tonic-gate 		bpn.bpn_level = new_level;
4397c478bd9Sstevel@tonic-gate 		bpn.bpn_private = bpc->bpc_private;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 		if (new_level > pwr_p->current_lvl)
4427c478bd9Sstevel@tonic-gate 			return (pci_raise_power(pwr_p, pwr_p->current_lvl,
4437c478bd9Sstevel@tonic-gate 			    new_level, impl_arg, bpn));
4447c478bd9Sstevel@tonic-gate 		else
4457c478bd9Sstevel@tonic-gate 			pci_pwr_change(pwr_p, pwr_p->current_lvl,
4467c478bd9Sstevel@tonic-gate 			    new_level);
4477c478bd9Sstevel@tonic-gate 		break;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	case BUS_POWER_PRE_NOTIFICATION:
4507c478bd9Sstevel@tonic-gate 		DEBUG5(DBG_PWR, dip, "PRE %s@%s cmp = %d old = %d "
4517c478bd9Sstevel@tonic-gate 		    "new = %d. TEMP FULL POWER\n",
4527c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4537c478bd9Sstevel@tonic-gate 		    bpc->bpc_comp, bpc->bpc_olevel, bpc->bpc_nlevel);
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 		/*
4567c478bd9Sstevel@tonic-gate 		 * Any state changes require that the bus be at full
4577c478bd9Sstevel@tonic-gate 		 * power (B0) so that the device configuration
4587c478bd9Sstevel@tonic-gate 		 * registers can be accessed.  Make a fp hold here
4597c478bd9Sstevel@tonic-gate 		 * so device remains at full power during power
4607c478bd9Sstevel@tonic-gate 		 * configuration.
4617c478bd9Sstevel@tonic-gate 		 */
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 		pwr_p->pwr_fp++;
4647c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
4657c478bd9Sstevel@tonic-gate 		    "incremented fp is %d in PRE_NOTE\n\n", pwr_p->pwr_fp);
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 		bpn.bpn_dip = pwr_p->pwr_dip;
4687c478bd9Sstevel@tonic-gate 		bpn.bpn_comp = PCI_PM_COMP_0;
4697c478bd9Sstevel@tonic-gate 		bpn.bpn_level = PM_LEVEL_B0;
4707c478bd9Sstevel@tonic-gate 		bpn.bpn_private = bpc->bpc_private;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 		if (PM_LEVEL_B0 > pwr_p->current_lvl)
4737c478bd9Sstevel@tonic-gate 			return (pci_raise_power(pwr_p, pwr_p->current_lvl,
4747c478bd9Sstevel@tonic-gate 			    PM_LEVEL_B0, impl_arg, bpn));
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		break;
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	case BUS_POWER_POST_NOTIFICATION:
4797c478bd9Sstevel@tonic-gate 		p_chld = pci_pwr_get_info(pwr_p, rdip);
4807c478bd9Sstevel@tonic-gate 		DEBUG5(DBG_PWR, dip, "POST %s@%s cmp = %d old = %d new = %d\n",
4817c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4827c478bd9Sstevel@tonic-gate 		    bpc->bpc_comp, bpc->bpc_olevel, bpc->bpc_nlevel);
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 		if (*res == DDI_FAILURE) {
4857c478bd9Sstevel@tonic-gate 			DEBUG0(DBG_PWR, rdip, "child's power routine FAILED\n");
4867c478bd9Sstevel@tonic-gate 		} else {
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 			/*
4897c478bd9Sstevel@tonic-gate 			 * pci_pwr_add_components must be called here if
4907c478bd9Sstevel@tonic-gate 			 * comp_pwr hasen't been set up yet.  It has to be done
4917c478bd9Sstevel@tonic-gate 			 * here rather than in post-attach, since it is possible
4927c478bd9Sstevel@tonic-gate 			 * for power() of child to get called before attach
4937c478bd9Sstevel@tonic-gate 			 * completes.
4947c478bd9Sstevel@tonic-gate 			 */
4957c478bd9Sstevel@tonic-gate 			if (p_chld->comp_pwr == NULL)
4967c478bd9Sstevel@tonic-gate 				pci_pwr_add_components(pwr_p, rdip, p_chld);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 			pci_pwr_update_comp(pwr_p, p_chld,
4997c478bd9Sstevel@tonic-gate 			    bpc->bpc_comp, bpc->bpc_nlevel);
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 		}
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 		pwr_p->pwr_fp--;
5047c478bd9Sstevel@tonic-gate 		DEBUG1(DBG_PWR, pwr_p->pwr_dip,
5057c478bd9Sstevel@tonic-gate 		    "decremented fp is %d in POST_NOTE\n\n", pwr_p->pwr_fp);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 		new_level = pci_pwr_new_lvl(pwr_p);
5087c478bd9Sstevel@tonic-gate 		bpn.bpn_dip = pwr_p->pwr_dip;
5097c478bd9Sstevel@tonic-gate 		bpn.bpn_comp = PCI_PM_COMP_0;
5107c478bd9Sstevel@tonic-gate 		bpn.bpn_level = new_level;
5117c478bd9Sstevel@tonic-gate 		bpn.bpn_private = bpc->bpc_private;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 		if (new_level > pwr_p->current_lvl)
5147c478bd9Sstevel@tonic-gate 			return (pci_raise_power(pwr_p, pwr_p->current_lvl,
5157c478bd9Sstevel@tonic-gate 			    new_level, impl_arg, bpn));
5167c478bd9Sstevel@tonic-gate 		else
5177c478bd9Sstevel@tonic-gate 			pci_pwr_change(pwr_p, pwr_p->current_lvl,
5187c478bd9Sstevel@tonic-gate 			    new_level);
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 		break;
5217c478bd9Sstevel@tonic-gate 	default:
5227c478bd9Sstevel@tonic-gate 		mutex_exit(&pwr_p->pwr_mutex);
5237c478bd9Sstevel@tonic-gate 		return (pm_busop_bus_power(dip, impl_arg, op, arg, result));
5247c478bd9Sstevel@tonic-gate 	}
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	mutex_exit(&pwr_p->pwr_mutex);
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	return (ret);
5297c478bd9Sstevel@tonic-gate }
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate void
pci_pwr_resume(dev_info_t * dip,pci_pwr_t * pwr_p)5327c478bd9Sstevel@tonic-gate pci_pwr_resume(dev_info_t *dip, pci_pwr_t *pwr_p)
5337c478bd9Sstevel@tonic-gate {
5347c478bd9Sstevel@tonic-gate 	dev_info_t *cdip;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	/*
5377c478bd9Sstevel@tonic-gate 	 * Inform the PM framework of the current state of the device.
5387c478bd9Sstevel@tonic-gate 	 * (it is unknown to PM framework at this point).
5397c478bd9Sstevel@tonic-gate 	 */
5407c478bd9Sstevel@tonic-gate 	if (PM_CAPABLE(pwr_p)) {
5417c478bd9Sstevel@tonic-gate 		pwr_p->current_lvl = pci_pwr_current_lvl(pwr_p);
5427c478bd9Sstevel@tonic-gate 		pm_power_has_changed(dip, PCI_PM_COMP_0,
5437c478bd9Sstevel@tonic-gate 		    pwr_p->current_lvl);
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/*
5477c478bd9Sstevel@tonic-gate 	 * Restore config registers for children that did not save
5487c478bd9Sstevel@tonic-gate 	 * their own registers.  Children pwr states are UNKNOWN after
5497c478bd9Sstevel@tonic-gate 	 * a resume since it is possible for the PM framework to call
5507c478bd9Sstevel@tonic-gate 	 * resume without an actual power cycle. (ie if suspend fails).
5517c478bd9Sstevel@tonic-gate 	 */
5527c478bd9Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip != NULL;
5537c478bd9Sstevel@tonic-gate 		cdip = ddi_get_next_sibling(cdip)) {
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 		/*
5567c478bd9Sstevel@tonic-gate 		 * Not interested in children who are not already
5577c478bd9Sstevel@tonic-gate 		 * init'ed.  They will be set up by init_child().
5587c478bd9Sstevel@tonic-gate 		 */
5597c478bd9Sstevel@tonic-gate 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
5607c478bd9Sstevel@tonic-gate 			DEBUG2(DBG_DETACH, dip,
5617c478bd9Sstevel@tonic-gate 			    "DDI_RESUME: skipping %s%d not in CF1\n",
5627c478bd9Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 			continue;
5657c478bd9Sstevel@tonic-gate 		}
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 		/*
5687c478bd9Sstevel@tonic-gate 		 * Only restore config registers if saved by nexus.
5697c478bd9Sstevel@tonic-gate 		 */
5707c478bd9Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
5717c478bd9Sstevel@tonic-gate 		    NEXUS_SAVED) == 1) {
5727c478bd9Sstevel@tonic-gate 			(void) pci_restore_config_regs(cdip);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 			DEBUG2(DBG_PWR, dip,
5757c478bd9Sstevel@tonic-gate 			    "DDI_RESUME: nexus restoring %s%d config regs\n",
5767c478bd9Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 			if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
5807c478bd9Sstevel@tonic-gate 			    NEXUS_SAVED) != DDI_PROP_SUCCESS) {
5817c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d can't remove prop %s",
5827c478bd9Sstevel@tonic-gate 				    ddi_driver_name(cdip),
5837c478bd9Sstevel@tonic-gate 				    ddi_get_instance(cdip),
5847c478bd9Sstevel@tonic-gate 				    NEXUS_SAVED);
5857c478bd9Sstevel@tonic-gate 			}
5867c478bd9Sstevel@tonic-gate 		}
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate }
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate void
pci_pwr_suspend(dev_info_t * dip,pci_pwr_t * pwr_p)5917c478bd9Sstevel@tonic-gate pci_pwr_suspend(dev_info_t *dip, pci_pwr_t *pwr_p)
5927c478bd9Sstevel@tonic-gate {
5937c478bd9Sstevel@tonic-gate 	dev_info_t *cdip;
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	/*
5967c478bd9Sstevel@tonic-gate 	 * Save the state of the configuration headers of child
5977c478bd9Sstevel@tonic-gate 	 * nodes.
5987c478bd9Sstevel@tonic-gate 	 */
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip != NULL;
6017c478bd9Sstevel@tonic-gate 	    cdip = ddi_get_next_sibling(cdip)) {
6027c478bd9Sstevel@tonic-gate 		pci_pwr_chld_t *p;
6037c478bd9Sstevel@tonic-gate 		int i;
6047c478bd9Sstevel@tonic-gate 		int num_comps;
6057c478bd9Sstevel@tonic-gate 		int ret;
6067c478bd9Sstevel@tonic-gate 		/*
6077c478bd9Sstevel@tonic-gate 		 * Not interested in children who are not already
6087c478bd9Sstevel@tonic-gate 		 * init'ed.  They will be set up in init_child().
6097c478bd9Sstevel@tonic-gate 		 */
6107c478bd9Sstevel@tonic-gate 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
6117c478bd9Sstevel@tonic-gate 			DEBUG2(DBG_DETACH, dip, "DDI_SUSPEND: skipping "
6127c478bd9Sstevel@tonic-gate 			    "%s%d not in CF1\n", ddi_driver_name(cdip),
6137c478bd9Sstevel@tonic-gate 			    ddi_get_instance(cdip));
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 			continue;
6167c478bd9Sstevel@tonic-gate 		}
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		/*
6197c478bd9Sstevel@tonic-gate 		 * Only save config registers if not already saved by child.
6207c478bd9Sstevel@tonic-gate 		 */
6217c478bd9Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
6227c478bd9Sstevel@tonic-gate 		    SAVED_CONFIG_REGS) == 1) {
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 			continue;
6257c478bd9Sstevel@tonic-gate 		}
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 		/*
6287c478bd9Sstevel@tonic-gate 		 * The nexus needs to save config registers.  Create a property
6297c478bd9Sstevel@tonic-gate 		 * so it knows to restore on resume.
6307c478bd9Sstevel@tonic-gate 		 */
6317c478bd9Sstevel@tonic-gate 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
6327c478bd9Sstevel@tonic-gate 		    NEXUS_SAVED);
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 		if (ret != DDI_PROP_SUCCESS) {
6357c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d can't update prop %s",
6367c478bd9Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
6377c478bd9Sstevel@tonic-gate 			    NEXUS_SAVED);
6387c478bd9Sstevel@tonic-gate 		}
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 		if (!PM_CAPABLE(pwr_p)) {
6417c478bd9Sstevel@tonic-gate 			(void) pci_save_config_regs(cdip);
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 			continue;
6447c478bd9Sstevel@tonic-gate 		}
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 		mutex_enter(&pwr_p->pwr_mutex);
6477c478bd9Sstevel@tonic-gate 		p = pci_pwr_get_info(pwr_p, cdip);
6487c478bd9Sstevel@tonic-gate 		num_comps = p->num_comps;
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 		/*
6517c478bd9Sstevel@tonic-gate 		 * If a device has components, reset the power level
6527c478bd9Sstevel@tonic-gate 		 * to unknown.  This will ensure that the bus is full
6537c478bd9Sstevel@tonic-gate 		 * power so that saving register won't panic (if
6547c478bd9Sstevel@tonic-gate 		 * the device is already powered off, the child should
6557c478bd9Sstevel@tonic-gate 		 * have already done the save, but an incorrect driver
6567c478bd9Sstevel@tonic-gate 		 * may have forgotten).  If resetting power levels
6577c478bd9Sstevel@tonic-gate 		 * to unknown isn't done here, it would have to be done
6587c478bd9Sstevel@tonic-gate 		 * in resume since pci driver has no way of knowing
6597c478bd9Sstevel@tonic-gate 		 * actual state of HW (power cycle may not have
6607c478bd9Sstevel@tonic-gate 		 * occurred, and it was decided that poking into a
6617c478bd9Sstevel@tonic-gate 		 * child's config space should be avoided unless
6627c478bd9Sstevel@tonic-gate 		 * absolutely necessary).
6637c478bd9Sstevel@tonic-gate 		 */
6647c478bd9Sstevel@tonic-gate 		if (p->comp_pwr == NULL) {
6657c478bd9Sstevel@tonic-gate 			(void) pci_save_config_regs(cdip);
6667c478bd9Sstevel@tonic-gate 		} else {
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 			for (i = 0; i < num_comps; i++) {
6697c478bd9Sstevel@tonic-gate 				pci_pwr_update_comp(pwr_p, p, i,
6707c478bd9Sstevel@tonic-gate 				    PM_LEVEL_UNKNOWN);
6717c478bd9Sstevel@tonic-gate 			}
6727c478bd9Sstevel@tonic-gate 			/*
6737c478bd9Sstevel@tonic-gate 			 * ensure bus power is on before saving
6747c478bd9Sstevel@tonic-gate 			 * config regs.
6757c478bd9Sstevel@tonic-gate 			 */
6767c478bd9Sstevel@tonic-gate 			pci_pwr_change(pwr_p, pwr_p->current_lvl,
6777c478bd9Sstevel@tonic-gate 			    pci_pwr_new_lvl(pwr_p));
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 			(void) pci_save_config_regs(cdip);
6807c478bd9Sstevel@tonic-gate 		}
6817c478bd9Sstevel@tonic-gate 		mutex_exit(&pwr_p->pwr_mutex);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate }
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate void
pci_pwr_component_busy(pci_pwr_t * p)6867c478bd9Sstevel@tonic-gate pci_pwr_component_busy(pci_pwr_t *p)
6877c478bd9Sstevel@tonic-gate {
6887c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&p->pwr_mutex));
6897c478bd9Sstevel@tonic-gate 	if ((p->pwr_flags & PCI_PWR_COMP_BUSY) == 0) {
6907c478bd9Sstevel@tonic-gate 		if (pm_busy_component(p->pwr_dip, PCI_PM_COMP_0) ==
6917c478bd9Sstevel@tonic-gate 		    DDI_FAILURE) {
6927c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
6937c478bd9Sstevel@tonic-gate 			    "%s%d pm_busy_component failed",
6947c478bd9Sstevel@tonic-gate 			    ddi_driver_name(p->pwr_dip),
6957c478bd9Sstevel@tonic-gate 			    ddi_get_instance(p->pwr_dip));
6967c478bd9Sstevel@tonic-gate 		} else {
6977c478bd9Sstevel@tonic-gate 			DEBUG0(DBG_PWR, p->pwr_dip,
6987c478bd9Sstevel@tonic-gate 			    "called PM_BUSY_COMPONENT().  BUSY BIT SET\n");
6997c478bd9Sstevel@tonic-gate 			p->pwr_flags |= PCI_PWR_COMP_BUSY;
7007c478bd9Sstevel@tonic-gate 		}
7017c478bd9Sstevel@tonic-gate 	} else {
7027c478bd9Sstevel@tonic-gate 		DEBUG0(DBG_PWR, p->pwr_dip, "BUSY BIT ALREADY SET\n");
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate void
pci_pwr_component_idle(pci_pwr_t * p)7077c478bd9Sstevel@tonic-gate pci_pwr_component_idle(pci_pwr_t *p)
7087c478bd9Sstevel@tonic-gate {
7097c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&p->pwr_mutex));
7107c478bd9Sstevel@tonic-gate 	if (p->pwr_flags & PCI_PWR_COMP_BUSY) {
7117c478bd9Sstevel@tonic-gate 		if (pm_idle_component(p->pwr_dip, PCI_PM_COMP_0) ==
7127c478bd9Sstevel@tonic-gate 		    DDI_FAILURE) {
7137c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
7147c478bd9Sstevel@tonic-gate 			    "%s%d pm_idle_component failed",
7157c478bd9Sstevel@tonic-gate 			    ddi_driver_name(p->pwr_dip),
7167c478bd9Sstevel@tonic-gate 			    ddi_get_instance(p->pwr_dip));
7177c478bd9Sstevel@tonic-gate 		} else {
7187c478bd9Sstevel@tonic-gate 			DEBUG0(DBG_PWR, p->pwr_dip,
7197c478bd9Sstevel@tonic-gate 			    "called PM_IDLE_COMPONENT() BUSY BIT CLEARED\n");
7207c478bd9Sstevel@tonic-gate 			p->pwr_flags &= ~PCI_PWR_COMP_BUSY;
7217c478bd9Sstevel@tonic-gate 		}
7227c478bd9Sstevel@tonic-gate 	} else {
7237c478bd9Sstevel@tonic-gate 		DEBUG0(DBG_PWR, p->pwr_dip, "BUSY BIT ALREADY CLEARED\n");
7247c478bd9Sstevel@tonic-gate 	}
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate void
pci_pwr_change(pci_pwr_t * pwr_p,int current,int new)7287c478bd9Sstevel@tonic-gate pci_pwr_change(pci_pwr_t *pwr_p, int current, int new)
7297c478bd9Sstevel@tonic-gate {
7307c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pwr_p->pwr_mutex));
7317c478bd9Sstevel@tonic-gate 	if (current == new) {
7327c478bd9Sstevel@tonic-gate 		DEBUG2(DBG_PWR, pwr_p->pwr_dip,
7337c478bd9Sstevel@tonic-gate 		    "No change in power required. Should be "
7347c478bd9Sstevel@tonic-gate 		    "busy. (current=%d) == (new=%d)\n",
7357c478bd9Sstevel@tonic-gate 		    current, new);
7367c478bd9Sstevel@tonic-gate 		pci_pwr_component_busy(pwr_p);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 		return;
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	if (new < current) {
7427c478bd9Sstevel@tonic-gate 		DEBUG2(DBG_PWR, pwr_p->pwr_dip,
7437c478bd9Sstevel@tonic-gate 		    "should be idle (new=%d) < (current=%d)\n",
7447c478bd9Sstevel@tonic-gate 		    new, current);
7457c478bd9Sstevel@tonic-gate 		pci_pwr_component_idle(pwr_p);
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 		return;
7487c478bd9Sstevel@tonic-gate 	}
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	if (new > current) {
7517c478bd9Sstevel@tonic-gate 		DEBUG2(DBG_PWR, pwr_p->pwr_dip, "pwr_change: "
7527c478bd9Sstevel@tonic-gate 		    "pm_raise_power() and should be busy. "
7537c478bd9Sstevel@tonic-gate 		    "(new=%d) > (current=%d)\n", new, current);
7547c478bd9Sstevel@tonic-gate 		pci_pwr_component_busy(pwr_p);
7557c478bd9Sstevel@tonic-gate 		mutex_exit(&pwr_p->pwr_mutex);
7567c478bd9Sstevel@tonic-gate 		if (pm_raise_power(pwr_p->pwr_dip, PCI_PM_COMP_0,
7577c478bd9Sstevel@tonic-gate 		    new) == DDI_FAILURE) {
7587c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d pm_raise_power failed",
7597c478bd9Sstevel@tonic-gate 			    ddi_driver_name(pwr_p->pwr_dip),
7607c478bd9Sstevel@tonic-gate 			    ddi_get_instance(pwr_p->pwr_dip));
7617c478bd9Sstevel@tonic-gate 		}
7627c478bd9Sstevel@tonic-gate 		mutex_enter(&pwr_p->pwr_mutex);
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 		return;
7657c478bd9Sstevel@tonic-gate 	}
7667c478bd9Sstevel@tonic-gate }
767