Cc: Andrew Jones <drjones@xxxxxxxxxx> Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxx> --- 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 kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/mailman/listinfo/kvmarm