From 9de0eec29c07a65c21323f934d84ce164b0d9205 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:49 +0100 Subject: PCI: Regroup all PCI related entries into drivers/pci/Makefile Clean up drivers/Makefile by moving the pci/endpoint and pci/dwc entries from drivers/Makefile into drivers/pci/Makefile. Since we don't want to introduce any dependency between CONFIG_PCI and CONFIG_PCI_ENDPOINT, we now always execute drivers/pci/Makefile. Hence all Makefiles in drivers/pci/ were updated accordingly so no file is compiled when CONFIG_PCI is not defined. Also, we add a comment to reinforce that EPC and EPF libraries must be initialized before their users. Hence built-in EPC drivers, such as those of Designware, are linked after the endpoint core libraries. Finally, we add another comment to explain why obj-y has been chosen instead of obj-$(CONFIG_PCIE_DW) to parse the dwc/ sub-folder. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/Makefile | 5 +---- drivers/pci/Kconfig | 1 + drivers/pci/Makefile | 13 ++++++++++--- drivers/pci/dwc/Makefile | 2 ++ drivers/pci/host/Makefile | 2 ++ 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index e06f7f633f73..8189b1edec00 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -16,10 +16,7 @@ obj-$(CONFIG_PINCTRL) += pinctrl/ obj-$(CONFIG_GPIOLIB) += gpio/ obj-y += pwm/ -obj-$(CONFIG_PCI) += pci/ -obj-$(CONFIG_PCI_ENDPOINT) += pci/endpoint/ -# PCI dwc controller drivers -obj-y += pci/dwc/ +obj-y += pci/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index bda151788f3f..7eeb969ab86a 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -125,6 +125,7 @@ config PCI_PASID config PCI_LABEL def_bool y if (DMI || ACPI) + depends on PCI select NLS config PCI_HYPERV diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c7819b973df7..ddb5aa6640d7 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -3,12 +3,15 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ +obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ irq.o vpd.o setup-bus.o vc.o mmap.o setup-irq.o +ifdef CONFIG_PCI obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o +obj-$(CONFIG_OF) += of.o +endif obj-$(CONFIG_PCI_QUIRKS) += quirks.o @@ -44,10 +47,14 @@ obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o -obj-$(CONFIG_OF) += of.o - ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG # PCI host controller drivers obj-y += host/ obj-y += switch/ + +obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ + +# Endpoint library must be initialized before its users +# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW +obj-y += dwc/ diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile index 41ba499c96ee..cb35e45cd186 100644 --- a/drivers/pci/dwc/Makefile +++ b/drivers/pci/dwc/Makefile @@ -27,4 +27,6 @@ obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o # ARM64 and use internal ifdefs to only build the pieces we need # depending on whether ACPI, the DT driver, or both are enabled. +ifdef CONFIG_PCI obj-$(CONFIG_ARM64) += pcie-hisi.o +endif diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 34ec1d88f961..3b1059190867 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_VMD) += vmd.o # ARM64 and use internal ifdefs to only build the pieces we need # depending on whether ACPI, the DT driver, or both are enabled. +ifdef CONFIG_PCI obj-$(CONFIG_ARM64) += pci-thunder-ecam.o obj-$(CONFIG_ARM64) += pci-thunder-pem.o obj-$(CONFIG_ARM64) += pci-xgene.o +endif -- cgit v1.2.2 From 3a8f77e48666a39adb3ac4d5ce8261563e039e31 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:50 +0100 Subject: PCI: OF: Add generic function to parse and allocate PCI resources The patch moves the gen_pci_parse_request_of_pci_ranges() function from drivers/pci/host/pci-host-common.c into drivers/pci/of.c to easily share common source code between PCI host drivers. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pci-host-common.c | 49 ++---------------------------------- drivers/pci/of.c | 51 ++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 9 +++++++ 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index 44a47d4f0b8f..efd904d93562 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -24,50 +24,6 @@ #include #include -static int gen_pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *resources, struct resource **bus_range) -{ - int err, res_valid = 0; - struct device_node *np = dev->of_node; - resource_size_t iobase; - struct resource_entry *win, *tmp; - - err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, resources); - if (err) - return err; - - resource_list_for_each_entry_safe(win, tmp, resources) { - struct resource *res = win->res; - - switch (resource_type(res)) { - case IORESOURCE_IO: - err = pci_remap_iospace(res, iobase); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - break; - case IORESOURCE_BUS: - *bus_range = res; - break; - } - } - - if (res_valid) - return 0; - - dev_err(dev, "non-prefetchable memory resource required\n"); - return -EINVAL; -} - static void gen_pci_unmap_cfg(void *ptr) { pci_ecam_free((struct pci_config_window *)ptr); @@ -82,9 +38,9 @@ static struct pci_config_window *gen_pci_init(struct device *dev, struct pci_config_window *cfg; /* Parse our PCI ranges and request their resources */ - err = gen_pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); if (err) - goto err_out; + return ERR_PTR(err); err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { @@ -135,7 +91,6 @@ int pci_host_common_probe(struct platform_device *pdev, of_pci_check_probe_only(); /* Parse and map our Configuration Space windows */ - INIT_LIST_HEAD(&resources); cfg = gen_pci_init(dev, &resources, ops); if (IS_ERR(cfg)) return PTR_ERR(cfg); diff --git a/drivers/pci/of.c b/drivers/pci/of.c index e112da11630e..54e210501b73 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -88,3 +88,54 @@ struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus) return NULL; #endif } + +int pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *resources, + struct resource **bus_range) +{ + int err, res_valid = 0; + struct device_node *np = dev->of_node; + resource_size_t iobase; + struct resource_entry *win, *tmp; + + INIT_LIST_HEAD(resources); + err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase); + if (err) + return err; + + err = devm_request_pci_bus_resources(dev, resources); + if (err) + goto out_release_res; + + resource_list_for_each_entry_safe(win, tmp, resources) { + struct resource *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + resource_list_destroy_entry(win); + } + break; + case IORESOURCE_MEM: + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + break; + case IORESOURCE_BUS: + if (bus_range) + *bus_range = res; + break; + } + } + + if (res_valid) + return 0; + + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + + out_release_res: + pci_free_resource_list(resources); + return err; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index c170c9250c8b..c7a701abbf17 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2182,6 +2182,9 @@ void pci_release_of_node(struct pci_dev *dev); void pci_set_bus_of_node(struct pci_bus *bus); void pci_release_bus_of_node(struct pci_bus *bus); struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus); +int pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *resources, + struct resource **bus_range); /* Arch may override this (weak) */ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus); @@ -2206,6 +2209,12 @@ static inline struct device_node * pci_device_to_OF_node(const struct pci_dev *pdev) { return NULL; } static inline struct irq_domain * pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } +static inline int pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *resources, + struct resource **bus_range) +{ + return -EINVAL; +} #endif /* CONFIG_OF */ #ifdef CONFIG_ACPI -- cgit v1.2.2 From c6dd8ecf303780a6ac818d61a7b83cfd7e2525d1 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:51 +0100 Subject: PCI: generic: fix missing call of pci_free_resource_list() Call pci_free_resource_list() from pci_host_common_probe() when probing fails, as done inside gen_pci_init() when this later function fails. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pci-host-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index efd904d93562..10e3f5b39499 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -110,6 +110,7 @@ int pci_host_common_probe(struct platform_device *pdev, ret = pci_scan_root_bus_bridge(bridge); if (ret < 0) { dev_err(dev, "Scanning root bridge failed"); + pci_free_resource_list(&resources); return ret; } -- cgit v1.2.2 From 49b8e3f3ed1d411a8f8fa5fbe72c88f3d1d43824 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:52 +0100 Subject: PCI: Add generic function to probe PCI host controllers This patchs moves generic source code from drivers/pci/host/pci-host-common.c into drivers/pci/probe.c. Indeed the extracted lines of code were duplicated by many host controller drivers. Regrouping them into a generic function gives a change to properly share this code without introducing a useless dependency to PCI_HOST_COMMON, which selects PCI_ECAM when not needed by most host controller drivers. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pci-host-common.c | 22 +--------------------- drivers/pci/probe.c | 33 +++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index 10e3f5b39499..160b3c1cb5a0 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -72,7 +72,6 @@ int pci_host_common_probe(struct platform_device *pdev, const char *type; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct pci_bus *bus, *child; struct pci_host_bridge *bridge; struct pci_config_window *cfg; struct list_head resources; @@ -107,30 +106,11 @@ int pci_host_common_probe(struct platform_device *pdev, bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(bridge); + ret = pci_host_probe(bridge); if (ret < 0) { - dev_err(dev, "Scanning root bridge failed"); pci_free_resource_list(&resources); return ret; } - bus = bridge->bus; - - /* - * We insert PCI resources into the iomem_resource and - * ioport_resource trees in either pci_bus_claim_resources() - * or pci_bus_assign_resources(). - */ - if (pci_has_flag(PCI_PROBE_ONLY)) { - pci_bus_claim_resources(bus); - } else { - pci_bus_size_bridges(bus); - pci_bus_assign_resources(bus); - - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - } - - pci_bus_add_devices(bus); return 0; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 14e0ea1ff38b..a42bbfc5f2ec 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2620,6 +2620,39 @@ err_out: } EXPORT_SYMBOL_GPL(pci_create_root_bus); +int pci_host_probe(struct pci_host_bridge *bridge) +{ + struct pci_bus *bus, *child; + int ret; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) { + dev_err(bridge->dev.parent, "Scanning root bridge failed"); + return ret; + } + + bus = bridge->bus; + + /* + * We insert PCI resources into the iomem_resource and + * ioport_resource trees in either pci_bus_claim_resources() + * or pci_bus_assign_resources(). + */ + if (pci_has_flag(PCI_PROBE_ONLY)) { + pci_bus_claim_resources(bus); + } else { + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + } + + pci_bus_add_devices(bus); + return 0; +} +EXPORT_SYMBOL_GPL(pci_host_probe); + int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) { struct resource *res = &b->busn_res; diff --git a/include/linux/pci.h b/include/linux/pci.h index c7a701abbf17..db6700137bd2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -887,6 +887,7 @@ struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); +int pci_host_probe(struct pci_host_bridge *bridge); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax); int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax); void pci_bus_release_busn_res(struct pci_bus *b); -- cgit v1.2.2 From 74b307012ff7ea9f8dffd5a44c83a5e8ec44ca0a Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:53 +0100 Subject: PCI: Add vendor ID for Cadence This patch adds a new PCI vendor ID for Cadence. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ab20dc5db423..eb13e84e1fef 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2381,6 +2381,8 @@ #define PCI_VENDOR_ID_LENOVO 0x17aa +#define PCI_VENDOR_ID_CDNS 0x17cd + #define PCI_VENDOR_ID_ARECA 0x17d3 #define PCI_DEVICE_ID_ARECA_1110 0x1110 #define PCI_DEVICE_ID_ARECA_1120 0x1120 -- cgit v1.2.2 From 2040fae4b22f419ed5cbc40ce8ec4d0fbc798a64 Mon Sep 17 00:00:00 2001 From: Scott Telford Date: Tue, 30 Jan 2018 21:56:54 +0100 Subject: dt-bindings: PCI: cadence: Add DT bindings for Cadence PCIe host controller This patch adds documentation for the DT bindings of the Cadence PCIe controller when configured in host (Root Complex) mode. Signed-off-by: Scott Telford Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../bindings/pci/cdns,cdns-pcie-host.txt | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt new file mode 100644 index 000000000000..20a33f38f69d --- /dev/null +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt @@ -0,0 +1,60 @@ +* Cadence PCIe host controller + +This PCIe controller inherits the base properties defined in +host-generic-pci.txt. + +Required properties: +- compatible: Should contain "cdns,cdns-pcie-host" to identify the IP used. +- reg: Should contain the controller register base address, PCIe configuration + window base address, and AXI interface region base address respectively. +- reg-names: Must be "reg", "cfg" and "mem" respectively. +- #address-cells: Set to <3> +- #size-cells: Set to <2> +- device_type: Set to "pci" +- ranges: Ranges for the PCI memory and I/O regions +- #interrupt-cells: Set to <1> +- interrupt-map-mask and interrupt-map: Standard PCI properties to define the + mapping of the PCIe interface to interrupt numbers. + +Optional properties: +- cdns,max-outbound-regions: Set to maximum number of outbound regions + (default 32) +- cdns,no-bar-match-nbits: Set into the no BAR match register to configure the + number of least significant bits kept during inbound (PCIe -> AXI) address + translations (default 32) +- vendor-id: The PCI vendor ID (16 bits, default is design dependent) +- device-id: The PCI device ID (16 bits, default is design dependent) + +Example: + +pcie@fb000000 { + compatible = "cdns,cdns-pcie-host"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x0 0xff>; + linux,pci-domain = <0>; + cdns,max-outbound-regions = <16>; + cdns,no-bar-match-nbits = <32>; + vendor-id = /bits/ 16 <0x17cd>; + device-id = /bits/ 16 <0x0200>; + + reg = <0x0 0xfb000000 0x0 0x01000000>, + <0x0 0x41000000 0x0 0x00001000>, + <0x0 0x40000000 0x0 0x04000000>; + reg-names = "reg", "cfg", "mem"; + + ranges = <0x02000000 0x0 0x42000000 0x0 0x42000000 0x0 0x1000000>, + <0x01000000 0x0 0x43000000 0x0 0x43000000 0x0 0x0010000>; + + #interrupt-cells = <0x1>; + + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 14 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 15 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 16 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 17 0x1>; + + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + + msi-parent = <&its_pci>; +}; -- cgit v1.2.2 From 1b79c5284439ac1a8f45aa62cf518714436fad5f Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:55 +0100 Subject: PCI: cadence: Add host driver for Cadence PCIe controller This patch adds support to the Cadence PCIe controller in host mode. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- MAINTAINERS | 7 + drivers/pci/host/Kconfig | 10 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-cadence-host.c | 397 +++++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-cadence.h | 189 +++++++++++++++++ 5 files changed, 604 insertions(+) create mode 100644 drivers/pci/host/pcie-cadence-host.c create mode 100644 drivers/pci/host/pcie-cadence.h diff --git a/MAINTAINERS b/MAINTAINERS index a6e86e20761e..460d5580f87e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10402,6 +10402,13 @@ S: Maintained F: Documentation/devicetree/bindings/pci/pci-armada8k.txt F: drivers/pci/dwc/pcie-armada8k.c +PCI DRIVER FOR CADENCE PCIE IP +M: Alan Douglas +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/cdns,*.txt +F: drivers/pci/host/pcie-cadence* + PCI DRIVER FOR FREESCALE LAYERSCAPE M: Minghuan Lian M: Mingkai Hu diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 38d12980db0f..d6866733d69d 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -210,6 +210,16 @@ config PCIE_TANGO_SMP8759 This can lead to data corruption if drivers perform concurrent config and MMIO accesses. +config PCIE_CADENCE_HOST + bool "Cadence PCIe host controller" + depends on OF + depends on PCI + select IRQ_DOMAIN + help + Say Y here if you want to support the Cadence PCIe controller in host + mode. This PCIe controller may be embedded into many different vendors + SoCs. + config VMD depends on PCI_MSI && X86_64 && SRCU tristate "Intel Volume Management Device Driver" diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 3b1059190867..2460f0785120 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o +obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o obj-$(CONFIG_VMD) += vmd.o # The following drivers are for devices that use the generic ACPI diff --git a/drivers/pci/host/pcie-cadence-host.c b/drivers/pci/host/pcie-cadence-host.c new file mode 100644 index 000000000000..9332601845ea --- /dev/null +++ b/drivers/pci/host/pcie-cadence-host.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe host controller driver. +// Author: Cyrille Pitchen + +#include +#include +#include +#include +#include + +#include "pcie-cadence.h" + +/** + * struct cdns_pcie_rc - private data for this PCIe Root Complex driver + * @pcie: Cadence PCIe controller + * @dev: pointer to PCIe device + * @cfg_res: start/end offsets in the physical system memory to map PCI + * configuration space accesses + * @bus_range: first/last buses behind the PCIe host controller + * @cfg_base: IO mapped window to access the PCI configuration space of a + * single function at a time + * @max_regions: maximum number of regions supported by the hardware + * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address + * translation (nbits sets into the "no BAR match" register) + * @vendor_id: PCI vendor ID + * @device_id: PCI device ID + */ +struct cdns_pcie_rc { + struct cdns_pcie pcie; + struct device *dev; + struct resource *cfg_res; + struct resource *bus_range; + void __iomem *cfg_base; + u32 max_regions; + u32 no_bar_nbits; + u16 vendor_id; + u16 device_id; +}; + +static void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, + u32 r, bool is_io, + u64 cpu_addr, u64 pci_addr, size_t size) +{ + /* + * roundup_pow_of_two() returns an unsigned long, which is not suited + * for 64bit values. + */ + u64 sz = 1ULL << fls64(size - 1); + int nbits = ilog2(sz); + u32 addr0, addr1, desc0, desc1; + + if (nbits < 8) + nbits = 8; + + /* Set the PCI address */ + addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) | + (lower_32_bits(pci_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(pci_addr); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), addr1); + + /* Set the PCIe header descriptor */ + if (is_io) + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO; + else + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM; + desc1 = 0; + + /* + * Whatever Bit [23] is set or not inside DESC0 register of the outbound + * PCIe descriptor, the PCI function number must be set into + * Bits [26:24] of DESC0 anyway. + * + * In Root Complex mode, the function number is always 0 but in Endpoint + * mode, the PCIe controller may support more than one function. This + * function number needs to be set properly into the outbound PCIe + * descriptor. + * + * Besides, setting Bit [23] is mandatory when in Root Complex mode: + * then the driver must provide the bus, resp. device, number in + * Bits [7:0] of DESC1, resp. Bits[31:27] of DESC0. Like the function + * number, the device number is always 0 in Root Complex mode. + */ + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | + CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); + desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); + + /* Set the CPU address */ + cpu_addr -= pcie->mem_res->start; + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); +} + +static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); + struct cdns_pcie *pcie = &rc->pcie; + unsigned int busn = bus->number; + u32 addr0, desc0; + + if (busn == rc->bus_range->start) { + /* + * Only the root port (devfn == 0) is connected to this bus. + * All other PCI devices are behind some bridge hence on another + * bus. + */ + if (devfn) + return NULL; + + return pcie->reg_base + (where & 0xfff); + } + + /* Update Output registers for AXI region 0. */ + addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | + CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) | + CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(busn); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(0), addr0); + + /* Configuration Type 0 or Type 1 access. */ + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | + CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); + /* + * The bus number was already set once for all in desc1 by + * cdns_pcie_host_init_address_translation(). + */ + if (busn == rc->bus_range->start + 1) + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0; + else + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1; + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(0), desc0); + + return rc->cfg_base + (where & 0xfff); +} + +static struct pci_ops cdns_pcie_host_ops = { + .map_bus = cdns_pci_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static const struct of_device_id cdns_pcie_host_of_match[] = { + { .compatible = "cdns,cdns-pcie-host" }, + + { }, +}; + +static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + u32 value, ctrl; + + /* + * Set the root complex BAR configuration register: + * - disable both BAR0 and BAR1. + * - enable Prefetchable Memory Base and Limit registers in type 1 + * config space (64 bits). + * - enable IO Base and Limit registers in type 1 config + * space (32 bits). + */ + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; + value = CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) | + CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) | + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE | + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS | + CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE | + CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS; + cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value); + + /* Set root port configuration space */ + if (rc->vendor_id != 0xffff) + cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id); + if (rc->device_id != 0xffff) + cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); + + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0); + cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); + cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); + + return 0; +} + +static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + struct resource *cfg_res = rc->cfg_res; + struct resource *mem_res = pcie->mem_res; + struct resource *bus_range = rc->bus_range; + struct device *dev = rc->dev; + struct device_node *np = dev->of_node; + struct of_pci_range_parser parser; + struct of_pci_range range; + u32 addr0, addr1, desc1; + u64 cpu_addr; + int r, err; + + /* + * Reserve region 0 for PCI configure space accesses: + * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by + * cdns_pci_map_bus(), other region registers are set here once for all. + */ + addr1 = 0; /* Should be programmed to zero. */ + desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus_range->start); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); + + cpu_addr = cfg_res->start - mem_res->start; + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(0), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(0), addr1); + + err = of_pci_range_parser_init(&parser, np); + if (err) + return err; + + r = 1; + for_each_of_pci_range(&parser, &range) { + bool is_io; + + if (r >= rc->max_regions) + break; + + if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) + is_io = false; + else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) + is_io = true; + else + continue; + + cdns_pcie_set_outbound_region(pcie, r, is_io, + range.cpu_addr, + range.pci_addr, + range.size); + r++; + } + + /* + * Set Root Port no BAR match Inbound Translation registers: + * needed for MSI and DMA. + * Root Port BAR0 and BAR1 are disabled, hence no need to set their + * inbound translation registers. + */ + addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(rc->no_bar_nbits); + addr1 = 0; + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(RP_NO_BAR), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(RP_NO_BAR), addr1); + + return 0; +} + +static int cdns_pcie_host_init(struct device *dev, + struct list_head *resources, + struct cdns_pcie_rc *rc) +{ + struct resource *bus_range = NULL; + int err; + + /* Parse our PCI ranges and request their resources */ + err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + if (err) + return err; + + rc->bus_range = bus_range; + rc->pcie.bus = bus_range->start; + + err = cdns_pcie_host_init_root_port(rc); + if (err) + goto err_out; + + err = cdns_pcie_host_init_address_translation(rc); + if (err) + goto err_out; + + return 0; + + err_out: + pci_free_resource_list(resources); + return err; +} + +static int cdns_pcie_host_probe(struct platform_device *pdev) +{ + const char *type; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + struct list_head resources; + struct cdns_pcie_rc *rc; + struct cdns_pcie *pcie; + struct resource *res; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) + return -ENOMEM; + + rc = pci_host_bridge_priv(bridge); + rc->dev = dev; + + pcie = &rc->pcie; + + rc->max_regions = 32; + of_property_read_u32(np, "cdns,max-outbound-regions", &rc->max_regions); + + rc->no_bar_nbits = 32; + of_property_read_u32(np, "cdns,no-bar-match-nbits", &rc->no_bar_nbits); + + rc->vendor_id = 0xffff; + of_property_read_u16(np, "vendor-id", &rc->vendor_id); + + rc->device_id = 0xffff; + of_property_read_u16(np, "device-id", &rc->device_id); + + type = of_get_property(np, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); + pcie->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->reg_base)) { + dev_err(dev, "missing \"reg\"\n"); + return PTR_ERR(pcie->reg_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + rc->cfg_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(rc->cfg_base)) { + dev_err(dev, "missing \"cfg\"\n"); + return PTR_ERR(rc->cfg_base); + } + rc->cfg_res = res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!res) { + dev_err(dev, "missing \"mem\"\n"); + return -EINVAL; + } + pcie->mem_res = res; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + goto err_get_sync; + } + + ret = cdns_pcie_host_init(dev, &resources, rc); + if (ret) + goto err_init; + + list_splice_init(&resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->busnr = pcie->bus; + bridge->ops = &cdns_pcie_host_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_host_probe(bridge); + if (ret < 0) + goto err_host_probe; + + return 0; + + err_host_probe: + pci_free_resource_list(&resources); + + err_init: + pm_runtime_put_sync(dev); + + err_get_sync: + pm_runtime_disable(dev); + + return ret; +} + +static struct platform_driver cdns_pcie_host_driver = { + .driver = { + .name = "cdns-pcie-host", + .of_match_table = cdns_pcie_host_of_match, + }, + .probe = cdns_pcie_host_probe, +}; +builtin_platform_driver(cdns_pcie_host_driver); diff --git a/drivers/pci/host/pcie-cadence.h b/drivers/pci/host/pcie-cadence.h new file mode 100644 index 000000000000..a28afdee0d02 --- /dev/null +++ b/drivers/pci/host/pcie-cadence.h @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe controller driver. +// Author: Cyrille Pitchen + +#ifndef _PCIE_CADENCE_H +#define _PCIE_CADENCE_H + +#include +#include + +/* + * Local Management Registers + */ +#define CDNS_PCIE_LM_BASE 0x00100000 + +/* Vendor ID Register */ +#define CDNS_PCIE_LM_ID (CDNS_PCIE_LM_BASE + 0x0044) +#define CDNS_PCIE_LM_ID_VENDOR_MASK GENMASK(15, 0) +#define CDNS_PCIE_LM_ID_VENDOR_SHIFT 0 +#define CDNS_PCIE_LM_ID_VENDOR(vid) \ + (((vid) << CDNS_PCIE_LM_ID_VENDOR_SHIFT) & CDNS_PCIE_LM_ID_VENDOR_MASK) +#define CDNS_PCIE_LM_ID_SUBSYS_MASK GENMASK(31, 16) +#define CDNS_PCIE_LM_ID_SUBSYS_SHIFT 16 +#define CDNS_PCIE_LM_ID_SUBSYS(sub) \ + (((sub) << CDNS_PCIE_LM_ID_SUBSYS_SHIFT) & CDNS_PCIE_LM_ID_SUBSYS_MASK) + +/* Root Port Requestor ID Register */ +#define CDNS_PCIE_LM_RP_RID (CDNS_PCIE_LM_BASE + 0x0228) +#define CDNS_PCIE_LM_RP_RID_MASK GENMASK(15, 0) +#define CDNS_PCIE_LM_RP_RID_SHIFT 0 +#define CDNS_PCIE_LM_RP_RID_(rid) \ + (((rid) << CDNS_PCIE_LM_RP_RID_SHIFT) & CDNS_PCIE_LM_RP_RID_MASK) + +/* Root Complex BAR Configuration Register */ +#define CDNS_PCIE_LM_RC_BAR_CFG (CDNS_PCIE_LM_BASE + 0x0300) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(5, 0) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE(a) \ + (((a) << 0) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(8, 6) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(c) \ + (((c) << 6) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(13, 9) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE(a) \ + (((a) << 9) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(16, 14) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(c) \ + (((c) << 14) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(17) +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_32BITS 0 +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(18) +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE BIT(19) +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_16BITS 0 +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS BIT(20) +#define CDNS_PCIE_LM_RC_BAR_CFG_CHECK_ENABLE BIT(31) + +/* BAR control values applicable to both Endpoint Function and Root Complex */ +#define CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED 0x0 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 + + +/* + * Root Port Registers (PCI configuration space for the root port function) + */ +#define CDNS_PCIE_RP_BASE 0x00200000 + + +/* + * Address Translation Registers + */ +#define CDNS_PCIE_AT_BASE 0x00400000 + +/* Region r Outbound AXI to PCIe Address Translation Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ + (CDNS_PCIE_AT_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ + (((devfn) << 12) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ + (((bus) << 20) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) + +/* Region r Outbound AXI to PCIe Address Translation Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ + (CDNS_PCIE_AT_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) + +/* Region r Outbound PCIe Descriptor Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_DESC0(r) \ + (CDNS_PCIE_AT_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(3, 0) +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM 0x2 +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO 0x6 +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 0xa +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 0xb +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG 0xc +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_VENDOR_MSG 0xd +/* Bit 23 MUST be set in RC mode. */ +#define CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) +#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) +#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ + (((devfn) << 24) & CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) + +/* Region r Outbound PCIe Descriptor Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_DESC1(r) \ + (CDNS_PCIE_AT_BASE + 0x000c + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK GENMASK(7, 0) +#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus) \ + ((bus) & CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK) + +/* Region r AXI Region Base Address Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ + (CDNS_PCIE_AT_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK) + +/* Region r AXI Region Base Address Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ + (CDNS_PCIE_AT_BASE + 0x001c + ((r) & 0x1f) * 0x0020) + +/* Root Port BAR Inbound PCIe to AXI Address Translation Register */ +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar) \ + (CDNS_PCIE_AT_BASE + 0x0800 + (bar) * 0x0008) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ + (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) + +enum cdns_pcie_rp_bar { + RP_BAR0, + RP_BAR1, + RP_NO_BAR +}; + +/** + * struct cdns_pcie - private data for Cadence PCIe controller drivers + * @reg_base: IO mapped register base + * @mem_res: start/end offsets in the physical system memory to map PCI accesses + * @bus: In Root Complex mode, the bus number + */ +struct cdns_pcie { + void __iomem *reg_base; + struct resource *mem_res; + u8 bus; +}; + +/* Register access */ +static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) +{ + writeb(value, pcie->reg_base + reg); +} + +static inline void cdns_pcie_writew(struct cdns_pcie *pcie, u32 reg, u16 value) +{ + writew(value, pcie->reg_base + reg); +} + +static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value) +{ + writel(value, pcie->reg_base + reg); +} + +static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) +{ + return readl(pcie->reg_base + reg); +} + +/* Root Port register access */ +static inline void cdns_pcie_rp_writeb(struct cdns_pcie *pcie, + u32 reg, u8 value) +{ + writeb(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); +} + +static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, + u32 reg, u16 value) +{ + writew(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); +} + +#endif /* _PCIE_CADENCE_H */ -- cgit v1.2.2 From 4494738de0d9dc79343fba7c8a57235e684fc549 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:56 +0100 Subject: PCI: endpoint: Add the function number as argument to EPC ops This patch updates the prototype of most handlers from 'struct pci_epc_ops' so the EPC library can now support multi-function devices. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-designware-ep.c | 20 +++++---- drivers/pci/endpoint/functions/pci-epf-test.c | 41 ++++++++++-------- drivers/pci/endpoint/pci-epc-core.c | 62 ++++++++++++++++----------- include/linux/pci-epc.h | 43 +++++++++++-------- 4 files changed, 96 insertions(+), 70 deletions(-) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index d53d5f168363..7a573d8bb62d 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -39,7 +39,7 @@ static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) dw_pcie_writel_dbi(pci, reg, 0x0); } -static int dw_pcie_ep_write_header(struct pci_epc *epc, +static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -112,7 +112,8 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, return 0; } -static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar) +static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, + enum pci_barno bar) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -124,7 +125,8 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar) clear_bit(atu_index, &ep->ib_window_map); } -static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar, +static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, + enum pci_barno bar, dma_addr_t bar_phys, size_t size, int flags) { int ret; @@ -163,7 +165,8 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, return -EINVAL; } -static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr) +static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t addr) { int ret; u32 atu_index; @@ -178,7 +181,8 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr) clear_bit(atu_index, &ep->ob_window_map); } -static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, +static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t addr, u64 pci_addr, size_t size) { int ret; @@ -194,7 +198,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, return 0; } -static int dw_pcie_ep_get_msi(struct pci_epc *epc) +static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) { int val; u32 lower_addr; @@ -214,7 +218,7 @@ static int dw_pcie_ep_get_msi(struct pci_epc *epc) return val; } -static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int) +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) { int val; struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -226,7 +230,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int) return 0; } -static int dw_pcie_ep_raise_irq(struct pci_epc *epc, +static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, enum pci_epc_irq_type type, u8 interrupt_num) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index f9308c2f22e6..7bacca8daec6 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -104,7 +104,8 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test) goto err; } - ret = pci_epc_map_addr(epc, src_phys_addr, reg->src_addr, reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, src_phys_addr, reg->src_addr, + reg->size); if (ret) { dev_err(dev, "failed to map source address\n"); reg->status = STATUS_SRC_ADDR_INVALID; @@ -119,7 +120,8 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test) goto err_src_map_addr; } - ret = pci_epc_map_addr(epc, dst_phys_addr, reg->dst_addr, reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, dst_phys_addr, reg->dst_addr, + reg->size); if (ret) { dev_err(dev, "failed to map destination address\n"); reg->status = STATUS_DST_ADDR_INVALID; @@ -128,13 +130,13 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test) memcpy(dst_addr, src_addr, reg->size); - pci_epc_unmap_addr(epc, dst_phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, dst_phys_addr); err_dst_addr: pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size); err_src_map_addr: - pci_epc_unmap_addr(epc, src_phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, src_phys_addr); err_src_addr: pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size); @@ -164,7 +166,8 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) goto err; } - ret = pci_epc_map_addr(epc, phys_addr, reg->src_addr, reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->src_addr, + reg->size); if (ret) { dev_err(dev, "failed to map address\n"); reg->status = STATUS_SRC_ADDR_INVALID; @@ -186,7 +189,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) kfree(buf); err_map_addr: - pci_epc_unmap_addr(epc, phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, phys_addr); err_addr: pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size); @@ -215,7 +218,8 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) goto err; } - ret = pci_epc_map_addr(epc, phys_addr, reg->dst_addr, reg->size); + ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->dst_addr, + reg->size); if (ret) { dev_err(dev, "failed to map address\n"); reg->status = STATUS_DST_ADDR_INVALID; @@ -242,7 +246,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) kfree(buf); err_map_addr: - pci_epc_unmap_addr(epc, phys_addr); + pci_epc_unmap_addr(epc, epf->func_no, phys_addr); err_addr: pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size); @@ -260,11 +264,11 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq) struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; reg->status |= STATUS_IRQ_RAISED; - msi_count = pci_epc_get_msi(epc); + msi_count = pci_epc_get_msi(epc, epf->func_no); if (irq > msi_count || msi_count <= 0) - pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0); + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0); else - pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq); + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); } static void pci_epf_test_cmd_handler(struct work_struct *work) @@ -291,7 +295,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) if (command & COMMAND_RAISE_LEGACY_IRQ) { reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0); + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0); goto reset_handler; } @@ -326,11 +330,11 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) } if (command & COMMAND_RAISE_MSI_IRQ) { - msi_count = pci_epc_get_msi(epc); + msi_count = pci_epc_get_msi(epc, epf->func_no); if (irq > msi_count || msi_count <= 0) goto reset_handler; reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq); + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); goto reset_handler; } @@ -358,7 +362,7 @@ static void pci_epf_test_unbind(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { if (epf_test->reg[bar]) { pci_epf_free_space(epf, epf_test->reg[bar], bar); - pci_epc_clear_bar(epc, bar); + pci_epc_clear_bar(epc, epf->func_no, bar); } } } @@ -380,7 +384,8 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - ret = pci_epc_set_bar(epc, bar, epf_bar->phys_addr, + ret = pci_epc_set_bar(epc, epf->func_no, bar, + epf_bar->phys_addr, epf_bar->size, flags); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); @@ -433,7 +438,7 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (WARN_ON_ONCE(!epc)) return -EINVAL; - ret = pci_epc_write_header(epc, header); + ret = pci_epc_write_header(epc, epf->func_no, header); if (ret) { dev_err(dev, "configuration header write failed\n"); return ret; @@ -447,7 +452,7 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (ret) return ret; - ret = pci_epc_set_msi(epc, epf->msi_interrupts); + ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); if (ret) return ret; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 42c2a1156325..e8908e0792ad 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -142,25 +142,26 @@ EXPORT_SYMBOL_GPL(pci_epc_start); /** * pci_epc_raise_irq() - interrupt the host system * @epc: the EPC device which has to interrupt the host + * @func_no: the endpoint function number in the EPC device * @type: specify the type of interrupt; legacy or MSI * @interrupt_num: the MSI interrupt number * * Invoke to raise an MSI or legacy interrupt */ -int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type, - u8 interrupt_num) +int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num) { int ret; unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; if (!epc->ops->raise_irq) return 0; spin_lock_irqsave(&epc->lock, flags); - ret = epc->ops->raise_irq(epc, type, interrupt_num); + ret = epc->ops->raise_irq(epc, func_no, type, interrupt_num); spin_unlock_irqrestore(&epc->lock, flags); return ret; @@ -170,22 +171,23 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq); /** * pci_epc_get_msi() - get the number of MSI interrupt numbers allocated * @epc: the EPC device to which MSI interrupts was requested + * @func_no: the endpoint function number in the EPC device * * Invoke to get the number of MSI interrupts allocated by the RC */ -int pci_epc_get_msi(struct pci_epc *epc) +int pci_epc_get_msi(struct pci_epc *epc, u8 func_no) { int interrupt; unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return 0; if (!epc->ops->get_msi) return 0; spin_lock_irqsave(&epc->lock, flags); - interrupt = epc->ops->get_msi(epc); + interrupt = epc->ops->get_msi(epc, func_no); spin_unlock_irqrestore(&epc->lock, flags); if (interrupt < 0) @@ -200,17 +202,18 @@ EXPORT_SYMBOL_GPL(pci_epc_get_msi); /** * pci_epc_set_msi() - set the number of MSI interrupt numbers required * @epc: the EPC device on which MSI has to be configured + * @func_no: the endpoint function number in the EPC device * @interrupts: number of MSI interrupts required by the EPF * * Invoke to set the required number of MSI interrupts. */ -int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts) +int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) { int ret; u8 encode_int; unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; if (!epc->ops->set_msi) @@ -219,7 +222,7 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts) encode_int = order_base_2(interrupts); spin_lock_irqsave(&epc->lock, flags); - ret = epc->ops->set_msi(epc, encode_int); + ret = epc->ops->set_msi(epc, func_no, encode_int); spin_unlock_irqrestore(&epc->lock, flags); return ret; @@ -229,22 +232,24 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msi); /** * pci_epc_unmap_addr() - unmap CPU address from PCI address * @epc: the EPC device on which address is allocated + * @func_no: the endpoint function number in the EPC device * @phys_addr: physical address of the local system * * Invoke to unmap the CPU address from PCI address. */ -void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr) +void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t phys_addr) { unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return; if (!epc->ops->unmap_addr) return; spin_lock_irqsave(&epc->lock, flags); - epc->ops->unmap_addr(epc, phys_addr); + epc->ops->unmap_addr(epc, func_no, phys_addr); spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); @@ -252,26 +257,27 @@ EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); /** * pci_epc_map_addr() - map CPU address to PCI address * @epc: the EPC device on which address is allocated + * @func_no: the endpoint function number in the EPC device * @phys_addr: physical address of the local system * @pci_addr: PCI address to which the physical address should be mapped * @size: the size of the allocation * * Invoke to map CPU address with PCI address. */ -int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr, - u64 pci_addr, size_t size) +int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t phys_addr, u64 pci_addr, size_t size) { int ret; unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; if (!epc->ops->map_addr) return 0; spin_lock_irqsave(&epc->lock, flags); - ret = epc->ops->map_addr(epc, phys_addr, pci_addr, size); + ret = epc->ops->map_addr(epc, func_no, phys_addr, pci_addr, size); spin_unlock_irqrestore(&epc->lock, flags); return ret; @@ -281,22 +287,23 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); /** * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared + * @func_no: the endpoint function number in the EPC device * @bar: the BAR number that has to be reset * * Invoke to reset the BAR of the endpoint device. */ -void pci_epc_clear_bar(struct pci_epc *epc, int bar) +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) { unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return; if (!epc->ops->clear_bar) return; spin_lock_irqsave(&epc->lock, flags); - epc->ops->clear_bar(epc, bar); + epc->ops->clear_bar(epc, func_no, bar); spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); @@ -304,26 +311,27 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar); /** * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space * @epc: the EPC device on which BAR has to be configured + * @func_no: the endpoint function number in the EPC device * @bar: the BAR number that has to be configured * @size: the size of the addr space * @flags: specify memory allocation/io allocation/32bit address/64 bit address * * Invoke to configure the BAR of the endpoint device. */ -int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar, +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, dma_addr_t bar_phys, size_t size, int flags) { int ret; unsigned long irq_flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; if (!epc->ops->set_bar) return 0; spin_lock_irqsave(&epc->lock, irq_flags); - ret = epc->ops->set_bar(epc, bar, bar_phys, size, flags); + ret = epc->ops->set_bar(epc, func_no, bar, bar_phys, size, flags); spin_unlock_irqrestore(&epc->lock, irq_flags); return ret; @@ -333,6 +341,7 @@ EXPORT_SYMBOL_GPL(pci_epc_set_bar); /** * pci_epc_write_header() - write standard configuration header * @epc: the EPC device to which the configuration header should be written + * @func_no: the endpoint function number in the EPC device * @header: standard configuration header fields * * Invoke to write the configuration header to the endpoint controller. Every @@ -340,19 +349,20 @@ EXPORT_SYMBOL_GPL(pci_epc_set_bar); * configuration header would be written. The callback function should write * the header fields to this dedicated location. */ -int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *header) +int pci_epc_write_header(struct pci_epc *epc, u8 func_no, + struct pci_epf_header *header) { int ret; unsigned long flags; - if (IS_ERR(epc)) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; if (!epc->ops->write_header) return 0; spin_lock_irqsave(&epc->lock, flags); - ret = epc->ops->write_header(epc, header); + ret = epc->ops->write_header(epc, func_no, header); spin_unlock_irqrestore(&epc->lock, flags); return ret; diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index f7a04e1af112..e58e2de8936b 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -39,17 +39,20 @@ enum pci_epc_irq_type { * @owner: the module owner containing the ops */ struct pci_epc_ops { - int (*write_header)(struct pci_epc *pci_epc, + int (*write_header)(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); - int (*set_bar)(struct pci_epc *epc, enum pci_barno bar, + int (*set_bar)(struct pci_epc *epc, u8 func_no, + enum pci_barno bar, dma_addr_t bar_phys, size_t size, int flags); - void (*clear_bar)(struct pci_epc *epc, enum pci_barno bar); - int (*map_addr)(struct pci_epc *epc, phys_addr_t addr, - u64 pci_addr, size_t size); - void (*unmap_addr)(struct pci_epc *epc, phys_addr_t addr); - int (*set_msi)(struct pci_epc *epc, u8 interrupts); - int (*get_msi)(struct pci_epc *epc); - int (*raise_irq)(struct pci_epc *pci_epc, + void (*clear_bar)(struct pci_epc *epc, u8 func_no, + enum pci_barno bar); + int (*map_addr)(struct pci_epc *epc, u8 func_no, + phys_addr_t addr, u64 pci_addr, size_t size); + void (*unmap_addr)(struct pci_epc *epc, u8 func_no, + phys_addr_t addr); + int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts); + int (*get_msi)(struct pci_epc *epc, u8 func_no); + int (*raise_irq)(struct pci_epc *epc, u8 func_no, enum pci_epc_irq_type type, u8 interrupt_num); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); @@ -124,17 +127,21 @@ void pci_epc_destroy(struct pci_epc *epc); int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf); void pci_epc_linkup(struct pci_epc *epc); void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); -int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *hdr); -int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar, +int pci_epc_write_header(struct pci_epc *epc, u8 func_no, + struct pci_epf_header *hdr); +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, + enum pci_barno bar, dma_addr_t bar_phys, size_t size, int flags); -void pci_epc_clear_bar(struct pci_epc *epc, int bar); -int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr, +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); +int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t phys_addr, u64 pci_addr, size_t size); -void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr); -int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts); -int pci_epc_get_msi(struct pci_epc *epc); -int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type, - u8 interrupt_num); +void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t phys_addr); +int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts); +int pci_epc_get_msi(struct pci_epc *epc, u8 func_no); +int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num); int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); struct pci_epc *pci_epc_get(const char *epc_name); -- cgit v1.2.2 From fc41df28f89e9d93deb4f0930cb0fb7eec26e5b4 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:57 +0100 Subject: PCI: endpoint: Fix EPF device name to support multi-function devices Fix the pci_epf_make() function so it can now bind many EPF devices to the same EPF driver. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-ep-cfs.c | 46 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 4f74386c1ced..48a89dff0e7f 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -18,18 +18,22 @@ */ #include +#include #include #include #include #include +static DEFINE_IDR(functions_idr); +static DEFINE_MUTEX(functions_mutex); static struct config_group *functions_group; static struct config_group *controllers_group; struct pci_epf_group { struct config_group group; struct pci_epf *epf; + int index; }; struct pci_epc_group { @@ -353,6 +357,9 @@ static void pci_epf_release(struct config_item *item) { struct pci_epf_group *epf_group = to_pci_epf_group(item); + mutex_lock(&functions_mutex); + idr_remove(&functions_idr, epf_group->index); + mutex_unlock(&functions_mutex); pci_epf_destroy(epf_group->epf); kfree(epf_group); } @@ -372,22 +379,57 @@ static struct config_group *pci_epf_make(struct config_group *group, { struct pci_epf_group *epf_group; struct pci_epf *epf; + char *epf_name; + int index, err; epf_group = kzalloc(sizeof(*epf_group), GFP_KERNEL); if (!epf_group) return ERR_PTR(-ENOMEM); + mutex_lock(&functions_mutex); + index = idr_alloc(&functions_idr, epf_group, 0, 0, GFP_KERNEL); + mutex_unlock(&functions_mutex); + if (index < 0) { + err = index; + goto free_group; + } + + epf_group->index = index; + config_group_init_type_name(&epf_group->group, name, &pci_epf_type); - epf = pci_epf_create(group->cg_item.ci_name); + epf_name = kasprintf(GFP_KERNEL, "%s.%d", + group->cg_item.ci_name, epf_group->index); + if (!epf_name) { + err = -ENOMEM; + goto remove_idr; + } + + epf = pci_epf_create(epf_name); if (IS_ERR(epf)) { pr_err("failed to create endpoint function device\n"); - return ERR_PTR(-EINVAL); + err = -EINVAL; + goto free_name; } epf_group->epf = epf; + kfree(epf_name); + return &epf_group->group; + +free_name: + kfree(epf_name); + +remove_idr: + mutex_lock(&functions_mutex); + idr_remove(&functions_idr, epf_group->index); + mutex_unlock(&functions_mutex); + +free_group: + kfree(epf_group); + + return ERR_PTR(err); } static void pci_epf_drop(struct config_group *group, struct config_item *item) -- cgit v1.2.2 From 6618f4d3f5d614d5cd4cbc4e81f0fef8e22a36c7 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 30 Jan 2018 21:56:58 +0100 Subject: dt-bindings: PCI: cadence: Add DT bindings for Cadence PCIe endpoint controller This patch documents the DT bindings for the Cadence PCIe controller when configured in endpoint mode. Signed-off-by: Cyrille Pitchen Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../devicetree/bindings/pci/cdns,cdns-pcie-ep.txt | 22 +++++++++++