133d78b07SAlexander Gordeev /* 233d78b07SAlexander Gordeev * Generic PCI host controller as described in PCI Bus Binding to Open Firmware 333d78b07SAlexander Gordeev * 433d78b07SAlexander Gordeev * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <agordeev@redhat.com> 533d78b07SAlexander Gordeev * 633d78b07SAlexander Gordeev * This work is licensed under the terms of the GNU LGPL, version 2. 733d78b07SAlexander Gordeev */ 833d78b07SAlexander Gordeev #include "libcflat.h" 933d78b07SAlexander Gordeev #include "devicetree.h" 1033d78b07SAlexander Gordeev #include "alloc.h" 1133d78b07SAlexander Gordeev #include "pci.h" 1233d78b07SAlexander Gordeev #include "asm/pci.h" 1333d78b07SAlexander Gordeev #include "asm/io.h" 1433d78b07SAlexander Gordeev #include "pci-host-generic.h" 1533d78b07SAlexander Gordeev #include <linux/pci_regs.h> 1633d78b07SAlexander Gordeev 1733d78b07SAlexander Gordeev static struct pci_host_bridge *pci_host_bridge; 1833d78b07SAlexander Gordeev 1933d78b07SAlexander Gordeev static int of_flags_to_pci_type(u32 of_flags) 2033d78b07SAlexander Gordeev { 2133d78b07SAlexander Gordeev static int type_map[] = { 2233d78b07SAlexander Gordeev [1] = PCI_BASE_ADDRESS_SPACE_IO, 2333d78b07SAlexander Gordeev [2] = PCI_BASE_ADDRESS_MEM_TYPE_32, 2433d78b07SAlexander Gordeev [3] = PCI_BASE_ADDRESS_MEM_TYPE_64 2533d78b07SAlexander Gordeev }; 2633d78b07SAlexander Gordeev int idx = (of_flags >> 24) & 0x03; 2733d78b07SAlexander Gordeev int res; 2833d78b07SAlexander Gordeev 2933d78b07SAlexander Gordeev assert(idx > 0); 3033d78b07SAlexander Gordeev res = type_map[idx]; 3133d78b07SAlexander Gordeev 3233d78b07SAlexander Gordeev if (of_flags & 0x40000000) 3333d78b07SAlexander Gordeev res |= PCI_BASE_ADDRESS_MEM_PREFETCH; 3433d78b07SAlexander Gordeev 3533d78b07SAlexander Gordeev return res; 3633d78b07SAlexander Gordeev } 3733d78b07SAlexander Gordeev 3833d78b07SAlexander Gordeev static int pci_bar_type(u32 bar) 3933d78b07SAlexander Gordeev { 4033d78b07SAlexander Gordeev if (bar & PCI_BASE_ADDRESS_SPACE) 4133d78b07SAlexander Gordeev return PCI_BASE_ADDRESS_SPACE_IO; 4233d78b07SAlexander Gordeev else 4333d78b07SAlexander Gordeev return bar & (PCI_BASE_ADDRESS_MEM_TYPE_MASK | 4433d78b07SAlexander Gordeev PCI_BASE_ADDRESS_MEM_PREFETCH); 4533d78b07SAlexander Gordeev } 4633d78b07SAlexander Gordeev 4733d78b07SAlexander Gordeev /* 4833d78b07SAlexander Gordeev * Probe DT for a generic PCI host controller 4933d78b07SAlexander Gordeev * See kernel Documentation/devicetree/bindings/pci/host-generic-pci.txt 5033d78b07SAlexander Gordeev * and function gen_pci_probe() in drivers/pci/host/pci-host-generic.c 5133d78b07SAlexander Gordeev */ 5233d78b07SAlexander Gordeev static struct pci_host_bridge *pci_dt_probe(void) 5333d78b07SAlexander Gordeev { 5433d78b07SAlexander Gordeev struct pci_host_bridge *host; 5533d78b07SAlexander Gordeev const void *fdt = dt_fdt(); 5633d78b07SAlexander Gordeev const struct fdt_property *prop; 5733d78b07SAlexander Gordeev struct dt_pbus_reg base; 5833d78b07SAlexander Gordeev struct dt_device dt_dev; 5933d78b07SAlexander Gordeev struct dt_bus dt_bus; 6033d78b07SAlexander Gordeev struct pci_addr_space *as; 6133d78b07SAlexander Gordeev fdt32_t *data; 6233d78b07SAlexander Gordeev u32 bus, bus_max; 6333d78b07SAlexander Gordeev u32 nac, nsc, nac_root, nsc_root; 6433d78b07SAlexander Gordeev int nr_range_cells, nr_addr_spaces; 6533d78b07SAlexander Gordeev int ret, node, len, i; 6633d78b07SAlexander Gordeev 6733d78b07SAlexander Gordeev if (!dt_available()) { 6833d78b07SAlexander Gordeev printf("No device tree found\n"); 6933d78b07SAlexander Gordeev return NULL; 7033d78b07SAlexander Gordeev } 7133d78b07SAlexander Gordeev 7233d78b07SAlexander Gordeev dt_bus_init_defaults(&dt_bus); 7333d78b07SAlexander Gordeev dt_device_init(&dt_dev, &dt_bus, NULL); 7433d78b07SAlexander Gordeev 7533d78b07SAlexander Gordeev node = fdt_path_offset(fdt, "/"); 7633d78b07SAlexander Gordeev assert(node >= 0); 7733d78b07SAlexander Gordeev 7833d78b07SAlexander Gordeev ret = dt_get_nr_cells(node, &nac_root, &nsc_root); 7933d78b07SAlexander Gordeev assert(ret == 0); 8033d78b07SAlexander Gordeev assert(nac_root == 1 || nac_root == 2); 8133d78b07SAlexander Gordeev 8233d78b07SAlexander Gordeev node = fdt_node_offset_by_compatible(fdt, node, 8333d78b07SAlexander Gordeev "pci-host-ecam-generic"); 8433d78b07SAlexander Gordeev if (node == -FDT_ERR_NOTFOUND) { 8533d78b07SAlexander Gordeev printf("No PCIe ECAM compatible controller found\n"); 8633d78b07SAlexander Gordeev return NULL; 8733d78b07SAlexander Gordeev } 8833d78b07SAlexander Gordeev assert(node >= 0); 8933d78b07SAlexander Gordeev 9033d78b07SAlexander Gordeev prop = fdt_get_property(fdt, node, "device_type", &len); 9133d78b07SAlexander Gordeev assert(prop && len == 4 && !strcmp((char *)prop->data, "pci")); 9233d78b07SAlexander Gordeev 9333d78b07SAlexander Gordeev dt_device_bind_node(&dt_dev, node); 9433d78b07SAlexander Gordeev ret = dt_pbus_get_base(&dt_dev, &base); 9533d78b07SAlexander Gordeev assert(ret == 0); 9633d78b07SAlexander Gordeev 9733d78b07SAlexander Gordeev prop = fdt_get_property(fdt, node, "bus-range", &len); 9833d78b07SAlexander Gordeev if (prop == NULL) { 9933d78b07SAlexander Gordeev assert(len == -FDT_ERR_NOTFOUND); 10033d78b07SAlexander Gordeev bus = 0x00; 10133d78b07SAlexander Gordeev bus_max = 0xff; 10233d78b07SAlexander Gordeev } else { 10333d78b07SAlexander Gordeev data = (fdt32_t *)prop->data; 10433d78b07SAlexander Gordeev bus = fdt32_to_cpu(data[0]); 10533d78b07SAlexander Gordeev bus_max = fdt32_to_cpu(data[1]); 10633d78b07SAlexander Gordeev assert(bus <= bus_max); 10733d78b07SAlexander Gordeev } 10833d78b07SAlexander Gordeev assert(bus_max < base.size / (1 << PCI_ECAM_BUS_SHIFT)); 10933d78b07SAlexander Gordeev 11033d78b07SAlexander Gordeev ret = dt_get_nr_cells(node, &nac, &nsc); 11133d78b07SAlexander Gordeev assert(ret == 0); 11233d78b07SAlexander Gordeev assert(nac == 3 && nsc == 2); 11333d78b07SAlexander Gordeev 11433d78b07SAlexander Gordeev prop = fdt_get_property(fdt, node, "ranges", &len); 11533d78b07SAlexander Gordeev assert(prop != NULL); 11633d78b07SAlexander Gordeev 11733d78b07SAlexander Gordeev nr_range_cells = nac + nsc + nac_root; 11833d78b07SAlexander Gordeev nr_addr_spaces = (len / 4) / nr_range_cells; 11933d78b07SAlexander Gordeev assert(nr_addr_spaces); 12033d78b07SAlexander Gordeev 12133d78b07SAlexander Gordeev host = malloc(sizeof(*host) + 12233d78b07SAlexander Gordeev sizeof(host->addr_space[0]) * nr_addr_spaces); 12333d78b07SAlexander Gordeev assert(host != NULL); 12433d78b07SAlexander Gordeev 12533d78b07SAlexander Gordeev host->start = base.addr; 12633d78b07SAlexander Gordeev host->size = base.size; 12733d78b07SAlexander Gordeev host->bus = bus; 12833d78b07SAlexander Gordeev host->bus_max = bus_max; 12933d78b07SAlexander Gordeev host->nr_addr_spaces = nr_addr_spaces; 13033d78b07SAlexander Gordeev 13133d78b07SAlexander Gordeev data = (fdt32_t *)prop->data; 13233d78b07SAlexander Gordeev as = &host->addr_space[0]; 13333d78b07SAlexander Gordeev 13433d78b07SAlexander Gordeev for (i = 0; i < nr_addr_spaces; i++) { 13533d78b07SAlexander Gordeev /* 13633d78b07SAlexander Gordeev * The PCI binding encodes the PCI address with three 13733d78b07SAlexander Gordeev * cells as follows: 13833d78b07SAlexander Gordeev * 13933d78b07SAlexander Gordeev * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr 14033d78b07SAlexander Gordeev * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh 14133d78b07SAlexander Gordeev * phys.lo cell: llllllll llllllll llllllll llllllll 14233d78b07SAlexander Gordeev * 14333d78b07SAlexander Gordeev * PCI device bus address and flags are encoded into phys.high 14433d78b07SAlexander Gordeev * PCI 64 bit address is encoded into phys.mid and phys.low 14533d78b07SAlexander Gordeev */ 14633d78b07SAlexander Gordeev as->type = of_flags_to_pci_type(fdt32_to_cpu(data[0])); 14733d78b07SAlexander Gordeev as->pci_start = ((u64)fdt32_to_cpu(data[1]) << 32) | 14833d78b07SAlexander Gordeev fdt32_to_cpu(data[2]); 14933d78b07SAlexander Gordeev 15033d78b07SAlexander Gordeev if (nr_range_cells == 6) { 15133d78b07SAlexander Gordeev as->start = fdt32_to_cpu(data[3]); 15233d78b07SAlexander Gordeev as->size = ((u64)fdt32_to_cpu(data[4]) << 32) | 15333d78b07SAlexander Gordeev fdt32_to_cpu(data[5]); 15433d78b07SAlexander Gordeev } else { 15533d78b07SAlexander Gordeev as->start = ((u64)fdt32_to_cpu(data[3]) << 32) | 15633d78b07SAlexander Gordeev fdt32_to_cpu(data[4]); 15733d78b07SAlexander Gordeev as->size = ((u64)fdt32_to_cpu(data[5]) << 32) | 15833d78b07SAlexander Gordeev fdt32_to_cpu(data[6]); 15933d78b07SAlexander Gordeev } 16033d78b07SAlexander Gordeev 16133d78b07SAlexander Gordeev data += nr_range_cells; 16233d78b07SAlexander Gordeev as++; 16333d78b07SAlexander Gordeev } 16433d78b07SAlexander Gordeev 16533d78b07SAlexander Gordeev return host; 16633d78b07SAlexander Gordeev } 16733d78b07SAlexander Gordeev 1684d6cefa9SPeter Xu static bool pci_alloc_resource(struct pci_dev *dev, int bar_num, u64 *addr) 16933d78b07SAlexander Gordeev { 17033d78b07SAlexander Gordeev struct pci_host_bridge *host = pci_host_bridge; 17133d78b07SAlexander Gordeev struct pci_addr_space *as = &host->addr_space[0]; 172ae88ce59SAlexander Gordeev u32 bar; 173ae17c20eSAlexander Gordeev u64 size, pci_addr; 17433d78b07SAlexander Gordeev int type, i; 17533d78b07SAlexander Gordeev 176*afb9a024SAlexander Gordeev *addr = INVALID_PHYS_ADDR; 17733d78b07SAlexander Gordeev 17833d78b07SAlexander Gordeev size = pci_bar_size(dev, bar_num); 17933d78b07SAlexander Gordeev if (!size) 18033d78b07SAlexander Gordeev return false; 18133d78b07SAlexander Gordeev 18233d78b07SAlexander Gordeev bar = pci_bar_get(dev, bar_num); 18333d78b07SAlexander Gordeev type = pci_bar_type(bar); 18433d78b07SAlexander Gordeev if (type & PCI_BASE_ADDRESS_MEM_TYPE_MASK) 18533d78b07SAlexander Gordeev type &= ~PCI_BASE_ADDRESS_MEM_PREFETCH; 18633d78b07SAlexander Gordeev 18733d78b07SAlexander Gordeev for (i = 0; i < host->nr_addr_spaces; i++) { 18833d78b07SAlexander Gordeev if (as->type == type) 18933d78b07SAlexander Gordeev break; 19033d78b07SAlexander Gordeev as++; 19133d78b07SAlexander Gordeev } 19233d78b07SAlexander Gordeev 19333d78b07SAlexander Gordeev if (i >= host->nr_addr_spaces) { 19433d78b07SAlexander Gordeev printf("%s: warning: can't satisfy request for ", __func__); 1954d6cefa9SPeter Xu pci_dev_print_id(dev->bdf); 19633d78b07SAlexander Gordeev printf(" "); 19733d78b07SAlexander Gordeev pci_bar_print(dev, bar_num); 19833d78b07SAlexander Gordeev printf("\n"); 19933d78b07SAlexander Gordeev return false; 20033d78b07SAlexander Gordeev } 20133d78b07SAlexander Gordeev 202ae17c20eSAlexander Gordeev pci_addr = ALIGN(as->pci_start + as->allocated, size); 203ae17c20eSAlexander Gordeev size += pci_addr - (as->pci_start + as->allocated); 20433d78b07SAlexander Gordeev assert(as->allocated + size <= as->size); 205ae17c20eSAlexander Gordeev *addr = pci_addr; 20633d78b07SAlexander Gordeev as->allocated += size; 20733d78b07SAlexander Gordeev 20833d78b07SAlexander Gordeev return true; 20933d78b07SAlexander Gordeev } 21033d78b07SAlexander Gordeev 21133d78b07SAlexander Gordeev bool pci_probe(void) 21233d78b07SAlexander Gordeev { 2134d6cefa9SPeter Xu struct pci_dev pci_dev; 21433d78b07SAlexander Gordeev pcidevaddr_t dev; 21533d78b07SAlexander Gordeev u8 header; 21633d78b07SAlexander Gordeev u32 cmd; 21733d78b07SAlexander Gordeev int i; 21833d78b07SAlexander Gordeev 21933d78b07SAlexander Gordeev assert(!pci_host_bridge); 22033d78b07SAlexander Gordeev pci_host_bridge = pci_dt_probe(); 22133d78b07SAlexander Gordeev if (!pci_host_bridge) 22233d78b07SAlexander Gordeev return false; 22333d78b07SAlexander Gordeev 2244d6cefa9SPeter Xu for (dev = 0; dev < PCI_DEVFN_MAX; dev++) { 22533d78b07SAlexander Gordeev if (!pci_dev_exists(dev)) 22633d78b07SAlexander Gordeev continue; 22733d78b07SAlexander Gordeev 2284d6cefa9SPeter Xu pci_dev_init(&pci_dev, dev); 2294d6cefa9SPeter Xu 23033d78b07SAlexander Gordeev /* We are only interested in normal PCI devices */ 23133d78b07SAlexander Gordeev header = pci_config_readb(dev, PCI_HEADER_TYPE); 23233d78b07SAlexander Gordeev if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL) 23333d78b07SAlexander Gordeev continue; 23433d78b07SAlexander Gordeev 23533d78b07SAlexander Gordeev cmd = PCI_COMMAND_SERR | PCI_COMMAND_PARITY; 23633d78b07SAlexander Gordeev 237e954ce23SPeter Xu for (i = 0; i < PCI_BAR_NUM; i++) { 23833d78b07SAlexander Gordeev u64 addr; 23933d78b07SAlexander Gordeev 2404d6cefa9SPeter Xu if (pci_alloc_resource(&pci_dev, i, &addr)) { 2414d6cefa9SPeter Xu pci_bar_set_addr(&pci_dev, i, addr); 24233d78b07SAlexander Gordeev 2434d6cefa9SPeter Xu if (pci_bar_is_memory(&pci_dev, i)) 24433d78b07SAlexander Gordeev cmd |= PCI_COMMAND_MEMORY; 24533d78b07SAlexander Gordeev else 24633d78b07SAlexander Gordeev cmd |= PCI_COMMAND_IO; 24733d78b07SAlexander Gordeev } 24833d78b07SAlexander Gordeev 2494d6cefa9SPeter Xu if (pci_bar_is64(&pci_dev, i)) 25033d78b07SAlexander Gordeev i++; 25133d78b07SAlexander Gordeev } 25233d78b07SAlexander Gordeev 25333d78b07SAlexander Gordeev pci_config_writew(dev, PCI_COMMAND, cmd); 25433d78b07SAlexander Gordeev } 25533d78b07SAlexander Gordeev 25633d78b07SAlexander Gordeev return true; 25733d78b07SAlexander Gordeev } 25833d78b07SAlexander Gordeev 25933d78b07SAlexander Gordeev /* 26033d78b07SAlexander Gordeev * This function is to be called from pci_translate_addr() to provide 26133d78b07SAlexander Gordeev * mapping between this host bridge's PCI busses address and CPU physical 26233d78b07SAlexander Gordeev * address. 26333d78b07SAlexander Gordeev */ 26433d78b07SAlexander Gordeev phys_addr_t pci_host_bridge_get_paddr(u64 pci_addr) 26533d78b07SAlexander Gordeev { 26633d78b07SAlexander Gordeev struct pci_host_bridge *host = pci_host_bridge; 26733d78b07SAlexander Gordeev struct pci_addr_space *as = &host->addr_space[0]; 26833d78b07SAlexander Gordeev int i; 26933d78b07SAlexander Gordeev 27033d78b07SAlexander Gordeev for (i = 0; i < host->nr_addr_spaces; i++) { 27133d78b07SAlexander Gordeev if (pci_addr >= as->pci_start && 27233d78b07SAlexander Gordeev pci_addr < as->pci_start + as->size) 27333d78b07SAlexander Gordeev return as->start + (pci_addr - as->pci_start); 27433d78b07SAlexander Gordeev as++; 27533d78b07SAlexander Gordeev } 27633d78b07SAlexander Gordeev 277b8b9fcf5SAlexander Gordeev return INVALID_PHYS_ADDR; 27833d78b07SAlexander Gordeev } 27933d78b07SAlexander Gordeev 28033d78b07SAlexander Gordeev static void __iomem *pci_get_dev_conf(struct pci_host_bridge *host, int devfn) 28133d78b07SAlexander Gordeev { 28233d78b07SAlexander Gordeev return (void __iomem *)(unsigned long) 28333d78b07SAlexander Gordeev host->start + (devfn << PCI_ECAM_DEVFN_SHIFT); 28433d78b07SAlexander Gordeev } 28533d78b07SAlexander Gordeev 28633d78b07SAlexander Gordeev u8 pci_config_readb(pcidevaddr_t dev, u8 off) 28733d78b07SAlexander Gordeev { 28833d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 28933d78b07SAlexander Gordeev return readb(conf + off); 29033d78b07SAlexander Gordeev } 29133d78b07SAlexander Gordeev 29233d78b07SAlexander Gordeev u16 pci_config_readw(pcidevaddr_t dev, u8 off) 29333d78b07SAlexander Gordeev { 29433d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 29533d78b07SAlexander Gordeev return readw(conf + off); 29633d78b07SAlexander Gordeev } 29733d78b07SAlexander Gordeev 29833d78b07SAlexander Gordeev u32 pci_config_readl(pcidevaddr_t dev, u8 off) 29933d78b07SAlexander Gordeev { 30033d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 30133d78b07SAlexander Gordeev return readl(conf + off); 30233d78b07SAlexander Gordeev } 30333d78b07SAlexander Gordeev 30433d78b07SAlexander Gordeev void pci_config_writeb(pcidevaddr_t dev, u8 off, u8 val) 30533d78b07SAlexander Gordeev { 30633d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 30733d78b07SAlexander Gordeev writeb(val, conf + off); 30833d78b07SAlexander Gordeev } 30933d78b07SAlexander Gordeev 31033d78b07SAlexander Gordeev void pci_config_writew(pcidevaddr_t dev, u8 off, u16 val) 31133d78b07SAlexander Gordeev { 31233d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 31333d78b07SAlexander Gordeev writew(val, conf + off); 31433d78b07SAlexander Gordeev } 31533d78b07SAlexander Gordeev 31633d78b07SAlexander Gordeev void pci_config_writel(pcidevaddr_t dev, u8 off, u32 val) 31733d78b07SAlexander Gordeev { 31833d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev); 31933d78b07SAlexander Gordeev writel(val, conf + off); 32033d78b07SAlexander Gordeev } 321