/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
int lvl);
#ifdef DEBUG
"PM_LEVEL_B1", "PM_LEVEL_B0"};
#endif
/*LINTLIBRARY*/
/*
* Retreive the pci_pwr_chld_t structure for a given devinfo node.
*/
{
pci_pwr_chld_t *p;
return (p);
}
}
/*NOTREACHED*/
return (NULL);
}
/*
* Create a pci_pwr_chld_t structure for a given devinfo node.
*/
void
{
pci_pwr_chld_t *p;
/*
* Until components are created for this device, bus
* should be at full power since power of child device
* is unknown. Increment # children requiring "full power"
*/
p->flags |= PWR_FP_HOLD;
}
void
{
int i;
infop = *prev_infop;
break;
}
}
return;
}
/*
* Remove any reference counts for this child.
*/
}
}
}
}
/*
* Allocate space for component state information in pci_pwr_chld_t
*/
void
{
int i;
/*
* Assume the power level of a component is UNKNOWN until
* notified otherwise.
*/
if (num_comps > 0) {
p->comp_pwr =
"ADDING %d COMPONENTS FOR %s@%s\n", num_comps,
} else {
return;
}
/*
* Release the fp hold that was made when the device
* was created.
*/
p->flags &= ~PWR_FP_HOLD;
for (i = 0; i < num_comps; i++) {
/*
* Initialize the component lvl so that the
* state reference counts will be updated correctly.
*/
p->comp_pwr[i] = PM_LEVEL_NOLEVEL;
}
}
/*
* Update the current power level for component. Then adjust the
* bus reference counter for given state.
*/
static void
int lvl)
{
/*
* Remove old pwr state count for old PM level.
*/
case PM_LEVEL_UNKNOWN:
p->u01--;
break;
case PM_LEVEL_D0:
p->u01--;
break;
case PM_LEVEL_D1:
p->u01--;
break;
case PM_LEVEL_D2:
break;
case PM_LEVEL_D3:
break;
default:
break;
}
/*
* Add new pwr state count for the new PM level.
*/
switch (lvl) {
case PM_LEVEL_UNKNOWN:
p->u01++;
break;
case PM_LEVEL_D0:
p->u01++;
break;
case PM_LEVEL_D1:
p->u01++;
break;
case PM_LEVEL_D2:
break;
case PM_LEVEL_D3:
break;
default:
break;
}
}
/*
* Knowing the current state of all devices on the bus, return the
* appropriate supported bus speed.
*/
int
{
int b_lvl;
return (PM_LEVEL_B0);
}
/*
* If any components are at unknown power levels, the
* highest power level has to be assumed for the device (D0).
*/
return (PM_LEVEL_B0);
}
/*
* Find the lowest theoretical level
* the bus can operate at.
*/
b_lvl = PM_LEVEL_B0;
"new_lvl: PM_LEVEL_B0 d0 count = %d\n",
b_lvl = PM_LEVEL_B1;
"new_lvl: PM_LEVEL_B1 d1 count = %d\n",
b_lvl = PM_LEVEL_B2;
"new_lvl: PM_LEVEL_B2 d2 count = %d\n",
b_lvl = PM_LEVEL_B3;
"new_lvl: PM_LEVEL_B3 d3 count = %d\n",
} else {
"new_lvl: PM_LEVEL_B3: all counts are 0\n");
b_lvl = PM_LEVEL_B3;
}
/*
* Now find the closest supported level available.
* If the level isn't available, have to find the
* next highest power level (or lowest in B# terms).
*/
switch (b_lvl) {
case PM_LEVEL_B3:
break;
}
/*FALLTHROUGH*/
case PM_LEVEL_B2:
b_lvl = PM_LEVEL_B2;
break;
}
/*FALLTHROUGH*/
case PM_LEVEL_B1:
b_lvl = PM_LEVEL_B1;
break;
}
/*FALLTHROUGH*/
case PM_LEVEL_B0:
/*
* This level always supported
*/
b_lvl = PM_LEVEL_B0;
break;
}
"new_lvl: Adjusted Level is %s\n",
return (b_lvl);
}
int
{
BUS_POWER_NEXUS_PWRUP, (void *) &bpn,
(void *) &pwrup_res);
}
return (ret);
}
int
{
switch (op) {
case BUS_POWER_HAS_CHANGED:
"old = %d new = %d\n",
if (*res == DDI_FAILURE) {
break;
} else {
/*
* pci_pwr_add_components must be called here if
* comp_pwr hasn't been set up yet. It has to be done
* here rather than in post-attach, since it is possible
* for power() of child to get called before attach
* completes.
*/
}
else
break;
"new = %d. TEMP FULL POWER\n",
/*
* Any state changes require that the bus be at full
* power (B0) so that the device configuration
* registers can be accessed. Make a fp hold here
* so device remains at full power during power
* configuration.
*/
break;
if (*res == DDI_FAILURE) {
} else {
/*
* pci_pwr_add_components must be called here if
* comp_pwr hasen't been set up yet. It has to be done
* here rather than in post-attach, since it is possible
* for power() of child to get called before attach
* completes.
*/
}
else
break;
default:
}
return (ret);
}
void
{
/*
* Inform the PM framework of the current state of the device.
* (it is unknown to PM framework at this point).
*/
if (PM_CAPABLE(pwr_p)) {
pwr_p->current_lvl);
}
/*
* Restore config registers for children that did not save
* their own registers. Children pwr states are UNKNOWN after
* a resume since it is possible for the PM framework to call
* resume without an actual power cycle. (ie if suspend fails).
*/
/*
* Not interested in children who are not already
* init'ed. They will be set up by init_child().
*/
"DDI_RESUME: skipping %s%d not in CF1\n",
continue;
}
/*
* Only restore config registers if saved by nexus.
*/
NEXUS_SAVED) == 1) {
(void) pci_restore_config_regs(cdip);
"DDI_RESUME: nexus restoring %s%d config regs\n",
NEXUS_SAVED) != DDI_PROP_SUCCESS) {
}
}
}
}
void
{
/*
* Save the state of the configuration headers of child
* nodes.
*/
pci_pwr_chld_t *p;
int i;
int num_comps;
int ret;
/*
* Not interested in children who are not already
* init'ed. They will be set up in init_child().
*/
continue;
}
/*
* Only save config registers if not already saved by child.
*/
SAVED_CONFIG_REGS) == 1) {
continue;
}
/*
* The nexus needs to save config registers. Create a property
* so it knows to restore on resume.
*/
if (ret != DDI_PROP_SUCCESS) {
}
if (!PM_CAPABLE(pwr_p)) {
(void) pci_save_config_regs(cdip);
continue;
}
/*
* If a device has components, reset the power level
* to unknown. This will ensure that the bus is full
* power so that saving register won't panic (if
* the device is already powered off, the child should
* have already done the save, but an incorrect driver
* may have forgotten). If resetting power levels
* to unknown isn't done here, it would have to be done
* in resume since pci driver has no way of knowing
* actual state of HW (power cycle may not have
* occurred, and it was decided that poking into a
* child's config space should be avoided unless
* absolutely necessary).
*/
(void) pci_save_config_regs(cdip);
} else {
for (i = 0; i < num_comps; i++) {
pci_pwr_update_comp(pwr_p, p, i,
}
/*
* ensure bus power is on before saving
* config regs.
*/
(void) pci_save_config_regs(cdip);
}
}
}
void
{
if ((p->pwr_flags & PCI_PWR_COMP_BUSY) == 0) {
DDI_FAILURE) {
"%s%d pm_busy_component failed",
ddi_driver_name(p->pwr_dip),
ddi_get_instance(p->pwr_dip));
} else {
"called PM_BUSY_COMPONENT(). BUSY BIT SET\n");
p->pwr_flags |= PCI_PWR_COMP_BUSY;
}
} else {
}
}
void
{
if (p->pwr_flags & PCI_PWR_COMP_BUSY) {
DDI_FAILURE) {
"%s%d pm_idle_component failed",
ddi_driver_name(p->pwr_dip),
ddi_get_instance(p->pwr_dip));
} else {
"called PM_IDLE_COMPONENT() BUSY BIT CLEARED\n");
p->pwr_flags &= ~PCI_PWR_COMP_BUSY;
}
} else {
}
}
void
{
"No change in power required. Should be "
"busy. (current=%d) == (new=%d)\n",
return;
}
"should be idle (new=%d) < (current=%d)\n",
return;
}
"pm_raise_power() and should be busy. "
new) == DDI_FAILURE) {
}
return;
}
}