Skip to content

Commit

Permalink
core/pci: Set slot power limit when supported
Browse files Browse the repository at this point in the history
The PCIe slot capability can be implemented in a root or switch
downstream port to set the maximum power a card is allowed to draw
from the system. This patch adds support for setting the power limit
when the platform has defined one.

Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
  • Loading branch information
oohal authored and stewartsmith committed Apr 11, 2018
1 parent ee7bb4b commit 778d86b
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/pci-slot.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ void pci_slot_add_dt_properties(struct pci_slot *slot,
dt_add_property_cells(np, "ibm,slot-card-desc", slot->card_desc);
dt_add_property_cells(np, "ibm,slot-card-mech", slot->card_mech);
dt_add_property_cells(np, "ibm,slot-wired-lanes", slot->wired_lanes);
dt_add_property_cells(np, "ibm,power-limit", slot->power_limit);

if (slot->ops.add_properties)
slot->ops.add_properties(slot, np);
Expand Down
37 changes: 37 additions & 0 deletions core/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,37 @@ void pci_remove_bus(struct phb *phb, struct list_head *list)
}
}

static void pci_set_power_limit(struct pci_device *pd)
{
uint32_t offset, val;
uint16_t caps;

offset = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
if (!offset)
return; /* legacy dev */

pci_cfg_read16(pd->phb, pd->bdfn,
offset + PCICAP_EXP_CAPABILITY_REG, &caps);

if (!(caps & PCICAP_EXP_CAP_SLOT))
return; /* bridge has no slot capabilities */
if (!pd->slot || !pd->slot->power_limit)
return;

pci_cfg_read32(pd->phb, pd->bdfn, offset + PCICAP_EXP_SLOTCAP, &val);

val = SETFIELD(PCICAP_EXP_SLOTCAP_SPLSC, val, 0); /* 1W scale */
val = SETFIELD(PCICAP_EXP_SLOTCAP_SPLVA, val, pd->slot->power_limit);

pci_cfg_write32(pd->phb, pd->bdfn, offset + PCICAP_EXP_SLOTCAP, val);

/* update the cached copy in the slot */
pd->slot->slot_cap = val;

PCIDBG(pd->phb, pd->bdfn, "Slot power limit set to %dW\n",
pd->slot->power_limit);
}

/* Perform a recursive scan of the bus at bus_number populating
* the list passed as an argument. This also performs the bus
* numbering, so it returns the largest bus number that was
Expand Down Expand Up @@ -777,6 +808,12 @@ uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
rc->subordinate_bus);
}

/* set the power limit for any downstream slots while we're here */
list_for_each(list, pd, link) {
if (pd->is_bridge)
pci_set_power_limit(pd);
}

/*
* We only scan downstream if instructed to do so by the
* caller. Typically we avoid the scan when we know the
Expand Down
1 change: 1 addition & 0 deletions include/pci-slot.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ struct pci_slot {
uint8_t card_desc;
uint8_t card_mech;
uint8_t wired_lanes;
uint8_t power_limit;

/*
* PCI slot is driven by state machine with polling function.
Expand Down

0 comments on commit 778d86b

Please sign in to comment.