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