From c662d99134b67c58e63ecc17c2531588a3a51596 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sat, 14 Feb 2015 09:49:41 -0600 Subject: [PATCH 110/114] pci: probe: identify known devices Author: Arjan van de Ven Modify-by: Miguel Bernal Marin Signed-off-by: Miguel Bernal Marin --- drivers/pci/probe.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7399a06698da..4fb2d7fed4c5 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.1