@@ -343,7 +343,7 @@ static void pci_register_region(struct pci_bus *bus, const char *name,
region.start = rstart;
region.end = rstart + size - 1UL;
pcibios_bus_to_resource(bus, res, ®ion);
- bus_res = pci_find_bus_resource(bus, res);
+ bus_res = pci_find_bus_resource(bus, res, res->flags);
if (!bus_res) {
kfree(res);
return;
@@ -415,7 +415,7 @@ int pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
EXPORT_SYMBOL_GPL(pci_find_ht_capability);
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 @@ 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 @@ 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);
@@ -336,4 +336,6 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
}
#endif
+int pci_resource_pref_compatible(const struct pci_dev *dev,
+ struct resource *res);
#endif /* DRIVERS_PCI_H */
@@ -1732,6 +1732,36 @@ static void pci_dma_configure(struct pci_dev *dev)
pci_put_host_bridge_device(bridge);
}
+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;
@@ -1762,6 +1792,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.
@@ -738,6 +738,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. */
@@ -1035,11 +1049,12 @@ 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 & IORESOURCE_PCI_FIXED) ||
- ((r->flags & mask) != type &&
- (r->flags & mask) != type2 &&
- (r->flags & mask) != type3))
+ if (r->parent || (flags & IORESOURCE_PCI_FIXED) ||
+ ((flags & mask) != type &&
+ (flags & mask) != type2 &&
+ (flags & mask) != type3))
continue;
r_size = resource_size(r);
#ifdef CONFIG_PCI_IOV
@@ -257,15 +257,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;
}
@@ -303,6 +303,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 */
@@ -806,7 +807,7 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
struct pci_bus_region *region);
struct resource *pci_find_bus_resource(const struct pci_bus *bus,
- struct resource *res);
+ struct resource *res, int flags);
void pcibios_scan_specific_bus(int busn);
struct pci_bus *pci_find_bus(int domain, int busnr);
void pci_bus_add_devices(const struct pci_bus *bus);