]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branches 'pci/host-altera', 'pci/host-designware', 'pci/host-generic', 'pci...
authorBjorn Helgaas <bhelgaas@google.com>
Thu, 29 Oct 2015 23:20:56 +0000 (18:20 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 29 Oct 2015 23:20:56 +0000 (18:20 -0500)
* pci/host-altera:
  PCI: altera: Add Altera PCIe MSI driver
  PCI: altera: Add Altera PCIe host controller driver
  ARM: Add msi.h to Kbuild

* pci/host-designware:
  PCI: designware: Make num-lanes an optional DT property
  PCI: designware: Require config accesses to be naturally aligned
  PCI: designware: Simplify dw_pcie_cfg_read/write() interfaces
  PCI: designware: Use exact access size in dw_pcie_cfg_read()
  PCI: spear: Fix dw_pcie_cfg_read/write() usage
  PCI: designware: Set up high part of MSI target address
  PCI: designware: Make get_msi_addr() return phys_addr_t, not u32
  PCI: designware: Implement multivector MSI IRQ setup
  PCI: designware: Factor out MSI msg setup
  PCI: Add msi_controller setup_irqs() method for special multivector setup
  PCI: designware: Fix PORT_LOGIC_LINK_WIDTH_MASK

* pci/host-generic:
  PCI: generic: Fix address window calculation for non-zero starting bus
  PCI: generic: Pass starting bus number to pci_scan_root_bus()
  PCI: generic: Allow multiple hosts with different map_bus() methods
  arm64: dts: Drop linux,pci-probe-only from the Seattle DTS
  powerpc/PCI: Fix lookup of linux,pci-probe-only property
  PCI: generic: Fix lookup of linux,pci-probe-only property
  of/pci: Add of_pci_check_probe_only to parse "linux,pci-probe-only"

* pci/host-imx6:
  PCI: imx6: Add PCIE_PHY_RX_ASIC_OUT_VALID definition
  PCI: imx6: Return real error code from imx6_add_pcie_port()

* pci/host-iproc:
  PCI: iproc: Fix header comment "Corporation" misspelling
  PCI: iproc: Add outbound mapping support
  PCI: iproc: Update PCIe device tree bindings
  PCI: iproc: Improve link detection logic
  PCI: iproc: Fix PCIe reset logic
  PCI: iproc: Call pci_fixup_irqs() for ARM64 as well as ARM
  PCI: iproc: Remove unused struct iproc_pcie.irqs[]
  PCI: iproc: Fix code comment to match code

* pci/host-mvebu:
  PCI: mvebu: Remove code restricting accesses to slot 0
  PCI: mvebu: Add PCI Express root complex capability block
  PCI: mvebu: Improve clock/reset handling
  PCI: mvebu: Use gpio_desc to carry around gpio
  PCI: mvebu: Use devm_kcalloc() to allocate an array
  PCI: mvebu: Use gpio_set_value_cansleep()
  PCI: mvebu: Split port parsing and resource claiming from  port setup
  PCI: mvebu: Fix memory leaks and refcount leaks
  PCI: mvebu: Move port parsing and resource claiming to  separate function
  PCI: mvebu: Use port->name rather than "PCIe%d.%d"
  PCI: mvebu: Report full node name when reporting a DT error
  PCI: mvebu: Use for_each_available_child_of_node() to walk child nodes
  PCI: mvebu: Use of_get_available_child_count()
  PCI: mvebu: Use exact config access size; don't read/modify/write
  PCI: mvebu: Return zero for reserved or unimplemented config space

* pci/host-tegra:
  PCI: tegra: Wrap static pgprot_t initializer with __pgprot()

* pci/host-xgene:
  PCI/MSI: xgene: Remove msi_controller assignment

30 files changed:
Documentation/devicetree/bindings/pci/altera-pcie-msi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/altera-pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/pci/host-generic-pci.txt
MAINTAINERS
arch/arm/include/asm/Kbuild
arch/arm64/boot/dts/amd/amd-overdrive.dts
arch/powerpc/platforms/pseries/setup.c
drivers/of/of_pci.c
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-host-generic.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone-dw.c
drivers/pci/host/pci-keystone.h
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-xgene.c
drivers/pci/host/pcie-altera-msi.c [new file with mode: 0644]
drivers/pci/host/pcie-altera.c [new file with mode: 0644]
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-iproc.h
drivers/pci/host/pcie-spear13xx.c
drivers/pci/msi.c
include/linux/msi.h
include/linux/of_pci.h

diff --git a/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt b/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
new file mode 100644 (file)
index 0000000..09cd3bc
--- /dev/null
@@ -0,0 +1,28 @@
+* Altera PCIe MSI controller
+
+Required properties:
+- compatible:  should contain "altr,msi-1.0"
+- reg:         specifies the physical base address of the controller and
+               the length of the memory mapped region.
+- reg-names:   must include the following entries:
+               "csr": CSR registers
+               "vector_slave": vectors slave port region
+- interrupt-parent:    interrupt source phandle.
+- interrupts:  specifies the interrupt source of the parent interrupt
+               controller. The format of the interrupt specifier depends on the
+               parent interrupt controller.
+- num-vectors: number of vectors, range 1 to 32.
+- msi-controller:      indicates that this is MSI controller node
+
+
+Example
+msi0: msi@0xFF200000 {
+       compatible = "altr,msi-1.0";
+       reg = <0xFF200000 0x00000010
+               0xFF200010 0x00000080>;
+       reg-names = "csr", "vector_slave";
+       interrupt-parent = <&hps_0_arm_gic_0>;
+       interrupts = <0 42 4>;
+       msi-controller;
+       num-vectors = <32>;
+};
diff --git a/Documentation/devicetree/bindings/pci/altera-pcie.txt b/Documentation/devicetree/bindings/pci/altera-pcie.txt
new file mode 100644 (file)
index 0000000..2951a6a
--- /dev/null
@@ -0,0 +1,49 @@
+* Altera PCIe controller
+
+Required properties:
+- compatible : should contain "altr,pcie-root-port-1.0"
+- reg:         a list of physical base address and length for TXS and CRA.
+- reg-names:   must include the following entries:
+               "Txs": TX slave port region
+               "Cra": Control register access region
+- interrupt-parent:    interrupt source phandle.
+- interrupts:  specifies the interrupt source of the parent interrupt controller.
+               The format of the interrupt specifier depends on the parent interrupt
+               controller.
+- device_type: must be "pci"
+- #address-cells:      set to <3>
+- #size-cells: set to <2>
+- #interrupt-cells:    set to <1>
+- ranges:              describes the translation of addresses for root ports and standard
+               PCI regions.
+- interrupt-map-mask and interrupt-map: standard PCI properties to define the
+               mapping of the PCIe interface to interrupt numbers.
+
+Optional properties:
+- msi-parent:  Link to the hardware entity that serves as the MSI controller for this PCIe
+               controller.
+- bus-range:   PCI bus numbers covered
+
+Example
+       pcie_0: pcie@0xc00000000 {
+               compatible = "altr,pcie-root-port-1.0";
+               reg = <0xc0000000 0x20000000>,
+                       <0xff220000 0x00004000>;
+               reg-names = "Txs", "Cra";
+               interrupt-parent = <&hps_0_arm_gic_0>;
+               interrupts = <0 40 4>;
+               interrupt-controller;
+               #interrupt-cells = <1>;
+               bus-range = <0x0 0xFF>;
+               device_type = "pci";
+               msi-parent = <&msi_to_gic_gen_0>;
+               #address-cells = <3>;
+               #size-cells = <2>;
+               interrupt-map-mask = <0 0 0 7>;
+               interrupt-map = <0 0 0 1 &pcie_0 1>,
+                                   <0 0 0 2 &pcie_0 2>,
+                                   <0 0 0 3 &pcie_0 3>,
+                                   <0 0 0 4 &pcie_0 4>;
+               ranges = <0x82000000 0x00000000 0x00000000 0xc0000000 0x00000000 0x10000000
+                           0x82000000 0x00000000 0x10000000 0xd0000000 0x00000000 0x10000000>;
+       };
index f7ce50e38ed495fdda7263878e1337b557632c5d..45c2a8094a9f7f7de65b37e41675bd0af4d6fe15 100644 (file)
@@ -17,6 +17,21 @@ Optional properties:
 - phys: phandle of the PCIe PHY device
 - phy-names: must be "pcie-phy"
 
+- brcm,pcie-ob: Some iProc SoCs do not have the outbound address mapping done
+by the ASIC after power on reset. In this case, SW needs to configure it
+
+If the brcm,pcie-ob property is present, the following properties become
+effective:
+
+Required:
+- brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
+address used by the iProc PCIe core (not the PCIe address)
+- brcm,pcie-ob-window-size: The outbound address mapping window size (in MB)
+
+Optional:
+- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
+increase the outbound window size
+
 Example:
        pcie0: pcie@18012000 {
                compatible = "brcm,iproc-pcie";
@@ -38,6 +53,11 @@ Example:
 
                phys = <&phy 0 5>;
                phy-names = "pcie-phy";
+
+               brcm,pcie-ob;
+               brcm,pcie-ob-oarr-size;
+               brcm,pcie-ob-axi-offset = <0x00000000>;
+               brcm,pcie-ob-window-size = <256>;
        };
 
        pcie1: pcie@18013000 {
index 9f4faa8e8d005ee1810f0dc4272d63f4af3e17e6..0036ab3065b8abb15d0ae2d3c7cb438478399c5f 100644 (file)
@@ -14,7 +14,6 @@ Required properties:
 - interrupt-map-mask and interrupt-map: standard PCI properties
        to define the mapping of the PCIe interface to interrupt
        numbers.
-- num-lanes: number of lanes to use
 - clocks: Must contain an entry for each entry in clock-names.
        See ../clocks/clock-bindings.txt for details.
 - clock-names: Must include the following entries:
@@ -22,6 +21,8 @@ Required properties:
        - "pcie_bus"
 
 Optional properties:
+- num-lanes: number of lanes to use (this property should be specified unless
+  the link is brought already up in BIOS)
 - reset-gpio: gpio pin number of power good signal
 - bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
   specify this property, to keep backwards compatibility a range of 0x00-0xff
index cf3e205e0b7e0de18e8e6650bdc347d996807960..3f1d3fca62bbf6a62afd1d270d271582c6c2e20e 100644 (file)
@@ -34,8 +34,9 @@ Properties of the host controller node:
 - #size-cells    : Must be 2.
 
 - reg            : The Configuration Space base address and size, as accessed
-                   from the parent bus.
-
+                   from the parent bus.  The base address corresponds to
+                   the first bus in the "bus-range" property.  If no
+                   "bus-range" is specified, this will be bus 0 (the default).
 
 Properties of the /chosen node:
 
index 7ba7ab749c853a7691dff808d85b2a8f80973fe6..eeb9ec999cdae4d8e5f85490eea55879750d8046 100644 (file)
@@ -7938,6 +7938,14 @@ F:       include/linux/pci*
 F:     arch/x86/pci/
 F:     arch/x86/kernel/quirks.c
 
+PCI DRIVER FOR ALTERA PCIE IP
+M:     Ley Foon Tan <lftan@altera.com>
+L:     rfi@lists.rocketboards.org (moderated for non-subscribers)
+L:     linux-pci@vger.kernel.org
+S:     Supported
+F:     Documentation/devicetree/bindings/pci/altera-pcie.txt
+F:     drivers/pci/host/pcie-altera.c
+
 PCI DRIVER FOR ARM VERSATILE PLATFORM
 M:     Rob Herring <robh@kernel.org>
 L:     linux-pci@vger.kernel.org
@@ -8039,6 +8047,14 @@ L:       linux-pci@vger.kernel.org
 S:     Maintained
 F:     drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR ALTERA MSI IP
+M:     Ley Foon Tan <lftan@altera.com>
+L:     rfi@lists.rocketboards.org (moderated for non-subscribers)
+L:     linux-pci@vger.kernel.org
+S:     Supported
+F:     Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+F:     drivers/pci/host/pcie-altera-msi.c
+
 PCI MSI DRIVER FOR APPLIEDMICRO XGENE
 M:     Duc Dang <dhdang@apm.com>
 L:     linux-pci@vger.kernel.org
index be648eb47cd9d80cbecc09a8144a8423a82f7907..bd425302c97a3ec9966d4c04bdb61c3c40252056 100644 (file)
@@ -14,6 +14,7 @@ generic-y += local.h
 generic-y += local64.h
 generic-y += mm-arch-hooks.h
 generic-y += msgbuf.h
+generic-y += msi.h
 generic-y += param.h
 generic-y += parport.h
 generic-y += poll.h
index 564a3f7df71d76992c04e210b40077a3f4adda69..128fa942f09e9f5adccf6ac214f0e10510ca2015 100644 (file)
@@ -14,7 +14,6 @@
 
        chosen {
                stdout-path = &serial0;
-               linux,pci-probe-only;
        };
 };
 
index 39a74fad3e04526eb73222d51a6bf4812c64b034..60167096fd10d1348a4d415342e343f133ad5010 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/seq_file.h>
 #include <linux/root_dev.h>
 #include <linux/of.h>
+#include <linux/of_pci.h>
 #include <linux/kexec.h>
 
 #include <asm/mmu.h>
@@ -495,18 +496,7 @@ static void __init find_and_init_phbs(void)
         * PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties
         * in chosen.
         */
-       if (of_chosen) {
-               const int *prop;
-
-               prop = of_get_property(of_chosen,
-                               "linux,pci-probe-only", NULL);
-               if (prop) {
-                       if (*prop)
-                               pci_add_flags(PCI_PROBE_ONLY);
-                       else
-                               pci_clear_flags(PCI_PROBE_ONLY);
-               }
-       }
+       of_pci_check_probe_only();
 }
 
 static void __init pSeries_setup_arch(void)
index 5751dc5b6494d41fd0ed9fd88cc877f3d10707a0..d0c8902dfbf4c564cf50af70b5b6cbb6798905c5 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/of_device.h>
 #include <linux/of_pci.h>
 #include <linux/slab.h>
+#include <asm-generic/pci-bridge.h>
 
 static inline int __of_pci_pci_compare(struct device_node *node,
                                       unsigned int data)
@@ -117,6 +118,31 @@ int of_get_pci_domain_nr(struct device_node *node)
 }
 EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
 
+/**
+ * of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only
+ *                           is present and valid
+ */
+void of_pci_check_probe_only(void)
+{
+       u32 val;
+       int ret;
+
+       ret = of_property_read_u32(of_chosen, "linux,pci-probe-only", &val);
+       if (ret) {
+               if (ret == -ENODATA || ret == -EOVERFLOW)
+                       pr_warn("linux,pci-probe-only without valid value, ignoring\n");
+               return;
+       }
+
+       if (val)
+               pci_add_flags(PCI_PROBE_ONLY);
+       else
+               pci_clear_flags(PCI_PROBE_ONLY);
+
+       pr_info("PCI: PROBE_ONLY %sabled\n", val ? "en" : "dis");
+}
+EXPORT_SYMBOL_GPL(of_pci_check_probe_only);
+
 /**
  * of_pci_dma_configure - Setup DMA configuration
  * @dev: ptr to pci_dev struct of the PCI device
index d5e58bae95cf2dbfa74a49ddce1a51dee6e98e9d..b06c2d97e7035ce154927f9dfc65e9c7f562b482 100644 (file)
@@ -145,4 +145,20 @@ config PCIE_IPROC_BCMA
          Say Y here if you want to use the Broadcom iProc PCIe controller
          through the BCMA bus interface
 
+config PCIE_ALTERA
+       bool "Altera PCIe controller"
+       depends on ARM || NIOS2
+       select PCI_DOMAINS
+       help
+         Say Y here if you want to enable PCIe controller support on Altera
+         FPGA.
+
+config PCIE_ALTERA_MSI
+       bool "Altera PCIe MSI feature"
+       depends on PCI_MSI
+       select PCI_MSI_IRQ_DOMAIN
+       help
+         Say Y here if you want PCIe MSI support for the Altera FPGA.
+         This MSI driver supports Altera MSI to GIC controller IP.
+
 endmenu
index 140d66f796e4b843673651bda56a00bdfb2317b1..6c4913dfd235ad2fa1c653d7ee3c56caef9d4168 100644 (file)
@@ -17,3 +17,5 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
 obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
 obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
+obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
+obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
index f9f468d9a819d8402244e31805d51a034fb6392f..01095e1160a474b414112cdc1dddae1d8f3b9434 100644 (file)
@@ -454,7 +454,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
        int ret;
 
        exynos_pcie_sideband_dbi_r_mode(pp, true);
-       ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+       ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
        exynos_pcie_sideband_dbi_r_mode(pp, false);
        return ret;
 }
@@ -465,8 +465,7 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        int ret;
 
        exynos_pcie_sideband_dbi_w_mode(pp, true);
-       ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3),
-                       where, size, val);
+       ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
        exynos_pcie_sideband_dbi_w_mode(pp, false);
        return ret;
 }
index 265dd25169bff9d2edfe827cd9321442d7c1e47d..5434c90db24345bc73ad5e00ecc00d599c40ddaf 100644 (file)
@@ -27,7 +27,7 @@
 
 struct gen_pci_cfg_bus_ops {
        u32 bus_shift;
-       void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int);
+       struct pci_ops ops;
 };
 
 struct gen_pci_cfg_windows {
@@ -35,7 +35,7 @@ struct gen_pci_cfg_windows {
        struct resource                         *bus_range;
        void __iomem                            **win;
 
-       const struct gen_pci_cfg_bus_ops        *ops;
+       struct gen_pci_cfg_bus_ops              *ops;
 };
 
 /*
@@ -65,7 +65,11 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
 
 static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
        .bus_shift      = 16,
-       .map_bus        = gen_pci_map_cfg_bus_cam,
+       .ops            = {
+               .map_bus        = gen_pci_map_cfg_bus_cam,
+               .read           = pci_generic_config_read,
+               .write          = pci_generic_config_write,
+       }
 };
 
 static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
@@ -80,12 +84,11 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
 
 static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
        .bus_shift      = 20,
-       .map_bus        = gen_pci_map_cfg_bus_ecam,
-};
-
-static struct pci_ops gen_pci_ops = {
-       .read   = pci_generic_config_read,
-       .write  = pci_generic_config_write,
+       .ops            = {
+               .map_bus        = gen_pci_map_cfg_bus_ecam,
+               .read           = pci_generic_config_read,
+               .write          = pci_generic_config_write,
+       }
 };
 
 static const struct of_device_id gen_pci_of_match[] = {
@@ -166,6 +169,7 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
        struct resource *bus_range;
        struct device *dev = pci->host.dev.parent;
        struct device_node *np = dev->of_node;
+       u32 sz = 1 << pci->cfg.ops->bus_shift;
 
        err = of_address_to_resource(np, 0, &pci->cfg.res);
        if (err) {
@@ -193,10 +197,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
        bus_range = pci->cfg.bus_range;
        for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
                u32 idx = busn - bus_range->start;
-               u32 sz = 1 << pci->cfg.ops->bus_shift;
 
                pci->cfg.win[idx] = devm_ioremap(dev,
-                                                pci->cfg.res.start + busn * sz,
+                                                pci->cfg.res.start + idx * sz,
                                                 sz);
                if (!pci->cfg.win[idx])
                        return -ENOMEM;
@@ -210,7 +213,6 @@ static int gen_pci_probe(struct platform_device *pdev)
        int err;
        const char *type;
        const struct of_device_id *of_id;
-       const int *prop;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
@@ -225,17 +227,10 @@ static int gen_pci_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL);
-       if (prop) {
-               if (*prop)
-                       pci_add_flags(PCI_PROBE_ONLY);
-               else
-                       pci_clear_flags(PCI_PROBE_ONLY);
-       }
+       of_pci_check_probe_only();
 
        of_id = of_match_node(gen_pci_of_match, np);
-       pci->cfg.ops = of_id->data;
-       gen_pci_ops.map_bus = pci->cfg.ops->map_bus;
+       pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
        pci->host.dev.parent = dev;
        INIT_LIST_HEAD(&pci->host.windows);
        INIT_LIST_HEAD(&pci->resources);
@@ -256,7 +251,9 @@ static int gen_pci_probe(struct platform_device *pdev)
        if (!pci_has_flag(PCI_PROBE_ONLY))
                pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
 
-       bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources);
+
+       bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
+                               &pci->cfg.ops->ops, pci, &pci->resources);
        if (!bus) {
                dev_err(dev, "Scanning rootbus failed");
                return -ENODEV;
index 8f3a9813c4e55a5f16ee48e3b54ccd1ec4985702..22e8224126fd9d9885ee06f2fb81cbf1f023b06b 100644 (file)
@@ -74,6 +74,7 @@ struct imx6_pcie {
 
 /* PHY registers (not memory-mapped) */
 #define PCIE_PHY_RX_ASIC_OUT 0x100D
+#define PCIE_PHY_RX_ASIC_OUT_VALID     (1 << 0)
 
 #define PHY_RX_OVRD_IN_LO 0x1005
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
@@ -503,7 +504,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
        pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
        debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
 
-       if (rx_valid & 0x01)
+       if (rx_valid & PCIE_PHY_RX_ASIC_OUT_VALID)
                return 0;
 
        if ((debug_r0 & 0x3f) != 0x0d)
@@ -539,7 +540,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
                                       IRQF_SHARED, "mx6-pcie-msi", pp);
                if (ret) {
                        dev_err(&pdev->dev, "failed to request MSI irq\n");
-                       return -ENODEV;
+                       return ret;
                }
        }
 
index e71da991949b1d598444a81dd775b3117c1c4744..0146b4121d49716c4e2ff704355ce8a37ced3ab7 100644 (file)
@@ -70,7 +70,7 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
        *bit_pos = offset >> 3;
 }
 
-u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
 {
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
@@ -398,7 +398,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
 
        addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
 
-       return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val);
+       return dw_pcie_cfg_read(addr + where, size, val);
 }
 
 int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
@@ -410,7 +410,7 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
 
        addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
 
-       return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val);
+       return dw_pcie_cfg_write(addr + where, size, val);
 }
 
 /**
index 478d932b602d40cc81dd7061b103ba0973c060b3..f0944e8c4b02f4e481a9178e9dfe4107d64c83b9 100644 (file)
@@ -37,7 +37,7 @@ struct keystone_pcie {
 
 /* Keystone DW specific MSI controller APIs/definitions */
 void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset);
-u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
 
 /* Keystone specific PCI controller APIs */
 void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
index 67ec5e1c99dbbb75a6f14943b25539ca50dc0f1e..53b79c5f055998761b837094b3ad43bd6c56de82 100644 (file)
@@ -30,6 +30,7 @@
 #define PCIE_DEV_REV_OFF       0x0008
 #define PCIE_BAR_LO_OFF(n)     (0x0010 + ((n) << 3))
 #define PCIE_BAR_HI_OFF(n)     (0x0014 + ((n) << 3))
+#define PCIE_CAP_PCIEXP                0x0060
 #define PCIE_HEADER_LOG_4_OFF  0x0128
 #define PCIE_BAR_CTRL_OFF(n)   (0x1804 + (((n) - 1) * 4))
 #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
 #define  PCIE_STAT_BUS                  0xff00
 #define  PCIE_STAT_DEV                  0x1f0000
 #define  PCIE_STAT_LINK_DOWN           BIT(0)
+#define PCIE_RC_RTSTA          0x1a14
 #define PCIE_DEBUG_CTRL         0x1a60
 #define  PCIE_DEBUG_SOFT_RESET         BIT(20)
 
+enum {
+       PCISWCAP = PCI_BRIDGE_CONTROL + 2,
+       PCISWCAP_EXP_LIST_ID    = PCISWCAP + PCI_CAP_LIST_ID,
+       PCISWCAP_EXP_DEVCAP     = PCISWCAP + PCI_EXP_DEVCAP,
+       PCISWCAP_EXP_DEVCTL     = PCISWCAP + PCI_EXP_DEVCTL,
+       PCISWCAP_EXP_LNKCAP     = PCISWCAP + PCI_EXP_LNKCAP,
+       PCISWCAP_EXP_LNKCTL     = PCISWCAP + PCI_EXP_LNKCTL,
+       PCISWCAP_EXP_SLTCAP     = PCISWCAP + PCI_EXP_SLTCAP,
+       PCISWCAP_EXP_SLTCTL     = PCISWCAP + PCI_EXP_SLTCTL,
+       PCISWCAP_EXP_RTCTL      = PCISWCAP + PCI_EXP_RTCTL,
+       PCISWCAP_EXP_RTSTA      = PCISWCAP + PCI_EXP_RTSTA,
+       PCISWCAP_EXP_DEVCAP2    = PCISWCAP + PCI_EXP_DEVCAP2,
+       PCISWCAP_EXP_DEVCTL2    = PCISWCAP + PCI_EXP_DEVCTL2,
+       PCISWCAP_EXP_LNKCAP2    = PCISWCAP + PCI_EXP_LNKCAP2,
+       PCISWCAP_EXP_LNKCTL2    = PCISWCAP + PCI_EXP_LNKCTL2,
+       PCISWCAP_EXP_SLTCAP2    = PCISWCAP + PCI_EXP_SLTCAP2,
+       PCISWCAP_EXP_SLTCTL2    = PCISWCAP + PCI_EXP_SLTCTL2,
+};
+
 /* PCI configuration space of a PCI-to-PCI bridge */
 struct mvebu_sw_pci_bridge {
        u16 vendor;
        u16 device;
        u16 command;
+       u16 status;
        u16 class;
        u8 interface;
        u8 revision;
@@ -84,13 +106,15 @@ struct mvebu_sw_pci_bridge {
        u16 memlimit;
        u16 iobaseupper;
        u16 iolimitupper;
-       u8 cappointer;
-       u8 reserved1;
-       u16 reserved2;
        u32 romaddr;
        u8 intline;
        u8 intpin;
        u16 bridgectrl;
+
+       /* PCI express capability */
+       u32 pcie_sltcap;
+       u16 pcie_devctl;
+       u16 pcie_rtctl;
 };
 
 struct mvebu_pcie_port;
@@ -119,8 +143,7 @@ struct mvebu_pcie_port {
        unsigned int io_target;
        unsigned int io_attr;
        struct clk *clk;
-       int reset_gpio;
-       int reset_active_low;
+       struct gpio_desc *reset_gpio;
        char *reset_name;
        struct mvebu_sw_pci_bridge bridge;
        struct device_node *dn;
@@ -254,15 +277,22 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
                                 struct pci_bus *bus,
                                 u32 devfn, int where, int size, u32 *val)
 {
+       void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
+
        mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
                     PCIE_CONF_ADDR_OFF);
 
-       *val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
-
-       if (size == 1)
-               *val = (*val >> (8 * (where & 3))) & 0xff;
-       else if (size == 2)
-               *val = (*val >> (8 * (where & 3))) & 0xffff;
+       switch (size) {
+       case 1:
+               *val = readb_relaxed(conf_data + (where & 3));
+               break;
+       case 2:
+               *val = readw_relaxed(conf_data + (where & 2));
+               break;
+       case 4:
+               *val = readl_relaxed(conf_data);
+               break;
+       }
 
        return PCIBIOS_SUCCESSFUL;
 }
@@ -271,22 +301,24 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
                                 struct pci_bus *bus,
                                 u32 devfn, int where, int size, u32 val)
 {
-       u32 _val, shift = 8 * (where & 3);
+       void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
 
        mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
                     PCIE_CONF_ADDR_OFF);
-       _val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
 
-       if (size == 4)
-               _val = val;
-       else if (size == 2)
-               _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
-       else if (size == 1)
-               _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
-       else
+       switch (size) {
+       case 1:
+               writeb(val, conf_data + (where & 3));
+               break;
+       case 2:
+               writew(val, conf_data + (where & 2));
+               break;
+       case 4:
+               writel(val, conf_data);
+               break;
+       default:
                return PCIBIOS_BAD_REGISTER_NUMBER;
-
-       mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
+       }
 
        return PCIBIOS_SUCCESSFUL;
 }
@@ -443,6 +475,9 @@ static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
        /* We support 32 bits I/O addressing */
        bridge->iobase = PCI_IO_RANGE_TYPE_32;
        bridge->iolimit = PCI_IO_RANGE_TYPE_32;
+
+       /* Add capabilities */
+       bridge->status = PCI_STATUS_CAP_LIST;
 }
 
 /*
@@ -460,7 +495,7 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
                break;
 
        case PCI_COMMAND:
-               *value = bridge->command;
+               *value = bridge->command | bridge->status << 16;
                break;
 
        case PCI_CLASS_REVISION:
@@ -505,6 +540,10 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
                *value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
                break;
 
+       case PCI_CAPABILITY_LIST:
+               *value = PCISWCAP;
+               break;
+
        case PCI_ROM_ADDRESS1:
                *value = 0;
                break;
@@ -514,9 +553,67 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
                *value = 0;
                break;
 
+       case PCISWCAP_EXP_LIST_ID:
+               /* Set PCIe v2, root port, slot support */
+               *value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
+                         PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP;
+               break;
+
+       case PCISWCAP_EXP_DEVCAP:
+               *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
+               break;
+
+       case PCISWCAP_EXP_DEVCTL:
+               *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) &
+                                ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
+                                  PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
+               *value |= bridge->pcie_devctl;
+               break;
+
+       case PCISWCAP_EXP_LNKCAP:
+               /*
+                * PCIe requires the clock power management capability to be
+                * hard-wired to zero for downstream ports
+                */
+               *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
+                        ~PCI_EXP_LNKCAP_CLKPM;
+               break;
+
+       case PCISWCAP_EXP_LNKCTL:
+               *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+               break;
+
+       case PCISWCAP_EXP_SLTCAP:
+               *value = bridge->pcie_sltcap;
+               break;
+
+       case PCISWCAP_EXP_SLTCTL:
+               *value = PCI_EXP_SLTSTA_PDS << 16;
+               break;
+
+       case PCISWCAP_EXP_RTCTL:
+               *value = bridge->pcie_rtctl;
+               break;
+
+       case PCISWCAP_EXP_RTSTA:
+               *value = mvebu_readl(port, PCIE_RC_RTSTA);
+               break;
+
+       /* PCIe requires the v2 fields to be hard-wired to zero */
+       case PCISWCAP_EXP_DEVCAP2:
+       case PCISWCAP_EXP_DEVCTL2:
+       case PCISWCAP_EXP_LNKCAP2:
+       case PCISWCAP_EXP_LNKCTL2:
+       case PCISWCAP_EXP_SLTCAP2:
+       case PCISWCAP_EXP_SLTCTL2:
        default:
-               *value = 0xffffffff;
-               return PCIBIOS_BAD_REGISTER_NUMBER;
+               /*
+                * PCI defines configuration read accesses to reserved or
+                * unimplemented registers to read as zero and complete
+                * normally.
+                */
+               *value = 0;
+               return PCIBIOS_SUCCESSFUL;
        }
 
        if (size == 2)
@@ -601,6 +698,51 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
                mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
                break;
 
+       case PCISWCAP_EXP_DEVCTL:
+               /*
+                * Armada370 data says these bits must always
+                * be zero when in root complex mode.
+                */
+               value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
+                          PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
+
+               /*
+                * If the mask is 0xffff0000, then we only want to write
+                * the device control register, rather than clearing the
+                * RW1C bits in the device status register.  Mask out the
+                * status register bits.
+                */
+               if (mask == 0xffff0000)
+                       value &= 0xffff;
+
+               mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
+               break;
+
+       case PCISWCAP_EXP_LNKCTL:
+               /*
+                * If we don't support CLKREQ, we must ensure that the
+                * CLKREQ enable bit always reads zero.  Since we haven't
+                * had this capability, and it's dependent on board wiring,
+                * disable it for the time being.
+                */
+               value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+
+               /*
+                * If the mask is 0xffff0000, then we only want to write
+                * the link control register, rather than clearing the
+                * RW1C bits in the link status register.  Mask out the
+                * status register bits.
+                */
+               if (mask == 0xffff0000)
+                       value &= 0xffff;
+
+               mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+               break;
+
+       case PCISWCAP_EXP_RTSTA:
+               mvebu_writel(port, value, PCIE_RC_RTSTA);
+               break;
+
        default:
                break;
        }
@@ -652,17 +794,6 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
        if (!mvebu_pcie_link_up(port))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
-       /*
-        * On the secondary bus, we don't want to expose any other
-        * device than the device physically connected in the PCIe
-        * slot, visible in slot 0. In slot 1, there's a special
-        * Marvell device that only makes sense when the Armada is
-        * used as a PCIe endpoint.
-        */
-       if (bus->number == port->bridge.secondary_bus &&
-           PCI_SLOT(devfn) != 0)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
        /* Access the real PCIe interface */
        ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
                                    where, size, val);
@@ -693,19 +824,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
 
-       /*
-        * On the secondary bus, we don't want to expose any other
-        * device than the device physically connected in the PCIe
-        * slot, visible in slot 0. In slot 1, there's a special
-        * Marvell device that only makes sense when the Armada is
-        * used as a PCIe endpoint.
-        */
-       if (bus->number == port->bridge.secondary_bus &&
-           PCI_SLOT(devfn) != 0) {
-               *val = 0xffffffff;
-               return PCIBIOS_DEVICE_NOT_FOUND;
-       }
-
        /* Access the real PCIe interface */
        ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
                                    where, size, val);
@@ -914,12 +1032,167 @@ static int mvebu_pcie_resume(struct device *dev)
        return 0;
 }
 
+static void mvebu_pcie_port_clk_put(void *data)
+{
+       struct mvebu_pcie_port *port = data;
+
+       clk_put(port->clk);
+}
+
+static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+       struct mvebu_pcie_port *port, struct device_node *child)
+{
+       struct device *dev = &pcie->pdev->dev;
+       enum of_gpio_flags flags;
+       int reset_gpio, ret;
+
+       port->pcie = pcie;
+
+       if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) {
+               dev_warn(dev, "ignoring %s, missing pcie-port property\n",
+                        of_node_full_name(child));
+               goto skip;
+       }
+
+       if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane))
+               port->lane = 0;
+
+       port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port,
+                                   port->lane);
+       if (!port->name) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       port->devfn = of_pci_get_devfn(child);
+       if (port->devfn < 0)
+               goto skip;
+
+       ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM,
+                                &port->mem_target, &port->mem_attr);
+       if (ret < 0) {
+               dev_err(dev, "%s: cannot get tgt/attr for mem window\n",
+                       port->name);
+               goto skip;
+       }
+
+       if (resource_size(&pcie->io) != 0) {
+               mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO,
+                                  &port->io_target, &port->io_attr);
+       } else {
+               port->io_target = -1;
+               port->io_attr = -1;
+       }
+
+       reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
+       if (reset_gpio == -EPROBE_DEFER) {
+               ret = reset_gpio;
+               goto err;
+       }
+
+       if (gpio_is_valid(reset_gpio)) {
+               unsigned long gpio_flags;
+
+               port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset",
+                                                 port->name);
+               if (!port->reset_name) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               if (flags & OF_GPIO_ACTIVE_LOW) {
+                       dev_info(dev, "%s: reset gpio is active low\n",
+                                of_node_full_name(child));
+                       gpio_flags = GPIOF_ACTIVE_LOW |
+                                    GPIOF_OUT_INIT_LOW;
+               } else {
+                       gpio_flags = GPIOF_OUT_INIT_HIGH;
+               }
+
+               ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags,
+                                           port->reset_name);
+               if (ret) {
+                       if (ret == -EPROBE_DEFER)
+                               goto err;
+                       goto skip;
+               }
+
+               port->reset_gpio = gpio_to_desc(reset_gpio);
+       }
+
+       port->clk = of_clk_get_by_name(child, NULL);
+       if (IS_ERR(port->clk)) {
+               dev_err(dev, "%s: cannot get clock\n", port->name);
+               goto skip;
+       }
+
+       ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port);
+       if (ret < 0) {
+               clk_put(port->clk);
+               goto err;
+       }
+
+       return 1;
+
+skip:
+       ret = 0;
+
+       /* In the case of skipping, we need to free these */
+       devm_kfree(dev, port->reset_name);
+       port->reset_name = NULL;
+       devm_kfree(dev, port->name);
+       port->name = NULL;
+
+err:
+       return ret;
+}
+
+/*
+ * Power up a PCIe port.  PCIe requires the refclk to be stable for 100µs
+ * prior to releasing PERST.  See table 2-4 in section 2.6.2 AC Specifications
+ * of the PCI Express Card Electromechanical Specification, 1.1.
+ */
+static int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
+{
+       int ret;
+
+       ret = clk_prepare_enable(port->clk);
+       if (ret < 0)
+               return ret;
+
+       if (port->reset_gpio) {
+               u32 reset_udelay = 20000;
+
+               of_property_read_u32(port->dn, "reset-delay-us",
+                                    &reset_udelay);
+
+               udelay(100);
+
+               gpiod_set_value_cansleep(port->reset_gpio, 0);
+               msleep(reset_udelay / 1000);
+       }
+
+       return 0;
+}
+
+/*
+ * Power down a PCIe port.  Strictly, PCIe requires us to place the card
+ * in D3hot state before asserting PERST#.
+ */
+static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
+{
+       if (port->reset_gpio)
+               gpiod_set_value_cansleep(port->reset_gpio, 1);
+
+       clk_disable_unprepare(port->clk);
+}
+
 static int mvebu_pcie_probe(struct platform_device *pdev)
 {
        struct mvebu_pcie *pcie;
        struct device_node *np = pdev->dev.of_node;
        struct device_node *child;
-       int i, ret;
+       int num, i, ret;
 
        pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
                            GFP_KERNEL);
@@ -955,112 +1228,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
                return ret;
        }
 
-       i = 0;
-       for_each_child_of_node(pdev->dev.of_node, child) {
-               if (!of_device_is_available(child))
-                       continue;
-               i++;
-       }
+       num = of_get_available_child_count(pdev->dev.of_node);
 
-       pcie->ports = devm_kzalloc(&pdev->dev, i *
-                                  sizeof(struct mvebu_pcie_port),
+       pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports),
                                   GFP_KERNEL);
        if (!pcie->ports)
                return -ENOMEM;
 
        i = 0;
-       for_each_child_of_node(pdev->dev.of_node, child) {
+       for_each_available_child_of_node(pdev->dev.of_node, child) {
                struct mvebu_pcie_port *port = &pcie->ports[i];
-               enum of_gpio_flags flags;
-
-               if (!of_device_is_available(child))
-                       continue;
 
-               port->pcie = pcie;
-
-               if (of_property_read_u32(child, "marvell,pcie-port",
-                                        &port->port)) {
-                       dev_warn(&pdev->dev,
-                                "ignoring PCIe DT node, missing pcie-port property\n");
-                       continue;
-               }
-
-               if (of_property_read_u32(child, "marvell,pcie-lane",
-                                        &port->lane))
-                       port->lane = 0;
-
-               port->name = kasprintf(GFP_KERNEL, "pcie%d.%d",
-                                      port->port, port->lane);
-
-               port->devfn = of_pci_get_devfn(child);
-               if (port->devfn < 0)
-                       continue;
-
-               ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_MEM,
-                                        &port->mem_target, &port->mem_attr);
+               ret = mvebu_pcie_parse_port(pcie, port, child);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for mem window\n",
-                               port->port, port->lane);
+                       of_node_put(child);
+                       return ret;
+               } else if (ret == 0) {
                        continue;
                }
 
-               if (resource_size(&pcie->io) != 0)
-                       mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
-                                          &port->io_target, &port->io_attr);
-               else {
-                       port->io_target = -1;
-                       port->io_attr = -1;
-               }
+               port->dn = child;
+               i++;
+       }
+       pcie->nports = i;
 
-               port->reset_gpio = of_get_named_gpio_flags(child,
-                                                  "reset-gpios", 0, &flags);
-               if (gpio_is_valid(port->reset_gpio)) {
-                       u32 reset_udelay = 20000;
-
-                       port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
-                       port->reset_name = kasprintf(GFP_KERNEL,
-                                    "pcie%d.%d-reset", port->port, port->lane);
-                       of_property_read_u32(child, "reset-delay-us",
-                                            &reset_udelay);
-
-                       ret = devm_gpio_request_one(&pdev->dev,
-                           port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
-                       if (ret) {
-                               if (ret == -EPROBE_DEFER)
-                                       return ret;
-                               continue;
-                       }
-
-                       gpio_set_value(port->reset_gpio,
-                                      (port->reset_active_low) ? 1 : 0);
-                       msleep(reset_udelay/1000);
-               }
+       for (i = 0; i < pcie->nports; i++) {
+               struct mvebu_pcie_port *port = &pcie->ports[i];
 
-               port->clk = of_clk_get_by_name(child, NULL);
-               if (IS_ERR(port->clk)) {
-                       dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
-                              port->port, port->lane);
+               child = port->dn;
+               if (!child)
                        continue;
-               }
 
-               ret = clk_prepare_enable(port->clk);
-               if (ret)
+               ret = mvebu_pcie_powerup(port);
+               if (ret < 0)
                        continue;
 
                port->base = mvebu_pcie_map_registers(pdev, child, port);
                if (IS_ERR(port->base)) {
-                       dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
-                               port->port, port->lane);
+                       dev_err(&pdev->dev, "%s: cannot map registers\n",
+                               port->name);
                        port->base = NULL;
-                       clk_disable_unprepare(port->clk);
+                       mvebu_pcie_powerdown(port);
                        continue;
                }
 
                mvebu_pcie_set_local_dev_nr(port, 1);
-
-               port->dn = child;
                mvebu_sw_pci_bridge_init(port);
-               i++;
        }
 
        pcie->nports = i;
index 0236ab9d5720133b57536c916591aab580157b28..ae00ce22d5a6eb2c7c2263f13a3fe0113c45da27 100644 (file)
@@ -509,24 +509,6 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
-static int xgene_pcie_msi_enable(struct pci_bus *bus)
-{
-       struct device_node *msi_node;
-
-       msi_node = of_parse_phandle(bus->dev.of_node,
-                                       "msi-parent", 0);
-       if (!msi_node)
-               return -ENODEV;
-
-       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
-       if (!bus->msi)
-               return -ENODEV;
-
-       of_node_put(msi_node);
-       bus->msi->dev = &bus->dev;
-       return 0;
-}
-
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -567,10 +549,6 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
-       if (IS_ENABLED(CONFIG_PCI_MSI))
-               if (xgene_pcie_msi_enable(bus))
-                       dev_info(port->dev, "failed to enable MSI\n");
-
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
diff --git a/drivers/pci/host/pcie-altera-msi.c b/drivers/pci/host/pcie-altera-msi.c
new file mode 100644 (file)
index 0000000..2c37e86
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Copyright Altera Corporation (C) 2013-2015. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define MSI_STATUS             0x0
+#define MSI_ERROR              0x4
+#define MSI_INTMASK            0x8
+
+#define MAX_MSI_VECTORS                32
+
+struct altera_msi {
+       DECLARE_BITMAP(used, MAX_MSI_VECTORS);
+       struct mutex            lock;   /* protect "used" bitmap */
+       struct platform_device  *pdev;
+       struct irq_domain       *msi_domain;
+       struct irq_domain       *inner_domain;
+       void __iomem            *csr_base;
+       void __iomem            *vector_base;
+       phys_addr_t             vector_phy;
+       u32                     num_of_vectors;
+       int                     irq;
+};
+
+static inline void msi_writel(struct altera_msi *msi, const u32 value,
+                             const u32 reg)
+{
+       writel_relaxed(value, msi->csr_base + reg);
+}
+
+static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
+{
+       return readl_relaxed(msi->csr_base + reg);
+}
+
+static void altera_msi_isr(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct altera_msi *msi;
+       unsigned long status;
+       u32 num_of_vectors;
+       u32 bit;
+       u32 virq;
+
+       chained_irq_enter(chip, desc);
+       msi = irq_desc_get_handler_data(desc);
+       num_of_vectors = msi->num_of_vectors;
+
+       while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
+               for_each_set_bit(bit, &status, msi->num_of_vectors) {
+                       /* Dummy read from vector to clear the interrupt */
+                       readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
+
+                       virq = irq_find_mapping(msi->inner_domain, bit);
+                       if (virq)
+                               generic_handle_irq(virq);
+                       else
+                               dev_err(&msi->pdev->dev, "unexpected MSI\n");
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static struct irq_chip altera_msi_irq_chip = {
+       .name = "Altera PCIe MSI",
+       .irq_mask = pci_msi_mask_irq,
+       .irq_unmask = pci_msi_unmask_irq,
+};
+
+static struct msi_domain_info altera_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                    MSI_FLAG_PCI_MSIX),
+       .chip   = &altera_msi_irq_chip,
+};
+
+static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct altera_msi *msi = irq_data_get_irq_chip_data(data);
+       phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
+
+       msg->address_lo = lower_32_bits(addr);
+       msg->address_hi = upper_32_bits(addr);
+       msg->data = data->hwirq;
+
+       dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
+               (int)data->hwirq, msg->address_hi, msg->address_lo);
+}
+
+static int altera_msi_set_affinity(struct irq_data *irq_data,
+                                  const struct cpumask *mask, bool force)
+{
+        return -EINVAL;
+}
+
+static struct irq_chip altera_msi_bottom_irq_chip = {
+       .name                   = "Altera MSI",
+       .irq_compose_msi_msg    = altera_compose_msi_msg,
+       .irq_set_affinity       = altera_msi_set_affinity,
+};
+
+static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                  unsigned int nr_irqs, void *args)
+{
+       struct altera_msi *msi = domain->host_data;
+       unsigned long bit;
+       u32 mask;
+
+       WARN_ON(nr_irqs != 1);
+       mutex_lock(&msi->lock);
+
+       bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
+       if (bit >= msi->num_of_vectors) {
+               mutex_unlock(&msi->lock);
+               return -ENOSPC;
+       }
+
+       set_bit(bit, msi->used);
+
+       mutex_unlock(&msi->lock);
+
+       irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq,
+                           NULL, NULL);
+
+       mask = msi_readl(msi, MSI_INTMASK);
+       mask |= 1 << bit;
+       msi_writel(msi, mask, MSI_INTMASK);
+
+       return 0;
+}
+
+static void altera_irq_domain_free(struct irq_domain *domain,
+                                  unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct altera_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 mask;
+
+       mutex_lock(&msi->lock);
+
+       if (!test_bit(d->hwirq, msi->used)) {
+               dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
+                       d->hwirq);
+       } else {
+               __clear_bit(d->hwirq, msi->used);
+               mask = msi_readl(msi, MSI_INTMASK);
+               mask &= ~(1 << d->hwirq);
+               msi_writel(msi, mask, MSI_INTMASK);
+       }
+
+       mutex_unlock(&msi->lock);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = altera_irq_domain_alloc,
+       .free   = altera_irq_domain_free,
+};
+
+static int altera_allocate_domains(struct altera_msi *msi)
+{
+       msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
+                                            &msi_domain_ops, msi);
+       if (!msi->inner_domain) {
+               dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
+               return -ENOMEM;
+       }
+
+       msi->msi_domain = pci_msi_create_irq_domain(msi->pdev->dev.of_node,
+                               &altera_msi_domain_info, msi->inner_domain);
+       if (!msi->msi_domain) {
+               dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
+               irq_domain_remove(msi->inner_domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void altera_free_domains(struct altera_msi *msi)
+{
+       irq_domain_remove(msi->msi_domain);
+       irq_domain_remove(msi->inner_domain);
+}
+
+static int altera_msi_remove(struct platform_device *pdev)
+{
+       struct altera_msi *msi = platform_get_drvdata(pdev);
+
+       msi_writel(msi, 0, MSI_INTMASK);
+       irq_set_chained_handler(msi->irq, NULL);
+       irq_set_handler_data(msi->irq, NULL);
+
+       altera_free_domains(msi);
+
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static int altera_msi_probe(struct platform_device *pdev)
+{
+       struct altera_msi *msi;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *res;
+       int ret;
+
+       msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
+                          GFP_KERNEL);
+       if (!msi)
+               return -ENOMEM;
+
+       mutex_init(&msi->lock);
+       msi->pdev = pdev;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
+       if (!res) {
+               dev_err(&pdev->dev, "no csr memory resource defined\n");
+               return -ENODEV;
+       }
+
+       msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(msi->csr_base)) {
+               dev_err(&pdev->dev, "failed to map csr memory\n");
+               return PTR_ERR(msi->csr_base);
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "vector_slave");
+       if (!res) {
+               dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
+               return -ENODEV;
+       }
+
+       msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(msi->vector_base)) {
+               dev_err(&pdev->dev, "failed to map vector_slave memory\n");
+               return PTR_ERR(msi->vector_base);
+       }
+
+       msi->vector_phy = res->start;
+
+       if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
+               dev_err(&pdev->dev, "failed to parse the number of vectors\n");
+               return -EINVAL;
+       }
+
+       ret = altera_allocate_domains(msi);
+       if (ret)
+               return ret;
+
+       msi->irq = platform_get_irq(pdev, 0);
+       if (msi->irq <= 0) {
+               dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
+       platform_set_drvdata(pdev, msi);
+
+       return 0;
+
+err:
+       altera_msi_remove(pdev);
+       return ret;
+}
+
+static const struct of_device_id altera_msi_of_match[] = {
+       { .compatible = "altr,msi-1.0", NULL },
+       { },
+};
+
+static struct platform_driver altera_msi_driver = {
+       .driver = {
+               .name = "altera-msi",
+               .of_match_table = altera_msi_of_match,
+       },
+       .probe = altera_msi_probe,
+       .remove = altera_msi_remove,
+};
+
+static int __init altera_msi_init(void)
+{
+       return platform_driver_register(&altera_msi_driver);
+}
+subsys_initcall(altera_msi_init);
+
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_DESCRIPTION("Altera PCIe MSI support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
new file mode 100644 (file)
index 0000000..e5dda38
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * Copyright Altera Corporation (C) 2013-2015. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define RP_TX_REG0                     0x2000
+#define RP_TX_REG1                     0x2004
+#define RP_TX_CNTRL                    0x2008
+#define RP_TX_EOP                      0x2
+#define RP_TX_SOP                      0x1
+#define RP_RXCPL_STATUS                        0x2010
+#define RP_RXCPL_EOP                   0x2
+#define RP_RXCPL_SOP                   0x1
+#define RP_RXCPL_REG0                  0x2014
+#define RP_RXCPL_REG1                  0x2018
+#define P2A_INT_STATUS                 0x3060
+#define P2A_INT_STS_ALL                        0xf
+#define P2A_INT_ENABLE                 0x3070
+#define P2A_INT_ENA_ALL                        0xf
+#define RP_LTSSM                       0x3c64
+#define LTSSM_L0                       0xf
+
+/* TLP configuration type 0 and 1 */
+#define TLP_FMTTYPE_CFGRD0             0x04    /* Configuration Read Type 0 */
+#define TLP_FMTTYPE_CFGWR0             0x44    /* Configuration Write Type 0 */
+#define TLP_FMTTYPE_CFGRD1             0x05    /* Configuration Read Type 1 */
+#define TLP_FMTTYPE_CFGWR1             0x45    /* Configuration Write Type 1 */
+#define TLP_PAYLOAD_SIZE               0x01
+#define TLP_READ_TAG                   0x1d
+#define TLP_WRITE_TAG                  0x10
+#define TLP_CFG_DW0(fmttype)           (((fmttype) << 24) | TLP_PAYLOAD_SIZE)
+#define TLP_CFG_DW1(reqid, tag, be)    (((reqid) << 16) | (tag << 8) | (be))
+#define TLP_CFG_DW2(bus, devfn, offset)        \
+                               (((bus) << 24) | ((devfn) << 16) | (offset))
+#define TLP_REQ_ID(bus, devfn)         (((bus) << 8) | (devfn))
+#define TLP_HDR_SIZE                   3
+#define TLP_LOOP                       500
+
+#define INTX_NUM                       4
+
+#define DWORD_MASK                     3
+
+struct altera_pcie {
+       struct platform_device  *pdev;
+       void __iomem            *cra_base;
+       int                     irq;
+       u8                      root_bus_nr;
+       struct irq_domain       *irq_domain;
+       struct resource         bus_range;
+       struct list_head        resources;
+};
+
+struct tlp_rp_regpair_t {
+       u32 ctrl;
+       u32 reg0;
+       u32 reg1;
+};
+
+static void altera_pcie_retrain(struct pci_dev *dev)
+{
+       u16 linkcap, linkstat;
+
+       /*
+        * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
+        * current speed is 2.5 GB/s.
+        */
+       pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
+
+       if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
+               return;
+
+       pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
+       if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
+               pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
+                                        PCI_EXP_LNKCTL_RL);
+}
+DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
+
+/*
+ * Altera PCIe port uses BAR0 of RC's configuration space as the translation
+ * from PCI bus to native BUS.  Entire DDR region is mapped into PCIe space
+ * using these registers, so it can be reached by DMA from EP devices.
+ * This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt
+ * from EP devices, eventually trigger interrupt to GIC.  The BAR0 of bridge
+ * should be hidden during enumeration to avoid the sizing and resource
+ * allocation by PCIe core.
+ */
+static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int  devfn,
+                                   int offset)
+{
+       if (pci_is_root_bus(bus) && (devfn == 0) &&
+           (offset == PCI_BASE_ADDRESS_0))
+               return true;
+
+       return false;
+}
+
+static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
+                             const u32 reg)
+{
+       writel_relaxed(value, pcie->cra_base + reg);
+}
+
+static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
+{
+       return readl_relaxed(pcie->cra_base + reg);
+}
+
+static void tlp_write_tx(struct altera_pcie *pcie,
+                        struct tlp_rp_regpair_t *tlp_rp_regdata)
+{
+       cra_writel(pcie, tlp_rp_regdata->reg0, RP_TX_REG0);
+       cra_writel(pcie, tlp_rp_regdata->reg1, RP_TX_REG1);
+       cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
+}
+
+static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
+{
+       return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
+}
+
+static bool altera_pcie_valid_config(struct altera_pcie *pcie,
+                                    struct pci_bus *bus, int dev)
+{
+       /* If there is no link, then there is no device */
+       if (bus->number != pcie->root_bus_nr) {
+               if (!altera_pcie_link_is_up(pcie))
+                       return false;
+       }
+
+       /* access only one slot on each root port */
+       if (bus->number == pcie->root_bus_nr && dev > 0)
+               return false;
+
+       /*
+        * Do not read more than one device on the bus directly attached
+        * to root port, root port can only attach to one downstream port.
+        */
+       if (bus->primary == pcie->root_bus_nr && dev > 0)
+               return false;
+
+        return true;
+}
+
+static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+{
+       u8 loop;
+       bool sop = 0;
+       u32 ctrl;
+       u32 reg0, reg1;
+
+       /*
+        * Minimum 2 loops to read TLP headers and 1 loop to read data
+        * payload.
+        */
+       for (loop = 0; loop < TLP_LOOP; loop++) {
+               ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
+               if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) {
+                       reg0 = cra_readl(pcie, RP_RXCPL_REG0);
+                       reg1 = cra_readl(pcie, RP_RXCPL_REG1);
+
+                       if (ctrl & RP_RXCPL_SOP)
+                               sop = true;
+
+                       if (ctrl & RP_RXCPL_EOP) {
+                               if (value)
+                                       *value = reg0;
+                               return PCIBIOS_SUCCESSFUL;
+                       }
+               }
+               udelay(5);
+       }
+
+       return -ENOENT;
+}
+
+static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
+                            u32 data, bool align)
+{
+       struct tlp_rp_regpair_t tlp_rp_regdata;
+
+       tlp_rp_regdata.reg0 = headers[0];
+       tlp_rp_regdata.reg1 = headers[1];
+       tlp_rp_regdata.ctrl = RP_TX_SOP;
+       tlp_write_tx(pcie, &tlp_rp_regdata);
+
+       if (align) {
+               tlp_rp_regdata.reg0 = headers[2];
+               tlp_rp_regdata.reg1 = 0;
+               tlp_rp_regdata.ctrl = 0;
+               tlp_write_tx(pcie, &tlp_rp_regdata);
+
+               tlp_rp_regdata.reg0 = data;
+               tlp_rp_regdata.reg1 = 0;
+       } else {
+               tlp_rp_regdata.reg0 = headers[2];
+               tlp_rp_regdata.reg1 = data;
+       }
+
+       tlp_rp_regdata.ctrl = RP_TX_EOP;
+       tlp_write_tx(pcie, &tlp_rp_regdata);
+}
+
+static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
+                             int where, u8 byte_en, u32 *value)
+{
+       u32 headers[TLP_HDR_SIZE];
+
+       if (bus == pcie->root_bus_nr)
+               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
+       else
+               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
+
+       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
+                                       TLP_READ_TAG, byte_en);
+       headers[2] = TLP_CFG_DW2(bus, devfn, where);
+
+       tlp_write_packet(pcie, headers, 0, false);
+
+       return tlp_read_packet(pcie, value);
+}
+
+static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
+                              int where, u8 byte_en, u32 value)
+{
+       u32 headers[TLP_HDR_SIZE];
+       int ret;
+
+       if (bus == pcie->root_bus_nr)
+               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
+       else
+               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
+
+       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
+                                       TLP_WRITE_TAG, byte_en);
+       headers[2] = TLP_CFG_DW2(bus, devfn, where);
+
+       /* check alignment to Qword */
+       if ((where & 0x7) == 0)
+               tlp_write_packet(pcie, headers, value, true);
+       else
+               tlp_write_packet(pcie, headers, value, false);
+
+       ret = tlp_read_packet(pcie, NULL);
+       if (ret != PCIBIOS_SUCCESSFUL)
+               return ret;
+
+       /*
+        * Monitor changes to PCI_PRIMARY_BUS register on root port
+        * and update local copy of root bus number accordingly.
+        */
+       if ((bus == pcie->root_bus_nr) && (where == PCI_PRIMARY_BUS))
+               pcie->root_bus_nr = (u8)(value);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
+                               int where, int size, u32 *value)
+{
+       struct altera_pcie *pcie = bus->sysdata;
+       int ret;
+       u32 data;
+       u8 byte_en;
+
+       if (altera_pcie_hide_rc_bar(bus, devfn, where))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
+       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
+               *value = 0xffffffff;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       switch (size) {
+       case 1:
+               byte_en = 1 << (where & 3);
+               break;
+       case 2:
+               byte_en = 3 << (where & 3);
+               break;
+       default:
+               byte_en = 0xf;
+               break;
+       }
+
+       ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
+                                (where & ~DWORD_MASK), byte_en, &data);
+       if (ret != PCIBIOS_SUCCESSFUL)
+               return ret;
+
+       switch (size) {
+       case 1:
+               *value = (data >> (8 * (where & 0x3))) & 0xff;
+               break;
+       case 2:
+               *value = (data >> (8 * (where & 0x2))) & 0xffff;
+               break;
+       default:
+               *value = data;
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
+                                int where, int size, u32 value)
+{
+       struct altera_pcie *pcie = bus->sysdata;
+       u32 data32;
+       u32 shift = 8 * (where & 3);
+       u8 byte_en;
+
+       if (altera_pcie_hide_rc_bar(bus, devfn, where))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
+       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       switch (size) {
+       case 1:
+               data32 = (value & 0xff) << shift;
+               byte_en = 1 << (where & 3);
+               break;
+       case 2:
+               data32 = (value & 0xffff) << shift;
+               byte_en = 3 << (where & 3);
+               break;
+       default:
+               data32 = value;
+               byte_en = 0xf;
+               break;
+       }
+
+       return tlp_cfg_dword_write(pcie, bus->number, devfn,
+               (where & ~DWORD_MASK), byte_en, data32);
+}
+
+static struct pci_ops altera_pcie_ops = {
+       .read = altera_pcie_cfg_read,
+       .write = altera_pcie_cfg_write,
+};
+
+static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+                               irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+       irq_set_chip_data(irq, domain->host_data);
+
+       return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+       .map = altera_pcie_intx_map,
+};
+
+static void altera_pcie_isr(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct altera_pcie *pcie;
+       unsigned long status;
+       u32 bit;
+       u32 virq;
+
+       chained_irq_enter(chip, desc);
+       pcie = irq_desc_get_handler_data(desc);
+
+       while ((status = cra_readl(pcie, P2A_INT_STATUS)
+               & P2A_INT_STS_ALL) != 0) {
+               for_each_set_bit(bit, &status, INTX_NUM) {
+                       /* clear interrupts */
+                       cra_writel(pcie, 1 << bit, P2A_INT_STATUS);
+
+                       virq = irq_find_mapping(pcie->irq_domain, bit + 1);
+                       if (virq)
+                               generic_handle_irq(virq);
+                       else
+                               dev_err(&pcie->pdev->dev,
+                                       "unexpected IRQ, INT%d\n", bit);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void altera_pcie_release_of_pci_ranges(struct altera_pcie *pcie)
+{
+       pci_free_resource_list(&pcie->resources);
+}
+
+static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie)
+{
+       int err, res_valid = 0;
+       struct device *dev = &pcie->pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct resource_entry *win;
+
+       err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources,
+                                              NULL);
+       if (err)
+               return err;
+
+       resource_list_for_each_entry(win, &pcie->resources) {
+               struct resource *parent, *res = win->res;
+
+               switch (resource_type(res)) {
+               case IORESOURCE_MEM:
+                       parent = &iomem_resource;
+                       res_valid |= !(res->flags & IORESOURCE_PREFETCH);
+                       break;
+               default:
+                       continue;
+               }
+
+               err = devm_request_resource(dev, parent, res);
+               if (err)
+                       goto out_release_res;
+       }
+
+       if (!res_valid) {
+               dev_err(dev, "non-prefetchable memory resource required\n");
+               err = -EINVAL;
+               goto out_release_res;
+       }
+
+       return 0;
+
+out_release_res:
+       altera_pcie_release_of_pci_ranges(pcie);
+       return err;
+}
+
+static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
+{
+       struct device *dev = &pcie->pdev->dev;
+       struct device_node *node = dev->of_node;
+
+       /* Setup INTx */
+       pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM,
+                                       &intx_domain_ops, pcie);
+       if (!pcie->irq_domain) {
+               dev_err(dev, "Failed to get a INTx IRQ domain\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int altera_pcie_parse_dt(struct altera_pcie *pcie)
+{
+       struct resource *cra;
+       struct platform_device *pdev = pcie->pdev;
+
+       cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
+       if (!cra) {
+               dev_err(&pdev->dev, "no Cra memory resource defined\n");
+               return -ENODEV;
+       }
+
+       pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra);
+       if (IS_ERR(pcie->cra_base)) {
+               dev_err(&pdev->dev, "failed to map cra memory\n");
+               return PTR_ERR(pcie->cra_base);
+       }
+
+       /* setup IRQ */
+       pcie->irq = platform_get_irq(pdev, 0);
+       if (pcie->irq <= 0) {
+               dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq);
+               return -EINVAL;
+       }
+
+       irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
+
+       return 0;
+}
+
+static int altera_pcie_probe(struct platform_device *pdev)
+{
+       struct altera_pcie *pcie;
+       struct pci_bus *bus;
+       struct pci_bus *child;
+       int ret;
+
+       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       if (!pcie)
+               return -ENOMEM;
+
+       pcie->pdev = pdev;
+
+       ret = altera_pcie_parse_dt(pcie);
+       if (ret) {
+               dev_err(&pdev->dev, "Parsing DT failed\n");
+               return ret;
+       }
+
+       INIT_LIST_HEAD(&pcie->resources);
+
+       ret = altera_pcie_parse_request_of_pci_ranges(pcie);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed add resources\n");
+               return ret;
+       }
+
+       ret = altera_pcie_init_irq_domain(pcie);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed creating IRQ Domain\n");
+               return ret;
+       }
+
+       /* clear all interrupts */
+       cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
+       /* enable all interrupts */
+       cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
+
+       bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
+                               pcie, &pcie->resources);
+       if (!bus)
+               return -ENOMEM;
+
+       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+       pci_assign_unassigned_bus_resources(bus);
+
+       /* Configure PCI Express setting. */
+       list_for_each_entry(child, &bus->children, node)
+               pcie_bus_configure_settings(child);
+
+       pci_bus_add_devices(bus);
+
+       platform_set_drvdata(pdev, pcie);
+       return ret;
+}
+
+static const struct of_device_id altera_pcie_of_match[] = {
+       { .compatible = "altr,pcie-root-port-1.0", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
+
+static struct platform_driver altera_pcie_driver = {
+       .probe          = altera_pcie_probe,
+       .driver = {
+               .name   = "altera-pcie",
+               .of_match_table = altera_pcie_of_match,
+               .suppress_bind_attrs = true,
+       },
+};
+
+static int altera_pcie_init(void)
+{
+       return platform_driver_register(&altera_pcie_driver);
+}
+module_init(altera_pcie_init);
+
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_DESCRIPTION("Altera PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
index 52aa6e34002bc2543f20b8607efdab9e2a5a669a..fb89ca23d9a8926e35547f650688deb76e8e870e 100644 (file)
@@ -35,7 +35,7 @@
 
 #define PCIE_LINK_WIDTH_SPEED_CONTROL  0x80C
 #define PORT_LOGIC_SPEED_CHANGE                (0x1 << 17)
-#define PORT_LOGIC_LINK_WIDTH_MASK     (0x1ff << 8)
+#define PORT_LOGIC_LINK_WIDTH_MASK     (0x1f << 8)
 #define PORT_LOGIC_LINK_WIDTH_1_LANES  (0x1 << 8)
 #define PORT_LOGIC_LINK_WIDTH_2_LANES  (0x2 << 8)
 #define PORT_LOGIC_LINK_WIDTH_4_LANES  (0x4 << 8)
@@ -80,28 +80,38 @@ static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
        return sys->private_data;
 }
 
-int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val)
+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
 {
-       *val = readl(addr);
+       if ((uintptr_t)addr & (size - 1)) {
+               *val = 0;
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
 
-       if (size == 1)
-               *val = (*val >> (8 * (where & 3))) & 0xff;
+       if (size == 4)
+               *val = readl(addr);
        else if (size == 2)
-               *val = (*val >> (8 * (where & 3))) & 0xffff;
-       else if (size != 4)
+               *val = readw(addr);
+       else if (size == 1)
+               *val = readb(addr);
+       else {
+               *val = 0;
                return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
 
        return PCIBIOS_SUCCESSFUL;
 }
 
-int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val)
+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
 {
+       if ((uintptr_t)addr & (size - 1))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
        if (size == 4)
                writel(val, addr);
        else if (size == 2)
-               writew(val, addr + (where & 2));
+               writew(val, addr);
        else if (size == 1)
-               writeb(val, addr + (where & 3));
+               writeb(val, addr);
        else
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
@@ -132,8 +142,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
        if (pp->ops->rd_own_conf)
                ret = pp->ops->rd_own_conf(pp, where, size, val);
        else
-               ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
-                               size, val);
+               ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
 
        return ret;
 }
@@ -146,8 +155,7 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        if (pp->ops->wr_own_conf)
                ret = pp->ops->wr_own_conf(pp, where, size, val);
        else
-               ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where,
-                               size, val);
+               ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
 
        return ret;
 }
@@ -205,12 +213,16 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 
 void dw_pcie_msi_init(struct pcie_port *pp)
 {
+       u64 msi_target;
+
        pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
+       msi_target = virt_to_phys((void *)pp->msi_data);
 
        /* program the msi_data */
        dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
-                       virt_to_phys((void *)pp->msi_data));
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+                           (u32)(msi_target & 0xffffffff));
+       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
+                           (u32)(msi_target >> 32 & 0xffffffff));
 }
 
 static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
@@ -286,6 +298,9 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
        }
 
        *pos = pos0;
+       desc->nvec_used = no_irqs;
+       desc->msi_attrib.multiple = order_base_2(no_irqs);
+
        return irq;
 
 no_valid_irq:
@@ -293,11 +308,31 @@ no_valid_irq:
        return -ENOSPC;
 }
 
+static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
+{
+       struct msi_msg msg;
+       u64 msi_target;
+
+       if (pp->ops->get_msi_addr)
+               msi_target = pp->ops->get_msi_addr(pp);
+       else
+               msi_target = virt_to_phys((void *)pp->msi_data);
+
+       msg.address_lo = (u32)(msi_target & 0xffffffff);
+       msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
+
+       if (pp->ops->get_msi_data)
+               msg.data = pp->ops->get_msi_data(pp, pos);
+       else
+               msg.data = pos;
+
+       pci_write_msi_msg(irq, &msg);
+}
+
 static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
                        struct msi_desc *desc)
 {
        int irq, pos;
-       struct msi_msg msg;
        struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
 
        if (desc->msi_attrib.is_msix)
@@ -307,20 +342,36 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
        if (irq < 0)
                return irq;
 
-       if (pp->ops->get_msi_addr)
-               msg.address_lo = pp->ops->get_msi_addr(pp);
-       else
-               msg.address_lo = virt_to_phys((void *)pp->msi_data);
-       msg.address_hi = 0x0;
+       dw_msi_setup_msg(pp, irq, pos);
 
-       if (pp->ops->get_msi_data)
-               msg.data = pp->ops->get_msi_data(pp, pos);
-       else
-               msg.data = pos;
+       return 0;
+}
 
-       pci_write_msi_msg(irq, &msg);
+static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
+                            int nvec, int type)
+{
+#ifdef CONFIG_PCI_MSI
+       int irq, pos;
+       struct msi_desc *desc;
+       struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
+
+       /* MSI-X interrupts are not supported */
+       if (type == PCI_CAP_ID_MSIX)
+               return -EINVAL;
+
+       WARN_ON(!list_is_singular(&pdev->dev.msi_list));
+       desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+
+       irq = assign_irq(nvec, desc, &pos);
+       if (irq < 0)
+               return irq;
+
+       dw_msi_setup_msg(pp, irq, pos);
 
        return 0;
+#else
+       return -EINVAL;
+#endif
 }
 
 static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
@@ -334,6 +385,7 @@ static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
 
 static struct msi_controller dw_pcie_msi_chip = {
        .setup_irq = dw_msi_setup_irq,
+       .setup_irqs = dw_msi_setup_irqs,
        .teardown_irq = dw_msi_teardown_irq,
 };
 
@@ -482,10 +534,9 @@ int dw_pcie_host_init(struct pcie_port *pp)
                }
        }
 
-       if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
-               dev_err(pp->dev, "Failed to parse the number of lanes\n");
-               return -EINVAL;
-       }
+       ret = of_property_read_u32(np, "num-lanes", &pp->lanes);
+       if (ret)
+               pp->lanes = 0;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                if (!pp->ops->msi_host_init) {
@@ -539,13 +590,12 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 *val)
 {
        int ret, type;
-       u32 address, busdev, cfg_size;
+       u32 busdev, cfg_size;
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
-       address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
                type = PCIE_ATU_TYPE_CFG0;
@@ -562,7 +612,7 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  type, cpu_addr,
                                  busdev, cfg_size);
-       ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
+       ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  PCIE_ATU_TYPE_IO, pp->io_mod_base,
                                  pp->io_bus_addr, pp->io_size);
@@ -574,13 +624,12 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 val)
 {
        int ret, type;
-       u32 address, busdev, cfg_size;
+       u32 busdev, cfg_size;
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
-       address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
                type = PCIE_ATU_TYPE_CFG0;
@@ -597,7 +646,7 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  type, cpu_addr,
                                  busdev, cfg_size);
-       ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
+       ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  PCIE_ATU_TYPE_IO, pp->io_mod_base,
                                  pp->io_bus_addr, pp->io_size);
@@ -764,6 +813,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
        case 8:
                val |= PORT_LINK_MODE_8_LANES;
                break;
+       default:
+               dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
+               return;
        }
        dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
 
index d0bbd276840dfcac4e7a66ba7ca7ad1f7a623e6e..e7b3279c3ff09ab5665fd251c4fdc695b4887eae 100644 (file)
@@ -70,14 +70,14 @@ struct pcie_host_ops {
        void (*host_init)(struct pcie_port *pp);
        void (*msi_set_irq)(struct pcie_port *pp, int irq);
        void (*msi_clear_irq)(struct pcie_port *pp, int irq);
-       u32 (*get_msi_addr)(struct pcie_port *pp);
+       phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
        u32 (*get_msi_data)(struct pcie_port *pp, int pos);
        void (*scan_bus)(struct pcie_port *pp);
        int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
 };
 
-int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
-int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
 void dw_pcie_msi_init(struct pcie_port *pp);
 int dw_pcie_link_up(struct pcie_port *pp);
index 9aedc8eb2c6eaa3ffcb29dc562171f9a75161e52..c9550dc8b8ed4b8fc330d1d21cefed43989db030 100644 (file)
@@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       if (of_property_read_bool(np, "brcm,pcie-ob")) {
+               u32 val;
+
+               ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
+                                          &val);
+               if (ret) {
+                       dev_err(pcie->dev,
+                               "missing brcm,pcie-ob-axi-offset property\n");
+                       return ret;
+               }
+               pcie->ob.axi_offset = val;
+
+               ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
+                                          &val);
+               if (ret) {
+                       dev_err(pcie->dev,
+                               "missing brcm,pcie-ob-window-size property\n");
+                       return ret;
+               }
+               pcie->ob.window_size = (resource_size_t)val * SZ_1M;
+
+               if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
+                       pcie->ob.set_oarr_size = true;
+
+               pcie->need_ob_cfg = true;
+       }
+
        /* PHY use is optional */
        pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
        if (IS_ERR(pcie->phy)) {
index fe2efb141a9bd3b74c22af5764ead81dcfc2ce6d..eac719af16aa835fa61f8ee96cd5c87ee737d520 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
- * Copyright (C) 2015 Broadcom Corporatcommon ion
+ * Copyright (C) 2015 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -31,6 +31,8 @@
 #include "pcie-iproc.h"
 
 #define CLK_CONTROL_OFFSET           0x000
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT       BIT(EP_PERST_SOURCE_SELECT_SHIFT)
 #define EP_MODE_SURVIVE_PERST_SHIFT  1
 #define EP_MODE_SURVIVE_PERST        BIT(EP_MODE_SURVIVE_PERST_SHIFT)
 #define RC_PCIE_RST_OUTPUT_SHIFT     0
 #define SYS_RC_INTX_EN               0x330
 #define SYS_RC_INTX_MASK             0xf
 
+#define PCIE_LINK_STATUS_OFFSET      0xf0c
+#define PCIE_PHYLINKUP_SHIFT         3
+#define PCIE_PHYLINKUP               BIT(PCIE_PHYLINKUP_SHIFT)
+#define PCIE_DL_ACTIVE_SHIFT         2
+#define PCIE_DL_ACTIVE               BIT(PCIE_DL_ACTIVE_SHIFT)
+
+#define OARR_VALID_SHIFT             0
+#define OARR_VALID                   BIT(OARR_VALID_SHIFT)
+#define OARR_SIZE_CFG_SHIFT          1
+#define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
+
+#define OARR_LO(window)              (0xd20 + (window) * 8)
+#define OARR_HI(window)              (0xd24 + (window) * 8)
+#define OMAP_LO(window)              (0xd40 + (window) * 8)
+#define OMAP_HI(window)              (0xd44 + (window) * 8)
+
+#define MAX_NUM_OB_WINDOWS           2
+
 static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
 {
        struct iproc_pcie *pcie;
@@ -119,23 +139,32 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
        u32 val;
 
        /*
-        * Configure the PCIe controller as root complex and send a downstream
-        * reset
+        * Select perst_b signal as reset source. Put the device into reset,
+        * and then bring it out of reset
         */
-       val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
+       val = readl(pcie->base + CLK_CONTROL_OFFSET);
+       val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+               ~RC_PCIE_RST_OUTPUT;
        writel(val, pcie->base + CLK_CONTROL_OFFSET);
        udelay(250);
-       val &= ~EP_MODE_SURVIVE_PERST;
+
+       val |= RC_PCIE_RST_OUTPUT;
        writel(val, pcie->base + CLK_CONTROL_OFFSET);
-       msleep(250);
+       msleep(100);
 }
 
 static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 {
        u8 hdr_type;
-       u32 link_ctrl;
+       u32 link_ctrl, class, val;
        u16 pos, link_status;
-       int link_is_active = 0;
+       bool link_is_active = false;
+
+       val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
+       if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
+               dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+               return -ENODEV;
+       }
 
        /* make sure we are not in EP mode */
        pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
@@ -145,14 +174,19 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
        }
 
        /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
-       pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE,
-                                 PCI_CLASS_BRIDGE_PCI);
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c
+#define PCI_CLASS_BRIDGE_MASK      0xffff00
+#define PCI_CLASS_BRIDGE_SHIFT     8
+       pci_bus_read_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, &class);
+       class &= ~PCI_CLASS_BRIDGE_MASK;
+       class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
+       pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
 
        /* check link status to see if link is active */
        pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
        if (link_status & PCI_EXP_LNKSTA_NLW)
-               link_is_active = 1;
+               link_is_active = true;
 
        if (!link_is_active) {
                /* try GEN 1 link speed */
@@ -176,7 +210,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
                        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
                                                 &link_status);
                        if (link_status & PCI_EXP_LNKSTA_NLW)
-                               link_is_active = 1;
+                               link_is_active = true;
                }
        }
 
@@ -190,6 +224,101 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
        writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
 }
 
+/**
+ * Some iProc SoCs require the SW to configure the outbound address mapping
+ *
+ * Outbound address translation:
+ *
+ * iproc_pcie_address = axi_address - axi_offset
+ * OARR = iproc_pcie_address
+ * OMAP = pci_addr
+ *
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
+ */
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
+                              u64 pci_addr, resource_size_t size)
+{
+       struct iproc_pcie_ob *ob = &pcie->ob;
+       unsigned i;
+       u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
+       u64 remainder;
+
+       if (size > max_size) {
+               dev_err(pcie->dev,
+                       "res size 0x%pap exceeds max supported size 0x%llx\n",
+                       &size, max_size);
+               return -EINVAL;
+       }
+
+       div64_u64_rem(size, ob->window_size, &remainder);
+       if (remainder) {
+               dev_err(pcie->dev,
+                       "res size %pap needs to be multiple of window size %pap\n",
+                       &size, &ob->window_size);
+               return -EINVAL;
+       }
+
+       if (axi_addr < ob->axi_offset) {
+               dev_err(pcie->dev,
+                       "axi address %pap less than offset %pap\n",
+                       &axi_addr, &ob->axi_offset);
+               return -EINVAL;
+       }
+
+       /*
+        * Translate the AXI address to the internal address used by the iProc
+        * PCIe core before programming the OARR
+        */
+       axi_addr -= ob->axi_offset;
+
+       for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
+               writel(lower_32_bits(axi_addr) | OARR_VALID |
+                      (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
+               writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
+               writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
+               writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
+
+               size -= ob->window_size;
+               if (size == 0)
+                       break;
+
+               axi_addr += ob->window_size;
+               pci_addr += ob->window_size;
+       }
+
+       return 0;
+}
+
+static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
+                                struct list_head *resources)
+{
+       struct resource_entry *window;
+       int ret;
+
+       resource_list_for_each_entry(window, resources) {
+               struct resource *res = window->res;
+               u64 res_type = resource_type(res);
+
+               switch (res_type) {
+               case IORESOURCE_IO:
+               case IORESOURCE_BUS:
+                       break;
+               case IORESOURCE_MEM:
+                       ret = iproc_pcie_setup_ob(pcie, res->start,
+                                                 res->start - window->offset,
+                                                 resource_size(res));
+                       if (ret)
+                               return ret;
+                       break;
+               default:
+                       dev_err(pcie->dev, "invalid resource %pR\n", res);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
        int ret;
@@ -213,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        iproc_pcie_reset(pcie);
 
+       if (pcie->need_ob_cfg) {
+               ret = iproc_pcie_map_ranges(pcie, res);
+               if (ret) {
+                       dev_err(pcie->dev, "map failed\n");
+                       goto err_power_off_phy;
+               }
+       }
+
 #ifdef CONFIG_ARM
        pcie->sysdata.private_data = pcie;
        sysdata = &pcie->sysdata;
@@ -238,9 +375,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
-#ifdef CONFIG_ARM
        pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
-#endif
        pci_bus_add_devices(bus);
 
        return 0;
index c9e4c10a462e80897eafec3fbbf3ba119c2307e3..d3dc940f773a2279d13ad295bc9bd70a86638d23 100644 (file)
 #ifndef _PCIE_IPROC_H
 #define _PCIE_IPROC_H
 
-#define IPROC_PCIE_MAX_NUM_IRQS 6
+/**
+ * iProc PCIe outbound mapping
+ * @set_oarr_size: indicates the OARR size bit needs to be set
+ * @axi_offset: offset from the AXI address to the internal address used by
+ * the iProc PCIe core
+ * @window_size: outbound window size
+ */
+struct iproc_pcie_ob {
+       bool set_oarr_size;
+       resource_size_t axi_offset;
+       resource_size_t window_size;
+};
 
 /**
  * iProc PCIe device
  * @dev: pointer to device data structure
  * @base: PCIe host controller I/O register base
- * @resources: linked list of all PCI resources
  * @sysdata: Per PCI controller data (ARM-specific)
  * @root_bus: pointer to root bus
  * @phy: optional PHY device that controls the Serdes
  * @irqs: interrupt IDs
+ * @map_irq: function callback to map interrupts
+ * @need_ob_cfg: indidates SW needs to configure the outbound mapping window
+ * @ob: outbound mapping parameters
  */
 struct iproc_pcie {
        struct device *dev;
@@ -34,8 +47,9 @@ struct iproc_pcie {
 #endif
        struct pci_bus *root_bus;
        struct phy *phy;
-       int irqs[IPROC_PCIE_MAX_NUM_IRQS];
        int (*map_irq)(const struct pci_dev *, u8, u8);
+       bool need_ob_cfg;
+       struct iproc_pcie_ob ob;
 };
 
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
index 98d2683181bc5fbbbff05b89170d864c02e12cb3..b95b7563c052135bbad5407073685d9ff8930df6 100644 (file)
@@ -163,34 +163,34 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
         * default value in capability register is 512 bytes. So force
         * it to 128 here.
         */
-       dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, &val);
+       dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
        val &= ~PCI_EXP_DEVCTL_READRQ;
-       dw_pcie_cfg_write(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, val);
+       dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
 
-       dw_pcie_cfg_write(pp->dbi_base, PCI_VENDOR_ID, 2, 0x104A);
-       dw_pcie_cfg_write(pp->dbi_base, PCI_DEVICE_ID, 2, 0xCD80);
+       dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
+       dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
 
        /*
         * if is_gen1 is set then handle it, so that some buggy card
         * also works
         */
        if (spear13xx_pcie->is_gen1) {
-               dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCAP, 4,
-                                &val);
+               dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
+                                       4, &val);
                if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
                        val &= ~((u32)PCI_EXP_LNKCAP_SLS);
                        val |= PCI_EXP_LNKCAP_SLS_2_5GB;
-                       dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
-                                         PCI_EXP_LNKCAP, 4, val);
+                       dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
+                               PCI_EXP_LNKCAP, 4, val);
                }
 
-               dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCTL2, 4,
-                                &val);
+               dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
+                                       2, &val);
                if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
                        val &= ~((u32)PCI_EXP_LNKCAP_SLS);
                        val |= PCI_EXP_LNKCAP_SLS_2_5GB;
-                       dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
-                                         PCI_EXP_LNKCTL2, 4, val);
+                       dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
+                                       PCI_EXP_LNKCTL2, 2, val);
                }
        }
 
index d4497141d083a71d5d5206496fee58fbddc5cb13..74319f49765670fd64098897b09227ce3bf1d8da 100644 (file)
@@ -105,9 +105,12 @@ void __weak arch_teardown_msi_irq(unsigned int irq)
 
 int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
+       struct msi_controller *chip = dev->bus->msi;
        struct msi_desc *entry;
        int ret;
 
+       if (chip && chip->setup_irqs)
+               return chip->setup_irqs(chip, dev, nvec, type);
        /*
         * If an architecture wants to support multiple MSI, it needs to
         * override arch_setup_msi_irqs()
index ad939d0ba816372b975d5c44d9582617b7b618e0..0be5db110bfabdab9341348ddda38d8e760ec5ce 100644 (file)
@@ -163,6 +163,8 @@ struct msi_controller {
 
        int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev,
                         struct msi_desc *desc);
+       int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev,
+                         int nvec, int type);
        void (*teardown_irq)(struct msi_controller *chip, unsigned int irq);
 };
 
index 29fd3fe1c035d10361e9926ee879d9b6c4c70662..38c0533a33598bc8891af481589687c282739b37 100644 (file)
@@ -17,6 +17,7 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
 int of_get_pci_domain_nr(struct device_node *node);
 void of_pci_dma_configure(struct pci_dev *pci_dev);
+void of_pci_check_probe_only(void);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -53,6 +54,8 @@ of_get_pci_domain_nr(struct device_node *node)
 }
 
 static inline void of_pci_dma_configure(struct pci_dev *pci_dev) { }
+
+static inline void of_pci_check_probe_only(void) { }
 #endif
 
 #if defined(CONFIG_OF_ADDRESS)