Cc: Andrew Jones <[email protected]>
Signed-off-by: Alexander Gordeev <[email protected]>
---
 lib/pci-host-generic.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/pci-host-generic.h | 10 ++++++
 2 files changed, 100 insertions(+)

diff --git a/lib/pci-host-generic.c b/lib/pci-host-generic.c
index e479989..b783d29 100644
--- a/lib/pci-host-generic.c
+++ b/lib/pci-host-generic.c
@@ -176,9 +176,91 @@ static struct pci_host_bridge *pci_host_bridge_probe(void)
        return host;
 }
 
+static void pci_get_bar_addr_size(void *conf, int bar, u32 *addr, u32 *size)
+{
+       int off = PCI_BASE_ADDRESS_0 + (bar * 4);
+
+       *addr = pci_config_readl(conf, off);
+       pci_config_writel(~0, conf, off);
+       *size = pci_config_readl(conf, off);
+       pci_config_writel(*addr, conf, off);
+}
+
+static bool pci_get_bar(void *conf, int bar, pci_res_type_t *type,
+                       u64 *addr, u64 *size, bool *is64)
+{
+       u32 addr_low, size_low;
+       u64 mask;
+
+       /*
+        * To determine the amount of address space needed by a PCI device,
+        * one must save the original value of the BAR, write a value of
+        * all 1's to the register, then read it back. The amount of memory
+        * can then be then determined by masking the information bits,
+        * performing a bitwise NOT and incrementing the value by 1.
+        * Use pci_get_bar_addr_size() helper to facilitate that algorithm.
+        */
+       pci_get_bar_addr_size(conf, bar, &addr_low, &size_low);
+
+       if (addr_low & PCI_BASE_ADDRESS_SPACE_IO) {
+               mask = PCI_BASE_ADDRESS_IO_MASK;
+               *type = PCI_RES_TYPE_IO;
+               *is64 = false;
+       } else {
+               mask = PCI_BASE_ADDRESS_MEM_MASK;
+               if ((addr_low & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+                               PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                       if (addr_low & PCI_BASE_ADDRESS_MEM_PREFETCH)
+                               *type = PCI_RES_TYPE_PREFMEM64;
+                       else
+                               *type = PCI_RES_TYPE_MEM64;
+                       *is64 = true;
+               } else {
+                       if (addr_low & PCI_BASE_ADDRESS_MEM_PREFETCH)
+                               *type = PCI_RES_TYPE_PREFMEM32;
+                       else
+                               *type = PCI_RES_TYPE_MEM32;
+                       *is64 = false;
+               }
+       }
+
+       if (*is64) {
+               u32 addr_high, size_high;
+               u64 size64;
+
+               assert(bar < 5);
+               pci_get_bar_addr_size(conf, bar + 1, &addr_high, &size_high);
+
+               size64 = (~((((u64)size_high << 32) | size_low) & mask)) + 1;
+               if (!size64)
+                       return false;
+
+               if (size)
+                       *size = size64;
+               if (addr)
+                       *addr = (((u64)addr_high << 32) | addr_low) & mask;
+       } else {
+               u32 size32;
+
+               size32 = (~(size_low & (u32)mask)) + 1;
+               if (!size32)
+                       return false;
+
+               if (size)
+                       *size = size32;
+               if (addr)
+                       *addr = addr_low & mask;
+       }
+
+       return true;
+}
+
 int pci_bus_scan(struct pci *pci)
 {
        void *conf;
+       pci_res_type_t type;
+       bool is64;
+       int bar;
        int dev;
        int nr_dev = 0;
 
@@ -187,6 +269,14 @@ int pci_bus_scan(struct pci *pci)
                if (pci_config_readb(conf, PCI_HEADER_TYPE) !=
                                           PCI_HEADER_TYPE_NORMAL)
                        continue;
+
+               for (bar = 0; bar < PCI_HEADER_TYPE_NORMAL_NUM_BARS; bar++) {
+                       if (!pci_get_bar(conf, bar, &type, NULL, NULL, &is64))
+                               break;
+                       if (is64)
+                               bar++;
+               }
+
                pci_config_writel(PCI_COMMAND_SERR, conf, PCI_COMMAND);
                nr_dev++;
        }
diff --git a/lib/pci-host-generic.h b/lib/pci-host-generic.h
index c14d32d..132dc5f 100644
--- a/lib/pci-host-generic.h
+++ b/lib/pci-host-generic.h
@@ -21,7 +21,17 @@ struct pci_host_bridge {
        struct pci_addr_space           addr_space[];
 };
 
+typedef enum pci_res_type {
+       PCI_RES_TYPE_CONF               = 0,
+       PCI_RES_TYPE_IO                 = 1,
+       PCI_RES_TYPE_MEM32              = 2,
+       PCI_RES_TYPE_MEM64              = 3,
+       PCI_RES_TYPE_PREFMEM32          = 6,
+       PCI_RES_TYPE_PREFMEM64          = 7
+} pci_res_type_t;
+
 #define PCI_ECAM_BUS_SIZE              (1 << 20)
 #define PCI_ECAM_CONFIG_SIZE           (1 << 12)
+#define PCI_HEADER_TYPE_NORMAL_NUM_BARS        6       /* # of BARs in PCI 
function */
 
 #endif
-- 
1.8.3.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to