pH/kernel/patches/0010-pci-probe-identify-known-devices.patch
2017-10-16 02:36:00 +00:00

191 lines
5.5 KiB
Diff

From 0f320ebfefc339814bc7efe46a83550cc6ee1453 Mon Sep 17 00:00:00 2001
From: Arjan van de Ven <arjan@linux.intel.com>
Date: Sat, 14 Feb 2015 09:49:41 -0600
Subject: [PATCH 10/22] pci: probe: identify known devices
Author: Arjan van de Ven <arjan@linux.intel.com>
Modify-by: Miguel Bernal Marin <miguel.bernal.marin@linux.intel.com>
Signed-off-by: Miguel Bernal Marin <miguel.bernal.marin@linux.intel.com>
---
drivers/pci/probe.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 156 insertions(+)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 73ebc222..d693b6b8 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -163,6 +163,159 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
#define PCI_COMMAND_DECODE_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
+/* shortcut version of __pci_read_base where we know the sizes already */
+int __pci_read_base_shortcut(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int pos, u32 sz_in, u32 sz2_in)
+{
+ u32 l, sz;
+ u64 l64, sz64, mask64;
+ struct pci_bus_region region, inverted_region;
+
+ res->name = pci_name(dev);
+
+ pci_read_config_dword(dev, pos, &l);
+
+ sz = sz_in;
+
+ /*
+ * All bits set in sz means the device isn't working properly.
+ * If the BAR isn't implemented, all bits must be 0. If it's a
+ * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
+ * 1 must be clear.
+ * Here we set the size and is not 0xffffffff
+ */
+
+ /*
+ * I don't know how l can have all bits set. Copied from old code.
+ * Maybe it fixes a bug on some ancient platform.
+ */
+ if (l == 0xffffffff)
+ l = 0;
+
+ if (type == pci_bar_unknown) {
+ res->flags = decode_bar(dev, l);
+ res->flags |= IORESOURCE_SIZEALIGN;
+ if (res->flags & IORESOURCE_IO) {
+ l64 = l & PCI_BASE_ADDRESS_IO_MASK;
+ sz64 = sz & PCI_BASE_ADDRESS_IO_MASK;
+ mask64 = PCI_BASE_ADDRESS_IO_MASK & (u32)IO_SPACE_LIMIT;
+ } else {
+ l64 = l & PCI_BASE_ADDRESS_MEM_MASK;
+ sz64 = sz & PCI_BASE_ADDRESS_MEM_MASK;
+ mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ } else {
+ res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ l64 = l & PCI_ROM_ADDRESS_MASK;
+ sz64 = sz & PCI_ROM_ADDRESS_MASK;
+ mask64 = (u32)PCI_ROM_ADDRESS_MASK;
+ }
+
+ if (res->flags & IORESOURCE_MEM_64) {
+ pci_read_config_dword(dev, pos + 4, &l);
+ sz = sz2_in;
+
+ l64 |= ((u64)l << 32);
+ sz64 |= ((u64)sz << 32);
+ mask64 |= ((u64)~0 << 32);
+ }
+
+ if (!sz64)
+ goto fail;
+
+ sz64 = pci_size(l64, sz64, mask64);
+ if (!sz64) {
+ dev_info(&dev->dev, FW_BUG "reg 0x%x: invalid BAR (can't size)\n",
+ pos);
+ goto fail;
+ }
+
+ if (res->flags & IORESOURCE_MEM_64) {
+ if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
+ sz64 > 0x100000000ULL) {
+ res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
+ res->start = 0;
+ res->end = 0;
+ dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",
+ pos, (unsigned long long)sz64);
+ goto out;
+ }
+
+ if ((sizeof(dma_addr_t) < 8) && l) {
+ /* Above 32-bit boundary; try to reallocate */
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = sz64;
+ dev_info(&dev->dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",
+ pos, (unsigned long long)l64);
+ goto out;
+ }
+ }
+
+ region.start = l64;
+ region.end = l64 + sz64;
+
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ pcibios_resource_to_bus(dev->bus, &inverted_region, res);
+
+ /*
+ * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is
+ * the corresponding resource address (the physical address used by
+ * the CPU. Converting that resource address back to a bus address
+ * should yield the original BAR value:
+ *
+ * resource_to_bus(bus_to_resource(A)) == A
+ *
+ * If it doesn't, CPU accesses to "bus_to_resource(A)" will not
+ * be claimed by the device.
+ */
+ if (inverted_region.start != region.start) {
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = region.end - region.start;
+ dev_info(&dev->dev, "reg 0x%x: initial BAR value %#010llx invalid\n",
+ pos, (unsigned long long)region.start);
+ }
+
+ goto out;
+
+
+fail:
+ res->flags = 0;
+out:
+ if (res->flags)
+ dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res);
+
+ return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
+}
+
+static int is_known_device(struct pci_dev *dev, int pos, int *sz)
+{
+ /* Red Hat, Inc : Virtio network device */
+ if (dev->vendor == 0x1af4 && dev->device == 0x1000) {
+ if (pos == 0x10) {
+ *sz = 0xffffffe1;
+ return 1;
+ }
+ if (pos == 0x14) {
+ *sz = 0xfffff000;
+ return 1;
+ }
+ }
+ /* Red Hat, Inc : Virtio block device */
+ if (dev->vendor == 0x1af4 && dev->device == 0x1001) {
+ if (pos == 0x10) {
+ *sz = 0xffffffc1;
+ return 1;
+ }
+ if (pos == 0x14) {
+ *sz = 0xfffff000;
+ return 1;
+ }
+ }
+ return 0;
+}
+
/**
* pci_read_base - read a PCI BAR
* @dev: the PCI device
@@ -182,6 +335,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
+ if (is_known_device(dev, pos, &sz))
+ return __pci_read_base_shortcut(dev, type, res, pos, sz, 0);
+
res->name = pci_name(dev);
printk("clr: Starting probe for %s\n", res->name);
--
2.11.0