Scan bus 0 only and function 0 only on each device. Operations pci_config_read*/pci_config_write* are just wrappers over read*/write* accessors and only needed to emphasize PCI configuration space access. Cc: Thomas Huth <thuth@xxxxxxxxxx> Cc: Andrew Jones <drjones@xxxxxxxxxx> Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxx> --- lib/pci-host-generic.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++- lib/pci-host-generic.h | 34 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/lib/pci-host-generic.c b/lib/pci-host-generic.c index 9bc6642..fd3bb34 100644 --- a/lib/pci-host-generic.c +++ b/lib/pci-host-generic.c @@ -38,6 +38,18 @@ static pci_res_type_t flags_to_type(u32 of_flags) return ((of_flags & 0x40000000) >> 28) | ((of_flags >> 24) & 0x03); } +int find_next_dev(struct gen_pci *pci, int dev) +{ + for (dev++; dev < PCI_NUM_DEVICES; dev++) { + void *conf = dev_conf(pci, dev); + + if (pci_config_readl(conf, PCI_VENDOR_ID) != ((u32)~0)) + return dev; + } + + return -1; +} + static u32 from_fdt32(fdt32_t val) { return fdt32_to_cpu(val); @@ -199,20 +211,80 @@ static struct gen_pci *gen_pci_probe(void) return pci; } +static void pci_dev_print(void *conf) +{ + u16 vendor_id = pci_config_readw(conf, PCI_VENDOR_ID); + u16 device_id = pci_config_readw(conf, PCI_DEVICE_ID); + u8 header = pci_config_readb(conf, PCI_HEADER_TYPE); + u8 progif = pci_config_readb(conf, PCI_CLASS_PROG); + u8 subcl = pci_config_readb(conf, PCI_CLASS_DEVICE); + u8 class = pci_config_readb(conf, PCI_CLASS_DEVICE + 1); + + printf("conf %p vendor_id %04x device_id %04x type %d " + "progif %02x class %02x subcl %02x\n", + conf, vendor_id, device_id, header, + progif, class, subcl); +} + +static int pci_bus_scan(struct gen_pci *pci) +{ + int dev; + int nr_dev = 0; + + if (!pci) + return -1; + + for_each_pci_dev(pci, dev) { + void *conf = dev_conf(pci, dev); + pci_dev_print(conf); + + /* We are only interested in normal PCI devices */ + if (pci_config_readb(conf, PCI_HEADER_TYPE) != + PCI_HEADER_TYPE_NORMAL) + continue; + + pci_config_writel(PCI_COMMAND_SERR, conf, PCI_COMMAND); + nr_dev++; + } + + return nr_dev; +} + bool pci_probe(void) { struct gen_pci *pci = get_pci(); assert(pci == NULL); pci = gen_pci_probe(); + if (!pci) + goto probe; set_pci(pci); - return (pci != NULL); + if (pci_bus_scan(pci) < 0) + goto scan; + + return true; + +scan: + pci_shutdown(); + +probe: + return false; } void pci_shutdown(void) { struct gen_pci *pci = get_pci(); + int dev; + + if (!pci) + return; + + for_each_pci_dev(pci, dev) { + void *conf = dev_conf(pci, dev); + + pci_config_writel(PCI_COMMAND_INTX_DISABLE, conf, PCI_COMMAND); + } set_pci(NULL); free(pci); diff --git a/lib/pci-host-generic.h b/lib/pci-host-generic.h index f4ae3e4..1d86b43 100644 --- a/lib/pci-host-generic.h +++ b/lib/pci-host-generic.h @@ -69,5 +69,39 @@ typedef enum pci_res_type { * */ #define PCI_ECAM_BUS_SIZE (1 << 20) +#define PCI_NUM_DEVICES (PCI_ECAM_BUS_SIZE / (1 << 15)) +#define PCI_ECAM_CONFIG_SIZE (1 << 12) + +#define for_each_pci_dev(pci, dev) \ + for (dev = find_next_dev(pci, -1); \ + dev >= 0; \ + dev = find_next_dev(pci, dev)) + +static void* dev_conf(struct gen_pci *pci, int dev) +{ + return (void*)pci->cpu_range.start + (dev * 8) * PCI_ECAM_CONFIG_SIZE; +} + +static u8 pci_config_readb(const void *conf, int off) +{ + return readb(conf + off); +} + +static u16 pci_config_readw(const void *conf, int off) +{ + return readw(conf + off); +} + +static u32 pci_config_readl(const void *conf, int off) +{ + return readl(conf + off); +} + +static void pci_config_writel(u32 val, void *conf, int off) +{ + writel(val, conf + off); +} + +extern int find_next_dev(struct gen_pci *pci, int dev); #endif -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html