summaryrefslogtreecommitdiff
path: root/src/device
diff options
context:
space:
mode:
Diffstat (limited to 'src/device')
-rw-r--r--src/device/pciexp_device.c108
1 files changed, 69 insertions, 39 deletions
diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c
index 70f65144b6..d8ed5d9e3d 100644
--- a/src/device/pciexp_device.c
+++ b/src/device/pciexp_device.c
@@ -127,66 +127,92 @@ static void pciexp_enable_clock_power_pm(struct device *endp, unsigned int endp_
pci_write_config16(endp, endp_cap + PCI_EXP_LNKCTL, lnkctl);
}
-static void pciexp_config_max_latency(struct device *root, struct device *dev)
+static bool _pciexp_ltr_supported(struct device *dev, unsigned int cap)
{
- unsigned int cap;
- cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_LTR_ID);
- if ((cap) && (root->ops->ops_pci != NULL) &&
- (root->ops->ops_pci->set_ltr_max_latencies != NULL))
- root->ops->ops_pci->set_ltr_max_latencies(dev, cap + 4);
+ return pci_read_config16(dev, cap + PCI_EXP_DEVCAP2) & PCI_EXP_DEVCAP2_LTR;
}
-static bool pciexp_is_ltr_supported(struct device *dev, unsigned int cap)
+static bool _pciexp_ltr_enabled(struct device *dev, unsigned int cap)
{
- unsigned int val;
+ return pci_read_config16(dev, cap + PCI_EXP_DEVCTL2) & PCI_EXP_DEV2_LTR;
+}
- val = pci_read_config16(dev, cap + PCI_EXP_DEVCAP2);
+static bool _pciexp_enable_ltr(struct device *parent, unsigned int parent_cap,
+ struct device *dev, unsigned int cap)
+{
+ if (!_pciexp_ltr_supported(dev, cap)) {
+ printk(BIOS_DEBUG, "%s: No LTR support\n", dev_path(dev));
+ return false;
+ }
- if (val & PCI_EXP_DEVCAP2_LTR)
+ if (_pciexp_ltr_enabled(dev, cap))
return true;
- return false;
+ if (parent &&
+ (parent->path.type != DEVICE_PATH_PCI ||
+ !_pciexp_ltr_supported(parent, parent_cap) ||
+ !_pciexp_ltr_enabled(parent, parent_cap)))
+ return false;
+
+ pci_or_config16(dev, cap + PCI_EXP_DEVCTL2, PCI_EXP_DEV2_LTR);
+ printk(BIOS_INFO, "%s: Enabled LTR\n", dev_path(dev));
+ return true;
}
-static void pciexp_configure_ltr(struct device *dev)
+static void pciexp_enable_ltr(struct device *dev)
{
- unsigned int cap;
-
- cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
+ const unsigned int cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
+ if (!cap)
+ return;
/*
- * Check if capability pointer is valid and
- * device supports LTR mechanism.
+ * If we have get_ltr_max_latencies(), treat `dev` as the root.
+ * If not, let _pciexp_enable_ltr() query the parent's state.
*/
- if (!cap || !pciexp_is_ltr_supported(dev, cap)) {
- printk(BIOS_INFO, "Failed to enable LTR for dev = %s\n",
- dev_path(dev));
- return;
+ struct device *parent = NULL;
+ unsigned int parent_cap = 0;
+ if (!dev->ops->ops_pci || !dev->ops->ops_pci->get_ltr_max_latencies) {
+ parent = dev->bus->dev;
+ parent_cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
+ if (!parent_cap)
+ return;
}
- cap += PCI_EXP_DEVCTL2;
+ (void)_pciexp_enable_ltr(parent, parent_cap, dev, cap);
+}
- /* Enable LTR for device */
- pci_update_config32(dev, cap, ~PCI_EXP_DEV2_LTR, PCI_EXP_DEV2_LTR);
+static bool pciexp_get_ltr_max_latencies(struct device *dev, u16 *max_snoop, u16 *max_nosnoop)
+{
+ /* Walk the hierarchy up to find get_ltr_max_latencies(). */
+ do {
+ if (dev->ops->ops_pci && dev->ops->ops_pci->get_ltr_max_latencies)
+ break;
+ if (dev->bus->dev == dev || dev->bus->dev->path.type != DEVICE_PATH_PCI)
+ return false;
+ dev = dev->bus->dev;
+ } while (true);
- /* Configure Max Snoop Latency */
- pciexp_config_max_latency(dev->bus->dev, dev);
+ dev->ops->ops_pci->get_ltr_max_latencies(max_snoop, max_nosnoop);
+ return true;
}
-static void pciexp_enable_ltr(struct device *dev)
+static void pciexp_configure_ltr(struct device *parent, unsigned int parent_cap,
+ struct device *dev, unsigned int cap)
{
- struct bus *bus;
- struct device *child;
+ if (!_pciexp_enable_ltr(parent, parent_cap, dev, cap))
+ return;
- for (bus = dev->link_list ; bus ; bus = bus->next) {
- for (child = bus->children; child; child = child->sibling) {
- if (child->path.type != DEVICE_PATH_PCI)
- continue;
- pciexp_configure_ltr(child);
- if (child->ops && child->ops->scan_bus)
- pciexp_enable_ltr(child);
- }
- }
+ const unsigned int ltr_cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_LTR_ID);
+ if (!ltr_cap)
+ return;
+
+ u16 max_snoop, max_nosnoop;
+ if (!pciexp_get_ltr_max_latencies(dev, &max_snoop, &max_nosnoop))
+ return;
+
+ pci_write_config16(dev, ltr_cap + PCI_LTR_MAX_SNOOP, max_snoop);
+ pci_write_config16(dev, ltr_cap + PCI_LTR_MAX_NOSNOOP, max_nosnoop);
+ printk(BIOS_INFO, "%s: Programmed LTR max latencies\n", dev_path(dev));
}
static unsigned char pciexp_L1_substate_cal(struct device *dev, unsigned int endp_cap,
@@ -471,12 +497,17 @@ static void pciexp_tune_dev(struct device *dev)
/* Adjust Max_Payload_Size of link ends. */
pciexp_set_max_payload_size(root, root_cap, dev, cap);
+
+ pciexp_configure_ltr(root, root_cap, dev, cap);
}
void pciexp_scan_bus(struct bus *bus, unsigned int min_devfn,
unsigned int max_devfn)
{
struct device *child;
+
+ pciexp_enable_ltr(bus->dev);
+
pci_scan_bus(bus, min_devfn, max_devfn);
for (child = bus->children; child; child = child->sibling) {
@@ -493,7 +524,6 @@ void pciexp_scan_bus(struct bus *bus, unsigned int min_devfn,
void pciexp_scan_bridge(struct device *dev)
{
do_pci_scan_bridge(dev, pciexp_scan_bus);
- pciexp_enable_ltr(dev);
}
/** Default device operations for PCI Express bridges */