x86/PCI: Infrastructure to maintain a list of FW-assigned BIOS BAR values
authorMyron Stowe <mstowe@redhat.com>
Mon, 21 Nov 2011 18:54:13 +0000 (11:54 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 14 Feb 2012 16:44:46 +0000 (08:44 -0800)
Commit 58c84eda075 introduced functionality to try and reinstate the
original BIOS BAR addresses of a PCI device when normal resource
assignment attempts fail.  To keep track of the BIOS BAR addresses,
struct pci_dev was augmented with an array to hold the BAR addresses
of the PCI device: 'resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]'.

The reinstatement of BAR addresses is an uncommon event leaving the
'fw_addr' array unused under normal circumstances.  This functionality
is also currently architecture specific with an implementation limited
to x86.  As the use of struct pci_dev is so prevalent, having the
'fw_addr' array residing within such seems somewhat wasteful.

This patch introduces a stand alone data structure and interfacing
routines for maintaining a list of FW-assigned BIOS BAR value entries.

Signed-off-by: Myron Stowe <myron.stowe@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
arch/x86/pci/i386.c
include/linux/pci.h

index 91821a1a0c3a7e1a7206eba6867dbe5dd8da769d..5a1edf2b53864f1d07d892b669c799c6b656fa9f 100644 (file)
 #include <asm/io_apic.h>
 
 
+/*
+ * This list of dynamic mappings is for temporarily maintaining
+ * original BIOS BAR addresses for possible reinstatement.
+ */
+struct pcibios_fwaddrmap {
+       struct list_head list;
+       struct pci_dev *dev;
+       resource_size_t fw_addr[DEVICE_COUNT_RESOURCE];
+};
+
+static LIST_HEAD(pcibios_fwaddrmappings);
+static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock);
+
+/* Must be called with 'pcibios_fwaddrmap_lock' lock held. */
+static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev)
+{
+       struct pcibios_fwaddrmap *map;
+
+       list_for_each_entry(map, &pcibios_fwaddrmappings, list)
+               if (map->dev == dev)
+                       return map;
+
+       return NULL;
+}
+
+static void
+pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr)
+{
+       unsigned long flags;
+       struct pcibios_fwaddrmap *map;
+
+       spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags);
+       map = pcibios_fwaddrmap_lookup(dev);
+       if (!map) {
+               spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags);
+               map = kzalloc(sizeof(*map), GFP_KERNEL);
+               if (!map)
+                       return;
+
+               map->dev = pci_dev_get(dev);
+               map->fw_addr[idx] = fw_addr;
+               INIT_LIST_HEAD(&map->list);
+
+               spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags);
+               list_add_tail(&map->list, &pcibios_fwaddrmappings);
+       } else
+               map->fw_addr[idx] = fw_addr;
+       spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags);
+}
+
+resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
+{
+       unsigned long flags;
+       struct pcibios_fwaddrmap *map;
+       resource_size_t fw_addr = 0;
+
+       spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags);
+       map = pcibios_fwaddrmap_lookup(dev);
+       if (map)
+               fw_addr = map->fw_addr[idx];
+       spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags);
+
+       return fw_addr;
+}
+
+static void pcibios_fw_addr_list_del(void)
+{
+       unsigned long flags;
+       struct pcibios_fwaddrmap *entry, *next;
+
+       spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags);
+       list_for_each_entry_safe(entry, next, &pcibios_fwaddrmappings, list) {
+               list_del(&entry->list);
+               pci_dev_put(entry->dev);
+               kfree(entry);
+       }
+       spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags);
+}
+
 static int
 skip_isa_ioresource_align(struct pci_dev *dev) {
 
index a16b1df3deff0c47212f09667704ef369a08cf1c..8e9a307e58b896c7d8998dd0803e482086378b4b 100644 (file)
@@ -891,6 +891,7 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void
 int pci_vpd_truncate(struct pci_dev *dev, size_t size);
 
 /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
+resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx);
 void pci_bus_assign_resources(const struct pci_bus *bus);
 void pci_bus_size_bridges(struct pci_bus *bus);
 int pci_claim_resource(struct pci_dev *, int);