191 lines
5.5 KiB
Diff
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, ®ion);
|
|
+ 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
|
|
|