This has been tested with QEMU AArch64 Virt. The default (-M virt,highmem=on) will have two ranges specified in the device trees for memory bars. One 32-bit and the other 64-bit. As barebox can't yet handle 64-bit BARs, the driver will prefer the 32-bit memory region if available. If none is available, consider using -M virt,highmem=off or fixing 64-bit support. Signed-off-by: Ahmad Fatoum <ahmad@xxxxxx> --- drivers/pci/Kconfig | 8 ++ drivers/pci/Makefile | 1 + drivers/pci/pci-ecam-generic.c | 207 +++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 drivers/pci/pci-ecam-generic.c diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 60e8e93a0739..e847e8e1a3a2 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -60,6 +60,14 @@ config PCI_EFI depends on EFI_BOOTUP select PCI +config PCI_ECAM_GENERIC + bool "Generic ECAM-based PCI host controller support" + select OF_PCI + select PCI + help + Say Y here if you want to enable support for generic ECAM-based + PCI host controllers, such as the one emulated by QEMU. + endmenu endif diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 60e1439ec7b2..b8a5c6392ad6 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o pcie-designware-host.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_EFI) += pci-efi.o +obj-$(CONFIG_PCI_ECAM_GENERIC) += pci-ecam-generic.o diff --git a/drivers/pci/pci-ecam-generic.c b/drivers/pci/pci-ecam-generic.c new file mode 100644 index 000000000000..ac2b6b9ea26c --- /dev/null +++ b/drivers/pci/pci-ecam-generic.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic PCIE host provided by e.g. QEMU + * + * Heavily based on drivers/pci/pcie_xilinx.c + * + * Copyright (C) 2016 Imagination Technologies + */ + +#include <common.h> +#include <malloc.h> +#include <io.h> +#include <of.h> +#include <of_address.h> +#include <init.h> +#include <linux/pci.h> +#include <linux/sizes.h> + +struct generic_ecam_pcie { + struct pci_controller pci; + struct resource *cfg; + int first_busno; + struct resource io; + struct resource mem; + struct resource prefetch; +}; + +static inline struct generic_ecam_pcie *host_to_ecam(struct pci_controller *host) +{ + return container_of(host, struct generic_ecam_pcie, pci); +} + +static void __iomem *pci_generic_ecam_conf_address(const struct pci_bus *bus, + u32 devfn, int where) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(bus->host); + void __iomem *addr; + + addr = IOMEM(ecam->cfg->start); + addr += (bus->number - ecam->first_busno) << 20; + addr += PCI_SLOT(devfn) << 15; + addr += PCI_FUNC(devfn) << 12; + addr += where; + + return addr; +} + +static bool pci_generic_ecam_addr_valid(const struct pci_bus *bus, u32 devfn) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(bus->host); + int num_buses = DIV_ROUND_UP(resource_size(ecam->cfg), 1 << 16); + + return (bus->number >= ecam->first_busno && + bus->number < ecam->first_busno + num_buses); +} + +static int pci_generic_ecam_read_config(struct pci_bus *bus, + u32 devfn, int where, + int size, u32 *val) +{ + void __iomem *addr; + + if (!pci_generic_ecam_addr_valid(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pci_generic_ecam_conf_address(bus, devfn, where); + + if (!IS_ALIGNED((uintptr_t)addr, size)) { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (size == 4) { + *val = readl(addr); + } else if (size == 2) { + *val = readw(addr); + } else if (size == 1) { + *val = readb(addr); + } else { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_generic_ecam_write_config(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + + if (!pci_generic_ecam_addr_valid(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pci_generic_ecam_conf_address(bus, devfn, where); + + if (!IS_ALIGNED((uintptr_t)addr, size)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (size == 4) + writel(val, addr); + else if (size == 2) + writew(val, addr); + else if (size == 1) + writeb(val, addr); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static void pcie_ecam_set_local_bus_nr(struct pci_controller *host, int busno) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(host); + + ecam->first_busno = busno; +} + +static const struct pci_ops pci_generic_ecam_ops = { + .read = pci_generic_ecam_read_config, + .write = pci_generic_ecam_write_config, +}; + +static inline bool is_64bit(const struct resource *res) +{ + return res->flags & IORESOURCE_MEM_64; +} + +static int pcie_ecam_parse_dt(struct generic_ecam_pcie *ecam) +{ + struct device_d *dev = ecam->pci.parent; + struct device_node *np = dev->device_node; + struct of_pci_range_parser parser; + struct of_pci_range range; + struct resource res; + + if (of_pci_range_parser_init(&parser, np)) { + dev_err(dev, "missing \"ranges\" property\n"); + return -EINVAL; + } + + for_each_of_pci_range(&parser, &range) { + of_pci_range_to_resource(&range, np, &res); + + switch (res.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: + memcpy(&ecam->io, &res, sizeof(res)); + ecam->io.name = "I/O"; + break; + + case IORESOURCE_MEM: + if (res.flags & IORESOURCE_PREFETCH) { + memcpy(&ecam->prefetch, &res, sizeof(res)); + ecam->prefetch.name = "PREFETCH"; + } else { + /* Choose 32-bit mappings over 64-bit ones if possible */ + if (ecam->mem.name && !is_64bit(&ecam->mem) && is_64bit(&res)) + break; + + memcpy(&ecam->mem, &res, sizeof(res)); + ecam->mem.name = "MEM"; + } + break; + } + } + + return 0; +} + +static int pcie_ecam_probe(struct device_d *dev) +{ + struct generic_ecam_pcie *ecam; + struct resource *iores; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + ecam = xzalloc(sizeof(*ecam)); + ecam->cfg = iores; + ecam->pci.parent = dev; + ecam->pci.pci_ops = &pci_generic_ecam_ops; + ecam->pci.set_busno = pcie_ecam_set_local_bus_nr; + ecam->pci.mem_resource = &ecam->mem; + ecam->pci.io_resource = &ecam->io; + ecam->pci.mem_pref_resource = &ecam->prefetch; + + ret = pcie_ecam_parse_dt(ecam); + if (ret) + return ret; + + register_pci_controller(&ecam->pci); + return 0; +} + +static struct of_device_id pcie_ecam_dt_ids[] = { + { .compatible = "pci-host-ecam-generic" }, + { /* sentinel */ }, +}; + +static struct driver_d pcie_ecam_driver = { + .name = "pcie-generic-ecam", + .probe = pcie_ecam_probe, + .of_compatible = pcie_ecam_dt_ids, +}; +device_platform_driver(pcie_ecam_driver); -- 2.33.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox