@@ -415,7 +415,7 @@ int pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
EXPORT_SYMBOL_GPL(pci_find_ht_capability);
static struct resource *pci_find_bus_resource(const struct pci_bus *bus,
- struct resource *res)
+ struct resource *res, int flags)
{
struct resource *r;
int i;
@@ -430,7 +430,7 @@ static struct resource *pci_find_bus_resource(const struct pci_bus *bus,
* not, the allocator made a mistake.
*/
if (r->flags & IORESOURCE_PREFETCH &&
- !(res->flags & IORESOURCE_PREFETCH))
+ !(flags & IORESOURCE_PREFETCH))
return NULL;
/*
@@ -458,7 +458,9 @@ static struct resource *pci_find_bus_resource(const struct pci_bus *bus,
struct resource *pci_find_parent_resource(const struct pci_dev *dev,
struct resource *res)
{
- return pci_find_bus_resource(dev->bus, res);
+ int flags = pci_resource_pref_compatible(dev, res);
+
+ return pci_find_bus_resource(dev->bus, res, flags);
}
EXPORT_SYMBOL(pci_find_parent_resource);
@@ -468,7 +470,7 @@ struct resource *pci_find_root_bus_resource(struct pci_bus *bus,
while (bus->parent)
bus = bus->parent;
- return pci_find_bus_resource(bus, res);
+ return pci_find_bus_resource(bus, res, res->flags);
}
/**
@@ -337,4 +337,6 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus);
+int pci_resource_pref_compatible(const struct pci_dev *dev,
+ struct resource *res);
#endif /* DRIVERS_PCI_H */
@@ -1633,6 +1633,36 @@ static void pci_set_msi_domain(struct pci_dev *dev)
dev_get_msi_domain(&dev->bus->dev));
}
+static bool pci_up_path_over_pcie(struct pci_bus *bus)
+{
+ if (pci_is_root_bus(bus))
+ return true;
+
+ if (bus->self && !pci_is_pcie(bus->self))
+ return false;
+
+ return pci_up_path_over_pcie(bus->parent);
+}
+
+/*
+ * According to
+ * https://www.pcisig.com/specifications/pciexpress/base2/PCIe_Base_r2.1_Errata_08Jun10.pdf
+ * page 13, system firmware could put some 64bit non-pref under 64bit pref,
+ * on some cases.
+ * Let's mark if entire path from the host to the adapter is over PCI
+ * Express. later will use that compute pref compaitable bit.
+ */
+static void pci_set_on_all_pcie_path(struct pci_dev *dev)
+{
+ if (!pci_is_pcie(dev))
+ return;
+
+ if (!pci_up_path_over_pcie(dev->bus))
+ return;
+
+ dev->on_all_pcie_path = 1;
+}
+
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
int ret;
@@ -1663,6 +1693,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
/* Initialize various capabilities */
pci_init_capabilities(dev);
+ /* After pcie_cap is assigned */
+ pci_set_on_all_pcie_path(dev);
+
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
@@ -739,6 +739,20 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i)
return -EINVAL;
}
+int pci_resource_pref_compatible(const struct pci_dev *dev,
+ struct resource *res)
+{
+ if (res->flags & IORESOURCE_PREFETCH)
+ return res->flags;
+
+ if ((res->flags & IORESOURCE_MEM) &&
+ (res->flags & IORESOURCE_MEM_64) &&
+ dev->on_all_pcie_path)
+ return res->flags | IORESOURCE_PREFETCH;
+
+ return res->flags;
+}
+
/* Check whether the bridge supports optional I/O and
prefetchable memory ranges. If not, the respective
base/limit registers must be read-only and read as 0. */
@@ -1036,10 +1050,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
resource_size_t r_size;
+ int flags = pci_resource_pref_compatible(dev, r);
- if (r->parent || ((r->flags & mask) != type &&
- (r->flags & mask) != type2 &&
- (r->flags & mask) != type3))
+ if (r->parent || ((flags & mask) != type &&
+ (flags & mask) != type2 &&
+ (flags & mask) != type3))
continue;
r_size = resource_size(r);
#ifdef CONFIG_PCI_IOV
@@ -250,15 +250,19 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
static int _pci_assign_resource(struct pci_dev *dev, int resno,
resource_size_t size, resource_size_t min_align)
{
+ struct resource *res = dev->resource + resno;
+ int old_flags = res->flags;
struct pci_bus *bus;
int ret;
+ res->flags = pci_resource_pref_compatible(dev, res);
bus = dev->bus;
while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
if (!bus->parent || !bus->self->transparent)
break;
bus = bus->parent;
}
+ res->flags = old_flags;
return ret;
}
@@ -311,6 +311,7 @@ struct pci_dev {
powered on/off by the
corresponding bridge */
unsigned int ignore_hotplug:1; /* Ignore hotplug events */
+ unsigned int on_all_pcie_path:1; /* up to host-bridge all pcie */
unsigned int d3_delay; /* D3->D0 transition time in ms */
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */