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
of_flags_to_pci_type(u32 of_flags)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
pci_bar_type(u32 bar)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 */
pci_dt_probe(void)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
125*340cf7dbSAndrew Jones host->start = ioremap(base.addr, base.size);
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
pci_alloc_resource(struct pci_dev * dev,int bar_num,u64 * addr)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
176afb9a024SAlexander 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__);
195cb026028SAlexander Gordeev pci_dev_print_id(dev);
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
pci_probe(void)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 */
pci_host_bridge_get_paddr(u64 pci_addr)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
pci_get_dev_conf(struct pci_host_bridge * host,int devfn)28033d78b07SAlexander Gordeev static void __iomem *pci_get_dev_conf(struct pci_host_bridge *host, int devfn)
28133d78b07SAlexander Gordeev {
282*340cf7dbSAndrew Jones return host->start + (devfn << PCI_ECAM_DEVFN_SHIFT);
28333d78b07SAlexander Gordeev }
28433d78b07SAlexander Gordeev
pci_config_readb(pcidevaddr_t dev,u8 off)28533d78b07SAlexander Gordeev u8 pci_config_readb(pcidevaddr_t dev, u8 off)
28633d78b07SAlexander Gordeev {
28733d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
28833d78b07SAlexander Gordeev return readb(conf + off);
28933d78b07SAlexander Gordeev }
29033d78b07SAlexander Gordeev
pci_config_readw(pcidevaddr_t dev,u8 off)29133d78b07SAlexander Gordeev u16 pci_config_readw(pcidevaddr_t dev, u8 off)
29233d78b07SAlexander Gordeev {
29333d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
29433d78b07SAlexander Gordeev return readw(conf + off);
29533d78b07SAlexander Gordeev }
29633d78b07SAlexander Gordeev
pci_config_readl(pcidevaddr_t dev,u8 off)29733d78b07SAlexander Gordeev u32 pci_config_readl(pcidevaddr_t dev, u8 off)
29833d78b07SAlexander Gordeev {
29933d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
30033d78b07SAlexander Gordeev return readl(conf + off);
30133d78b07SAlexander Gordeev }
30233d78b07SAlexander Gordeev
pci_config_writeb(pcidevaddr_t dev,u8 off,u8 val)30333d78b07SAlexander Gordeev void pci_config_writeb(pcidevaddr_t dev, u8 off, u8 val)
30433d78b07SAlexander Gordeev {
30533d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
30633d78b07SAlexander Gordeev writeb(val, conf + off);
30733d78b07SAlexander Gordeev }
30833d78b07SAlexander Gordeev
pci_config_writew(pcidevaddr_t dev,u8 off,u16 val)30933d78b07SAlexander Gordeev void pci_config_writew(pcidevaddr_t dev, u8 off, u16 val)
31033d78b07SAlexander Gordeev {
31133d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
31233d78b07SAlexander Gordeev writew(val, conf + off);
31333d78b07SAlexander Gordeev }
31433d78b07SAlexander Gordeev
pci_config_writel(pcidevaddr_t dev,u8 off,u32 val)31533d78b07SAlexander Gordeev void pci_config_writel(pcidevaddr_t dev, u8 off, u32 val)
31633d78b07SAlexander Gordeev {
31733d78b07SAlexander Gordeev void __iomem *conf = pci_get_dev_conf(pci_host_bridge, dev);
31833d78b07SAlexander Gordeev writel(val, conf + off);
31933d78b07SAlexander Gordeev }
320