Merge branches 'pci/iommu' and 'pci/misc' into next
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 20 Jan 2016 17:47:54 +0000 (11:47 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 20 Jan 2016 17:47:54 +0000 (11:47 -0600)
* pci/iommu:
  PCI: Add function 1 DMA alias quirk for Lite-On/Plextor M6e/Marvell 88SS9183

* pci/misc:
  PCI: Limit config space size for Netronome NFP4000
  PCI: Add Netronome NFP4000 PF device ID

66 files changed:
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
Documentation/devicetree/bindings/pci/pci-rcar-gen2.txt
Documentation/devicetree/bindings/pci/qcom,pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/rcar-pci.txt
MAINTAINERS
arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
arch/arm/boot/dts/qcom-apq8064.dtsi
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/pci_of_scan.c
arch/x86/Kconfig
arch/x86/include/asm/device.h
arch/x86/include/asm/hw_irq.h
arch/x86/include/asm/pci_x86.h
arch/x86/pci/Makefile
arch/x86/pci/common.c
arch/x86/pci/pcbios.c
arch/x86/pci/vmd.c [new file with mode: 0644]
drivers/pci/bus.c
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-dra7xx.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-host-generic.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-rcar-gen2.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-versatile.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-hisi.c
drivers/pci/host/pcie-iproc-bcma.c
drivers/pci/host/pcie-iproc-msi.c [new file with mode: 0644]
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-iproc.h
drivers/pci/host/pcie-qcom.c [new file with mode: 0644]
drivers/pci/host/pcie-rcar.c
drivers/pci/host/pcie-spear13xx.c
drivers/pci/host/pcie-xilinx.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/ibmphp_hpc.c
drivers/pci/hotplug/ibmphp_pci.c
drivers/pci/hotplug/ibmphp_res.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pcihp_skeleton.c
drivers/pci/hotplug/rpadlpar_core.c
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/s390_pci_hpc.c
drivers/pci/hotplug/shpchp_core.c
drivers/pci/msi.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.h
drivers/pci/pcie/aer/aer_inject.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aspm.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/rom.c
include/linux/of_pci.h
include/linux/pci.h
include/linux/pci_ids.h
kernel/irq/irqdomain.c
kernel/irq/msi.c

index 45c2a8094a9f7f7de65b37e41675bd0af4d6fe15..01b88f4e0d5bd8372f6ecd038469e649b59e2dde 100644 (file)
@@ -1,7 +1,10 @@
 * Broadcom iProc PCIe controller with the platform bus interface
 
 Required properties:
-- compatible: Must be "brcm,iproc-pcie"
+- compatible: Must be "brcm,iproc-pcie" for PAXB, or "brcm,iproc-pcie-paxc"
+  for PAXC.  PAXB-based root complex is used for external endpoint devices.
+  PAXC-based root complex is connected to emulated endpoint devices
+  internal to the ASIC
 - reg: base address and length of the PCIe controller I/O register space
 - #interrupt-cells: set to <1>
 - interrupt-map-mask and interrupt-map, standard PCI properties to define the
@@ -32,6 +35,28 @@ Optional:
 - brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
 increase the outbound window size
 
+MSI support (optional):
+
+For older platforms without MSI integrated in the GIC, iProc PCIe core provides
+an event queue based MSI support.  The iProc MSI uses host memories to store
+MSI posted writes in the event queues
+
+- msi-parent: Link to the device node of the MSI controller.  On newer iProc
+platforms, the MSI controller may be gicv2m or gicv3-its.  On older iProc
+platforms without MSI support in its interrupt controller, one may use the
+event queue based MSI support integrated within the iProc PCIe core.
+
+When the iProc event queue based MSI is used, one needs to define the
+following properties in the MSI device node:
+- compatible: Must be "brcm,iproc-msi"
+- msi-controller: claims itself as an MSI controller
+- interrupt-parent: Link to its parent interrupt device
+- interrupts: List of interrupt IDs from its parent interrupt device
+
+Optional properties:
+- brcm,pcie-msi-inten: Needs to be present for some older iProc platforms that
+require the interrupt enable registers to be set explicitly to enable MSI
+
 Example:
        pcie0: pcie@18012000 {
                compatible = "brcm,iproc-pcie";
@@ -58,6 +83,19 @@ Example:
                brcm,pcie-ob-oarr-size;
                brcm,pcie-ob-axi-offset = <0x00000000>;
                brcm,pcie-ob-window-size = <256>;
+
+               msi-parent = <&msi0>;
+
+               /* iProc event queue based MSI */
+               msi0: msi@18012000 {
+                       compatible = "brcm,iproc-msi";
+                       msi-controller;
+                       interrupt-parent = <&gic>;
+                       interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
+                                    <GIC_SPI 97 IRQ_TYPE_NONE>,
+                                    <GIC_SPI 98 IRQ_TYPE_NONE>,
+                                    <GIC_SPI 99 IRQ_TYPE_NONE>,
+               };
        };
 
        pcie1: pcie@18013000 {
index 17c6ed9c6059f50b1959fbdc408fa14b172467a6..b721beacfe4dae6c0bff3f429365080e46043d4a 100644 (file)
@@ -1,4 +1,4 @@
-HiSilicon PCIe host bridge DT description
+HiSilicon Hip05 and Hip06 PCIe host bridge DT description
 
 HiSilicon PCIe host controller is based on Designware PCI core.
 It shares common functions with PCIe Designware core driver and inherits
@@ -7,8 +7,8 @@ Documentation/devicetree/bindings/pci/designware-pci.txt.
 
 Additional properties are described here:
 
-Required properties:
-- compatible: Should contain "hisilicon,hip05-pcie".
+Required properties
+- compatible: Should contain "hisilicon,hip05-pcie" or "hisilicon,hip06-pcie".
 - reg: Should contain rc_dbi, config registers location and length.
 - reg-names: Must include the following entries:
   "rc_dbi": controller configuration registers;
@@ -20,7 +20,7 @@ Optional properties:
 - status: Either "ok" or "disabled".
 - dma-coherent: Present if DMA operations are coherent.
 
-Example:
+Hip05 Example (note that Hip06 is the same except compatible):
        pcie@0xb0080000 {
                compatible = "hisilicon,hip05-pcie", "snps,dw-pcie";
                reg = <0 0xb0080000 0 0x10000>, <0x220 0x00000000 0 0x2000>;
index 7fab84b335313bbd8abc668f347f06c8d0cfe87d..4e8b90e43dd83c72d81df6d644317ba60b489559 100644 (file)
@@ -8,7 +8,14 @@ OHCI and EHCI controllers.
 Required properties:
 - compatible: "renesas,pci-r8a7790" for the R8A7790 SoC;
              "renesas,pci-r8a7791" for the R8A7791 SoC;
-             "renesas,pci-r8a7794" for the R8A7794 SoC.
+             "renesas,pci-r8a7794" for the R8A7794 SoC;
+             "renesas,pci-rcar-gen2" for a generic R-Car Gen2 compatible device
+
+
+             When compatible with the generic version, nodes must list the
+             SoC-specific version corresponding to the platform first
+             followed by the generic version.
+
 - reg: A list of physical regions to access the device: the first is
        the operational registers for the OHCI/EHCI controllers and the
        second is for the bridge configuration and control registers.
@@ -24,10 +31,15 @@ Required properties:
 - interrupt-map-mask: standard property that helps to define the interrupt
   mapping.
 
+Optional properties:
+- dma-ranges: a single range for the inbound memory region. If not supplied,
+  defaults to 1GiB at 0x40000000. Note there are hardware restrictions on the
+  allowed combinations of address and size.
+
 Example SoC configuration:
 
        pci0: pci@ee090000  {
-               compatible = "renesas,pci-r8a7790";
+               compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2";
                clocks = <&mstp7_clks R8A7790_CLK_EHCI>;
                reg = <0x0 0xee090000 0x0 0xc00>,
                      <0x0 0xee080000 0x0 0x1100>;
@@ -38,6 +50,7 @@ Example SoC configuration:
                #address-cells = <3>;
                #size-cells = <2>;
                #interrupt-cells = <1>;
+               dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x40000000>;
                interrupt-map-mask = <0xff00 0 0 0x7>;
                interrupt-map = <0x0000 0 0 1 &gic 0 108 IRQ_TYPE_LEVEL_HIGH
                                 0x0800 0 0 1 &gic 0 108 IRQ_TYPE_LEVEL_HIGH
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
new file mode 100644 (file)
index 0000000..4059a6f
--- /dev/null
@@ -0,0 +1,233 @@
+* Qualcomm PCI express root complex
+
+- compatible:
+       Usage: required
+       Value type: <stringlist>
+       Definition: Value should contain
+                       - "qcom,pcie-ipq8064" for ipq8064
+                       - "qcom,pcie-apq8064" for apq8064
+                       - "qcom,pcie-apq8084" for apq8084
+
+- reg:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: Register ranges as listed in the reg-names property
+
+- reg-names:
+       Usage: required
+       Value type: <stringlist>
+       Definition: Must include the following entries
+                       - "parf"   Qualcomm specific registers
+                       - "dbi"    Designware PCIe registers
+                       - "elbi"   External local bus interface registers
+                       - "config" PCIe configuration space
+
+- device_type:
+       Usage: required
+       Value type: <string>
+       Definition: Should be "pci". As specified in designware-pcie.txt
+
+- #address-cells:
+       Usage: required
+       Value type: <u32>
+       Definition: Should be 3. As specified in designware-pcie.txt
+
+- #size-cells:
+       Usage: required
+       Value type: <u32>
+       Definition: Should be 2. As specified in designware-pcie.txt
+
+- ranges:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: As specified in designware-pcie.txt
+
+- interrupts:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: MSI interrupt
+
+- interrupt-names:
+       Usage: required
+       Value type: <stringlist>
+       Definition: Should contain "msi"
+
+- #interrupt-cells:
+       Usage: required
+       Value type: <u32>
+       Definition: Should be 1. As specified in designware-pcie.txt
+
+- interrupt-map-mask:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: As specified in designware-pcie.txt
+
+- interrupt-map:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: As specified in designware-pcie.txt
+
+- clocks:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: List of phandle and clock specifier pairs as listed
+                   in clock-names property
+
+- clock-names:
+       Usage: required
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "iface"       Configuration AHB clock
+
+- clock-names:
+       Usage: required for ipq/apq8064
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "core"        Clocks the pcie hw block
+                       - "phy"         Clocks the pcie PHY block
+- clock-names:
+       Usage: required for apq8084
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "aux"         Auxiliary (AUX) clock
+                       - "bus_master"  Master AXI clock
+                       - "bus_slave"   Slave AXI clock
+- resets:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: List of phandle and reset specifier pairs as listed
+                   in reset-names property
+
+- reset-names:
+       Usage: required for ipq/apq8064
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "axi"  AXI reset
+                       - "ahb"  AHB reset
+                       - "por"  POR reset
+                       - "pci"  PCI reset
+                       - "phy"  PHY reset
+
+- reset-names:
+       Usage: required for apq8084
+       Value type: <stringlist>
+       Definition: Should contain the following entries
+                       - "core" Core reset
+
+- power-domains:
+       Usage: required for apq8084
+       Value type: <prop-encoded-array>
+       Definition: A phandle and power domain specifier pair to the
+                   power domain which is responsible for collapsing
+                   and restoring power to the peripheral
+
+- vdda-supply:
+       Usage: required
+       Value type: <phandle>
+       Definition: A phandle to the core analog power supply
+
+- vdda_phy-supply:
+       Usage: required for ipq/apq8064
+       Value type: <phandle>
+       Definition: A phandle to the analog power supply for PHY
+
+- vdda_refclk-supply:
+       Usage: required for ipq/apq8064
+       Value type: <phandle>
+       Definition: A phandle to the analog power supply for IC which generates
+                   reference clock
+
+- phys:
+       Usage: required for apq8084
+       Value type: <phandle>
+       Definition: List of phandle(s) as listed in phy-names property
+
+- phy-names:
+       Usage: required for apq8084
+       Value type: <stringlist>
+       Definition: Should contain "pciephy"
+
+- <name>-gpios:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: List of phandle and gpio specifier pairs. Should contain
+                       - "perst-gpios" PCIe endpoint reset signal line
+                       - "wake-gpios"  PCIe endpoint wake signal line
+
+* Example for ipq/apq8064
+       pcie@1b500000 {
+               compatible = "qcom,pcie-apq8064", "qcom,pcie-ipq8064", "snps,dw-pcie";
+               reg = <0x1b500000 0x1000
+                      0x1b502000 0x80
+                      0x1b600000 0x100
+                      0x0ff00000 0x100000>;
+               reg-names = "dbi", "elbi", "parf", "config";
+               device_type = "pci";
+               linux,pci-domain = <0>;
+               bus-range = <0x00 0xff>;
+               num-lanes = <1>;
+               #address-cells = <3>;
+               #size-cells = <2>;
+               ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000   /* I/O */
+                         0x82000000 0 0 0x08000000 0 0x07e00000>; /* memory */
+               interrupts = <GIC_SPI 238 IRQ_TYPE_NONE>;
+               interrupt-names = "msi";
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 0 0x7>;
+               interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+                               <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+                               <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+                               <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+               clocks = <&gcc PCIE_A_CLK>,
+                        <&gcc PCIE_H_CLK>,
+                        <&gcc PCIE_PHY_CLK>;
+               clock-names = "core", "iface", "phy";
+               resets = <&gcc PCIE_ACLK_RESET>,
+                        <&gcc PCIE_HCLK_RESET>,
+                        <&gcc PCIE_POR_RESET>,
+                        <&gcc PCIE_PCI_RESET>,
+                        <&gcc PCIE_PHY_RESET>;
+               reset-names = "axi", "ahb", "por", "pci", "phy";
+               pinctrl-0 = <&pcie_pins_default>;
+               pinctrl-names = "default";
+       };
+
+* Example for apq8084
+       pcie0@fc520000 {
+               compatible = "qcom,pcie-apq8084", "snps,dw-pcie";
+               reg = <0xfc520000 0x2000>,
+                     <0xff000000 0x1000>,
+                     <0xff001000 0x1000>,
+                     <0xff002000 0x2000>;
+               reg-names = "parf", "dbi", "elbi", "config";
+               device_type = "pci";
+               linux,pci-domain = <0>;
+               bus-range = <0x00 0xff>;
+               num-lanes = <1>;
+               #address-cells = <3>;
+               #size-cells = <2>;
+               ranges = <0x81000000 0 0          0xff200000 0 0x00100000   /* I/O */
+                         0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
+               interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
+               interrupt-names = "msi";
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 0 0x7>;
+               interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+                               <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+                               <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+                               <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+               clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
+                        <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
+                        <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
+                        <&gcc GCC_PCIE_0_AUX_CLK>;
+               clock-names = "iface", "master_bus", "slave_bus", "aux";
+               resets = <&gcc GCC_PCIE_0_BCR>;
+               reset-names = "core";
+               power-domains = <&gcc PCIE0_GDSC>;
+               vdda-supply = <&pma8084_l3>;
+               phys = <&pciephy0>;
+               phy-names = "pciephy";
+               perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
+               pinctrl-0 = <&pcie0_pins_default>;
+               pinctrl-names = "default";
+       };
index 29d3b989d3b0c607f9f11a16e00e92cbbd056cf9..558fe528ae1951104b6266a2f5ed4849017c6b35 100644 (file)
@@ -1,8 +1,16 @@
 * Renesas RCar PCIe interface
 
 Required properties:
-- compatible: should contain one of the following
-       "renesas,pcie-r8a7779", "renesas,pcie-r8a7790", "renesas,pcie-r8a7791"
+compatible: "renesas,pcie-r8a7779" for the R8A7779 SoC;
+           "renesas,pcie-r8a7790" for the R8A7790 SoC;
+           "renesas,pcie-r8a7791" for the R8A7791 SoC;
+           "renesas,pcie-r8a7795" for the R8A7795 SoC;
+           "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device.
+
+           When compatible with the generic version, nodes must list the
+           SoC-specific version corresponding to the platform first
+           followed by the generic version.
+
 - reg: base address and length of the pcie controller registers.
 - #address-cells: set to <3>
 - #size-cells: set to <2>
@@ -25,7 +33,7 @@ Example:
 SoC specific DT Entry:
 
        pcie: pcie@fe000000 {
-               compatible = "renesas,pcie-r8a7791";
+               compatible = "renesas,pcie-r8a7791", "renesas,pcie-rcar-gen2";
                reg = <0 0xfe000000 0 0x80000>;
                #address-cells = <3>;
                #size-cells = <2>;
index 050d0e77a2cf00f7d6a33e6d11bccf88c22ad761..94544db330001bbd68c9c4cbba091f258ea82eec 100644 (file)
@@ -8216,6 +8216,12 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/pci/host-generic-pci.txt
 F:     drivers/pci/host/pci-host-generic.c
 
+PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
+M:     Keith Busch <keith.busch@intel.com>
+L:     linux-pci@vger.kernel.org
+S:     Supported
+F:     arch/x86/pci/vmd.c
+
 PCIE DRIVER FOR ST SPEAR13XX
 M:     Pratyush Anand <pratyush.anand@gmail.com>
 L:     linux-pci@vger.kernel.org
@@ -8240,11 +8246,19 @@ F:      drivers/pci/host/pci-xgene-msi.c
 
 PCIE DRIVER FOR HISILICON
 M:     Zhou Wang <wangzhou1@hisilicon.com>
+M:     Gabriele Paoloni <gabriele.paoloni@huawei.com>
 L:     linux-pci@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
 F:     drivers/pci/host/pcie-hisi.c
 
+PCIE DRIVER FOR QUALCOMM MSM
+M:     Stanimir Varbanov <svarbanov@mm-sol.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-msm@vger.kernel.org
+S:     Maintained
+F:     drivers/pci/host/*qcom*
+
 PCMCIA SUBSYSTEM
 P:     Linux PCMCIA Team
 L:     linux-pcmcia@lists.infradead.org
index 11ac608b6d50e716e6fc2aabf47839ce1b871d96..955c697b4dd4fba88d7c2dd39e1186c9b3a1f3ba 100644 (file)
                                        bias-disable;
                                };
                        };
+
+                       pcie_pins: pcie_pinmux {
+                               mux {
+                                       pins = "gpio27";
+                                       function = "gpio";
+                               };
+                               conf {
+                                       pins = "gpio27";
+                                       drive-strength = <12>;
+                                       bias-disable;
+                               };
+                       };
                };
 
                rpm@108000 {
                                pm8921_lvs1: lvs1 {
                                        bias-pull-down;
                                };
+
+                               lvs6 {
+                                       bias-pull-down;
+                               };
                        };
                };
 
                        status = "okay";
                };
 
+               pci@1b500000 {
+                       status = "ok";
+                       vdda-supply = <&pm8921_s3>;
+                       vdda_phy-supply = <&pm8921_lvs6>;
+                       vdda_refclk-supply = <&ext_3p3v>;
+                       pinctrl-0 = <&pcie_pins>;
+                       pinctrl-names = "default";
+                       perst-gpio = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>;
+               };
+
                qcom,ssbi@500000 {
                        pmic@0 {
                                gpio@150 {
index a4c1762b53ea3712a46e5f8a04cff5783d93b14d..847150fbfdbf74a30fcbca608cf216f536ab3010 100644 (file)
                        compatible = "qcom,tcsr-apq8064", "syscon";
                        reg = <0x1a400000 0x100>;
                };
+
+               pcie: pci@1b500000 {
+                       compatible = "qcom,pcie-apq8064", "snps,dw-pcie";
+                       reg = <0x1b500000 0x1000
+                              0x1b502000 0x80
+                              0x1b600000 0x100
+                              0x0ff00000 0x100000>;
+                       reg-names = "dbi", "elbi", "parf", "config";
+                       device_type = "pci";
+                       linux,pci-domain = <0>;
+                       bus-range = <0x00 0xff>;
+                       num-lanes = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000   /* I/O */
+                                 0x82000000 0 0 0x08000000 0 0x07e00000>; /* memory */
+                       interrupts = <GIC_SPI 238 IRQ_TYPE_NONE>;
+                       interrupt-names = "msi";
+                       #interrupt-cells = <1>;
+                       interrupt-map-mask = <0 0 0 0x7>;
+                       interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+                                       <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+                                       <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+                                       <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+                       clocks = <&gcc PCIE_A_CLK>,
+                                <&gcc PCIE_H_CLK>,
+                                <&gcc PCIE_PHY_REF_CLK>;
+                       clock-names = "core", "iface", "phy";
+                       resets = <&gcc PCIE_ACLK_RESET>,
+                                <&gcc PCIE_HCLK_RESET>,
+                                <&gcc PCIE_POR_RESET>,
+                                <&gcc PCIE_PCI_RESET>,
+                                <&gcc PCIE_PHY_RESET>;
+                       reset-names = "axi", "ahb", "por", "pci", "phy";
+                       status = "disabled";
+               };
        };
 };
index 80dfe8965df9f7d49fc57a1f1d6773f0c5ffd736..44bca7892f6608b79d8d42f442821c8820ef7492 100644 (file)
@@ -400,7 +400,7 @@ static void *eeh_rmv_device(void *data, void *userdata)
         * support EEH. So we just care about PCI devices for
         * simplicity here.
         */
-       if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
+       if (!dev || (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE))
                return NULL;
 
        /*
index 2e710c15893fe0da0295f9cf1ddeb6a827d88fcb..526ac6750e4d846e7c9dc489774d3743280fcfc3 100644 (file)
@@ -187,9 +187,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
 
        pci_device_add(dev, bus);
 
-       /* Setup MSI caps & disable MSI/MSI-X interrupts */
-       pci_msi_setup_pci_dev(dev);
-
        return dev;
 }
 EXPORT_SYMBOL(of_create_pci_dev);
index db3622f22b618303a8bc9c3c2f34762f400dbafb..3e6aca8222951f7d11bab79656fb5f5c586b48f4 100644 (file)
@@ -2665,6 +2665,19 @@ config PMC_ATOM
        def_bool y
         depends on PCI
 
+config VMD
+       depends on PCI_MSI
+       tristate "Volume Management Device Driver"
+       default N
+       ---help---
+         Adds support for the Intel Volume Management Device (VMD). VMD is a
+         secondary PCI host bridge that allows PCI Express root ports,
+         and devices attached to them, to be removed from the default
+         PCI domain and placed within the VMD domain. This provides
+         more bus resources than are otherwise possible with a
+         single domain. If you know your system provides one of these and
+         has devices attached to it, say Y; if you are not sure, say N.
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
index 03dd72957d2f635a570fa26a978ddb0e26027873..684ed6c3aa679d15dac80a48cd832ecf721b52dd 100644 (file)
@@ -10,6 +10,16 @@ struct dev_archdata {
 #endif
 };
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+struct dma_domain {
+       struct list_head node;
+       struct dma_map_ops *dma_ops;
+       int domain_nr;
+};
+void add_dma_domain(struct dma_domain *domain);
+void del_dma_domain(struct dma_domain *domain);
+#endif
+
 struct pdev_archdata {
 };
 
index 1e3408e88604cd5b18bb67884aaa496d5344b969..1815b736269d1ef46265f8e580f27335371e64de 100644 (file)
@@ -129,6 +129,11 @@ struct irq_alloc_info {
                        unsigned long   uv_offset;
                        char            *uv_name;
                };
+#endif
+#if IS_ENABLED(CONFIG_VMD)
+               struct {
+                       struct msi_desc *desc;
+               };
 #endif
        };
 };
index fa1195dae42541aaa1d836782a3a65aa25640e74..46873fbd44e1b522b5d40ad5ebf963714df566a1 100644 (file)
@@ -151,11 +151,11 @@ extern struct list_head pci_mmcfg_list;
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
- * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
- * on their northbrige except through the * %eax register. As such, you MUST
- * NOT use normal IOMEM accesses, you need to only use the magic mmio-config
- * accessor functions.
- * In fact just use pci_config_*, nothing else please.
+ * On AMD Fam10h CPUs, all PCI MMIO configuration space accesses must use
+ * %eax.  No other source or target registers may be used.  The following
+ * mmio_config_* accessors enforce this.  See "BIOS and Kernel Developer's
+ * Guide (BKDG) For AMD Family 10h Processors", rev. 3.48, sec 2.11.1,
+ * "MMIO Configuration Coding Requirements".
  */
 static inline unsigned char mmio_config_readb(void __iomem *pos)
 {
index 5c6fc3577a49f80a75c86cd3815a6c5758ba1288..97062a635b7715d1c5d55014d70d5ddbf77b25a0 100644 (file)
@@ -23,6 +23,8 @@ obj-y                         += bus_numa.o
 obj-$(CONFIG_AMD_NB)           += amd_bus.o
 obj-$(CONFIG_PCI_CNB20LE_QUIRK)        += broadcom_bus.o
 
+obj-$(CONFIG_VMD) += vmd.o
+
 ifeq ($(CONFIG_PCI_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
index eccd4d99e6a4a7182bf74469f5ecd580e8f22417..2879efc73a967bca70606547014decd3703c84bf 100644 (file)
@@ -641,6 +641,43 @@ unsigned int pcibios_assign_all_busses(void)
        return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
 }
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+static LIST_HEAD(dma_domain_list);
+static DEFINE_SPINLOCK(dma_domain_list_lock);
+
+void add_dma_domain(struct dma_domain *domain)
+{
+       spin_lock(&dma_domain_list_lock);
+       list_add(&domain->node, &dma_domain_list);
+       spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(add_dma_domain);
+
+void del_dma_domain(struct dma_domain *domain)
+{
+       spin_lock(&dma_domain_list_lock);
+       list_del(&domain->node);
+       spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(del_dma_domain);
+
+static void set_dma_domain_ops(struct pci_dev *pdev)
+{
+       struct dma_domain *domain;
+
+       spin_lock(&dma_domain_list_lock);
+       list_for_each_entry(domain, &dma_domain_list, node) {
+               if (pci_domain_nr(pdev->bus) == domain->domain_nr) {
+                       pdev->dev.archdata.dma_ops = domain->dma_ops;
+                       break;
+               }
+       }
+       spin_unlock(&dma_domain_list_lock);
+}
+#else
+static void set_dma_domain_ops(struct pci_dev *pdev) {}
+#endif
+
 int pcibios_add_device(struct pci_dev *dev)
 {
        struct setup_data *data;
@@ -670,6 +707,7 @@ int pcibios_add_device(struct pci_dev *dev)
                pa_data = data->next;
                iounmap(data);
        }
+       set_dma_domain_ops(dev);
        return 0;
 }
 
index 9b83b9051ae7bb78daf009e1d6c95927c74f2877..9770e55e768fde446cff8482e7c903a5ba6a61e0 100644 (file)
@@ -180,6 +180,7 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
        unsigned long result = 0;
        unsigned long flags;
        unsigned long bx = (bus << 8) | devfn;
+       u16 number = 0, mask = 0;
 
        WARN_ON(seg);
        if (!value || (bus > 255) || (devfn > 255) || (reg > 255))
@@ -189,53 +190,35 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
 
        switch (len) {
        case 1:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=c" (*value),
-                         "=a" (result)
-                       : "1" (PCIBIOS_READ_CONFIG_BYTE),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
-               /*
-                * Zero-extend the result beyond 8 bits, do not trust the
-                * BIOS having done it:
-                */
-               *value &= 0xff;
+               number = PCIBIOS_READ_CONFIG_BYTE;
+               mask = 0xff;
                break;
        case 2:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=c" (*value),
-                         "=a" (result)
-                       : "1" (PCIBIOS_READ_CONFIG_WORD),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
-               /*
-                * Zero-extend the result beyond 16 bits, do not trust the
-                * BIOS having done it:
-                */
-               *value &= 0xffff;
+               number = PCIBIOS_READ_CONFIG_WORD;
+               mask = 0xffff;
                break;
        case 4:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=c" (*value),
-                         "=a" (result)
-                       : "1" (PCIBIOS_READ_CONFIG_DWORD),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
+               number = PCIBIOS_READ_CONFIG_DWORD;
                break;
        }
 
+       __asm__("lcall *(%%esi); cld\n\t"
+               "jc 1f\n\t"
+               "xor %%ah, %%ah\n"
+               "1:"
+               : "=c" (*value),
+                 "=a" (result)
+               : "1" (number),
+                 "b" (bx),
+                 "D" ((long)reg),
+                 "S" (&pci_indirect));
+       /*
+        * Zero-extend the result beyond 8 or 16 bits, do not trust the
+        * BIOS having done it:
+        */
+       if (mask)
+               *value &= mask;
+
        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 
        return (int)((result & 0xff00) >> 8);
@@ -247,6 +230,7 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
        unsigned long result = 0;
        unsigned long flags;
        unsigned long bx = (bus << 8) | devfn;
+       u16 number = 0;
 
        WARN_ON(seg);
        if ((bus > 255) || (devfn > 255) || (reg > 255)) 
@@ -256,43 +240,27 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
 
        switch (len) {
        case 1:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=a" (result)
-                       : "0" (PCIBIOS_WRITE_CONFIG_BYTE),
-                         "c" (value),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
+               number = PCIBIOS_WRITE_CONFIG_BYTE;
                break;
        case 2:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=a" (result)
-                       : "0" (PCIBIOS_WRITE_CONFIG_WORD),
-                         "c" (value),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
+               number = PCIBIOS_WRITE_CONFIG_WORD;
                break;
        case 4:
-               __asm__("lcall *(%%esi); cld\n\t"
-                       "jc 1f\n\t"
-                       "xor %%ah, %%ah\n"
-                       "1:"
-                       : "=a" (result)
-                       : "0" (PCIBIOS_WRITE_CONFIG_DWORD),
-                         "c" (value),
-                         "b" (bx),
-                         "D" ((long)reg),
-                         "S" (&pci_indirect));
+               number = PCIBIOS_WRITE_CONFIG_DWORD;
                break;
        }
 
+       __asm__("lcall *(%%esi); cld\n\t"
+               "jc 1f\n\t"
+               "xor %%ah, %%ah\n"
+               "1:"
+               : "=a" (result)
+               : "0" (number),
+                 "c" (value),
+                 "b" (bx),
+                 "D" ((long)reg),
+                 "S" (&pci_indirect));
+
        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 
        return (int)((result & 0xff00) >> 8);
diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c
new file mode 100644 (file)
index 0000000..d57e480
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+ * Volume Management Device driver
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+
+#include <asm/irqdomain.h>
+#include <asm/device.h>
+#include <asm/msi.h>
+#include <asm/msidef.h>
+
+#define VMD_CFGBAR     0
+#define VMD_MEMBAR1    2
+#define VMD_MEMBAR2    4
+
+/*
+ * Lock for manipulating VMD IRQ lists.
+ */
+static DEFINE_RAW_SPINLOCK(list_lock);
+
+/**
+ * struct vmd_irq - private data to map driver IRQ to the VMD shared vector
+ * @node:      list item for parent traversal.
+ * @rcu:       RCU callback item for freeing.
+ * @irq:       back pointer to parent.
+ * @virq:      the virtual IRQ value provided to the requesting driver.
+ *
+ * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to
+ * a VMD IRQ using this structure.
+ */
+struct vmd_irq {
+       struct list_head        node;
+       struct rcu_head         rcu;
+       struct vmd_irq_list     *irq;
+       unsigned int            virq;
+};
+
+/**
+ * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
+ * @irq_list:  the list of irq's the VMD one demuxes to.
+ * @vmd_vector:        the h/w IRQ assigned to the VMD.
+ * @index:     index into the VMD MSI-X table; used for message routing.
+ * @count:     number of child IRQs assigned to this vector; used to track
+ *             sharing.
+ */
+struct vmd_irq_list {
+       struct list_head        irq_list;
+       struct vmd_dev          *vmd;
+       unsigned int            vmd_vector;
+       unsigned int            index;
+       unsigned int            count;
+};
+
+struct vmd_dev {
+       struct pci_dev          *dev;
+
+       spinlock_t              cfg_lock;
+       char __iomem            *cfgbar;
+
+       int msix_count;
+       struct msix_entry       *msix_entries;
+       struct vmd_irq_list     *irqs;
+
+       struct pci_sysdata      sysdata;
+       struct resource         resources[3];
+       struct irq_domain       *irq_domain;
+       struct pci_bus          *bus;
+
+#ifdef CONFIG_X86_DEV_DMA_OPS
+       struct dma_map_ops      dma_ops;
+       struct dma_domain       dma_domain;
+#endif
+};
+
+static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
+{
+       return container_of(bus->sysdata, struct vmd_dev, sysdata);
+}
+
+/*
+ * Drivers managing a device in a VMD domain allocate their own IRQs as before,
+ * but the MSI entry for the hardware it's driving will be programmed with a
+ * destination ID for the VMD MSI-X table.  The VMD muxes interrupts in its
+ * domain into one of its own, and the VMD driver de-muxes these for the
+ * handlers sharing that VMD IRQ.  The vmd irq_domain provides the operations
+ * and irq_chip to set this up.
+ */
+static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct vmd_irq *vmdirq = data->chip_data;
+       struct vmd_irq_list *irq = vmdirq->irq;
+
+       msg->address_hi = MSI_ADDR_BASE_HI;
+       msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_DEST_ID(irq->index);
+       msg->data = 0;
+}
+
+/*
+ * We rely on MSI_FLAG_USE_DEF_CHIP_OPS to set the IRQ mask/unmask ops.
+ */
+static void vmd_irq_enable(struct irq_data *data)
+{
+       struct vmd_irq *vmdirq = data->chip_data;
+
+       raw_spin_lock(&list_lock);
+       list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list);
+       raw_spin_unlock(&list_lock);
+
+       data->chip->irq_unmask(data);
+}
+
+static void vmd_irq_disable(struct irq_data *data)
+{
+       struct vmd_irq *vmdirq = data->chip_data;
+
+       data->chip->irq_mask(data);
+
+       raw_spin_lock(&list_lock);
+       list_del_rcu(&vmdirq->node);
+       raw_spin_unlock(&list_lock);
+}
+
+/*
+ * XXX: Stubbed until we develop acceptable way to not create conflicts with
+ * other devices sharing the same vector.
+ */
+static int vmd_irq_set_affinity(struct irq_data *data,
+                               const struct cpumask *dest, bool force)
+{
+       return -EINVAL;
+}
+
+static struct irq_chip vmd_msi_controller = {
+       .name                   = "VMD-MSI",
+       .irq_enable             = vmd_irq_enable,
+       .irq_disable            = vmd_irq_disable,
+       .irq_compose_msi_msg    = vmd_compose_msi_msg,
+       .irq_set_affinity       = vmd_irq_set_affinity,
+};
+
+static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info,
+                                    msi_alloc_info_t *arg)
+{
+       return 0;
+}
+
+/*
+ * XXX: We can be even smarter selecting the best IRQ once we solve the
+ * affinity problem.
+ */
+static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd)
+{
+       int i, best = 0;
+
+       raw_spin_lock(&list_lock);
+       for (i = 1; i < vmd->msix_count; i++)
+               if (vmd->irqs[i].count < vmd->irqs[best].count)
+                       best = i;
+       vmd->irqs[best].count++;
+       raw_spin_unlock(&list_lock);
+
+       return &vmd->irqs[best];
+}
+
+static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
+                       unsigned int virq, irq_hw_number_t hwirq,
+                       msi_alloc_info_t *arg)
+{
+       struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(arg->desc)->bus);
+       struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
+
+       if (!vmdirq)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&vmdirq->node);
+       vmdirq->irq = vmd_next_irq(vmd);
+       vmdirq->virq = virq;
+
+       irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
+                           vmdirq, handle_simple_irq, vmd, NULL);
+       return 0;
+}
+
+static void vmd_msi_free(struct irq_domain *domain,
+                       struct msi_domain_info *info, unsigned int virq)
+{
+       struct vmd_irq *vmdirq = irq_get_chip_data(virq);
+
+       /* XXX: Potential optimization to rebalance */
+       raw_spin_lock(&list_lock);
+       vmdirq->irq->count--;
+       raw_spin_unlock(&list_lock);
+
+       kfree_rcu(vmdirq, rcu);
+}
+
+static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev,
+                          int nvec, msi_alloc_info_t *arg)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
+
+       if (nvec > vmd->msix_count)
+               return vmd->msix_count;
+
+       memset(arg, 0, sizeof(*arg));
+       return 0;
+}
+
+static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+       arg->desc = desc;
+}
+
+static struct msi_domain_ops vmd_msi_domain_ops = {
+       .get_hwirq      = vmd_get_hwirq,
+       .msi_init       = vmd_msi_init,
+       .msi_free       = vmd_msi_free,
+       .msi_prepare    = vmd_msi_prepare,
+       .set_desc       = vmd_set_desc,
+};
+
+static struct msi_domain_info vmd_msi_domain_info = {
+       .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                         MSI_FLAG_PCI_MSIX,
+       .ops            = &vmd_msi_domain_ops,
+       .chip           = &vmd_msi_controller,
+};
+
+#ifdef CONFIG_X86_DEV_DMA_OPS
+/*
+ * VMD replaces the requester ID with its own.  DMA mappings for devices in a
+ * VMD domain need to be mapped for the VMD, not the device requiring
+ * the mapping.
+ */
+static struct device *to_vmd_dev(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
+
+       return &vmd->dev->dev;
+}
+
+static struct dma_map_ops *vmd_dma_ops(struct device *dev)
+{
+       return to_vmd_dev(dev)->archdata.dma_ops;
+}
+
+static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr,
+                      gfp_t flag, struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->alloc(to_vmd_dev(dev), size, addr, flag,
+                                      attrs);
+}
+
+static void vmd_free(struct device *dev, size_t size, void *vaddr,
+                    dma_addr_t addr, struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->free(to_vmd_dev(dev), size, vaddr, addr,
+                                     attrs);
+}
+
+static int vmd_mmap(struct device *dev, struct vm_area_struct *vma,
+                   void *cpu_addr, dma_addr_t addr, size_t size,
+                   struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->mmap(to_vmd_dev(dev), vma, cpu_addr, addr,
+                                     size, attrs);
+}
+
+static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt,
+                          void *cpu_addr, dma_addr_t addr, size_t size,
+                          struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->get_sgtable(to_vmd_dev(dev), sgt, cpu_addr,
+                                            addr, size, attrs);
+}
+
+static dma_addr_t vmd_map_page(struct device *dev, struct page *page,
+                              unsigned long offset, size_t size,
+                              enum dma_data_direction dir,
+                              struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->map_page(to_vmd_dev(dev), page, offset, size,
+                                         dir, attrs);
+}
+
+static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size,
+                          enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+       vmd_dma_ops(dev)->unmap_page(to_vmd_dev(dev), addr, size, dir, attrs);
+}
+
+static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+                     enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+       return vmd_dma_ops(dev)->map_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
+}
+
+static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
+                        enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+       vmd_dma_ops(dev)->unmap_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
+}
+
+static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
+                                   size_t size, enum dma_data_direction dir)
+{
+       vmd_dma_ops(dev)->sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir);
+}
+
+static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr,
+                                      size_t size, enum dma_data_direction dir)
+{
+       vmd_dma_ops(dev)->sync_single_for_device(to_vmd_dev(dev), addr, size,
+                                                dir);
+}
+
+static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
+                               int nents, enum dma_data_direction dir)
+{
+       vmd_dma_ops(dev)->sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir);
+}
+
+static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
+                                  int nents, enum dma_data_direction dir)
+{
+       vmd_dma_ops(dev)->sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir);
+}
+
+static int vmd_mapping_error(struct device *dev, dma_addr_t addr)
+{
+       return vmd_dma_ops(dev)->mapping_error(to_vmd_dev(dev), addr);
+}
+
+static int vmd_dma_supported(struct device *dev, u64 mask)
+{
+       return vmd_dma_ops(dev)->dma_supported(to_vmd_dev(dev), mask);
+}
+
+#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
+static u64 vmd_get_required_mask(struct device *dev)
+{
+       return vmd_dma_ops(dev)->get_required_mask(to_vmd_dev(dev));
+}
+#endif
+
+static void vmd_teardown_dma_ops(struct vmd_dev *vmd)
+{
+       struct dma_domain *domain = &vmd->dma_domain;
+
+       if (vmd->dev->dev.archdata.dma_ops)
+               del_dma_domain(domain);
+}
+
+#define ASSIGN_VMD_DMA_OPS(source, dest, fn)   \
+       do {                                    \
+               if (source->fn)                 \
+                       dest->fn = vmd_##fn;    \
+       } while (0)
+
+static void vmd_setup_dma_ops(struct vmd_dev *vmd)
+{
+       const struct dma_map_ops *source = vmd->dev->dev.archdata.dma_ops;
+       struct dma_map_ops *dest = &vmd->dma_ops;
+       struct dma_domain *domain = &vmd->dma_domain;
+
+       domain->domain_nr = vmd->sysdata.domain;
+       domain->dma_ops = dest;
+
+       if (!source)
+               return;
+       ASSIGN_VMD_DMA_OPS(source, dest, alloc);
+       ASSIGN_VMD_DMA_OPS(source, dest, free);
+       ASSIGN_VMD_DMA_OPS(source, dest, mmap);
+       ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable);
+       ASSIGN_VMD_DMA_OPS(source, dest, map_page);
+       ASSIGN_VMD_DMA_OPS(source, dest, unmap_page);
+       ASSIGN_VMD_DMA_OPS(source, dest, map_sg);
+       ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg);
+       ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu);
+       ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device);
+       ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu);
+       ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device);
+       ASSIGN_VMD_DMA_OPS(source, dest, mapping_error);
+       ASSIGN_VMD_DMA_OPS(source, dest, dma_supported);
+#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
+       ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask);
+#endif
+       add_dma_domain(domain);
+}
+#undef ASSIGN_VMD_DMA_OPS
+#else
+static void vmd_teardown_dma_ops(struct vmd_dev *vmd) {}
+static void vmd_setup_dma_ops(struct vmd_dev *vmd) {}
+#endif
+
+static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
+                                 unsigned int devfn, int reg, int len)
+{
+       char __iomem *addr = vmd->cfgbar +
+                            (bus->number << 20) + (devfn << 12) + reg;
+
+       if ((addr - vmd->cfgbar) + len >=
+           resource_size(&vmd->dev->resource[VMD_CFGBAR]))
+               return NULL;
+
+       return addr;
+}
+
+/*
+ * CPU may deadlock if config space is not serialized on some versions of this
+ * hardware, so all config space access is done under a spinlock.
+ */
+static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg,
+                       int len, u32 *value)
+{
+       struct vmd_dev *vmd = vmd_from_bus(bus);
+       char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
+       unsigned long flags;
+       int ret = 0;
+
+       if (!addr)
+               return -EFAULT;
+
+       spin_lock_irqsave(&vmd->cfg_lock, flags);
+       switch (len) {
+       case 1:
+               *value = readb(addr);
+               break;
+       case 2:
+               *value = readw(addr);
+               break;
+       case 4:
+               *value = readl(addr);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       spin_unlock_irqrestore(&vmd->cfg_lock, flags);
+       return ret;
+}
+
+/*
+ * VMD h/w converts non-posted config writes to posted memory writes. The
+ * read-back in this function forces the completion so it returns only after
+ * the config space was written, as expected.
+ */
+static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg,
+                        int len, u32 value)
+{
+       struct vmd_dev *vmd = vmd_from_bus(bus);
+       char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
+       unsigned long flags;
+       int ret = 0;
+
+       if (!addr)
+               return -EFAULT;
+
+       spin_lock_irqsave(&vmd->cfg_lock, flags);
+       switch (len) {
+       case 1:
+               writeb(value, addr);
+               readb(addr);
+               break;
+       case 2:
+               writew(value, addr);
+               readw(addr);
+               break;
+       case 4:
+               writel(value, addr);
+               readl(addr);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       spin_unlock_irqrestore(&vmd->cfg_lock, flags);
+       return ret;
+}
+
+static struct pci_ops vmd_ops = {
+       .read           = vmd_pci_read,
+       .write          = vmd_pci_write,
+};
+
+/*
+ * VMD domains start at 0x1000 to not clash with ACPI _SEG domains.
+ */
+static int vmd_find_free_domain(void)
+{
+       int domain = 0xffff;
+       struct pci_bus *bus = NULL;
+
+       while ((bus = pci_find_next_bus(bus)) != NULL)
+               domain = max_t(int, domain, pci_domain_nr(bus));
+       return domain + 1;
+}
+
+static int vmd_enable_domain(struct vmd_dev *vmd)
+{
+       struct pci_sysdata *sd = &vmd->sysdata;
+       struct resource *res;
+       u32 upper_bits;
+       unsigned long flags;
+       LIST_HEAD(resources);
+
+       res = &vmd->dev->resource[VMD_CFGBAR];
+       vmd->resources[0] = (struct resource) {
+               .name  = "VMD CFGBAR",
+               .start = res->start,
+               .end   = (resource_size(res) >> 20) - 1,
+               .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
+       };
+
+       res = &vmd->dev->resource[VMD_MEMBAR1];
+       upper_bits = upper_32_bits(res->end);
+       flags = res->flags & ~IORESOURCE_SIZEALIGN;
+       if (!upper_bits)
+               flags &= ~IORESOURCE_MEM_64;
+       vmd->resources[1] = (struct resource) {
+               .name  = "VMD MEMBAR1",
+               .start = res->start,
+               .end   = res->end,
+               .flags = flags,
+       };
+
+       res = &vmd->dev->resource[VMD_MEMBAR2];
+       upper_bits = upper_32_bits(res->end);
+       flags = res->flags & ~IORESOURCE_SIZEALIGN;
+       if (!upper_bits)
+               flags &= ~IORESOURCE_MEM_64;
+       vmd->resources[2] = (struct resource) {
+               .name  = "VMD MEMBAR2",
+               .start = res->start + 0x2000,
+               .end   = res->end,
+               .flags = flags,
+       };
+
+       sd->domain = vmd_find_free_domain();
+       if (sd->domain < 0)
+               return sd->domain;
+
+       sd->node = pcibus_to_node(vmd->dev->bus);
+
+       vmd->irq_domain = pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info,
+                                                   NULL);
+       if (!vmd->irq_domain)
+               return -ENODEV;
+
+       pci_add_resource(&resources, &vmd->resources[0]);
+       pci_add_resource(&resources, &vmd->resources[1]);
+       pci_add_resource(&resources, &vmd->resources[2]);
+       vmd->bus = pci_create_root_bus(&vmd->dev->dev, 0, &vmd_ops, sd,
+                                      &resources);
+       if (!vmd->bus) {
+               pci_free_resource_list(&resources);
+               irq_domain_remove(vmd->irq_domain);
+               return -ENODEV;
+       }
+
+       vmd_setup_dma_ops(vmd);
+       dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
+       pci_rescan_bus(vmd->bus);
+
+       WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
+                              "domain"), "Can't create symlink to domain\n");
+       return 0;
+}
+
+static irqreturn_t vmd_irq(int irq, void *data)
+{
+       struct vmd_irq_list *irqs = data;
+       struct vmd_irq *vmdirq;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
+               generic_handle_irq(vmdirq->virq);
+       rcu_read_unlock();
+
+       return IRQ_HANDLED;
+}
+
+static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct vmd_dev *vmd;
+       int i, err;
+
+       if (resource_size(&dev->resource[VMD_CFGBAR]) < (1 << 20))
+               return -ENOMEM;
+
+       vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL);
+       if (!vmd)
+               return -ENOMEM;
+
+       vmd->dev = dev;
+       err = pcim_enable_device(dev);
+       if (err < 0)
+               return err;
+
+       vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0);
+       if (!vmd->cfgbar)
+               return -ENOMEM;
+
+       pci_set_master(dev);
+       if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) &&
+           dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)))
+               return -ENODEV;
+
+       vmd->msix_count = pci_msix_vec_count(dev);
+       if (vmd->msix_count < 0)
+               return -ENODEV;
+
+       vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
+                                GFP_KERNEL);
+       if (!vmd->irqs)
+               return -ENOMEM;
+
+       vmd->msix_entries = devm_kcalloc(&dev->dev, vmd->msix_count,
+                                        sizeof(*vmd->msix_entries),
+                                        GFP_KERNEL);
+       if (!vmd->msix_entries)
+               return -ENOMEM;
+       for (i = 0; i < vmd->msix_count; i++)
+               vmd->msix_entries[i].entry = i;
+
+       vmd->msix_count = pci_enable_msix_range(vmd->dev, vmd->msix_entries, 1,
+                                               vmd->msix_count);
+       if (vmd->msix_count < 0)
+               return vmd->msix_count;
+
+       for (i = 0; i < vmd->msix_count; i++) {
+               INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
+               vmd->irqs[i].vmd_vector = vmd->msix_entries[i].vector;
+               vmd->irqs[i].index = i;
+
+               err = devm_request_irq(&dev->dev, vmd->irqs[i].vmd_vector,
+                                      vmd_irq, 0, "vmd", &vmd->irqs[i]);
+               if (err)
+                       return err;
+       }
+
+       spin_lock_init(&vmd->cfg_lock);
+       pci_set_drvdata(dev, vmd);
+       err = vmd_enable_domain(vmd);
+       if (err)
+               return err;
+
+       dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
+                vmd->sysdata.domain);
+       return 0;
+}
+
+static void vmd_remove(struct pci_dev *dev)
+{
+       struct vmd_dev *vmd = pci_get_drvdata(dev);
+
+       pci_set_drvdata(dev, NULL);
+       sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
+       pci_stop_root_bus(vmd->bus);
+       pci_remove_root_bus(vmd->bus);
+       vmd_teardown_dma_ops(vmd);
+       irq_domain_remove(vmd->irq_domain);
+}
+
+#ifdef CONFIG_PM
+static int vmd_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       pci_save_state(pdev);
+       return 0;
+}
+
+static int vmd_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       pci_restore_state(pdev);
+       return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume);
+
+static const struct pci_device_id vmd_ids[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x201d),},
+       {0,}
+};
+MODULE_DEVICE_TABLE(pci, vmd_ids);
+
+static struct pci_driver vmd_drv = {
+       .name           = "vmd",
+       .id_table       = vmd_ids,
+       .probe          = vmd_probe,
+       .remove         = vmd_remove,
+       .driver         = {
+               .pm     = &vmd_dev_pm_ops,
+       },
+};
+module_pci_driver(vmd_drv);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.6");
index d3346d23963b87606517b44b15c196ee3393ac4e..89b3befc7155325b12f21d4667a675b52423bb6d 100644 (file)
@@ -140,6 +140,8 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
        type_mask |= IORESOURCE_TYPE_BITS;
 
        pci_bus_for_each_resource(bus, r, i) {
+               resource_size_t min_used = min;
+
                if (!r)
                        continue;
 
@@ -163,12 +165,12 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
                 * overrides "min".
                 */
                if (avail.start)
-                       min = avail.start;
+                       min_used = avail.start;
 
                max = avail.end;
 
                /* Ok, try it out.. */
-               ret = allocate_resource(r, res, size, min, max,
+               ret = allocate_resource(r, res, size, min_used, max,
                                        align, alignf, alignf_data);
                if (ret == 0)
                        return 0;
index f131ba947dc6fe47941b09b0e6d1d35f2a53c255..6d181ba09ccd5f0d496f1f0787b2dac85eb48474 100644 (file)
@@ -48,8 +48,7 @@ config PCI_RCAR_GEN2
 
 config PCI_RCAR_GEN2_PCIE
        bool "Renesas R-Car PCIe controller"
-       depends on ARM
-       depends on ARCH_SHMOBILE || COMPILE_TEST
+       depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
        help
          Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
 
@@ -118,13 +117,11 @@ config PCI_VERSATILE
        depends on ARCH_VERSATILE
 
 config PCIE_IPROC
-       tristate "Broadcom iProc PCIe controller"
-       depends on OF && (ARM || ARM64)
-       default n
+       tristate
        help
          This enables the iProc PCIe core controller support for Broadcom's
-         iProc family of SoCs. An appropriate bus interface driver also needs
-         to be enabled
+         iProc family of SoCs. An appropriate bus interface driver needs
+         to be enabled to select this.
 
 config PCIE_IPROC_PLATFORM
        tristate "Broadcom iProc PCIe platform bus driver"
@@ -147,6 +144,16 @@ config PCIE_IPROC_BCMA
          Say Y here if you want to use the Broadcom iProc PCIe controller
          through the BCMA bus interface
 
+config PCIE_IPROC_MSI
+       bool "Broadcom iProc PCIe MSI support"
+       depends on PCIE_IPROC_PLATFORM || PCIE_IPROC_BCMA
+       depends on PCI_MSI
+       select PCI_MSI_IRQ_DOMAIN
+       default ARCH_BCM_IPROC
+       help
+         Say Y here if you want to enable MSI support for Broadcom's iProc
+         PCIe controller
+
 config PCIE_ALTERA
        bool "Altera PCIe controller"
        depends on ARM || NIOS2
@@ -166,10 +173,21 @@ config PCIE_ALTERA_MSI
 
 config PCI_HISI
        depends on OF && ARM64
-       bool "HiSilicon SoC HIP05 PCIe controller"
+       bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers"
        select PCIEPORTBUS
        select PCIE_DW
        help
-         Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
+         Say Y here if you want PCIe controller support on HiSilicon
+         Hip05 and Hip06 SoCs
+
+config PCIE_QCOM
+       bool "Qualcomm PCIe controller"
+       depends on ARCH_QCOM && OF
+       select PCIE_DW
+       select PCIEPORTBUS
+       help
+         Say Y here to enable PCIe controller support on Qualcomm SoCs. The
+         PCIe controller uses the Designware core plus Qualcomm-specific
+         hardware wrappers.
 
 endmenu
index 9d4d3c6924a1da4956105c587b13e35f4ea71a43..7b2f20c6ccc62bca6317d35e85579eba7429f85a 100644 (file)
@@ -15,8 +15,10 @@ obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
+obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.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
 obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
index 8c3688046c02ff40446833f1a8f3eecebfd9713f..923607bdabc5c63ef940453f3ffe42180b29b02b 100644 (file)
@@ -302,7 +302,8 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
        }
 
        ret = devm_request_irq(&pdev->dev, pp->irq,
-                              dra7xx_pcie_msi_irq_handler, IRQF_SHARED,
+                              dra7xx_pcie_msi_irq_handler,
+                              IRQF_SHARED | IRQF_NO_THREAD,
                               "dra7-pcie-msi", pp);
        if (ret) {
                dev_err(&pdev->dev, "failed to request irq\n");
index 01095e1160a474b414112cdc1dddae1d8f3b9434..d997d22d4231da6de08cab29dd101d699dbb4fe0 100644 (file)
@@ -522,7 +522,8 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
 
                ret = devm_request_irq(&pdev->dev, pp->msi_irq,
                                        exynos_pcie_msi_irq_handler,
-                                       IRQF_SHARED, "exynos-pcie", pp);
+                                       IRQF_SHARED | IRQF_NO_THREAD,
+                                       "exynos-pcie", pp);
                if (ret) {
                        dev_err(&pdev->dev, "failed to request msi irq\n");
                        return ret;
index 5434c90db24345bc73ad5e00ecc00d599c40ddaf..1652bc70b1453663cc4a7614d68a3e7c5ea12524 100644 (file)
@@ -38,16 +38,7 @@ struct gen_pci_cfg_windows {
        struct gen_pci_cfg_bus_ops              *ops;
 };
 
-/*
- * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
- * sysdata.  Add pci_sys_data as the first element in struct gen_pci so
- * that when we use a gen_pci pointer as sysdata, it is also a pointer to
- * a struct pci_sys_data.
- */
 struct gen_pci {
-#ifdef CONFIG_ARM
-       struct pci_sys_data                     sys;
-#endif
        struct pci_host_bridge                  host;
        struct gen_pci_cfg_windows              cfg;
        struct list_head                        resources;
index 22e8224126fd9d9885ee06f2fb81cbf1f023b06b..b039f0c6c8ff8d8e7df348d1ffac962523d94657 100644 (file)
@@ -32,7 +32,7 @@
 #define to_imx6_pcie(x)        container_of(x, struct imx6_pcie, pp)
 
 struct imx6_pcie {
-       int                     reset_gpio;
+       struct gpio_desc        *reset_gpio;
        struct clk              *pcie_bus;
        struct clk              *pcie_phy;
        struct clk              *pcie;
@@ -287,10 +287,10 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        usleep_range(200, 500);
 
        /* Some boards don't have PCIe reset GPIO. */
-       if (gpio_is_valid(imx6_pcie->reset_gpio)) {
-               gpio_set_value(imx6_pcie->reset_gpio, 0);
+       if (imx6_pcie->reset_gpio) {
+               gpiod_set_value_cansleep(imx6_pcie->reset_gpio, 0);
                msleep(100);
-               gpio_set_value(imx6_pcie->reset_gpio, 1);
+               gpiod_set_value_cansleep(imx6_pcie->reset_gpio, 1);
        }
        return 0;
 
@@ -537,7 +537,8 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
 
                ret = devm_request_irq(&pdev->dev, pp->msi_irq,
                                       imx6_pcie_msi_handler,
-                                      IRQF_SHARED, "mx6-pcie-msi", pp);
+                                      IRQF_SHARED | IRQF_NO_THREAD,
+                                      "mx6-pcie-msi", pp);
                if (ret) {
                        dev_err(&pdev->dev, "failed to request MSI irq\n");
                        return ret;
@@ -560,7 +561,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
 {
        struct imx6_pcie *imx6_pcie;
        struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
        struct resource *dbi_base;
        int ret;
 
@@ -581,15 +581,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(pp->dbi_base);
 
        /* Fetch GPIOs */
-       imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-       if (gpio_is_valid(imx6_pcie->reset_gpio)) {
-               ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
-                                           GPIOF_OUT_INIT_LOW, "PCIe reset");
-               if (ret) {
-                       dev_err(&pdev->dev, "unable to get reset gpio\n");
-                       return ret;
-               }
-       }
+       imx6_pcie->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
+                                                       GPIOD_OUT_LOW);
 
        /* Fetch clocks */
        imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
index c4f64bfee551b6cd46c50dabb05cbb5d7b4f5c48..9980a4bdae7ee1c68c1f3c9bab2bdfee0a6b9ab2 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
 #include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
@@ -102,6 +103,8 @@ struct rcar_pci_priv {
        unsigned busnr;
        int irq;
        unsigned long window_size;
+       unsigned long window_addr;
+       unsigned long window_pci;
 };
 
 /* PCI configuration space operations */
@@ -239,8 +242,8 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
               RCAR_PCI_ARBITER_PCIBP_MODE;
        iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
 
-       /* PCI-AHB mapping: 0x40000000 base */
-       iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16,
+       /* PCI-AHB mapping */
+       iowrite32(priv->window_addr | RCAR_PCIAHB_PREFETCH16,
                  reg + RCAR_PCIAHB_WIN1_CTR_REG);
 
        /* AHB-PCI mapping: OHCI/EHCI registers */
@@ -251,7 +254,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
        iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,
                  reg + RCAR_AHBPCI_WIN1_CTR_REG);
        /* Set PCI-AHB Window1 address */
-       iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH,
+       iowrite32(priv->window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH,
                  reg + PCI_BASE_ADDRESS_1);
        /* Set AHB-PCI bridge PCI communication area address */
        val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;
@@ -284,6 +287,64 @@ static struct pci_ops rcar_pci_ops = {
        .write  = pci_generic_config_write,
 };
 
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+                                    struct device_node *node)
+{
+       const int na = 3, ns = 2;
+       int rlen;
+
+       parser->node = node;
+       parser->pna = of_n_addr_cells(node);
+       parser->np = parser->pna + na + ns;
+
+       parser->range = of_get_property(node, "dma-ranges", &rlen);
+       if (!parser->range)
+               return -ENOENT;
+
+       parser->end = parser->range + rlen / sizeof(__be32);
+       return 0;
+}
+
+static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
+                                        struct device_node *np)
+{
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+       int index = 0;
+
+       /* Failure to parse is ok as we fall back to defaults */
+       if (pci_dma_range_parser_init(&parser, np))
+               return 0;
+
+       /* Get the dma-ranges from DT */
+       for_each_of_pci_range(&parser, &range) {
+               /* Hardware only allows one inbound 32-bit range */
+               if (index)
+                       return -EINVAL;
+
+               pci->window_addr = (unsigned long)range.cpu_addr;
+               pci->window_pci = (unsigned long)range.pci_addr;
+               pci->window_size = (unsigned long)range.size;
+
+               /* Catch HW limitations */
+               if (!(range.flags & IORESOURCE_PREFETCH)) {
+                       dev_err(pci->dev, "window must be prefetchable\n");
+                       return -EINVAL;
+               }
+               if (pci->window_addr) {
+                       u32 lowaddr = 1 << (ffs(pci->window_addr) - 1);
+
+                       if (lowaddr < pci->window_size) {
+                               dev_err(pci->dev, "invalid window size/addr\n");
+                               return -EINVAL;
+                       }
+               }
+               index++;
+       }
+
+       return 0;
+}
+
 static int rcar_pci_probe(struct platform_device *pdev)
 {
        struct resource *cfg_res, *mem_res;
@@ -329,6 +390,9 @@ static int rcar_pci_probe(struct platform_device *pdev)
                return priv->irq;
        }
 
+       /* default window addr and size if not specified in DT */
+       priv->window_addr = 0x40000000;
+       priv->window_pci = 0x40000000;
        priv->window_size = SZ_1G;
 
        if (pdev->dev.of_node) {
@@ -344,6 +408,12 @@ static int rcar_pci_probe(struct platform_device *pdev)
                priv->busnr = busnr.start;
                if (busnr.end != busnr.start)
                        dev_warn(&pdev->dev, "only one bus number supported\n");
+
+               ret = rcar_pci_parse_map_dma_ranges(priv, pdev->dev.of_node);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to parse dma-range\n");
+                       return ret;
+               }
        } else {
                priv->busnr = pdev->id;
        }
@@ -360,6 +430,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
 }
 
 static struct of_device_id rcar_pci_of_match[] = {
+       { .compatible = "renesas,pci-rcar-gen2", },
        { .compatible = "renesas,pci-r8a7790", },
        { .compatible = "renesas,pci-r8a7791", },
        { .compatible = "renesas,pci-r8a7794", },
index 3018ae52e0923d4e2e8c8c2dc75f07d5528598bc..30323114c53cca80b0c4f7bee7f361d65565fc26 100644 (file)
@@ -1288,7 +1288,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 
        msi->irq = err;
 
-       err = request_irq(msi->irq, tegra_pcie_msi_irq, 0,
+       err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD,
                          tegra_msi_irq_chip.name, pcie);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
index 0863d9cc25f8619e9abc42c4adccdc00cc14d03a..f843a72dc51c268173c682a26a895a93e3c8d381 100644 (file)
@@ -125,9 +125,6 @@ out_release_res:
        return err;
 }
 
-/* Unused, temporary to satisfy ARM arch code */
-struct pci_sys_data sys;
-
 static int versatile_pci_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -208,7 +205,7 @@ static int versatile_pci_probe(struct platform_device *pdev)
        pci_add_flags(PCI_ENABLE_PROC_DOMAINS);
        pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC);
 
-       bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res);
+       bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, NULL, &pci_res);
        if (!bus)
                return -ENOMEM;
 
index 540f077c37eae7b59b983c31fa6ddce51d8bda6e..e28bc0bd3a7da773c744152af79cbc2726dc30a0 100644 (file)
@@ -128,32 +128,26 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
 static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
                               u32 *val)
 {
-       int ret;
-
        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, size, val);
+               return pp->ops->rd_own_conf(pp, where, size, val);
 
-       return ret;
+       return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
 }
 
 static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
                               u32 val)
 {
-       int ret;
-
        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, size, val);
+               return pp->ops->wr_own_conf(pp, where, size, val);
 
-       return ret;
+       return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
 }
 
 static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
                int type, u64 cpu_addr, u64 pci_addr, u32 size)
 {
+       u32 val;
+
        dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
                          PCIE_ATU_VIEWPORT);
        dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
@@ -164,6 +158,12 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
        dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
        dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
        dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
 }
 
 static struct irq_chip dw_msi_irq_chip = {
@@ -384,8 +384,8 @@ int dw_pcie_link_up(struct pcie_port *pp)
 {
        if (pp->ops->link_up)
                return pp->ops->link_up(pp);
-       else
-               return 0;
+
+       return 0;
 }
 
 static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
@@ -572,6 +572,9 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
+       if (pp->ops->rd_other_conf)
+               return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val);
+
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
 
@@ -606,6 +609,9 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
+       if (pp->ops->wr_other_conf)
+               return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val);
+
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
 
@@ -659,46 +665,30 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                        int size, u32 *val)
 {
        struct pcie_port *pp = bus->sysdata;
-       int ret;
 
        if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
 
-       if (bus->number != pp->root_bus_nr)
-               if (pp->ops->rd_other_conf)
-                       ret = pp->ops->rd_other_conf(pp, bus, devfn,
-                                               where, size, val);
-               else
-                       ret = dw_pcie_rd_other_conf(pp, bus, devfn,
-                                               where, size, val);
-       else
-               ret = dw_pcie_rd_own_conf(pp, where, size, val);
+       if (bus->number == pp->root_bus_nr)
+               return dw_pcie_rd_own_conf(pp, where, size, val);
 
-       return ret;
+       return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val);
 }
 
 static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
                        int where, int size, u32 val)
 {
        struct pcie_port *pp = bus->sysdata;
-       int ret;
 
        if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
-       if (bus->number != pp->root_bus_nr)
-               if (pp->ops->wr_other_conf)
-                       ret = pp->ops->wr_other_conf(pp, bus, devfn,
-                                               where, size, val);
-               else
-                       ret = dw_pcie_wr_other_conf(pp, bus, devfn,
-                                               where, size, val);
-       else
-               ret = dw_pcie_wr_own_conf(pp, where, size, val);
+       if (bus->number == pp->root_bus_nr)
+               return dw_pcie_wr_own_conf(pp, where, size, val);
 
-       return ret;
+       return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val);
 }
 
 static struct pci_ops dw_pcie_ops = {
index 35457ecd8e70f4d6f856573f45f8bbc2991d1f6d..4bad6954019b1e8d2a6bafd4f880c553eac5022c 100644 (file)
@@ -1,10 +1,11 @@
 /*
- * PCIe host controller driver for HiSilicon Hip05 SoC
+ * PCIe host controller driver for HiSilicon SoCs
  *
  * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
  *
- * Author: Zhou Wang <wangzhou1@hisilicon.com>
- *         Dacai Zhu <zhudacai@hisilicon.com>
+ * Authors: Zhou Wang <wangzhou1@hisilicon.com>
+ *          Dacai Zhu <zhudacai@hisilicon.com>
+ *          Gabriele Paoloni <gabriele.paoloni@huawei.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
 #include <linux/platform_device.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 
 #include "pcie-designware.h"
 
-#define PCIE_SUBCTRL_SYS_STATE4_REG                     0x6818
-#define PCIE_LTSSM_LINKUP_STATE                         0x11
-#define PCIE_LTSSM_STATE_MASK                           0x3F
+#define PCIE_LTSSM_LINKUP_STATE                                0x11
+#define PCIE_LTSSM_STATE_MASK                          0x3F
+#define PCIE_SUBCTRL_SYS_STATE4_REG                    0x6818
+#define PCIE_SYS_STATE4                                                0x31c
+#define PCIE_HIP06_CTRL_OFF                                    0x1000
 
 #define to_hisi_pcie(x)        container_of(x, struct hisi_pcie, pp)
 
+struct hisi_pcie;
+
+struct pcie_soc_ops {
+       int (*hisi_pcie_link_up)(struct hisi_pcie *pcie);
+};
+
 struct hisi_pcie {
        struct regmap *subctrl;
        void __iomem *reg_base;
        u32 port_id;
        struct pcie_port pp;
+       struct pcie_soc_ops *soc_ops;
 };
 
 static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
@@ -44,7 +55,7 @@ static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
        return readl(pcie->reg_base + reg);
 }
 
-/* Hip05 PCIe host only supports 32-bit config access */
+/* HipXX PCIe host only supports 32-bit config access */
 static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
                              u32 *val)
 {
@@ -67,7 +78,7 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
        return PCIBIOS_SUCCESSFUL;
 }
 
-/* Hip05 PCIe host only supports 32-bit config access */
+/* HipXX PCIe host only supports 32-bit config access */
 static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
                                u32 val)
 {
@@ -94,10 +105,9 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
        return PCIBIOS_SUCCESSFUL;
 }
 
-static int hisi_pcie_link_up(struct pcie_port *pp)
+static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
 {
        u32 val;
-       struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
 
        regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
                    0x100 * hisi_pcie->port_id, &val);
@@ -105,6 +115,23 @@ static int hisi_pcie_link_up(struct pcie_port *pp)
        return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
 }
 
+static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
+{
+       u32 val;
+
+       val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF +
+                       PCIE_SYS_STATE4);
+
+       return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
+}
+
+static int hisi_pcie_link_up(struct pcie_port *pp)
+{
+       struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
+
+       return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
+}
+
 static struct pcie_host_ops hisi_pcie_host_ops = {
        .rd_own_conf = hisi_pcie_cfg_read,
        .wr_own_conf = hisi_pcie_cfg_write,
@@ -143,7 +170,9 @@ static int __init hisi_pcie_probe(struct platform_device *pdev)
 {
        struct hisi_pcie *hisi_pcie;
        struct pcie_port *pp;
+       const struct of_device_id *match;
        struct resource *reg;
+       struct device_driver *driver;
        int ret;
 
        hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
@@ -152,6 +181,10 @@ static int __init hisi_pcie_probe(struct platform_device *pdev)
 
        pp = &hisi_pcie->pp;
        pp->dev = &pdev->dev;
+       driver = (pdev->dev).driver;
+
+       match = of_match_device(driver->of_match_table, &pdev->dev);
+       hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
 
        hisi_pcie->subctrl =
        syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
@@ -180,11 +213,27 @@ static int __init hisi_pcie_probe(struct platform_device *pdev)
        return 0;
 }
 
+static struct pcie_soc_ops hip05_ops = {
+               &hisi_pcie_link_up_hip05
+};
+
+static struct pcie_soc_ops hip06_ops = {
+               &hisi_pcie_link_up_hip06
+};
+
 static const struct of_device_id hisi_pcie_of_match[] = {
-       {.compatible = "hisilicon,hip05-pcie",},
+       {
+                       .compatible = "hisilicon,hip05-pcie",
+                       .data       = (void *) &hip05_ops,
+       },
+       {
+                       .compatible = "hisilicon,hip06-pcie",
+                       .data       = (void *) &hip06_ops,
+       },
        {},
 };
 
+
 MODULE_DEVICE_TABLE(of, hisi_pcie_of_match);
 
 static struct platform_driver hisi_pcie_driver = {
@@ -196,3 +245,8 @@ static struct platform_driver hisi_pcie_driver = {
 };
 
 module_platform_driver(hisi_pcie_driver);
+
+MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
+MODULE_AUTHOR("Dacai Zhu <zhudacai@hisilicon.com>");
+MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@huawei.com>");
+MODULE_LICENSE("GPL v2");
index 96a7d999fd5e1efad1963925e0c06dd0eb5075f3..0d7bee4a0d26da4221f7a16899384a7e4e44420a 100644 (file)
@@ -55,6 +55,7 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
        bcma_set_drvdata(bdev, pcie);
 
        pcie->base = bdev->io_addr;
+       pcie->base_addr = bdev->addr;
 
        res_mem.start = bdev->addr_s[0];
        res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c
new file mode 100644 (file)
index 0000000..9a2973b
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * 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
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+
+#include "pcie-iproc.h"
+
+#define IPROC_MSI_INTR_EN_SHIFT        11
+#define IPROC_MSI_INTR_EN              BIT(IPROC_MSI_INTR_EN_SHIFT)
+#define IPROC_MSI_INT_N_EVENT_SHIFT    1
+#define IPROC_MSI_INT_N_EVENT          BIT(IPROC_MSI_INT_N_EVENT_SHIFT)
+#define IPROC_MSI_EQ_EN_SHIFT          0
+#define IPROC_MSI_EQ_EN                BIT(IPROC_MSI_EQ_EN_SHIFT)
+
+#define IPROC_MSI_EQ_MASK              0x3f
+
+/* Max number of GIC interrupts */
+#define NR_HW_IRQS                     6
+
+/* Number of entries in each event queue */
+#define EQ_LEN                         64
+
+/* Size of each event queue memory region */
+#define EQ_MEM_REGION_SIZE             SZ_4K
+
+/* Size of each MSI address region */
+#define MSI_MEM_REGION_SIZE            SZ_4K
+
+enum iproc_msi_reg {
+       IPROC_MSI_EQ_PAGE = 0,
+       IPROC_MSI_EQ_PAGE_UPPER,
+       IPROC_MSI_PAGE,
+       IPROC_MSI_PAGE_UPPER,
+       IPROC_MSI_CTRL,
+       IPROC_MSI_EQ_HEAD,
+       IPROC_MSI_EQ_TAIL,
+       IPROC_MSI_INTS_EN,
+       IPROC_MSI_REG_SIZE,
+};
+
+struct iproc_msi;
+
+/**
+ * iProc MSI group
+ *
+ * One MSI group is allocated per GIC interrupt, serviced by one iProc MSI
+ * event queue.
+ *
+ * @msi: pointer to iProc MSI data
+ * @gic_irq: GIC interrupt
+ * @eq: Event queue number
+ */
+struct iproc_msi_grp {
+       struct iproc_msi *msi;
+       int gic_irq;
+       unsigned int eq;
+};
+
+/**
+ * iProc event queue based MSI
+ *
+ * Only meant to be used on platforms without MSI support integrated into the
+ * GIC.
+ *
+ * @pcie: pointer to iProc PCIe data
+ * @reg_offsets: MSI register offsets
+ * @grps: MSI groups
+ * @nr_irqs: number of total interrupts connected to GIC
+ * @nr_cpus: number of toal CPUs
+ * @has_inten_reg: indicates the MSI interrupt enable register needs to be
+ * set explicitly (required for some legacy platforms)
+ * @bitmap: MSI vector bitmap
+ * @bitmap_lock: lock to protect access to the MSI bitmap
+ * @nr_msi_vecs: total number of MSI vectors
+ * @inner_domain: inner IRQ domain
+ * @msi_domain: MSI IRQ domain
+ * @nr_eq_region: required number of 4K aligned memory region for MSI event
+ * queues
+ * @nr_msi_region: required number of 4K aligned address region for MSI posted
+ * writes
+ * @eq_cpu: pointer to allocated memory region for MSI event queues
+ * @eq_dma: DMA address of MSI event queues
+ * @msi_addr: MSI address
+ */
+struct iproc_msi {
+       struct iproc_pcie *pcie;
+       const u16 (*reg_offsets)[IPROC_MSI_REG_SIZE];
+       struct iproc_msi_grp *grps;
+       int nr_irqs;
+       int nr_cpus;
+       bool has_inten_reg;
+       unsigned long *bitmap;
+       struct mutex bitmap_lock;
+       unsigned int nr_msi_vecs;
+       struct irq_domain *inner_domain;
+       struct irq_domain *msi_domain;
+       unsigned int nr_eq_region;
+       unsigned int nr_msi_region;
+       void *eq_cpu;
+       dma_addr_t eq_dma;
+       phys_addr_t msi_addr;
+};
+
+static const u16 iproc_msi_reg_paxb[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = {
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x210, 0x250, 0x254, 0x208 },
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x214, 0x258, 0x25c, 0x208 },
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x218, 0x260, 0x264, 0x208 },
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x21c, 0x268, 0x26c, 0x208 },
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x220, 0x270, 0x274, 0x208 },
+       { 0x200, 0x2c0, 0x204, 0x2c4, 0x224, 0x278, 0x27c, 0x208 },
+};
+
+static const u16 iproc_msi_reg_paxc[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = {
+       { 0xc00, 0xc04, 0xc08, 0xc0c, 0xc40, 0xc50, 0xc60 },
+       { 0xc10, 0xc14, 0xc18, 0xc1c, 0xc44, 0xc54, 0xc64 },
+       { 0xc20, 0xc24, 0xc28, 0xc2c, 0xc48, 0xc58, 0xc68 },
+       { 0xc30, 0xc34, 0xc38, 0xc3c, 0xc4c, 0xc5c, 0xc6c },
+};
+
+static inline u32 iproc_msi_read_reg(struct iproc_msi *msi,
+                                    enum iproc_msi_reg reg,
+                                    unsigned int eq)
+{
+       struct iproc_pcie *pcie = msi->pcie;
+
+       return readl_relaxed(pcie->base + msi->reg_offsets[eq][reg]);
+}
+
+static inline void iproc_msi_write_reg(struct iproc_msi *msi,
+                                      enum iproc_msi_reg reg,
+                                      int eq, u32 val)
+{
+       struct iproc_pcie *pcie = msi->pcie;
+
+       writel_relaxed(val, pcie->base + msi->reg_offsets[eq][reg]);
+}
+
+static inline u32 hwirq_to_group(struct iproc_msi *msi, unsigned long hwirq)
+{
+       return (hwirq % msi->nr_irqs);
+}
+
+static inline unsigned int iproc_msi_addr_offset(struct iproc_msi *msi,
+                                                unsigned long hwirq)
+{
+       if (msi->nr_msi_region > 1)
+               return hwirq_to_group(msi, hwirq) * MSI_MEM_REGION_SIZE;
+       else
+               return hwirq_to_group(msi, hwirq) * sizeof(u32);
+}
+
+static inline unsigned int iproc_msi_eq_offset(struct iproc_msi *msi, u32 eq)
+{
+       if (msi->nr_eq_region > 1)
+               return eq * EQ_MEM_REGION_SIZE;
+       else
+               return eq * EQ_LEN * sizeof(u32);
+}
+
+static struct irq_chip iproc_msi_irq_chip = {
+       .name = "iProc-MSI",
+};
+
+static struct msi_domain_info iproc_msi_domain_info = {
+       .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+               MSI_FLAG_PCI_MSIX,
+       .chip = &iproc_msi_irq_chip,
+};
+
+/*
+ * In iProc PCIe core, each MSI group is serviced by a GIC interrupt and a
+ * dedicated event queue.  Each MSI group can support up to 64 MSI vectors.
+ *
+ * The number of MSI groups varies between different iProc SoCs.  The total
+ * number of CPU cores also varies.  To support MSI IRQ affinity, we
+ * distribute GIC interrupts across all available CPUs.  MSI vector is moved
+ * from one GIC interrupt to another to steer to the target CPU.
+ *
+ * Assuming:
+ * - the number of MSI groups is M
+ * - the number of CPU cores is N
+ * - M is always a multiple of N
+ *
+ * Total number of raw MSI vectors = M * 64
+ * Total number of supported MSI vectors = (M * 64) / N
+ */
+static inline int hwirq_to_cpu(struct iproc_msi *msi, unsigned long hwirq)
+{
+       return (hwirq % msi->nr_cpus);
+}
+
+static inline unsigned long hwirq_to_canonical_hwirq(struct iproc_msi *msi,
+                                                    unsigned long hwirq)
+{
+       return (hwirq - hwirq_to_cpu(msi, hwirq));
+}
+
+static int iproc_msi_irq_set_affinity(struct irq_data *data,
+                                     const struct cpumask *mask, bool force)
+{
+       struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       curr_cpu = hwirq_to_cpu(msi, data->hwirq);
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* steer MSI to the target CPU */
+       data->hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq) + target_cpu;
+
+       return IRQ_SET_MASK_OK;
+}
+
+static void iproc_msi_irq_compose_msi_msg(struct irq_data *data,
+                                         struct msi_msg *msg)
+{
+       struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
+       dma_addr_t addr;
+
+       addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq);
+       msg->address_lo = lower_32_bits(addr);
+       msg->address_hi = upper_32_bits(addr);
+       msg->data = data->hwirq;
+}
+
+static struct irq_chip iproc_msi_bottom_irq_chip = {
+       .name = "MSI",
+       .irq_set_affinity = iproc_msi_irq_set_affinity,
+       .irq_compose_msi_msg = iproc_msi_irq_compose_msi_msg,
+};
+
+static int iproc_msi_irq_domain_alloc(struct irq_domain *domain,
+                                     unsigned int virq, unsigned int nr_irqs,
+                                     void *args)
+{
+       struct iproc_msi *msi = domain->host_data;
+       int hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       /* Allocate 'nr_cpus' number of MSI vectors each time */
+       hwirq = bitmap_find_next_zero_area(msi->bitmap, msi->nr_msi_vecs, 0,
+                                          msi->nr_cpus, 0);
+       if (hwirq < msi->nr_msi_vecs) {
+               bitmap_set(msi->bitmap, hwirq, msi->nr_cpus);
+       } else {
+               mutex_unlock(&msi->bitmap_lock);
+               return -ENOSPC;
+       }
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_set_info(domain, virq, hwirq, &iproc_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq, NULL, NULL);
+
+       return 0;
+}
+
+static void iproc_msi_irq_domain_free(struct irq_domain *domain,
+                                     unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *data = irq_domain_get_irq_data(domain, virq);
+       struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
+       unsigned int hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq);
+       bitmap_clear(msi->bitmap, hwirq, msi->nr_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc = iproc_msi_irq_domain_alloc,
+       .free = iproc_msi_irq_domain_free,
+};
+
+static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head)
+{
+       u32 *msg, hwirq;
+       unsigned int offs;
+
+       offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32);
+       msg = (u32 *)(msi->eq_cpu + offs);
+       hwirq = *msg & IPROC_MSI_EQ_MASK;
+
+       /*
+        * Since we have multiple hwirq mapped to a single MSI vector,
+        * now we need to derive the hwirq at CPU0.  It can then be used to
+        * mapped back to virq.
+        */
+       return hwirq_to_canonical_hwirq(msi, hwirq);
+}
+
+static void iproc_msi_handler(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct iproc_msi_grp *grp;
+       struct iproc_msi *msi;
+       struct iproc_pcie *pcie;
+       u32 eq, head, tail, nr_events;
+       unsigned long hwirq;
+       int virq;
+
+       chained_irq_enter(chip, desc);
+
+       grp = irq_desc_get_handler_data(desc);
+       msi = grp->msi;
+       pcie = msi->pcie;
+       eq = grp->eq;
+
+       /*
+        * iProc MSI event queue is tracked by head and tail pointers.  Head
+        * pointer indicates the next entry (MSI data) to be consumed by SW in
+        * the queue and needs to be updated by SW.  iProc MSI core uses the
+        * tail pointer as the next data insertion point.
+        *
+        * Entries between head and tail pointers contain valid MSI data.  MSI
+        * data is guaranteed to be in the event queue memory before the tail
+        * pointer is updated by the iProc MSI core.
+        */
+       head = iproc_msi_read_reg(msi, IPROC_MSI_EQ_HEAD,
+                                 eq) & IPROC_MSI_EQ_MASK;
+       do {
+               tail = iproc_msi_read_reg(msi, IPROC_MSI_EQ_TAIL,
+                                         eq) & IPROC_MSI_EQ_MASK;
+
+               /*
+                * Figure out total number of events (MSI data) to be
+                * processed.
+                */
+               nr_events = (tail < head) ?
+                       (EQ_LEN - (head - tail)) : (tail - head);
+               if (!nr_events)
+                       break;
+
+               /* process all outstanding events */
+               while (nr_events--) {
+                       hwirq = decode_msi_hwirq(msi, eq, head);
+                       virq = irq_find_mapping(msi->inner_domain, hwirq);
+                       generic_handle_irq(virq);
+
+                       head++;
+                       head %= EQ_LEN;
+               }
+
+               /*
+                * Now all outstanding events have been processed.  Update the
+                * head pointer.
+                */
+               iproc_msi_write_reg(msi, IPROC_MSI_EQ_HEAD, eq, head);
+
+               /*
+                * Now go read the tail pointer again to see if there are new
+                * oustanding events that came in during the above window.
+                */
+       } while (true);
+
+       chained_irq_exit(chip, desc);
+}
+
+static void iproc_msi_enable(struct iproc_msi *msi)
+{
+       int i, eq;
+       u32 val;
+
+       /* Program memory region for each event queue */
+       for (i = 0; i < msi->nr_eq_region; i++) {
+               dma_addr_t addr = msi->eq_dma + (i * EQ_MEM_REGION_SIZE);
+
+               iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE, i,
+                                   lower_32_bits(addr));
+               iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE_UPPER, i,
+                                   upper_32_bits(addr));
+       }
+
+       /* Program address region for MSI posted writes */
+       for (i = 0; i < msi->nr_msi_region; i++) {
+               phys_addr_t addr = msi->msi_addr + (i * MSI_MEM_REGION_SIZE);
+
+               iproc_msi_write_reg(msi, IPROC_MSI_PAGE, i,
+                                   lower_32_bits(addr));
+               iproc_msi_write_reg(msi, IPROC_MSI_PAGE_UPPER, i,
+                                   upper_32_bits(addr));
+       }
+
+       for (eq = 0; eq < msi->nr_irqs; eq++) {
+               /* Enable MSI event queue */
+               val = IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT |
+                       IPROC_MSI_EQ_EN;
+               iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val);
+
+               /*
+                * Some legacy platforms require the MSI interrupt enable
+                * register to be set explicitly.
+                */
+               if (msi->has_inten_reg) {
+                       val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq);
+                       val |= BIT(eq);
+                       iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val);
+               }
+       }
+}
+
+static void iproc_msi_disable(struct iproc_msi *msi)
+{
+       u32 eq, val;
+
+       for (eq = 0; eq < msi->nr_irqs; eq++) {
+               if (msi->has_inten_reg) {
+                       val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq);
+                       val &= ~BIT(eq);
+                       iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val);
+               }
+
+               val = iproc_msi_read_reg(msi, IPROC_MSI_CTRL, eq);
+               val &= ~(IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT |
+                        IPROC_MSI_EQ_EN);
+               iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val);
+       }
+}
+
+static int iproc_msi_alloc_domains(struct device_node *node,
+                                  struct iproc_msi *msi)
+{
+       msi->inner_domain = irq_domain_add_linear(NULL, msi->nr_msi_vecs,
+                                                 &msi_domain_ops, msi);
+       if (!msi->inner_domain)
+               return -ENOMEM;
+
+       msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+                                                   &iproc_msi_domain_info,
+                                                   msi->inner_domain);
+       if (!msi->msi_domain) {
+               irq_domain_remove(msi->inner_domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void iproc_msi_free_domains(struct iproc_msi *msi)
+{
+       if (msi->msi_domain)
+               irq_domain_remove(msi->msi_domain);
+
+       if (msi->inner_domain)
+               irq_domain_remove(msi->inner_domain);
+}
+
+static void iproc_msi_irq_free(struct iproc_msi *msi, unsigned int cpu)
+{
+       int i;
+
+       for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) {
+               irq_set_chained_handler_and_data(msi->grps[i].gic_irq,
+                                                NULL, NULL);
+       }
+}
+
+static int iproc_msi_irq_setup(struct iproc_msi *msi, unsigned int cpu)
+{
+       int i, ret;
+       cpumask_var_t mask;
+       struct iproc_pcie *pcie = msi->pcie;
+
+       for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) {
+               irq_set_chained_handler_and_data(msi->grps[i].gic_irq,
+                                                iproc_msi_handler,
+                                                &msi->grps[i]);
+               /* Dedicate GIC interrupt to each CPU core */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       ret = irq_set_affinity(msi->grps[i].gic_irq, mask);
+                       if (ret)
+                               dev_err(pcie->dev,
+                                       "failed to set affinity for IRQ%d\n",
+                                       msi->grps[i].gic_irq);
+                       free_cpumask_var(mask);
+               } else {
+                       dev_err(pcie->dev, "failed to alloc CPU mask\n");
+                       ret = -EINVAL;
+               }
+
+               if (ret) {
+                       /* Free all configured/unconfigured IRQs */
+                       iproc_msi_irq_free(msi, cpu);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node)
+{
+       struct iproc_msi *msi;
+       int i, ret;
+       unsigned int cpu;
+
+       if (!of_device_is_compatible(node, "brcm,iproc-msi"))
+               return -ENODEV;
+
+       if (!of_find_property(node, "msi-controller", NULL))
+               return -ENODEV;
+
+       if (pcie->msi)
+               return -EBUSY;
+
+       msi = devm_kzalloc(pcie->dev, sizeof(*msi), GFP_KERNEL);
+       if (!msi)
+               return -ENOMEM;
+
+       msi->pcie = pcie;
+       pcie->msi = msi;
+       msi->msi_addr = pcie->base_addr;
+       mutex_init(&msi->bitmap_lock);
+       msi->nr_cpus = num_possible_cpus();
+
+       msi->nr_irqs = of_irq_count(node);
+       if (!msi->nr_irqs) {
+               dev_err(pcie->dev, "found no MSI GIC interrupt\n");
+               return -ENODEV;
+       }
+
+       if (msi->nr_irqs > NR_HW_IRQS) {
+               dev_warn(pcie->dev, "too many MSI GIC interrupts defined %d\n",
+                        msi->nr_irqs);
+               msi->nr_irqs = NR_HW_IRQS;
+       }
+
+       if (msi->nr_irqs < msi->nr_cpus) {
+               dev_err(pcie->dev,
+                       "not enough GIC interrupts for MSI affinity\n");
+               return -EINVAL;
+       }
+
+       if (msi->nr_irqs % msi->nr_cpus != 0) {
+               msi->nr_irqs -= msi->nr_irqs % msi->nr_cpus;
+               dev_warn(pcie->dev, "Reducing number of interrupts to %d\n",
+                        msi->nr_irqs);
+       }
+
+       switch (pcie->type) {
+       case IPROC_PCIE_PAXB:
+               msi->reg_offsets = iproc_msi_reg_paxb;
+               msi->nr_eq_region = 1;
+               msi->nr_msi_region = 1;
+               break;
+       case IPROC_PCIE_PAXC:
+               msi->reg_offsets = iproc_msi_reg_paxc;
+               msi->nr_eq_region = msi->nr_irqs;
+               msi->nr_msi_region = msi->nr_irqs;
+               break;
+       default:
+               dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
+               return -EINVAL;
+       }
+
+       if (of_find_property(node, "brcm,pcie-msi-inten", NULL))
+               msi->has_inten_reg = true;
+
+       msi->nr_msi_vecs = msi->nr_irqs * EQ_LEN;
+       msi->bitmap = devm_kcalloc(pcie->dev, BITS_TO_LONGS(msi->nr_msi_vecs),
+                                  sizeof(*msi->bitmap), GFP_KERNEL);
+       if (!msi->bitmap)
+               return -ENOMEM;
+
+       msi->grps = devm_kcalloc(pcie->dev, msi->nr_irqs, sizeof(*msi->grps),
+                                GFP_KERNEL);
+       if (!msi->grps)
+               return -ENOMEM;
+
+       for (i = 0; i < msi->nr_irqs; i++) {
+               unsigned int irq = irq_of_parse_and_map(node, i);
+
+               if (!irq) {
+                       dev_err(pcie->dev, "unable to parse/map interrupt\n");
+                       ret = -ENODEV;
+                       goto free_irqs;
+               }
+               msi->grps[i].gic_irq = irq;
+               msi->grps[i].msi = msi;
+               msi->grps[i].eq = i;
+       }
+
+       /* Reserve memory for event queue and make sure memories are zeroed */
+       msi->eq_cpu = dma_zalloc_coherent(pcie->dev,
+                                         msi->nr_eq_region * EQ_MEM_REGION_SIZE,
+                                         &msi->eq_dma, GFP_KERNEL);
+       if (!msi->eq_cpu) {
+               ret = -ENOMEM;
+               goto free_irqs;
+       }
+
+       ret = iproc_msi_alloc_domains(node, msi);
+       if (ret) {
+               dev_err(pcie->dev, "failed to create MSI domains\n");
+               goto free_eq_dma;
+       }
+
+       for_each_online_cpu(cpu) {
+               ret = iproc_msi_irq_setup(msi, cpu);
+               if (ret)
+                       goto free_msi_irq;
+       }
+
+       iproc_msi_enable(msi);
+
+       return 0;
+
+free_msi_irq:
+       for_each_online_cpu(cpu)
+               iproc_msi_irq_free(msi, cpu);
+       iproc_msi_free_domains(msi);
+
+free_eq_dma:
+       dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE,
+                         msi->eq_cpu, msi->eq_dma);
+
+free_irqs:
+       for (i = 0; i < msi->nr_irqs; i++) {
+               if (msi->grps[i].gic_irq)
+                       irq_dispose_mapping(msi->grps[i].gic_irq);
+       }
+       pcie->msi = NULL;
+       return ret;
+}
+EXPORT_SYMBOL(iproc_msi_init);
+
+void iproc_msi_exit(struct iproc_pcie *pcie)
+{
+       struct iproc_msi *msi = pcie->msi;
+       unsigned int i, cpu;
+
+       if (!msi)
+               return;
+
+       iproc_msi_disable(msi);
+
+       for_each_online_cpu(cpu)
+               iproc_msi_irq_free(msi, cpu);
+
+       iproc_msi_free_domains(msi);
+
+       dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE,
+                         msi->eq_cpu, msi->eq_dma);
+
+       for (i = 0; i < msi->nr_irqs; i++) {
+               if (msi->grps[i].gic_irq)
+                       irq_dispose_mapping(msi->grps[i].gic_irq);
+       }
+}
+EXPORT_SYMBOL(iproc_msi_exit);
index c9550dc8b8ed4b8fc330d1d21cefed43989db030..1738c5288eb6b06bc7806e725cdfd999a9609f8a 100644 (file)
 
 #include "pcie-iproc.h"
 
+static const struct of_device_id iproc_pcie_of_match_table[] = {
+       {
+               .compatible = "brcm,iproc-pcie",
+               .data = (int *)IPROC_PCIE_PAXB,
+       }, {
+               .compatible = "brcm,iproc-pcie-paxc",
+               .data = (int *)IPROC_PCIE_PAXC,
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
+
 static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id;
        struct iproc_pcie *pcie;
        struct device_node *np = pdev->dev.of_node;
        struct resource reg;
@@ -35,11 +48,16 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
        LIST_HEAD(res);
        int ret;
 
+       of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+
        pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
        pcie->dev = &pdev->dev;
+       pcie->type = (enum iproc_pcie_type)of_id->data;
        platform_set_drvdata(pdev, pcie);
 
        ret = of_address_to_resource(np, 0, &reg);
@@ -53,6 +71,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                dev_err(pcie->dev, "unable to map controller registers\n");
                return -ENOMEM;
        }
+       pcie->base_addr = reg.start;
 
        if (of_property_read_bool(np, "brcm,pcie-ob")) {
                u32 val;
@@ -114,12 +133,6 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
        return iproc_pcie_remove(pcie);
 }
 
-static const struct of_device_id iproc_pcie_of_match_table[] = {
-       { .compatible = "brcm,iproc-pcie", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
-
 static struct platform_driver iproc_pcie_pltfm_driver = {
        .driver = {
                .name = "iproc-pcie",
index eac719af16aa835fa61f8ee96cd5c87ee737d520..5816bceddb650c24576464913f9b7044d38be585 100644 (file)
 
 #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 RC_PCIE_RST_OUTPUT           BIT(RC_PCIE_RST_OUTPUT_SHIFT)
+#define PAXC_RESET_MASK              0x7f
 
-#define CFG_IND_ADDR_OFFSET          0x120
 #define CFG_IND_ADDR_MASK            0x00001ffc
 
-#define CFG_IND_DATA_OFFSET          0x124
-
-#define CFG_ADDR_OFFSET              0x1f8
 #define CFG_ADDR_BUS_NUM_SHIFT       20
 #define CFG_ADDR_BUS_NUM_MASK        0x0ff00000
 #define CFG_ADDR_DEV_NUM_SHIFT       15
 #define CFG_ADDR_CFG_TYPE_SHIFT      0
 #define CFG_ADDR_CFG_TYPE_MASK       0x00000003
 
-#define CFG_DATA_OFFSET              0x1fc
-
-#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 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
+#define MAX_NUM_PAXC_PF              4
+
+#define IPROC_PCIE_REG_INVALID 0xffff
+
+enum iproc_pcie_reg {
+       IPROC_PCIE_CLK_CTRL = 0,
+       IPROC_PCIE_CFG_IND_ADDR,
+       IPROC_PCIE_CFG_IND_DATA,
+       IPROC_PCIE_CFG_ADDR,
+       IPROC_PCIE_CFG_DATA,
+       IPROC_PCIE_INTX_EN,
+       IPROC_PCIE_OARR_LO,
+       IPROC_PCIE_OARR_HI,
+       IPROC_PCIE_OMAP_LO,
+       IPROC_PCIE_OMAP_HI,
+       IPROC_PCIE_LINK_STATUS,
+};
+
+/* iProc PCIe PAXB registers */
+static const u16 iproc_pcie_reg_paxb[] = {
+       [IPROC_PCIE_CLK_CTRL]     = 0x000,
+       [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
+       [IPROC_PCIE_CFG_IND_DATA] = 0x124,
+       [IPROC_PCIE_CFG_ADDR]     = 0x1f8,
+       [IPROC_PCIE_CFG_DATA]     = 0x1fc,
+       [IPROC_PCIE_INTX_EN]      = 0x330,
+       [IPROC_PCIE_OARR_LO]      = 0xd20,
+       [IPROC_PCIE_OARR_HI]      = 0xd24,
+       [IPROC_PCIE_OMAP_LO]      = 0xd40,
+       [IPROC_PCIE_OMAP_HI]      = 0xd44,
+       [IPROC_PCIE_LINK_STATUS]  = 0xf0c,
+};
+
+/* iProc PCIe PAXC v1 registers */
+static const u16 iproc_pcie_reg_paxc[] = {
+       [IPROC_PCIE_CLK_CTRL]     = 0x000,
+       [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
+       [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
+       [IPROC_PCIE_CFG_ADDR]     = 0x1f8,
+       [IPROC_PCIE_CFG_DATA]     = 0x1fc,
+       [IPROC_PCIE_INTX_EN]      = IPROC_PCIE_REG_INVALID,
+       [IPROC_PCIE_OARR_LO]      = IPROC_PCIE_REG_INVALID,
+       [IPROC_PCIE_OARR_HI]      = IPROC_PCIE_REG_INVALID,
+       [IPROC_PCIE_OMAP_LO]      = IPROC_PCIE_REG_INVALID,
+       [IPROC_PCIE_OMAP_HI]      = IPROC_PCIE_REG_INVALID,
+       [IPROC_PCIE_LINK_STATUS]  = IPROC_PCIE_REG_INVALID,
+};
 
 static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
 {
@@ -91,6 +125,65 @@ static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
        return pcie;
 }
 
+static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset)
+{
+       return !!(reg_offset == IPROC_PCIE_REG_INVALID);
+}
+
+static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie,
+                                       enum iproc_pcie_reg reg)
+{
+       return pcie->reg_offsets[reg];
+}
+
+static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie,
+                                     enum iproc_pcie_reg reg)
+{
+       u16 offset = iproc_pcie_reg_offset(pcie, reg);
+
+       if (iproc_pcie_reg_is_invalid(offset))
+               return 0;
+
+       return readl(pcie->base + offset);
+}
+
+static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
+                                       enum iproc_pcie_reg reg, u32 val)
+{
+       u16 offset = iproc_pcie_reg_offset(pcie, reg);
+
+       if (iproc_pcie_reg_is_invalid(offset))
+               return;
+
+       writel(val, pcie->base + offset);
+}
+
+static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie,
+                                      enum iproc_pcie_reg reg,
+                                      unsigned window, u32 val)
+{
+       u16 offset = iproc_pcie_reg_offset(pcie, reg);
+
+       if (iproc_pcie_reg_is_invalid(offset))
+               return;
+
+       writel(val, pcie->base + offset + (window * 8));
+}
+
+static inline bool iproc_pcie_device_is_valid(struct iproc_pcie *pcie,
+                                             unsigned int slot,
+                                             unsigned int fn)
+{
+       if (slot > 0)
+               return false;
+
+       /* PAXC can only support limited number of functions */
+       if (pcie->type == IPROC_PCIE_PAXC && fn >= MAX_NUM_PAXC_PF)
+               return false;
+
+       return true;
+}
+
 /**
  * Note access to the configuration registers are protected at the higher layer
  * by 'pci_lock' in drivers/pci/access.c
@@ -104,28 +197,34 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
        unsigned fn = PCI_FUNC(devfn);
        unsigned busno = bus->number;
        u32 val;
+       u16 offset;
+
+       if (!iproc_pcie_device_is_valid(pcie, slot, fn))
+               return NULL;
 
        /* root complex access */
        if (busno == 0) {
-               if (slot >= 1)
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR,
+                                    where & CFG_IND_ADDR_MASK);
+               offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA);
+               if (iproc_pcie_reg_is_invalid(offset))
                        return NULL;
-               writel(where & CFG_IND_ADDR_MASK,
-                      pcie->base + CFG_IND_ADDR_OFFSET);
-               return (pcie->base + CFG_IND_DATA_OFFSET);
+               else
+                       return (pcie->base + offset);
        }
 
-       if (fn > 1)
-               return NULL;
-
        /* EP device access */
        val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
                (slot << CFG_ADDR_DEV_NUM_SHIFT) |
                (fn << CFG_ADDR_FUNC_NUM_SHIFT) |
                (where & CFG_ADDR_REG_NUM_MASK) |
                (1 & CFG_ADDR_CFG_TYPE_MASK);
-       writel(val, pcie->base + CFG_ADDR_OFFSET);
-
-       return (pcie->base + CFG_DATA_OFFSET);
+       iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
+       offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
+       if (iproc_pcie_reg_is_invalid(offset))
+               return NULL;
+       else
+               return (pcie->base + offset);
 }
 
 static struct pci_ops iproc_pcie_ops = {
@@ -138,18 +237,29 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
 {
        u32 val;
 
+       if (pcie->type == IPROC_PCIE_PAXC) {
+               val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
+               val &= ~PAXC_RESET_MASK;
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+               udelay(100);
+               val |= PAXC_RESET_MASK;
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+               udelay(100);
+               return;
+       }
+
        /*
         * Select perst_b signal as reset source. Put the device into reset,
         * and then bring it out of reset
         */
-       val = readl(pcie->base + CLK_CONTROL_OFFSET);
+       val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
        val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
                ~RC_PCIE_RST_OUTPUT;
-       writel(val, pcie->base + CLK_CONTROL_OFFSET);
+       iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
        udelay(250);
 
        val |= RC_PCIE_RST_OUTPUT;
-       writel(val, pcie->base + CLK_CONTROL_OFFSET);
+       iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
        msleep(100);
 }
 
@@ -160,7 +270,14 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
        u16 pos, link_status;
        bool link_is_active = false;
 
-       val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
+       /*
+        * PAXC connects to emulated endpoint devices directly and does not
+        * have a Serdes.  Therefore skip the link detection logic here.
+        */
+       if (pcie->type == IPROC_PCIE_PAXC)
+               return 0;
+
+       val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
        if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
                dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
                return -ENODEV;
@@ -221,7 +338,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 
 static void iproc_pcie_enable(struct iproc_pcie *pcie)
 {
-       writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
+       iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
 }
 
 /**
@@ -245,7 +362,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 
        if (size > max_size) {
                dev_err(pcie->dev,
-                       "res size 0x%pap exceeds max supported size 0x%llx\n",
+                       "res size %pap exceeds max supported size 0x%llx\n",
                        &size, max_size);
                return -EINVAL;
        }
@@ -272,11 +389,15 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
        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));
+               iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_LO, i,
+                                   lower_32_bits(axi_addr) | OARR_VALID |
+                                   (ob->set_oarr_size ? 1 : 0));
+               iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_HI, i,
+                                   upper_32_bits(axi_addr));
+               iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_LO, i,
+                                   lower_32_bits(pci_addr));
+               iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_HI, i,
+                                   upper_32_bits(pci_addr));
 
                size -= ob->window_size;
                if (size == 0)
@@ -319,6 +440,26 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
        return 0;
 }
 
+static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       /*
+        * If another MSI controller is being used, the call below should fail
+        * but that is okay
+        */
+       return iproc_msi_init(pcie, msi_node);
+}
+
+static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
+{
+       iproc_msi_exit(pcie);
+}
+
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
        int ret;
@@ -340,6 +481,19 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
                goto err_exit_phy;
        }
 
+       switch (pcie->type) {
+       case IPROC_PCIE_PAXB:
+               pcie->reg_offsets = iproc_pcie_reg_paxb;
+               break;
+       case IPROC_PCIE_PAXC:
+               pcie->reg_offsets = iproc_pcie_reg_paxc;
+               break;
+       default:
+               dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
+               ret = -EINVAL;
+               goto err_power_off_phy;
+       }
+
        iproc_pcie_reset(pcie);
 
        if (pcie->need_ob_cfg) {
@@ -373,6 +527,10 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        iproc_pcie_enable(pcie);
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (iproc_pcie_msi_enable(pcie))
+                       dev_info(pcie->dev, "not using iProc MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
@@ -397,6 +555,8 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
        pci_stop_root_bus(pcie->root_bus);
        pci_remove_root_bus(pcie->root_bus);
 
+       iproc_pcie_msi_disable(pcie);
+
        phy_power_off(pcie->phy);
        phy_exit(pcie->phy);
 
index d3dc940f773a2279d13ad295bc9bd70a86638d23..e84d93c53c7b59ee5cf75a002f95860f1ee2fba9 100644 (file)
 #ifndef _PCIE_IPROC_H
 #define _PCIE_IPROC_H
 
+/**
+ * iProc PCIe interface type
+ *
+ * PAXB is the wrapper used in root complex that can be connected to an
+ * external endpoint device.
+ *
+ * PAXC is the wrapper used in root complex dedicated for internal emulated
+ * endpoint devices.
+ */
+enum iproc_pcie_type {
+       IPROC_PCIE_PAXB = 0,
+       IPROC_PCIE_PAXC,
+};
+
 /**
  * iProc PCIe outbound mapping
  * @set_oarr_size: indicates the OARR size bit needs to be set
@@ -27,21 +41,30 @@ struct iproc_pcie_ob {
        resource_size_t window_size;
 };
 
+struct iproc_msi;
+
 /**
  * iProc PCIe device
+ *
  * @dev: pointer to device data structure
+ * @type: iProc PCIe interface type
+ * @reg_offsets: register offsets
  * @base: PCIe host controller I/O register base
+ * @base_addr: PCIe host controller register base physical address
  * @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
+ * @need_ob_cfg: indicates SW needs to configure the outbound mapping window
  * @ob: outbound mapping parameters
+ * @msi: MSI data
  */
 struct iproc_pcie {
        struct device *dev;
+       enum iproc_pcie_type type;
+       const u16 *reg_offsets;
        void __iomem *base;
+       phys_addr_t base_addr;
 #ifdef CONFIG_ARM
        struct pci_sys_data sysdata;
 #endif
@@ -50,9 +73,24 @@ struct iproc_pcie {
        int (*map_irq)(const struct pci_dev *, u8, u8);
        bool need_ob_cfg;
        struct iproc_pcie_ob ob;
+       struct iproc_msi *msi;
 };
 
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
 int iproc_pcie_remove(struct iproc_pcie *pcie);
 
+#ifdef CONFIG_PCIE_IPROC_MSI
+int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);
+void iproc_msi_exit(struct iproc_pcie *pcie);
+#else
+static inline int iproc_msi_init(struct iproc_pcie *pcie,
+                                struct device_node *node)
+{
+       return -ENODEV;
+}
+static inline void iproc_msi_exit(struct iproc_pcie *pcie)
+{
+}
+#endif
+
 #endif /* _PCIE_IPROC_H */
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c
new file mode 100644 (file)
index 0000000..e845fba
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright 2015 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define PCIE20_PARF_PHY_CTRL                   0x40
+#define PCIE20_PARF_PHY_REFCLK                 0x4C
+#define PCIE20_PARF_DBI_BASE_ADDR              0x168
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE                0x16c
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT      0x178
+
+#define PCIE20_ELBI_SYS_CTRL                   0x04
+#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE         BIT(0)
+
+#define PCIE20_CAP                             0x70
+
+#define PERST_DELAY_US                         1000
+
+struct qcom_pcie_resources_v0 {
+       struct clk *iface_clk;
+       struct clk *core_clk;
+       struct clk *phy_clk;
+       struct reset_control *pci_reset;
+       struct reset_control *axi_reset;
+       struct reset_control *ahb_reset;
+       struct reset_control *por_reset;
+       struct reset_control *phy_reset;
+       struct regulator *vdda;
+       struct regulator *vdda_phy;
+       struct regulator *vdda_refclk;
+};
+
+struct qcom_pcie_resources_v1 {
+       struct clk *iface;
+       struct clk *aux;
+       struct clk *master_bus;
+       struct clk *slave_bus;
+       struct reset_control *core;
+       struct regulator *vdda;
+};
+
+union qcom_pcie_resources {
+       struct qcom_pcie_resources_v0 v0;
+       struct qcom_pcie_resources_v1 v1;
+};
+
+struct qcom_pcie;
+
+struct qcom_pcie_ops {
+       int (*get_resources)(struct qcom_pcie *pcie);
+       int (*init)(struct qcom_pcie *pcie);
+       void (*deinit)(struct qcom_pcie *pcie);
+};
+
+struct qcom_pcie {
+       struct pcie_port pp;
+       struct device *dev;
+       union qcom_pcie_resources res;
+       void __iomem *parf;
+       void __iomem *dbi;
+       void __iomem *elbi;
+       struct phy *phy;
+       struct gpio_desc *reset;
+       struct qcom_pcie_ops *ops;
+};
+
+#define to_qcom_pcie(x)                container_of(x, struct qcom_pcie, pp)
+
+static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
+{
+       gpiod_set_value(pcie->reset, 1);
+       usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
+}
+
+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
+{
+       gpiod_set_value(pcie->reset, 0);
+       usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
+}
+
+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
+{
+       struct pcie_port *pp = arg;
+
+       return dw_handle_msi_irq(pp);
+}
+
+static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+{
+       struct device *dev = pcie->dev;
+       unsigned int retries = 0;
+       u32 val;
+
+       if (dw_pcie_link_up(&pcie->pp))
+               return 0;
+
+       /* enable link training */
+       val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+       val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
+       writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+
+       do {
+               if (dw_pcie_link_up(&pcie->pp))
+                       return 0;
+               usleep_range(250, 1000);
+       } while (retries < 200);
+
+       dev_warn(dev, "phy link never came up\n");
+
+       return -ETIMEDOUT;
+}
+
+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
+       struct device *dev = pcie->dev;
+
+       res->vdda = devm_regulator_get(dev, "vdda");
+       if (IS_ERR(res->vdda))
+               return PTR_ERR(res->vdda);
+
+       res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
+       if (IS_ERR(res->vdda_phy))
+               return PTR_ERR(res->vdda_phy);
+
+       res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
+       if (IS_ERR(res->vdda_refclk))
+               return PTR_ERR(res->vdda_refclk);
+
+       res->iface_clk = devm_clk_get(dev, "iface");
+       if (IS_ERR(res->iface_clk))
+               return PTR_ERR(res->iface_clk);
+
+       res->core_clk = devm_clk_get(dev, "core");
+       if (IS_ERR(res->core_clk))
+               return PTR_ERR(res->core_clk);
+
+       res->phy_clk = devm_clk_get(dev, "phy");
+       if (IS_ERR(res->phy_clk))
+               return PTR_ERR(res->phy_clk);
+
+       res->pci_reset = devm_reset_control_get(dev, "pci");
+       if (IS_ERR(res->pci_reset))
+               return PTR_ERR(res->pci_reset);
+
+       res->axi_reset = devm_reset_control_get(dev, "axi");
+       if (IS_ERR(res->axi_reset))
+               return PTR_ERR(res->axi_reset);
+
+       res->ahb_reset = devm_reset_control_get(dev, "ahb");
+       if (IS_ERR(res->ahb_reset))
+               return PTR_ERR(res->ahb_reset);
+
+       res->por_reset = devm_reset_control_get(dev, "por");
+       if (IS_ERR(res->por_reset))
+               return PTR_ERR(res->por_reset);
+
+       res->phy_reset = devm_reset_control_get(dev, "phy");
+       if (IS_ERR(res->phy_reset))
+               return PTR_ERR(res->phy_reset);
+
+       return 0;
+}
+
+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
+       struct device *dev = pcie->dev;
+
+       res->vdda = devm_regulator_get(dev, "vdda");
+       if (IS_ERR(res->vdda))
+               return PTR_ERR(res->vdda);
+
+       res->iface = devm_clk_get(dev, "iface");
+       if (IS_ERR(res->iface))
+               return PTR_ERR(res->iface);
+
+       res->aux = devm_clk_get(dev, "aux");
+       if (IS_ERR(res->aux))
+               return PTR_ERR(res->aux);
+
+       res->master_bus = devm_clk_get(dev, "master_bus");
+       if (IS_ERR(res->master_bus))
+               return PTR_ERR(res->master_bus);
+
+       res->slave_bus = devm_clk_get(dev, "slave_bus");
+       if (IS_ERR(res->slave_bus))
+               return PTR_ERR(res->slave_bus);
+
+       res->core = devm_reset_control_get(dev, "core");
+       if (IS_ERR(res->core))
+               return PTR_ERR(res->core);
+
+       return 0;
+}
+
+static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
+
+       reset_control_assert(res->pci_reset);
+       reset_control_assert(res->axi_reset);
+       reset_control_assert(res->ahb_reset);
+       reset_control_assert(res->por_reset);
+       reset_control_assert(res->pci_reset);
+       clk_disable_unprepare(res->iface_clk);
+       clk_disable_unprepare(res->core_clk);
+       clk_disable_unprepare(res->phy_clk);
+       regulator_disable(res->vdda);
+       regulator_disable(res->vdda_phy);
+       regulator_disable(res->vdda_refclk);
+}
+
+static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
+       struct device *dev = pcie->dev;
+       u32 val;
+       int ret;
+
+       ret = regulator_enable(res->vdda);
+       if (ret) {
+               dev_err(dev, "cannot enable vdda regulator\n");
+               return ret;
+       }
+
+       ret = regulator_enable(res->vdda_refclk);
+       if (ret) {
+               dev_err(dev, "cannot enable vdda_refclk regulator\n");
+               goto err_refclk;
+       }
+
+       ret = regulator_enable(res->vdda_phy);
+       if (ret) {
+               dev_err(dev, "cannot enable vdda_phy regulator\n");
+               goto err_vdda_phy;
+       }
+
+       ret = reset_control_assert(res->ahb_reset);
+       if (ret) {
+               dev_err(dev, "cannot assert ahb reset\n");
+               goto err_assert_ahb;
+       }
+
+       ret = clk_prepare_enable(res->iface_clk);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable iface clock\n");
+               goto err_assert_ahb;
+       }
+
+       ret = clk_prepare_enable(res->phy_clk);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable phy clock\n");
+               goto err_clk_phy;
+       }
+
+       ret = clk_prepare_enable(res->core_clk);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable core clock\n");
+               goto err_clk_core;
+       }
+
+       ret = reset_control_deassert(res->ahb_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert ahb reset\n");
+               goto err_deassert_ahb;
+       }
+
+       /* enable PCIe clocks and resets */
+       val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+       val &= ~BIT(0);
+       writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+       /* enable external reference clock */
+       val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
+       val |= BIT(16);
+       writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
+
+       ret = reset_control_deassert(res->phy_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert phy reset\n");
+               return ret;
+       }
+
+       ret = reset_control_deassert(res->pci_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert pci reset\n");
+               return ret;
+       }
+
+       ret = reset_control_deassert(res->por_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert por reset\n");
+               return ret;
+       }
+
+       ret = reset_control_deassert(res->axi_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert axi reset\n");
+               return ret;
+       }
+
+       /* wait for clock acquisition */
+       usleep_range(1000, 1500);
+
+       return 0;
+
+err_deassert_ahb:
+       clk_disable_unprepare(res->core_clk);
+err_clk_core:
+       clk_disable_unprepare(res->phy_clk);
+err_clk_phy:
+       clk_disable_unprepare(res->iface_clk);
+err_assert_ahb:
+       regulator_disable(res->vdda_phy);
+err_vdda_phy:
+       regulator_disable(res->vdda_refclk);
+err_refclk:
+       regulator_disable(res->vdda);
+
+       return ret;
+}
+
+static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
+
+       reset_control_assert(res->core);
+       clk_disable_unprepare(res->slave_bus);
+       clk_disable_unprepare(res->master_bus);
+       clk_disable_unprepare(res->iface);
+       clk_disable_unprepare(res->aux);
+       regulator_disable(res->vdda);
+}
+
+static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
+{
+       struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
+       struct device *dev = pcie->dev;
+       int ret;
+
+       ret = reset_control_deassert(res->core);
+       if (ret) {
+               dev_err(dev, "cannot deassert core reset\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(res->aux);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable aux clock\n");
+               goto err_res;
+       }
+
+       ret = clk_prepare_enable(res->iface);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable iface clock\n");
+               goto err_aux;
+       }
+
+       ret = clk_prepare_enable(res->master_bus);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable master_bus clock\n");
+               goto err_iface;
+       }
+
+       ret = clk_prepare_enable(res->slave_bus);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable slave_bus clock\n");
+               goto err_master;
+       }
+
+       ret = regulator_enable(res->vdda);
+       if (ret) {
+               dev_err(dev, "cannot enable vdda regulator\n");
+               goto err_slave;
+       }
+
+       /* change DBI base address */
+       writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               u32 val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+
+               val |= BIT(31);
+               writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+       }
+
+       return 0;
+err_slave:
+       clk_disable_unprepare(res->slave_bus);
+err_master:
+       clk_disable_unprepare(res->master_bus);
+err_iface:
+       clk_disable_unprepare(res->iface);
+err_aux:
+       clk_disable_unprepare(res->aux);
+err_res:
+       reset_control_assert(res->core);
+
+       return ret;
+}
+
+static int qcom_pcie_link_up(struct pcie_port *pp)
+{
+       struct qcom_pcie *pcie = to_qcom_pcie(pp);
+       u16 val = readw(pcie->dbi + PCIE20_CAP + PCI_EXP_LNKSTA);
+
+       return !!(val & PCI_EXP_LNKSTA_DLLLA);
+}
+
+static void qcom_pcie_host_init(struct pcie_port *pp)
+{
+       struct qcom_pcie *pcie = to_qcom_pcie(pp);
+       int ret;
+
+       qcom_ep_reset_assert(pcie);
+
+       ret = pcie->ops->init(pcie);
+       if (ret)
+               goto err_deinit;
+
+       ret = phy_power_on(pcie->phy);
+       if (ret)
+               goto err_deinit;
+
+       dw_pcie_setup_rc(pp);
+
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               dw_pcie_msi_init(pp);
+
+       qcom_ep_reset_deassert(pcie);
+
+       ret = qcom_pcie_establish_link(pcie);
+       if (ret)
+               goto err;
+
+       return;
+err:
+       qcom_ep_reset_assert(pcie);
+       phy_power_off(pcie->phy);
+err_deinit:
+       pcie->ops->deinit(pcie);
+}
+
+static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+                                u32 *val)
+{
+       /* the device class is not reported correctly from the register */
+       if (where == PCI_CLASS_REVISION && size == 4) {
+               *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
+               *val &= 0xff;   /* keep revision id */
+               *val |= PCI_CLASS_BRIDGE_PCI << 16;
+               return PCIBIOS_SUCCESSFUL;
+       }
+
+       return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
+}
+
+static struct pcie_host_ops qcom_pcie_dw_ops = {
+       .link_up = qcom_pcie_link_up,
+       .host_init = qcom_pcie_host_init,
+       .rd_own_conf = qcom_pcie_rd_own_conf,
+};
+
+static const struct qcom_pcie_ops ops_v0 = {
+       .get_resources = qcom_pcie_get_resources_v0,
+       .init = qcom_pcie_init_v0,
+       .deinit = qcom_pcie_deinit_v0,
+};
+
+static const struct qcom_pcie_ops ops_v1 = {
+       .get_resources = qcom_pcie_get_resources_v1,
+       .init = qcom_pcie_init_v1,
+       .deinit = qcom_pcie_deinit_v1,
+};
+
+static int qcom_pcie_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct qcom_pcie *pcie;
+       struct pcie_port *pp;
+       int ret;
+
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
+       if (!pcie)
+               return -ENOMEM;
+
+       pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
+       pcie->dev = dev;
+
+       pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
+       if (IS_ERR(pcie->reset))
+               return PTR_ERR(pcie->reset);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
+       pcie->parf = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pcie->parf))
+               return PTR_ERR(pcie->parf);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+       pcie->dbi = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pcie->dbi))
+               return PTR_ERR(pcie->dbi);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+       pcie->elbi = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pcie->elbi))
+               return PTR_ERR(pcie->elbi);
+
+       pcie->phy = devm_phy_optional_get(dev, "pciephy");
+       if (IS_ERR(pcie->phy))
+               return PTR_ERR(pcie->phy);
+
+       ret = pcie->ops->get_resources(pcie);
+       if (ret)
+               return ret;
+
+       pp = &pcie->pp;
+       pp->dev = dev;
+       pp->dbi_base = pcie->dbi;
+       pp->root_bus_nr = -1;
+       pp->ops = &qcom_pcie_dw_ops;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               pp->msi_irq = platform_get_irq_byname(pdev, "msi");
+               if (pp->msi_irq < 0)
+                       return pp->msi_irq;
+
+               ret = devm_request_irq(dev, pp->msi_irq,
+                                      qcom_pcie_msi_irq_handler,
+                                      IRQF_SHARED, "qcom-pcie-msi", pp);
+               if (ret) {
+                       dev_err(dev, "cannot request msi irq\n");
+                       return ret;
+               }
+       }
+
+       ret = phy_init(pcie->phy);
+       if (ret)
+               return ret;
+
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "cannot initialize host\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, pcie);
+
+       return 0;
+}
+
+static int qcom_pcie_remove(struct platform_device *pdev)
+{
+       struct qcom_pcie *pcie = platform_get_drvdata(pdev);
+
+       qcom_ep_reset_assert(pcie);
+       phy_power_off(pcie->phy);
+       phy_exit(pcie->phy);
+       pcie->ops->deinit(pcie);
+
+       return 0;
+}
+
+static const struct of_device_id qcom_pcie_match[] = {
+       { .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
+       { .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
+       { .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_pcie_match);
+
+static struct platform_driver qcom_pcie_driver = {
+       .probe = qcom_pcie_probe,
+       .remove = qcom_pcie_remove,
+       .driver = {
+               .name = "qcom-pcie",
+               .of_match_table = qcom_pcie_match,
+       },
+};
+
+module_platform_driver(qcom_pcie_driver);
+
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
+MODULE_LICENSE("GPL v2");
index f4fa6c537448cae9d25519a8697e212aa70facaa..4edb5181f4e2ca64dc24d8c2cc1ebec46b2d7384 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define DRV_NAME "rcar-pcie"
 #define H1_PCIEPHYDOUTR                0x040014
 #define H1_PCIEPHYSR           0x040018
 
+/* R-Car Gen2 PHY */
+#define GEN2_PCIEPHYADDR       0x780
+#define GEN2_PCIEPHYDATA       0x784
+#define GEN2_PCIEPHYCTRL       0x78c
+
 #define INT_PCI_MSI_NR 32
 
 #define RCONF(x)       (PCICONF(0)+(x))
 #define RCAR_PCI_MAX_RESOURCES 4
 #define MAX_NR_INBOUND_MAPS 6
 
-static unsigned long global_io_offset;
-
 struct rcar_msi {
        DECLARE_BITMAP(used, INT_PCI_MSI_NR);
        struct irq_domain *domain;
@@ -126,20 +130,10 @@ static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip)
 }
 
 /* Structure representing the PCIe interface */
-/*
- * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
- * sysdata.  Add pci_sys_data as the first element in struct gen_pci so
- * that when we use a gen_pci pointer as sysdata, it is also a pointer to
- * a struct pci_sys_data.
- */
 struct rcar_pcie {
-#ifdef CONFIG_ARM
-       struct pci_sys_data     sys;
-#endif
        struct device           *dev;
        void __iomem            *base;
-       struct resource         res[RCAR_PCI_MAX_RESOURCES];
-       struct resource         busn;
+       struct list_head        resources;
        int                     root_bus_nr;
        struct clk              *clk;
        struct clk              *bus_clk;
@@ -323,10 +317,9 @@ static struct pci_ops rcar_pcie_ops = {
        .write  = rcar_pcie_write_conf,
 };
 
-static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
+static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie,
+                                  struct resource *res)
 {
-       struct resource *res = &pcie->res[win];
-
        /* Setup PCIe address space mappings for each resource */
        resource_size_t size;
        resource_size_t res_start;
@@ -359,31 +352,33 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
        rcar_pci_write_reg(pcie, mask, PCIEPTCTLR(win));
 }
 
-static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pcie)
+static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci)
 {
-       struct resource *res;
-       int i;
-
-       pcie->root_bus_nr = pcie->busn.start;
+       struct resource_entry *win;
+       int i = 0;
 
        /* Setup PCI resources */
-       for (i = 0; i < RCAR_PCI_MAX_RESOURCES; i++) {
+       resource_list_for_each_entry(win, &pci->resources) {
+               struct resource *res = win->res;
 
-               res = &pcie->res[i];
                if (!res->flags)
                        continue;
 
-               rcar_pcie_setup_window(i, pcie);
-
-               if (res->flags & IORESOURCE_IO) {
-                       phys_addr_t io_start = pci_pio_to_address(res->start);
-                       pci_ioremap_io(global_io_offset, io_start);
-                       global_io_offset += SZ_64K;
+               switch (resource_type(res)) {
+               case IORESOURCE_IO:
+               case IORESOURCE_MEM:
+                       rcar_pcie_setup_window(i, pci, res);
+                       i++;
+                       break;
+               case IORESOURCE_BUS:
+                       pci->root_bus_nr = res->start;
+                       break;
+               default:
+                       continue;
                }
 
                pci_add_resource(resource, res);
        }
-       pci_add_resource(resource, &pcie->busn);
 
        return 1;
 }
@@ -578,6 +573,26 @@ static int rcar_pcie_hw_init_h1(struct rcar_pcie *pcie)
        return -ETIMEDOUT;
 }
 
+static int rcar_pcie_hw_init_gen2(struct rcar_pcie *pcie)
+{
+       /*
+        * These settings come from the R-Car Series, 2nd Generation User's
+        * Manual, section 50.3.1 (2) Initialization of the physical layer.
+        */
+       rcar_pci_write_reg(pcie, 0x000f0030, GEN2_PCIEPHYADDR);
+       rcar_pci_write_reg(pcie, 0x00381203, GEN2_PCIEPHYDATA);
+       rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL);
+       rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL);
+
+       rcar_pci_write_reg(pcie, 0x000f0054, GEN2_PCIEPHYADDR);
+       /* The following value is for DC connection, no termination resistor */
+       rcar_pci_write_reg(pcie, 0x13802007, GEN2_PCIEPHYDATA);
+       rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL);
+       rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL);
+
+       return rcar_pcie_hw_init(pcie);
+}
+
 static int rcar_msi_alloc(struct rcar_msi *chip)
 {
        int msi;
@@ -720,14 +735,16 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
 
        /* Two irqs are for MSI, but they are also used for non-MSI irqs */
        err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
-                              IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
+                              IRQF_SHARED | IRQF_NO_THREAD,
+                              rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
        err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq,
-                              IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
+                              IRQF_SHARED | IRQF_NO_THREAD,
+                              rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
                goto err;
@@ -917,20 +934,71 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
 
 static const struct of_device_id rcar_pcie_of_match[] = {
        { .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
-       { .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init },
-       { .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init },
+       { .compatible = "renesas,pcie-rcar-gen2", .data = rcar_pcie_hw_init_gen2 },
+       { .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init_gen2 },
+       { .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init_gen2 },
+       { .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
        {},
 };
 MODULE_DEVICE_TABLE(of, rcar_pcie_of_match);
 
+static void rcar_pcie_release_of_pci_ranges(struct rcar_pcie *pci)
+{
+       pci_free_resource_list(&pci->resources);
+}
+
+static int rcar_pcie_parse_request_of_pci_ranges(struct rcar_pcie *pci)
+{
+       int err;
+       struct device *dev = pci->dev;
+       struct device_node *np = dev->of_node;
+       resource_size_t iobase;
+       struct resource_entry *win;
+
+       err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase);
+       if (err)
+               return err;
+
+       resource_list_for_each_entry(win, &pci->resources) {
+               struct resource *parent, *res = win->res;
+
+               switch (resource_type(res)) {
+               case IORESOURCE_IO:
+                       parent = &ioport_resource;
+                       err = pci_remap_iospace(res, iobase);
+                       if (err) {
+                               dev_warn(dev, "error %d: failed to map resource %pR\n",
+                                        err, res);
+                               continue;
+                       }
+                       break;
+               case IORESOURCE_MEM:
+                       parent = &iomem_resource;
+                       break;
+
+               case IORESOURCE_BUS:
+               default:
+                       continue;
+               }
+
+               err = devm_request_resource(dev, parent, res);
+               if (err)
+                       goto out_release_res;
+       }
+
+       return 0;
+
+out_release_res:
+       rcar_pcie_release_of_pci_ranges(pci);
+       return err;
+}
+
 static int rcar_pcie_probe(struct platform_device *pdev)
 {
        struct rcar_pcie *pcie;
        unsigned int data;
-       struct of_pci_range range;
-       struct of_pci_range_parser parser;
        const struct of_device_id *of_id;
-       int err, win = 0;
+       int err;
        int (*hw_init_fn)(struct rcar_pcie *);
 
        pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
@@ -940,16 +1008,9 @@ static int rcar_pcie_probe(struct platform_device *pdev)
        pcie->dev = &pdev->dev;
        platform_set_drvdata(pdev, pcie);
 
-       /* Get the bus range */
-       if (of_pci_parse_bus_range(pdev->dev.of_node, &pcie->busn)) {
-               dev_err(&pdev->dev, "failed to parse bus-range property\n");
-               return -EINVAL;
-       }
+       INIT_LIST_HEAD(&pcie->resources);
 
-       if (of_pci_range_parser_init(&parser, pdev->dev.of_node)) {
-               dev_err(&pdev->dev, "missing ranges property\n");
-               return -EINVAL;
-       }
+       rcar_pcie_parse_request_of_pci_ranges(pcie);
 
        err = rcar_pcie_get_resources(pdev, pcie);
        if (err < 0) {
@@ -957,46 +1018,55 @@ static int rcar_pcie_probe(struct platform_device *pdev)
                return err;
        }
 
-       for_each_of_pci_range(&parser, &range) {
-               err = of_pci_range_to_resource(&range, pdev->dev.of_node,
-                                               &pcie->res[win++]);
-               if (err < 0)
-                       return err;
-
-               if (win > RCAR_PCI_MAX_RESOURCES)
-                       break;
-       }
-
         err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
         if (err)
                return err;
 
-       if (IS_ENABLED(CONFIG_PCI_MSI)) {
-               err = rcar_pcie_enable_msi(pcie);
-               if (err < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to enable MSI support: %d\n",
-                               err);
-                       return err;
-               }
-       }
-
        of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
        if (!of_id || !of_id->data)
                return -EINVAL;
        hw_init_fn = of_id->data;
 
+       pm_runtime_enable(pcie->dev);
+       err = pm_runtime_get_sync(pcie->dev);
+       if (err < 0) {
+               dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
+               goto err_pm_disable;
+       }
+
        /* Failure to get a link might just be that no cards are inserted */
        err = hw_init_fn(pcie);
        if (err) {
                dev_info(&pdev->dev, "PCIe link down\n");
-               return 0;
+               err = 0;
+               goto err_pm_put;
        }
 
        data = rcar_pci_read_reg(pcie, MACSR);
        dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
 
-       return rcar_pcie_enable(pcie);
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               err = rcar_pcie_enable_msi(pcie);
+               if (err < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to enable MSI support: %d\n",
+                               err);
+                       goto err_pm_put;
+               }
+       }
+
+       err = rcar_pcie_enable(pcie);
+       if (err)
+               goto err_pm_put;
+
+       return 0;
+
+err_pm_put:
+       pm_runtime_put(pcie->dev);
+
+err_pm_disable:
+       pm_runtime_disable(pcie->dev);
+       return err;
 }
 
 static struct platform_driver rcar_pcie_driver = {
index b95b7563c052135bbad5407073685d9ff8930df6..a6cd8233e8c08c8fee6e1df3a1a7d40fdc2250a8 100644 (file)
@@ -279,7 +279,8 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
                return -ENODEV;
        }
        ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler,
-                              IRQF_SHARED, "spear1340-pcie", pp);
+                              IRQF_SHARED | IRQF_NO_THREAD,
+                              "spear1340-pcie", pp);
        if (ret) {
                dev_err(dev, "failed to request irq %d\n", pp->irq);
                return ret;
index 3c7a0d580b1ed990d9978efc5927c3a8d2abc8af..4cfa46360d1229976533f3a0c42152c30a282099 100644 (file)
@@ -781,7 +781,8 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
 
        port->irq = irq_of_parse_and_map(node, 0);
        err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler,
-                              IRQF_SHARED, "xilinx-pcie", port);
+                              IRQF_SHARED | IRQF_NO_THREAD,
+                              "xilinx-pcie", port);
        if (err) {
                dev_err(dev, "unable to request irq %d\n", port->irq);
                return err;
index 6ca23998ee8f36b1d486dcaa4c50c77ae74c8979..9d16c9dbd76e9d6f94f389ef7dbe38a1bd9c6009 100644 (file)
@@ -154,7 +154,8 @@ static union apci_descriptor *ibm_slot_from_id(int id)
 ibm_slot_done:
        if (ret) {
                ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
-               memcpy(ret, des, sizeof(union apci_descriptor));
+               if (ret)
+                       memcpy(ret, des, sizeof(union apci_descriptor));
        }
        kfree(table);
        return ret;
@@ -175,8 +176,13 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
        acpi_status stat;
        unsigned long long rc;
        union apci_descriptor *ibm_slot;
+       int id = hpslot_to_sun(slot);
 
-       ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+       ibm_slot = ibm_slot_from_id(id);
+       if (!ibm_slot) {
+               pr_err("APLS null ACPI descriptor for slot %d\n", id);
+               return -ENODEV;
+       }
 
        pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
                        ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
@@ -215,8 +221,13 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
 static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
 {
        union apci_descriptor *ibm_slot;
+       int id = hpslot_to_sun(slot);
 
-       ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+       ibm_slot = ibm_slot_from_id(id);
+       if (!ibm_slot) {
+               pr_err("APLS null ACPI descriptor for slot %d\n", id);
+               return -ENODEV;
+       }
 
        if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
                *status = 1;
index 15302475f5b71dd1144e9fe598ead5555b4fe075..49adf2278c14d5c2ad47649c503e302893238fd8 100644 (file)
@@ -116,11 +116,9 @@ static inline int slot_update(struct slot **sl)
 static int __init get_max_slots (void)
 {
        struct slot *slot_cur;
-       struct list_head *tmp;
        u8 slot_count = 0;
 
-       list_for_each(tmp, &ibmphp_slot_head) {
-               slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+       list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
                /* sometimes the hot-pluggable slots start with 4 (not always from 1) */
                slot_count = max(slot_count, slot_cur->number);
        }
@@ -501,16 +499,10 @@ static int get_bus_name(struct hotplug_slot *hotplug_slot, char *value)
 static int __init init_ops(void)
 {
        struct slot *slot_cur;
-       struct list_head *tmp;
        int retval;
        int rc;
 
-       list_for_each(tmp, &ibmphp_slot_head) {
-               slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
-
-               if (!slot_cur)
-                       return -ENODEV;
-
+       list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
                debug("BEFORE GETTING SLOT STATUS, slot # %x\n",
                                                        slot_cur->number);
                if (slot_cur->ctrl->revision == 0xFF)
@@ -669,9 +661,7 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
 {
        struct pci_func *func_cur;
        struct slot *slot_cur;
-       struct list_head *tmp;
-       list_for_each(tmp, &ibmphp_slot_head) {
-               slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+       list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
                if (slot_cur->func) {
                        func_cur = slot_cur->func;
                        while (func_cur) {
@@ -693,14 +683,12 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
  *************************************************************/
 static void free_slots(void)
 {
-       struct slot *slot_cur;
-       struct list_head *tmp;
-       struct list_head *next;
+       struct slot *slot_cur, *next;
 
        debug("%s -- enter\n", __func__);
 
-       list_for_each_safe(tmp, next, &ibmphp_slot_head) {
-               slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+       list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
+                                ibm_slot_list) {
                pci_hp_deregister(slot_cur->hotplug_slot);
        }
        debug("%s -- exit\n", __func__);
index d9b197d5c6b450e2ba44439aaf0ec9a41f3e08f6..664b5d1efb8fa3aed8d478d9cd2a559938b716e6 100644 (file)
@@ -1117,25 +1117,21 @@ int ibmphp_get_bus_index (u8 num)
 
 void ibmphp_free_bus_info_queue (void)
 {
-       struct bus_info *bus_info;
-       struct list_head *list;
-       struct list_head *next;
+       struct bus_info *bus_info, *next;
 
-       list_for_each_safe (list, next, &bus_info_head ) {
-               bus_info = list_entry (list, struct bus_info, bus_info_list);
+       list_for_each_entry_safe(bus_info, next, &bus_info_head,
+                                bus_info_list) {
                kfree (bus_info);
        }
 }
 
 void ibmphp_free_ebda_hpc_queue (void)
 {
-       struct controller *controller = NULL;
-       struct list_head *list;
-       struct list_head *next;
+       struct controller *controller = NULL, *next;
        int pci_flag = 0;
 
-       list_for_each_safe (list, next, &ebda_hpc_head) {
-               controller = list_entry (list, struct controller, ebda_hpc_list);
+       list_for_each_entry_safe(controller, next, &ebda_hpc_head,
+                                ebda_hpc_list) {
                if (controller->ctlr_type == 0)
                        release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1));
                else if ((controller->ctlr_type == 1) && (!pci_flag)) {
@@ -1148,12 +1144,10 @@ void ibmphp_free_ebda_hpc_queue (void)
 
 void ibmphp_free_ebda_pci_rsrc_queue (void)
 {
-       struct ebda_pci_rsrc *resource;
-       struct list_head *list;
-       struct list_head *next;
+       struct ebda_pci_rsrc *resource, *next;
 
-       list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) {
-               resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+       list_for_each_entry_safe(resource, next, &ibmphp_ebda_pci_rsrc_head,
+                                ebda_pci_rsrc_list) {
                kfree (resource);
                resource = NULL;
        }
index 220876715a08493a315a8ef9d04e2a697972710f..e2608585abd315ddb9fb6ddcae5271d9492d4553 100644 (file)
@@ -537,7 +537,6 @@ int ibmphp_hpc_readslot (struct slot *pslot, u8 cmd, u8 *pstatus)
 {
        void __iomem *wpg_bbar = NULL;
        struct controller *ctlr_ptr;
-       struct list_head *pslotlist;
        u8 index, status;
        int rc = 0;
        int busindex;
@@ -628,8 +627,8 @@ int ibmphp_hpc_readslot (struct slot *pslot, u8 cmd, u8 *pstatus)
 
                        // Not used
                case READ_ALLSLOT:
-                       list_for_each (pslotlist, &ibmphp_slot_head) {
-                               pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+                       list_for_each_entry(pslot, &ibmphp_slot_head,
+                                           ibm_slot_list) {
                                index = pslot->ctlr_index;
                                rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr,
                                                                wpg_bbar, &status);
@@ -820,7 +819,6 @@ static int poll_hpc(void *data)
 {
        struct slot myslot;
        struct slot *pslot = NULL;
-       struct list_head *pslotlist;
        int rc;
        int poll_state = POLL_LATCH_REGISTER;
        u8 oldlatchlow = 0x00;
@@ -838,10 +836,10 @@ static int poll_hpc(void *data)
                case POLL_LATCH_REGISTER:
                        oldlatchlow = curlatchlow;
                        ctrl_count = 0x00;
-                       list_for_each (pslotlist, &ibmphp_slot_head) {
+                       list_for_each_entry(pslot, &ibmphp_slot_head,
+                                           ibm_slot_list) {
                                if (ctrl_count >= ibmphp_get_total_controllers())
                                        break;
-                               pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
                                if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
                                        ctrl_count++;
                                        if (READ_SLOT_LATCH (pslot->ctrl)) {
@@ -859,8 +857,8 @@ static int poll_hpc(void *data)
                        poll_state = POLL_SLEEP;
                        break;
                case POLL_SLOTS:
-                       list_for_each (pslotlist, &ibmphp_slot_head) {
-                               pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+                       list_for_each_entry(pslot, &ibmphp_slot_head,
+                                           ibm_slot_list) {
                                // make a copy of the old status
                                memcpy ((void *) &myslot, (void *) pslot,
                                        sizeof (struct slot));
@@ -870,10 +868,10 @@ static int poll_hpc(void *data)
                                        process_changeinstatus (pslot, &myslot);
                        }
                        ctrl_count = 0x00;
-                       list_for_each (pslotlist, &ibmphp_slot_head) {
+                       list_for_each_entry(pslot, &ibmphp_slot_head,
+                                           ibm_slot_list) {
                                if (ctrl_count >= ibmphp_get_total_controllers())
                                        break;
-                               pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
                                if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
                                        ctrl_count++;
                                        if (READ_SLOT_LATCH (pslot->ctrl))
index 814cea22a9fa5f599fdd44e0af479a0552d8f274..5824df538f72a5de03ab17a1203c89d3601dca2c 100644 (file)
@@ -1119,7 +1119,7 @@ static struct res_needed *scan_behind_bridge (struct pci_func *func, u8 busno)
                                pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class);
 
                                debug ("hdr_type behind the bridge is %x\n", hdr_type);
-                               if (hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+                               if ((hdr_type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
                                        err ("embedded bridges not supported for hot-plugging.\n");
                                        amount->not_correct = 1;
                                        return amount;
index f279060cf6e25c7b6e9c6b09c1c0a21d66b7e13e..b5f2851e8cbe7c9d7616112f9dcbdf692dcb4a08 100644 (file)
@@ -203,15 +203,13 @@ int __init ibmphp_rsrc_init (void)
        struct bus_node *newbus = NULL;
        struct bus_node *bus_cur;
        struct bus_node *bus_prev;
-       struct list_head *tmp;
        struct resource_node *new_io = NULL;
        struct resource_node *new_mem = NULL;
        struct resource_node *new_pfmem = NULL;
        int rc;
-       struct list_head *tmp_ebda;
 
-       list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) {
-               curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+       list_for_each_entry(curr, &ibmphp_ebda_pci_rsrc_head,
+                           ebda_pci_rsrc_list) {
                if (!(curr->rsrc_type & PCIDEVMASK)) {
                        /* EBDA still lists non PCI devices, so ignore... */
                        debug ("this is not a PCI DEVICE in rsrc_init, please take care\n");
@@ -369,8 +367,7 @@ int __init ibmphp_rsrc_init (void)
                }
        }
 
-       list_for_each (tmp, &gbuses) {
-               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+       list_for_each_entry(bus_cur, &gbuses, bus_list) {
                /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
                rc = update_bridge_ranges (&bus_cur);
                if (rc)
@@ -1571,19 +1568,16 @@ int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resour
  ***********************************************************************/
 void ibmphp_free_resources (void)
 {
-       struct bus_node *bus_cur = NULL;
+       struct bus_node *bus_cur = NULL, *next;
        struct bus_node *bus_tmp;
        struct range_node *range_cur;
        struct range_node *range_tmp;
        struct resource_node *res_cur;
        struct resource_node *res_tmp;
-       struct list_head *tmp;
-       struct list_head *next;
        int i = 0;
        flags = 1;
 
-       list_for_each_safe (tmp, next, &gbuses) {
-               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+       list_for_each_entry_safe(bus_cur, next, &gbuses, bus_list) {
                if (bus_cur->noIORanges) {
                        range_cur = bus_cur->rangeIO;
                        for (i = 0; i < bus_cur->noIORanges; i++) {
@@ -1691,10 +1685,8 @@ static int __init once_over (void)
        struct resource_node *pfmem_prev;
        struct resource_node *mem;
        struct bus_node *bus_cur;
-       struct list_head *tmp;
 
-       list_for_each (tmp, &gbuses) {
-               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+       list_for_each_entry(bus_cur, &gbuses, bus_list) {
                if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
                        for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
                                pfmem_cur->fromMem = 1;
@@ -1767,14 +1759,10 @@ struct bus_node *ibmphp_find_res_bus (u8 bus_number)
 static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag)
 {
        struct bus_node *bus_cur;
-       struct list_head *tmp;
-       struct list_head *tmp_prev;
 
-       list_for_each (tmp, &gbuses) {
-               tmp_prev = tmp->prev;
-               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+       list_for_each_entry(bus_cur, &gbuses, bus_list) {
                if (flag)
-                       *prev = list_entry (tmp_prev, struct bus_node, bus_list);
+                       *prev = list_prev_entry(bus_cur, bus_list);
                if (bus_cur->busno == bus_number)
                        return bus_cur;
        }
@@ -1788,7 +1776,6 @@ void ibmphp_print_test (void)
        struct bus_node *bus_cur = NULL;
        struct range_node *range;
        struct resource_node *res;
-       struct list_head *tmp;
 
        debug_pci ("*****************START**********************\n");
 
@@ -1797,8 +1784,7 @@ void ibmphp_print_test (void)
                return;
        }
 
-       list_for_each (tmp, &gbuses) {
-               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+       list_for_each_entry(bus_cur, &gbuses, bus_list) {
                debug_pci ("This is bus # %d.  There are\n", bus_cur->busno);
                debug_pci ("IORanges = %d\t", bus_cur->noIORanges);
                debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges);
index d1fab97d6b01ff4cb2900ac1336ed0ae69852a39..fcd5e73c5b48e0d976961088b5285a87f4d1f401 100644 (file)
@@ -396,10 +396,8 @@ static void fs_remove_slot(struct pci_slot *pci_slot)
 static struct hotplug_slot *get_slot_from_name(const char *name)
 {
        struct hotplug_slot *slot;
-       struct list_head *tmp;
 
-       list_for_each(tmp, &pci_hotplug_slot_list) {
-               slot = list_entry(tmp, struct hotplug_slot, slot_list);
+       list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {
                if (strcmp(hotplug_slot_name(slot), name) == 0)
                        return slot;
        }
index 4c8f4cde68540933e0a7c83ac8011bfd68d561e7..880978b6d534cf2f03417b483c8acfde359ea9c6 100644 (file)
@@ -511,7 +511,9 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
        case STATIC_STATE:
                p_slot->state = POWEROFF_STATE;
                mutex_unlock(&p_slot->lock);
+               mutex_lock(&p_slot->hotplug_lock);
                retval = pciehp_disable_slot(p_slot);
+               mutex_unlock(&p_slot->hotplug_lock);
                mutex_lock(&p_slot->lock);
                p_slot->state = STATIC_STATE;
                break;
index d062c008fc95ffa0f075a7189e9467bbed9737dd..9d4a95e66bdabb1d2169efc79b023e9f82922f1c 100644 (file)
@@ -321,17 +321,14 @@ error:
 
 static void __exit cleanup_slots(void)
 {
-       struct list_head *tmp;
-       struct list_head *next;
-       struct slot *slot;
+       struct slot *slot, *next;
 
        /*
         * Unregister all of our slots with the pci_hotplug subsystem.
         * Memory will be freed in release_slot() callback after slot's
         * lifespan is finished.
         */
-       list_for_each_safe(tmp, next, &slot_list) {
-               slot = list_entry(tmp, struct slot, slot_list);
+       list_for_each_entry_safe(slot, next, &slot_list, slot_list) {
                list_del(&slot->slot_list);
                pci_hp_deregister(slot->hotplug_slot);
        }
index e12bafdc42e003e3d2c18eb187baf354e0945aa5..b46b57d870fc9e44c20564300248ca6fcd46cd32 100644 (file)
@@ -114,11 +114,10 @@ static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
  */
 static struct slot *find_php_slot(struct device_node *dn)
 {
-       struct list_head *tmp, *n;
-       struct slot *slot;
+       struct slot *slot, *next;
 
-       list_for_each_safe(tmp, n, &rpaphp_slot_head) {
-               slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+       list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
+                                rpaphp_slot_list) {
                if (slot->dn == dn)
                        return slot;
        }
index f2945fa73d4ffeeb5010358b5cc63fce32b73a5a..5a11232cf7666008cd5452c700463fb12b0844ef 100644 (file)
@@ -356,8 +356,7 @@ EXPORT_SYMBOL_GPL(rpaphp_add_slot);
 
 static void __exit cleanup_slots(void)
 {
-       struct list_head *tmp, *n;
-       struct slot *slot;
+       struct slot *slot, *next;
 
        /*
         * Unregister all of our slots with the pci_hotplug subsystem,
@@ -365,8 +364,8 @@ static void __exit cleanup_slots(void)
         * memory will be freed in release_slot callback.
         */
 
-       list_for_each_safe(tmp, n, &rpaphp_slot_head) {
-               slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+       list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
+                                rpaphp_slot_list) {
                list_del(&slot->rpaphp_slot_list);
                pci_hp_deregister(slot->hotplug_slot);
        }
index d77e46bca54ca5e55b635abf7769c001ce97925e..eb5efaef06ea5572b339d2aa3cead268e57722cc 100644 (file)
@@ -201,11 +201,10 @@ error:
 
 void zpci_exit_slot(struct zpci_dev *zdev)
 {
-       struct list_head *tmp, *n;
-       struct slot *slot;
+       struct slot *slot, *next;
 
-       list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
-               slot = list_entry(tmp, struct slot, slot_list);
+       list_for_each_entry_safe(slot, next, &s390_hotplug_slot_list,
+                                slot_list) {
                if (slot->zdev != zdev)
                        continue;
                list_del(&slot->slot_list);
index 294ef4b10cf198031da6a67e91c10ef965cffccf..7c854b6847d104ba69a788507517c580b3be0f2c 100644 (file)
@@ -178,12 +178,9 @@ error:
 
 void cleanup_slots(struct controller *ctrl)
 {
-       struct list_head *tmp;
-       struct list_head *next;
-       struct slot *slot;
+       struct slot *slot, *next;
 
-       list_for_each_safe(tmp, next, &ctrl->slot_list) {
-               slot = list_entry(tmp, struct slot, slot_list);
+       list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {
                list_del(&slot->slot_list);
                cancel_delayed_work(&slot->work);
                destroy_workqueue(slot->wq);
index 53e463244bb7e35ac368ff088a4976c2153fce3d..b64d26719906ac0839f6d9c8f8b9e813e782493d 100644 (file)
@@ -1024,10 +1024,6 @@ int pci_msi_enabled(void)
 }
 EXPORT_SYMBOL(pci_msi_enabled);
 
-void pci_msi_init_pci_dev(struct pci_dev *dev)
-{
-}
-
 /**
  * pci_enable_msi_range - configure device's MSI capability structure
  * @dev: device to configure
index 92618686604cb9d314aa1e6bf833363cfbaaa1b5..9cd27b703dd683b73947d8844b67072b079f5c88 100644 (file)
@@ -1369,10 +1369,10 @@ int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
        if (!sysfs_initialized)
                return -EACCES;
 
-       if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
-               retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
-       else
+       if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
                retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+       else
+               retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
        if (retval)
                goto err;
 
@@ -1424,10 +1424,10 @@ err_rom_file:
 err_resource_files:
        pci_remove_resource_files(pdev);
 err_config_file:
-       if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
-               sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
-       else
+       if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
                sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+       else
+               sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
 err:
        return retval;
 }
@@ -1461,10 +1461,10 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 
        pci_remove_capabilities_sysfs(pdev);
 
-       if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
-               sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
-       else
+       if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
                sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+       else
+               sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
 
        pci_remove_resource_files(pdev);
 
index fd2f03fa53f33a34977fc8713fd8cf6759cad2d5..44d9859057b4fd8cbad53fb0d57f9e46b3c03b9c 100644 (file)
@@ -144,10 +144,8 @@ extern unsigned int pci_pm_d3_delay;
 
 #ifdef CONFIG_PCI_MSI
 void pci_no_msi(void);
-void pci_msi_init_pci_dev(struct pci_dev *dev);
 #else
 static inline void pci_no_msi(void) { }
-static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
 #endif
 
 static inline void pci_msi_set_enable(struct pci_dev *dev, int enable)
index 182224acedbe79c18dbc357cb45763afef40e387..20db790465dd682efae0ade88d7ac42cfd52b060 100644 (file)
@@ -41,12 +41,12 @@ struct aer_error_inj {
        u32 header_log1;
        u32 header_log2;
        u32 header_log3;
-       u16 domain;
+       u32 domain;
 };
 
 struct aer_error {
        struct list_head list;
-       u16 domain;
+       u32 domain;
        unsigned int bus;
        unsigned int devfn;
        int pos_cap_err;
@@ -74,7 +74,7 @@ static LIST_HEAD(pci_bus_ops_list);
 /* Protect einjected and pci_bus_ops_list */
 static DEFINE_SPINLOCK(inject_lock);
 
-static void aer_error_init(struct aer_error *err, u16 domain,
+static void aer_error_init(struct aer_error *err, u32 domain,
                           unsigned int bus, unsigned int devfn,
                           int pos_cap_err)
 {
@@ -86,7 +86,7 @@ static void aer_error_init(struct aer_error *err, u16 domain,
 }
 
 /* inject_lock must be held before calling */
-static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
+static struct aer_error *__find_aer_error(u32 domain, unsigned int bus,
                                          unsigned int devfn)
 {
        struct aer_error *err;
@@ -106,7 +106,7 @@ static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
        int domain = pci_domain_nr(dev->bus);
        if (domain < 0)
                return NULL;
-       return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
+       return __find_aer_error(domain, dev->bus->number, dev->devfn);
 }
 
 /* inject_lock must be held before calling */
@@ -196,7 +196,7 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
        domain = pci_domain_nr(bus);
        if (domain < 0)
                goto out;
-       err = __find_aer_error((u16)domain, bus->number, devfn);
+       err = __find_aer_error(domain, bus->number, devfn);
        if (!err)
                goto out;
 
@@ -228,7 +228,7 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
        domain = pci_domain_nr(bus);
        if (domain < 0)
                goto out;
-       err = __find_aer_error((u16)domain, bus->number, devfn);
+       err = __find_aer_error(domain, bus->number, devfn);
        if (!err)
                goto out;
 
@@ -329,7 +329,7 @@ static int aer_inject(struct aer_error_inj *einj)
        u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0;
        int ret = 0;
 
-       dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
+       dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
        if (!dev)
                return -ENODEV;
        rpdev = pcie_find_root_port(dev);
index fba785e9df75570b35a9b90e671bc8d5dbad6e91..712392504ed9b9f1a8992079f801cf2c271a0ae6 100644 (file)
@@ -246,7 +246,7 @@ static int report_error_detected(struct pci_dev *dev, void *data)
                !dev->driver->err_handler ||
                !dev->driver->err_handler->error_detected) {
                if (result_data->state == pci_channel_io_frozen &&
-                       !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
+                       dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
                        /*
                         * In case of fatal recovery, if one of down-
                         * stream device has no driver. We might be
@@ -269,7 +269,7 @@ static int report_error_detected(struct pci_dev *dev, void *data)
                 * without recovery.
                 */
 
-               if (!(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
+               if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
                        vote = PCI_ERS_RESULT_NO_AER_DRIVER;
                else
                        vote = PCI_ERS_RESULT_NONE;
@@ -369,7 +369,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
        else
                result_data.result = PCI_ERS_RESULT_RECOVERED;
 
-       if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
                /*
                 * If the error is reported by a bridge, we think this error
                 * is related to the downstream link of the bridge, so we
@@ -440,7 +440,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
        pci_ers_result_t status;
        struct pcie_port_service_driver *driver;
 
-       if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
                /* Reset this port for all subordinates */
                udev = dev;
        } else {
@@ -660,7 +660,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
                        &info->mask);
                if (!(info->status & ~info->mask))
                        return 0;
-       } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
+       } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
                info->severity == AER_NONFATAL) {
 
                /* Link is still healthy for IO reads */
index 317e3558a35e00d621d838c4825386f28d823910..2dfe7fdb77e7fe95d6b22d1cc925fe24a06c685b 100644 (file)
@@ -834,21 +834,15 @@ static ssize_t link_state_store(struct device *dev,
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct pcie_link_state *link, *root = pdev->link_state->root;
-       u32 val, state = 0;
-
-       if (kstrtouint(buf, 10, &val))
-               return -EINVAL;
+       u32 state;
 
        if (aspm_disabled)
                return -EPERM;
-       if (n < 1 || val > 3)
-               return -EINVAL;
 
-       /* Convert requested state to ASPM state */
-       if (val & PCIE_LINK_STATE_L0S)
-               state |= ASPM_STATE_L0S;
-       if (val & PCIE_LINK_STATE_L1)
-               state |= ASPM_STATE_L1;
+       if (kstrtouint(buf, 10, &state))
+               return -EINVAL;
+       if ((state & ~ASPM_STATE_ALL) != 0)
+               return -EINVAL;
 
        down_read(&pci_bus_sem);
        mutex_lock(&aspm_lock);
index edb1984201e9702162321c50f33f76ba362bac5a..32b9f1b8826621e27d6bb2b0153d233110c19dbf 100644 (file)
@@ -1107,14 +1107,11 @@ static int pci_cfg_space_size_ext(struct pci_dev *dev)
        int pos = PCI_CFG_SPACE_SIZE;
 
        if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL)
-               goto fail;
+               return PCI_CFG_SPACE_SIZE;
        if (status == 0xffffffff || pci_ext_cfg_is_aliased(dev))
-               goto fail;
+               return PCI_CFG_SPACE_SIZE;
 
        return PCI_CFG_SPACE_EXP_SIZE;
-
- fail:
-       return PCI_CFG_SPACE_SIZE;
 }
 
 int pci_cfg_space_size(struct pci_dev *dev)
@@ -1127,25 +1124,23 @@ int pci_cfg_space_size(struct pci_dev *dev)
        if (class == PCI_CLASS_BRIDGE_HOST)
                return pci_cfg_space_size_ext(dev);
 
-       if (!pci_is_pcie(dev)) {
-               pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
-               if (!pos)
-                       goto fail;
+       if (pci_is_pcie(dev))
+               return pci_cfg_space_size_ext(dev);
 
-               pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
-               if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
-                       goto fail;
-       }
+       pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+       if (!pos)
+               return PCI_CFG_SPACE_SIZE;
 
-       return pci_cfg_space_size_ext(dev);
+       pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
+       if (status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ))
+               return pci_cfg_space_size_ext(dev);
 
- fail:
        return PCI_CFG_SPACE_SIZE;
 }
 
 #define LEGACY_IO_RESOURCE     (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
-void pci_msi_setup_pci_dev(struct pci_dev *dev)
+static void pci_msi_setup_pci_dev(struct pci_dev *dev)
 {
        /*
         * Disable the MSI hardware to avoid screaming interrupts
@@ -1212,8 +1207,6 @@ int pci_setup_device(struct pci_dev *dev)
        /* "Unknown power state" */
        dev->current_state = PCI_UNKNOWN;
 
-       pci_msi_setup_pci_dev(dev);
-
        /* Early fixups, before probing the BARs */
        pci_fixup_device(pci_fixup_early, dev);
        /* device class may be changed after fixup */
@@ -1603,8 +1596,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
        /* Enhanced Allocation */
        pci_ea_init(dev);
 
-       /* MSI/MSI-X list */
-       pci_msi_init_pci_dev(dev);
+       /* Setup MSI caps & disable MSI/MSI-X interrupts */
+       pci_msi_setup_pci_dev(dev);
 
        /* Buffers for saving PCIe and PCI-X capabilities */
        pci_allocate_cap_save_buffers(dev);
index 9f61be560ed8daf74bfa5c46a21ec88af1ff1fd2..13ac34ddec196aece54734fed550590523a263f3 100644 (file)
@@ -287,6 +287,18 @@ static void quirk_citrine(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM,    PCI_DEVICE_ID_IBM_CITRINE,      quirk_citrine);
 
+/*
+ * This chip can cause bus lockups if config addresses above 0x600
+ * are read or written.
+ */
+static void quirk_nfp6000(struct pci_dev *dev)
+{
+       dev->cfg_size = 0x600;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP4000,        quirk_nfp6000);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP6000,        quirk_nfp6000);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP6000_VF,     quirk_nfp6000);
+
 /*  On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
 static void quirk_extend_bar_to_page(struct pci_dev *dev)
 {
index eb0ad530dc430268ea699891fff5a64d6a38545a..9eaca39ef38d1ccca5829b708d23b800d52fae61 100644 (file)
@@ -77,25 +77,24 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
        do {
                void __iomem *pds;
                /* Standard PCI ROMs start out with these bytes 55 AA */
-               if (readb(image) != 0x55) {
-                       dev_err(&pdev->dev, "Invalid ROM contents\n");
+               if (readw(image) != 0xAA55) {
+                       dev_err(&pdev->dev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n",
+                               readw(image));
                        break;
                }
-               if (readb(image + 1) != 0xAA)
-                       break;
-               /* get the PCI data structure and check its signature */
+               /* get the PCI data structure and check its "PCIR" signature */
                pds = image + readw(image + 24);
-               if (readb(pds) != 'P')
-                       break;
-               if (readb(pds + 1) != 'C')
-                       break;
-               if (readb(pds + 2) != 'I')
-                       break;
-               if (readb(pds + 3) != 'R')
+               if (readl(pds) != 0x52494350) {
+                       dev_err(&pdev->dev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n",
+                               readl(pds));
                        break;
+               }
                last_image = readb(pds + 21) & 0x80;
                length = readw(pds + 16);
                image += length * 512;
+               /* Avoid iterating through memory outside the resource window */
+               if (image > rom + size)
+                       break;
        } while (length && !last_image);
 
        /* never return a size larger than the PCI resource window */
index 2c51ee78b1c0e31074ee17eb9a341021ee3162e0..f6e9e85164e8bd8dc3c110d01fdca23a504201ba 100644 (file)
@@ -59,6 +59,13 @@ static inline void of_pci_check_probe_only(void) { }
 int of_pci_get_host_bridge_resources(struct device_node *dev,
                        unsigned char busno, unsigned char bus_max,
                        struct list_head *resources, resource_size_t *io_base);
+#else
+static inline int of_pci_get_host_bridge_resources(struct device_node *dev,
+                       unsigned char busno, unsigned char bus_max,
+                       struct list_head *resources, resource_size_t *io_base)
+{
+       return -EINVAL;
+}
 #endif
 
 #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
index e828e7b4afec67a4b9f083cfa65c2ff3cffe8571..f9f79add0afb802ab012179fb4cb9cd2be47eff8 100644 (file)
@@ -1248,8 +1248,6 @@ struct msix_entry {
        u16     entry;  /* driver uses to specify entry, OS writes */
 };
 
-void pci_msi_setup_pci_dev(struct pci_dev *dev);
-
 #ifdef CONFIG_PCI_MSI
 int pci_msi_vec_count(struct pci_dev *dev);
 void pci_msi_shutdown(struct pci_dev *dev);
index d9ba49cedc5dfe19b1391a5156d465331df8e05e..37f05cb1dfd6d93cae1ceeffc8a3de1c0fcb0529 100644 (file)
 #define PCI_DEVICE_ID_KORENIX_JETCARDF2        0x1700
 #define PCI_DEVICE_ID_KORENIX_JETCARDF3        0x17ff
 
+#define PCI_VENDOR_ID_NETRONOME                0x19ee
+#define PCI_DEVICE_ID_NETRONOME_NFP3200        0x3200
+#define PCI_DEVICE_ID_NETRONOME_NFP3240        0x3240
+#define PCI_DEVICE_ID_NETRONOME_NFP4000        0x4000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000        0x6000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000_VF     0x6003
+
 #define PCI_VENDOR_ID_QMI              0x1a32
 
 #define PCI_VENDOR_ID_AZWAVE           0x1a3b
index 22aa9612ef7ca98cd8796188a7021810cc86d05a..ca05cc841f001c10b40ac4c9898a422f67c3f8be 100644 (file)
@@ -1058,6 +1058,7 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
        __irq_set_handler(virq, handler, 0, handler_name);
        irq_set_handler_data(virq, handler_data);
 }
+EXPORT_SYMBOL(irq_domain_set_info);
 
 /**
  * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data
index 6b0c0b74a2a1a88c0d3f81519fea7c520b290967..5e15cb4b2f0097fa025af0a9f22f0dee0f22f2b0 100644 (file)
@@ -109,9 +109,11 @@ static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
        if (irq_find_mapping(domain, hwirq) > 0)
                return -EEXIST;
 
-       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
-       if (ret < 0)
-               return ret;
+       if (domain->parent) {
+               ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+               if (ret < 0)
+                       return ret;
+       }
 
        for (i = 0; i < nr_irqs; i++) {
                ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg);