ipq806x: add pcie support to ipq806x based platforms
authorFelix Fietkau <nbd@openwrt.org>
Sun, 10 May 2015 11:47:09 +0000 (11:47 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 10 May 2015 11:47:09 +0000 (11:47 +0000)
This change adds PCIe support to IPQ806x based platforms. The driver is
actually cherry-picked from the following LKML thread:
*https://lwn.net/Articles/643086/ (patches 110-111)

We also add here an additional fix to support multiple PCI controllers
on the same platform (patch 112), and to patch the ap148 & dbs149 DTS
files (patch 113).

Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
SVN-Revision: 45663

12 files changed:
target/linux/ipq806x/config-3.18
target/linux/ipq806x/config-4.0
target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/700-add-gmac-dts-suport.patch
target/linux/ipq806x/patches-4.0/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.0/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.0/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.0/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.0/700-add-gmac-dts-suport.patch

index 48a737452f52899d4d5e0ae4dbdd962f5299a4c2..d32e9891fba4f1f1a4a2230ce72aef475882223c 100644 (file)
@@ -258,6 +258,11 @@ CONFIG_OLD_SIGSUSPEND3=y
 CONFIG_PAGEFLAGS_EXTENDED=y
 CONFIG_PAGE_OFFSET=0xC0000000
 CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_MSI=y
 CONFIG_PERF_EVENTS=y
 CONFIG_PERF_USE_VMALLOC=y
@@ -295,6 +300,7 @@ CONFIG_QCOM_BAM_DMA=y
 CONFIG_QCOM_GSBI=y
 CONFIG_QCOM_SCM=y
 CONFIG_QCOM_WDT=y
+CONFIG_RAS=y
 # CONFIG_RCU_BOOST is not set
 CONFIG_RCU_CPU_STALL_TIMEOUT=21
 CONFIG_RCU_CPU_STALL_VERBOSE=y
index 9d8ff658e404b0c7bc928fa59c0a11759321ebaf..96644cf529084cde032c952a8f5cecc6e7f26491 100644 (file)
@@ -267,7 +267,14 @@ CONFIG_OLD_SIGSUSPEND3=y
 CONFIG_PAGEFLAGS_EXTENDED=y
 CONFIG_PAGE_OFFSET=0xC0000000
 CONFIG_PCI=y
-# CONFIG_PCI_DOMAINS_GENERIC is not set
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_PME=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DEBUG=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
 CONFIG_PCI_MSI=y
 CONFIG_PERF_EVENTS=y
 CONFIG_PERF_USE_VMALLOC=y
@@ -307,6 +314,7 @@ CONFIG_QCOM_BAM_DMA=y
 CONFIG_QCOM_GSBI=y
 CONFIG_QCOM_SCM=y
 CONFIG_QCOM_WDT=y
+CONFIG_RAS=y
 # CONFIG_RCU_BOOST is not set
 CONFIG_RCU_CPU_STALL_TIMEOUT=21
 CONFIG_RCU_STALL_COMMON=y
diff --git a/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644 (file)
index 0000000..95398e0
--- /dev/null
@@ -0,0 +1,266 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326181
+Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:16 +0300
+
+Document Qualcomm PCIe driver devicetree bindings.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+.../devicetree/bindings/pci/qcom,pcie.txt          |  231 ++++++++++++++++++++
+ 1 files changed, 231 insertions(+), 0 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt
+
+diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+new file mode 100644
+index 0000000..dcf7348
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+@@ -0,0 +1,231 @@
++* Qualcomm PCI express root complex
++
++- compatible:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Value shall include
++                  - "qcom,pcie-v0" for apq/ipq8064
++                  - "qcom,pcie-v1" 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 set to 3. As specified in designware-pcie.txt
++
++- #size-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: Should be set 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
++                  * should be populated for v0 and v1
++                      - "iface"      Configuration AHB clock
++
++                  * should be populated for v0
++                      - "core"       Clocks the pcie hw block
++                      - "phy"        Clocks the pcie PHY block
++
++                  * should be populated for v1
++                      - "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
++      Value type: <stringlist>
++      Definition: Should contain the following entries
++                  * should be populated for v0
++                      - "axi"  AXI reset
++                      - "ahb"  AHB reset
++                      - "por"  POR reset
++                      - "pci"  PCI reset
++                      - "phy"  PHY reset
++
++                  * should be populated for v1
++                      - "core" Core reset
++
++- power-domains:
++      Usage: required (for v1 only)
++      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
++
++- <name>-supply:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles to the power supply regulator(s)
++                  * should be populated for v0 and v1
++                      - "vdda"        core analog power supply
++
++                  * should be populated for v0
++                      - "vdda_phy"    analog power supply for PHY
++                      - "vdda_refclk" analog power supply for IC which generate
++                                      reference clock
++
++- phys:
++      Usage: required (for v1 only)
++      Value type: <phandle>
++      Definition: List of phandle(s) as listed in phy-names property
++
++- phy-names:
++      Usage: required (for v1 only)
++      Value type: <stringlist>
++      Definition: Should contain "pciephy"
++
++- <name>-gpio:
++      Usage: optional
++      Value type: <prop-encoded-array>
++      Definition: List of phandle and gpio specifier pairs. Should contain
++                  - "perst"  PCIe endpoint reset signal line
++                  - "pewake" PCIe endpoint wake signal line
++
++- pinctrl-0:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles pointing at a pin(s) configuration
++
++- pinctrl-names
++      Usage: required
++      Value type: <stringlist>
++      Definition: List of names of pinctrl-0 state
++
++* Example for v0
++      pcie0: pci@1b500000 {
++              compatible = "qcom,pcie-v0";
++              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 0x00000000 0x08000000 0 0x07e00000>; /* memory */
++              interrupts = <GIC_SPI 35 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";
++      };
++
++* Example for v1
++      pcie0@fc520000 {
++              compatible = "qcom,pcie-v1";
++              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";
++      };
diff --git a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644 (file)
index 0000000..a0c9c7c
--- /dev/null
@@ -0,0 +1,753 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326161
+Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:17 +0300
+
+The PCIe driver reuse the Designware common code for host
+and MSI initialization, and also program the Qualcomm
+application specific registers.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+MAINTAINERS                  |    7 +
+ drivers/pci/host/Kconfig     |    9 +
+ drivers/pci/host/Makefile    |    1 +
+ drivers/pci/host/pcie-qcom.c |  677 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 694 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/pci/host/pcie-qcom.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7127,6 +7127,13 @@ L:      linux-pci@vger.kernel.org
+ S:    Maintained
+ F:    drivers/pci/host/*spear*
++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
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -91,4 +91,13 @@ config PCI_XGENE
+         There are 5 internal PCIe ports available. Each port is GEN3 capable
+         and have varied lanes from x1 to x8.
++config PCIE_QCOM
++      bool "Qualcomm PCIe controller"
++      depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
++      select PCIE_DW
++      select PCIEPORTBUS
++      help
++        Say Y here to enable PCIe controller support on Qualcomm SoCs. The
++        PCIe controller use Designware core plus Qualcomm specific hardware
++        wrappers.
+ endmenu
+--- /dev/null
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -0,0 +1,677 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * 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/kernel.h>
++#include <linux/module.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_STTS                  0x08
++#define XMLH_LINK_UP                          BIT(10)
++
++#define PCIE20_CAP                            0x70
++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
++
++#define PERST_DELAY_MIN_US                    1000
++#define PERST_DELAY_MAX_US                    1005
++
++#define LINKUP_DELAY_MIN_US                   5000
++#define LINKUP_DELAY_MAX_US                   5100
++#define LINKUP_RETRIES_COUNT                  20
++
++#define PCIE_V0                                       0       /* apq8064 */
++#define PCIE_V1                                       1       /* apq8084 */
++
++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 pcie_resources {
++      struct qcom_pcie_resources_v0 v0;
++      struct qcom_pcie_resources_v1 v1;
++};
++
++struct qcom_pcie {
++      struct pcie_port pp;
++      struct device *dev;
++      union pcie_resources res;
++      void __iomem *parf;
++      void __iomem *dbi;
++      void __iomem *elbi;
++      struct phy *phy;
++      struct gpio_desc *reset;
++      unsigned int version;
++};
++
++#define to_qcom_pcie(x)               container_of(x, struct qcom_pcie, pp)
++
++static inline void
++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
++{
++      u32 val = readl(addr);
++
++      val &= ~clear_mask;
++      val |= set_mask;
++      writel(val, addr);
++}
++
++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
++{
++      int val, active_low;
++
++      if (IS_ERR_OR_NULL(pcie->reset))
++              return;
++
++      active_low = gpiod_is_active_low(pcie->reset);
++
++      if (assert)
++              val = !!active_low;
++      else
++              val = !active_low;
++
++      gpiod_set_value(pcie->reset, val);
++
++      usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
++}
++
++static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 1);
++}
++
++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 0);
++}
++
++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_link_up(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
++
++      return val & BIT(29) ? 1 : 0;
++}
++
++static void qcom_pcie_disable_resources_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 void qcom_pcie_disable_resources_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_enable_resources_v0(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      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 = clk_prepare_enable(res->iface_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable iface clock\n");
++              goto err_iface;
++      }
++
++      ret = clk_prepare_enable(res->core_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable core clock\n");
++              goto err_clk_core;
++      }
++
++      ret = clk_prepare_enable(res->phy_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable phy clock\n");
++              goto err_clk_phy;
++      }
++
++      ret = reset_control_deassert(res->ahb_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert ahb reset\n");
++              goto err_reset_ahb;
++      }
++
++      return 0;
++
++err_reset_ahb:
++      clk_disable_unprepare(res->phy_clk);
++err_clk_phy:
++      clk_disable_unprepare(res->core_clk);
++err_clk_core:
++      clk_disable_unprepare(res->iface_clk);
++err_iface:
++      regulator_disable(res->vdda_phy);
++err_vdda_phy:
++      regulator_disable(res->vdda_refclk);
++err_refclk:
++      regulator_disable(res->vdda);
++      return ret;
++}
++
++static int qcom_pcie_enable_resources_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;
++      }
++
++      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_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) && PTR_ERR(res->aux) == -EPROBE_DEFER)
++              return -EPROBE_DEFER;
++      else if (IS_ERR(res->aux))
++              res->aux = NULL;
++
++      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 int qcom_pcie_enable_link_training(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct device *dev = pp->dev;
++      int retries;
++      u32 val;
++
++      /* enable link training */
++      writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
++
++      /* wait for up to 100ms for the link to come up */
++      retries = LINKUP_RETRIES_COUNT;
++      do {
++              val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
++              if (val & XMLH_LINK_UP)
++                      break;
++              usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
++      } while (retries--);
++
++      if (retries < 0 || !dw_pcie_link_up(pp)) {
++              dev_err(dev, "link initialization failed\n");
++              return -ENXIO;
++      }
++
++      return 0;
++}
++
++static void qcom_pcie_host_init_v1(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v1(pcie);
++      if (ret)
++              return;
++
++      /* change DBI base address */
++      writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
++                            0, BIT(31));
++
++      ret = phy_init(pcie->phy);
++      if (ret)
++              goto err_res;
++
++      ret = phy_power_on(pcie->phy);
++      if (ret)
++              goto err_phy;
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++
++err:
++      qcom_ep_reset_assert(pcie);
++      phy_power_off(pcie->phy);
++err_phy:
++      phy_exit(pcie->phy);
++err_res:
++      qcom_pcie_disable_resources_v1(pcie);
++}
++
++static void qcom_pcie_host_init_v0(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v0(pcie);
++      if (ret)
++              return;
++
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
++
++      /* enable external reference clock */
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
++
++      ret = reset_control_deassert(res->phy_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert phy reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->pci_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert pci reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->por_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert por reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->axi_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert axi reset\n");
++              return;
++      }
++
++      /* wait 150ms for clock acquisition */
++      usleep_range(10000, 15000);
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++err:
++      qcom_ep_reset_assert(pcie);
++      qcom_pcie_disable_resources_v0(pcie);
++}
++
++static void qcom_pcie_host_init(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++
++      if (pcie->version == PCIE_V0)
++              return qcom_pcie_host_init_v0(pp);
++      else
++              return qcom_pcie_host_init_v1(pp);
++}
++
++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 &= ~(0xffff << 16);
++              *val |= PCI_CLASS_BRIDGE_PCI << 16;
++              return PCIBIOS_SUCCESSFUL;
++      }
++
++      return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
++                              size, val);
++}
++
++static struct pcie_host_ops qcom_pcie_ops = {
++      .link_up = qcom_pcie_link_up,
++      .host_init = qcom_pcie_host_init,
++      .rd_own_conf = qcom_pcie_rd_own_conf,
++};
++
++static const struct of_device_id qcom_pcie_match[] = {
++      { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
++      { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
++      { }
++};
++
++static int qcom_pcie_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      const struct of_device_id *match;
++      struct resource *res;
++      struct qcom_pcie *pcie;
++      struct pcie_port *pp;
++      int ret;
++
++      match = of_match_node(qcom_pcie_match, dev->of_node);
++      if (!match)
++              return -ENXIO;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pcie->version = (unsigned int)match->data;
++
++      pcie->reset = devm_gpiod_get_optional(dev, "perst");
++      if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
++              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);
++
++      pcie->dev = dev;
++
++      if (pcie->version == PCIE_V0)
++              ret = qcom_pcie_get_resources_v0(pcie);
++      else
++              ret = qcom_pcie_get_resources_v1(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_ops;
++
++      if (IS_ENABLED(CONFIG_PCI_MSI)) {
++              pp->msi_irq = platform_get_irq_byname(pdev, "msi");
++              if (pp->msi_irq < 0) {
++                      dev_err(dev, "cannot get msi irq\n");
++                      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 = 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);
++      if (pcie->version == PCIE_V0)
++              qcom_pcie_disable_resources_v0(pcie);
++      else
++              qcom_pcie_disable_resources_v1(pcie);
++
++      return 0;
++}
++
++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");
++MODULE_ALIAS("platform:qcom-pcie");
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+ obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644 (file)
index 0000000..2613dff
--- /dev/null
@@ -0,0 +1,268 @@
+From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:01:42 -0700
+Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms
+
+qcom-pcie driver now supports version 0 of the controller. This change
+adds the corresponding entries to the IPQ806x dtsi file and
+corresponding platform (AP148).
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts |  30 ++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi      | 124 +++++++++++++++++++++++++++++++
+ 2 files changed, 154 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -30,6 +30,22 @@
+                               bias-disable;
+                       };
++                      pcie1_pins: pcie1_pinmux {
++                              mux {
++                                      pins = "gpio3";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie2_pins: pcie2_pinmux {
++                              mux {
++                                      pins = "gpio48";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
+                       spi_pins: spi_pins {
+                               mux {
+                                       pins = "gpio18", "gpio19", "gpio21";
+@@ -136,5 +152,19 @@
+               usb30@1 {
+                       status = "ok";
+               };
++
++              pcie0: pci@1b500000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 3 0>;
++                      pinctrl-0 = <&pcie1_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 48 0>;
++                      pinctrl-0 = <&pcie2_pins>;
++                      pinctrl-names = "default";
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -37,6 +37,30 @@
+                               bias-disable;
+                       };
++                      pcie1_pins: pcie1_pinmux {
++                              mux {
++                                      pins = "gpio3";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie2_pins: pcie2_pinmux {
++                              mux {
++                                      pins = "gpio48";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie3_pins: pcie3_pinmux {
++                              mux {
++                                      pins = "gpio63";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
+                       spi_pins: spi_pins {
+                               mux {
+                                       pins = "gpio18", "gpio19", "gpio21";
+@@ -153,6 +177,27 @@
+                       status = "ok";
+               };
++              pcie0: pci@1b500000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 3 0>;
++                      pinctrl-0 = <&pcie1_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 48 0>;
++                      pinctrl-0 = <&pcie2_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie2: pci@1b900000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 63 0>;
++                      pinctrl-0 = <&pcie3_pins>;
++                      pinctrl-names = "default";
++              };
++
+               mdio0: mdio {
+                       compatible = "virtual,mdio-gpio";
+                       #address-cells = <1>;
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -3,6 +3,7 @@
+ #include "skeleton.dtsi"
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ / {
+       model = "Qualcomm IPQ8064";
+@@ -306,6 +307,129 @@
+                       #reset-cells = <1>;
+               };
++              pcie0: pci@1b500000 {
++                      compatible = "qcom,pcie-v0";
++                      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   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 35 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";
++
++                      status = "disabled";
++              };
++
++              pcie1: pci@1b700000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b700000 0x1000
++                             0x1b702000 0x80
++                             0x1b800000 0x100
++                             0x31f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <1>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0          0x31e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_1_A_CLK>,
++                               <&gcc PCIE_1_H_CLK>,
++                               <&gcc PCIE_1_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_1_ACLK_RESET>,
++                               <&gcc PCIE_1_HCLK_RESET>,
++                               <&gcc PCIE_1_POR_RESET>,
++                               <&gcc PCIE_1_PCI_RESET>,
++                               <&gcc PCIE_1_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      status = "disabled";
++              };
++
++              pcie2: pci@1b900000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b900000 0x1000
++                             0x1b902000 0x80
++                             0x1ba00000 0x100
++                             0x35f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <2>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0          0x35e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_2_A_CLK>,
++                               <&gcc PCIE_2_H_CLK>,
++                               <&gcc PCIE_2_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_2_ACLK_RESET>,
++                               <&gcc PCIE_2_HCLK_RESET>,
++                               <&gcc PCIE_2_POR_RESET>,
++                               <&gcc PCIE_2_PCI_RESET>,
++                               <&gcc PCIE_2_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      status = "disabled";
++              };
++
+               hs_phy_1: phy@100f8800 {
+                       compatible = "qcom,dwc3-hs-usb-phy";
+                       reg = <0x100f8800 0x30>;
+@@ -389,6 +513,5 @@
+                               dr_mode = "host";
+                       };
+               };
+-
+       };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644 (file)
index 0000000..4f0e45f
--- /dev/null
@@ -0,0 +1,29 @@
+From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:09:07 -0700
+Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is
+ enabled
+
+If multiple PCIe devices are present in the system, the kernel will
+panic at boot time when trying to scan the PCI buses. This happens on
+IPQ806x based platforms, which has 3 PCIe ports.
+
+Enabling this option allows the kernel to assign the pci-domains
+according to the device-tree content. This allows multiple PCIe
+controllers to coexist in the system.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
+       select CLKSRC_OF
+       select PINCTRL
+       select QCOM_SCM if SMP
++      select PCI_DOMAINS if PCI
+       help
+         Support for Qualcomm's devicetree based systems.
index 348f24b921ac2a80ca960f8a88465e6129732e6e..fe670cf99e52e686ff811a3e73df210385c13b82 100644 (file)
@@ -16,7 +16,7 @@
                        i2c4_pins: i2c4_pinmux {
                                pins = "gpio12", "gpio13";
                                function = "gsbi4";
-@@ -38,6 +45,25 @@
+@@ -54,6 +61,25 @@
                                        bias-none;
                                };
                        };
@@ -42,7 +42,7 @@
                };
  
                gsbi@16300000 {
-@@ -76,6 +102,7 @@
+@@ -92,6 +118,7 @@
                                        #size-cells = <1>;
                                        spi-max-frequency = <50000000>;
                                        reg = <0>;
@@ -50,9 +50,9 @@
  
                                        partition@0 {
                                                label = "lowlevel_init";
-@@ -136,5 +163,66 @@
-               usb30@1 {
-                       status = "ok";
+@@ -166,5 +193,66 @@
+                       pinctrl-0 = <&pcie2_pins>;
+                       pinctrl-names = "default";
                };
 +
 +              mdio0: mdio {
  };
 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
-@@ -3,6 +3,7 @@
- #include "skeleton.dtsi"
+@@ -4,6 +4,7 @@
  #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
  #include <dt-bindings/soc/qcom,gsbi.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
 +#include <dt-bindings/interrupt-controller/arm-gic.h>
  
  / {
        model = "Qualcomm IPQ8064";
-@@ -390,5 +391,41 @@
+@@ -513,5 +514,42 @@
+                               dr_mode = "host";
                        };
                };
++
 +              nss-gmac-common {
 +                      reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>;
 +                      reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base";
diff --git a/target/linux/ipq806x/patches-4.0/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.0/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644 (file)
index 0000000..95398e0
--- /dev/null
@@ -0,0 +1,266 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326181
+Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:16 +0300
+
+Document Qualcomm PCIe driver devicetree bindings.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+.../devicetree/bindings/pci/qcom,pcie.txt          |  231 ++++++++++++++++++++
+ 1 files changed, 231 insertions(+), 0 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt
+
+diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+new file mode 100644
+index 0000000..dcf7348
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+@@ -0,0 +1,231 @@
++* Qualcomm PCI express root complex
++
++- compatible:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Value shall include
++                  - "qcom,pcie-v0" for apq/ipq8064
++                  - "qcom,pcie-v1" 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 set to 3. As specified in designware-pcie.txt
++
++- #size-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: Should be set 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
++                  * should be populated for v0 and v1
++                      - "iface"      Configuration AHB clock
++
++                  * should be populated for v0
++                      - "core"       Clocks the pcie hw block
++                      - "phy"        Clocks the pcie PHY block
++
++                  * should be populated for v1
++                      - "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
++      Value type: <stringlist>
++      Definition: Should contain the following entries
++                  * should be populated for v0
++                      - "axi"  AXI reset
++                      - "ahb"  AHB reset
++                      - "por"  POR reset
++                      - "pci"  PCI reset
++                      - "phy"  PHY reset
++
++                  * should be populated for v1
++                      - "core" Core reset
++
++- power-domains:
++      Usage: required (for v1 only)
++      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
++
++- <name>-supply:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles to the power supply regulator(s)
++                  * should be populated for v0 and v1
++                      - "vdda"        core analog power supply
++
++                  * should be populated for v0
++                      - "vdda_phy"    analog power supply for PHY
++                      - "vdda_refclk" analog power supply for IC which generate
++                                      reference clock
++
++- phys:
++      Usage: required (for v1 only)
++      Value type: <phandle>
++      Definition: List of phandle(s) as listed in phy-names property
++
++- phy-names:
++      Usage: required (for v1 only)
++      Value type: <stringlist>
++      Definition: Should contain "pciephy"
++
++- <name>-gpio:
++      Usage: optional
++      Value type: <prop-encoded-array>
++      Definition: List of phandle and gpio specifier pairs. Should contain
++                  - "perst"  PCIe endpoint reset signal line
++                  - "pewake" PCIe endpoint wake signal line
++
++- pinctrl-0:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles pointing at a pin(s) configuration
++
++- pinctrl-names
++      Usage: required
++      Value type: <stringlist>
++      Definition: List of names of pinctrl-0 state
++
++* Example for v0
++      pcie0: pci@1b500000 {
++              compatible = "qcom,pcie-v0";
++              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 0x00000000 0x08000000 0 0x07e00000>; /* memory */
++              interrupts = <GIC_SPI 35 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";
++      };
++
++* Example for v1
++      pcie0@fc520000 {
++              compatible = "qcom,pcie-v1";
++              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";
++      };
diff --git a/target/linux/ipq806x/patches-4.0/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.0/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644 (file)
index 0000000..b64b759
--- /dev/null
@@ -0,0 +1,753 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326161
+Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:17 +0300
+
+The PCIe driver reuse the Designware common code for host
+and MSI initialization, and also program the Qualcomm
+application specific registers.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+MAINTAINERS                  |    7 +
+ drivers/pci/host/Kconfig     |    9 +
+ drivers/pci/host/Makefile    |    1 +
+ drivers/pci/host/pcie-qcom.c |  677 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 694 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/pci/host/pcie-qcom.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7511,6 +7511,13 @@ L:      linux-pci@vger.kernel.org
+ S:    Maintained
+ F:    drivers/pci/host/*spear*
++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
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -106,4 +106,13 @@ config PCI_VERSATILE
+       bool "ARM Versatile PB PCI controller"
+       depends on ARCH_VERSATILE
++config PCIE_QCOM
++      bool "Qualcomm PCIe controller"
++      depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
++      select PCIE_DW
++      select PCIEPORTBUS
++      help
++        Say Y here to enable PCIe controller support on Qualcomm SoCs. The
++        PCIe controller use Designware core plus Qualcomm specific hardware
++        wrappers.
+ endmenu
+--- /dev/null
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -0,0 +1,677 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * 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/kernel.h>
++#include <linux/module.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_STTS                  0x08
++#define XMLH_LINK_UP                          BIT(10)
++
++#define PCIE20_CAP                            0x70
++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
++
++#define PERST_DELAY_MIN_US                    1000
++#define PERST_DELAY_MAX_US                    1005
++
++#define LINKUP_DELAY_MIN_US                   5000
++#define LINKUP_DELAY_MAX_US                   5100
++#define LINKUP_RETRIES_COUNT                  20
++
++#define PCIE_V0                                       0       /* apq8064 */
++#define PCIE_V1                                       1       /* apq8084 */
++
++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 pcie_resources {
++      struct qcom_pcie_resources_v0 v0;
++      struct qcom_pcie_resources_v1 v1;
++};
++
++struct qcom_pcie {
++      struct pcie_port pp;
++      struct device *dev;
++      union pcie_resources res;
++      void __iomem *parf;
++      void __iomem *dbi;
++      void __iomem *elbi;
++      struct phy *phy;
++      struct gpio_desc *reset;
++      unsigned int version;
++};
++
++#define to_qcom_pcie(x)               container_of(x, struct qcom_pcie, pp)
++
++static inline void
++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
++{
++      u32 val = readl(addr);
++
++      val &= ~clear_mask;
++      val |= set_mask;
++      writel(val, addr);
++}
++
++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
++{
++      int val, active_low;
++
++      if (IS_ERR_OR_NULL(pcie->reset))
++              return;
++
++      active_low = gpiod_is_active_low(pcie->reset);
++
++      if (assert)
++              val = !!active_low;
++      else
++              val = !active_low;
++
++      gpiod_set_value(pcie->reset, val);
++
++      usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
++}
++
++static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 1);
++}
++
++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 0);
++}
++
++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_link_up(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
++
++      return val & BIT(29) ? 1 : 0;
++}
++
++static void qcom_pcie_disable_resources_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 void qcom_pcie_disable_resources_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_enable_resources_v0(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      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 = clk_prepare_enable(res->iface_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable iface clock\n");
++              goto err_iface;
++      }
++
++      ret = clk_prepare_enable(res->core_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable core clock\n");
++              goto err_clk_core;
++      }
++
++      ret = clk_prepare_enable(res->phy_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable phy clock\n");
++              goto err_clk_phy;
++      }
++
++      ret = reset_control_deassert(res->ahb_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert ahb reset\n");
++              goto err_reset_ahb;
++      }
++
++      return 0;
++
++err_reset_ahb:
++      clk_disable_unprepare(res->phy_clk);
++err_clk_phy:
++      clk_disable_unprepare(res->core_clk);
++err_clk_core:
++      clk_disable_unprepare(res->iface_clk);
++err_iface:
++      regulator_disable(res->vdda_phy);
++err_vdda_phy:
++      regulator_disable(res->vdda_refclk);
++err_refclk:
++      regulator_disable(res->vdda);
++      return ret;
++}
++
++static int qcom_pcie_enable_resources_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;
++      }
++
++      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_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) && PTR_ERR(res->aux) == -EPROBE_DEFER)
++              return -EPROBE_DEFER;
++      else if (IS_ERR(res->aux))
++              res->aux = NULL;
++
++      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 int qcom_pcie_enable_link_training(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct device *dev = pp->dev;
++      int retries;
++      u32 val;
++
++      /* enable link training */
++      writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
++
++      /* wait for up to 100ms for the link to come up */
++      retries = LINKUP_RETRIES_COUNT;
++      do {
++              val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
++              if (val & XMLH_LINK_UP)
++                      break;
++              usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
++      } while (retries--);
++
++      if (retries < 0 || !dw_pcie_link_up(pp)) {
++              dev_err(dev, "link initialization failed\n");
++              return -ENXIO;
++      }
++
++      return 0;
++}
++
++static void qcom_pcie_host_init_v1(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v1(pcie);
++      if (ret)
++              return;
++
++      /* change DBI base address */
++      writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
++                            0, BIT(31));
++
++      ret = phy_init(pcie->phy);
++      if (ret)
++              goto err_res;
++
++      ret = phy_power_on(pcie->phy);
++      if (ret)
++              goto err_phy;
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++
++err:
++      qcom_ep_reset_assert(pcie);
++      phy_power_off(pcie->phy);
++err_phy:
++      phy_exit(pcie->phy);
++err_res:
++      qcom_pcie_disable_resources_v1(pcie);
++}
++
++static void qcom_pcie_host_init_v0(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v0(pcie);
++      if (ret)
++              return;
++
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
++
++      /* enable external reference clock */
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
++
++      ret = reset_control_deassert(res->phy_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert phy reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->pci_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert pci reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->por_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert por reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->axi_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert axi reset\n");
++              return;
++      }
++
++      /* wait 150ms for clock acquisition */
++      usleep_range(10000, 15000);
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++err:
++      qcom_ep_reset_assert(pcie);
++      qcom_pcie_disable_resources_v0(pcie);
++}
++
++static void qcom_pcie_host_init(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++
++      if (pcie->version == PCIE_V0)
++              return qcom_pcie_host_init_v0(pp);
++      else
++              return qcom_pcie_host_init_v1(pp);
++}
++
++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 &= ~(0xffff << 16);
++              *val |= PCI_CLASS_BRIDGE_PCI << 16;
++              return PCIBIOS_SUCCESSFUL;
++      }
++
++      return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
++                              size, val);
++}
++
++static struct pcie_host_ops qcom_pcie_ops = {
++      .link_up = qcom_pcie_link_up,
++      .host_init = qcom_pcie_host_init,
++      .rd_own_conf = qcom_pcie_rd_own_conf,
++};
++
++static const struct of_device_id qcom_pcie_match[] = {
++      { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
++      { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
++      { }
++};
++
++static int qcom_pcie_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      const struct of_device_id *match;
++      struct resource *res;
++      struct qcom_pcie *pcie;
++      struct pcie_port *pp;
++      int ret;
++
++      match = of_match_node(qcom_pcie_match, dev->of_node);
++      if (!match)
++              return -ENXIO;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pcie->version = (unsigned int)match->data;
++
++      pcie->reset = devm_gpiod_get_optional(dev, "perst");
++      if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
++              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);
++
++      pcie->dev = dev;
++
++      if (pcie->version == PCIE_V0)
++              ret = qcom_pcie_get_resources_v0(pcie);
++      else
++              ret = qcom_pcie_get_resources_v1(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_ops;
++
++      if (IS_ENABLED(CONFIG_PCI_MSI)) {
++              pp->msi_irq = platform_get_irq_byname(pdev, "msi");
++              if (pp->msi_irq < 0) {
++                      dev_err(dev, "cannot get msi irq\n");
++                      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 = 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);
++      if (pcie->version == PCIE_V0)
++              qcom_pcie_disable_resources_v0(pcie);
++      else
++              qcom_pcie_disable_resources_v1(pcie);
++
++      return 0;
++}
++
++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");
++MODULE_ALIAS("platform:qcom-pcie");
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -13,3 +13,4 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx
+ obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-4.0/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.0/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644 (file)
index 0000000..593bc32
--- /dev/null
@@ -0,0 +1,260 @@
+From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:01:42 -0700
+Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms
+
+qcom-pcie driver now supports version 0 of the controller. This change
+adds the corresponding entries to the IPQ806x dtsi file and
+corresponding platform (AP148).
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts |  30 ++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi      | 124 +++++++++++++++++++++++++++++++
+ 2 files changed, 154 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -30,6 +30,22 @@
+                               bias-disable;
+                       };
++                      pcie1_pins: pcie1_pinmux {
++                              mux {
++                                      pins = "gpio3";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie2_pins: pcie2_pinmux {
++                              mux {
++                                      pins = "gpio48";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
+                       spi_pins: spi_pins {
+                               mux {
+                                       pins = "gpio18", "gpio19", "gpio21";
+@@ -112,5 +128,19 @@
+               sata@29000000 {
+                       status = "ok";
+               };
++
++              pcie0: pci@1b500000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 3 0>;
++                      pinctrl-0 = <&pcie1_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 48 0>;
++                      pinctrl-0 = <&pcie2_pins>;
++                      pinctrl-names = "default";
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -37,6 +37,30 @@
+                               bias-disable;
+                       };
++                      pcie1_pins: pcie1_pinmux {
++                              mux {
++                                      pins = "gpio3";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie2_pins: pcie2_pinmux {
++                              mux {
++                                      pins = "gpio48";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie3_pins: pcie3_pinmux {
++                              mux {
++                                      pins = "gpio63";
++                                      drive-strength = <2>;
++                                      bias-disable;
++                              };
++                      };
++
+                       spi_pins: spi_pins {
+                               mux {
+                                       pins = "gpio18", "gpio19", "gpio21";
+@@ -153,6 +177,27 @@
+                       status = "ok";
+               };
++              pcie0: pci@1b500000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 3 0>;
++                      pinctrl-0 = <&pcie1_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 48 0>;
++                      pinctrl-0 = <&pcie2_pins>;
++                      pinctrl-names = "default";
++              };
++
++              pcie2: pci@1b900000 {
++                      status = "ok";
++                      reset-gpio = <&qcom_pinmux 63 0>;
++                      pinctrl-0 = <&pcie3_pins>;
++                      pinctrl-names = "default";
++              };
++
+               mdio0: mdio {
+                       compatible = "virtual,mdio-gpio";
+                       #address-cells = <1>;
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -3,6 +3,7 @@
+ #include "skeleton.dtsi"
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ / {
+       model = "Qualcomm IPQ8064";
+@@ -291,5 +292,128 @@
+                       #clock-cells = <1>;
+                       #reset-cells = <1>;
+               };
++
++              pcie0: pci@1b500000 {
++                      compatible = "qcom,pcie-v0";
++                      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   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 35 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";
++
++                      status = "disabled";
++              };
++
++              pcie1: pci@1b700000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b700000 0x1000
++                             0x1b702000 0x80
++                             0x1b800000 0x100
++                             0x31f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <1>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0          0x31e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_1_A_CLK>,
++                               <&gcc PCIE_1_H_CLK>,
++                               <&gcc PCIE_1_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_1_ACLK_RESET>,
++                               <&gcc PCIE_1_HCLK_RESET>,
++                               <&gcc PCIE_1_POR_RESET>,
++                               <&gcc PCIE_1_PCI_RESET>,
++                               <&gcc PCIE_1_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      status = "disabled";
++              };
++
++              pcie2: pci@1b900000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b900000 0x1000
++                             0x1b902000 0x80
++                             0x1ba00000 0x100
++                             0x35f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <2>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0          0x35e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x00000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_2_A_CLK>,
++                               <&gcc PCIE_2_H_CLK>,
++                               <&gcc PCIE_2_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_2_ACLK_RESET>,
++                               <&gcc PCIE_2_HCLK_RESET>,
++                               <&gcc PCIE_2_POR_RESET>,
++                               <&gcc PCIE_2_PCI_RESET>,
++                               <&gcc PCIE_2_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      status = "disabled";
++              };
+       };
+ };
diff --git a/target/linux/ipq806x/patches-4.0/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.0/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644 (file)
index 0000000..e2d3135
--- /dev/null
@@ -0,0 +1,29 @@
+From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:09:07 -0700
+Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is
+ enabled
+
+If multiple PCIe devices are present in the system, the kernel will
+panic at boot time when trying to scan the PCI buses. This happens on
+IPQ806x based platforms, which has 3 PCIe ports.
+
+Enabling this option allows the kernel to assign the pci-domains
+according to the device-tree content. This allows multiple PCIe
+controllers to coexist in the system.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -5,6 +5,7 @@ menuconfig ARCH_QCOM
+       select ARM_AMBA
+       select PINCTRL
+       select QCOM_SCM if SMP
++      select PCI_DOMAINS if PCI
+       help
+         Support for Qualcomm's devicetree based systems.
index 89ebe66946f0deac63f200f821aa2da4b5146347..16defd135d9c4a714bed46d574d3a503ac3d63dd 100644 (file)
@@ -1,7 +1,7 @@
 --- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
-@@ -18,8 +18,15 @@
-               bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2";
+@@ -22,8 +22,15 @@
+               linux,stdout-path = "serial0:115200n8";
        };
  
 +      aliases {
@@ -16,7 +16,7 @@
                        i2c4_pins: i2c4_pinmux {
                                pins = "gpio12", "gpio13";
                                function = "gsbi4";
-@@ -34,6 +41,25 @@
+@@ -54,6 +61,25 @@
                                        bias-none;
                                };
                        };
                };
  
                gsbi@16300000 {
-@@ -72,6 +98,7 @@
+@@ -92,6 +118,7 @@
                                        #size-cells = <1>;
                                        spi-max-frequency = <50000000>;
                                        reg = <0>;
 +                                      m25p,fast-read;
  
                                        partition@0 {
-                                               label = "0:SBL1";
-@@ -148,5 +175,66 @@
-               sata@29000000 {
-                       status = "ok";
+                                               label = "lowlevel_init";
+@@ -142,5 +169,66 @@
+                       pinctrl-0 = <&pcie2_pins>;
+                       pinctrl-names = "default";
                };
 +
 +              mdio0: mdio {
  };
 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
-@@ -3,6 +3,7 @@
- #include "skeleton.dtsi"
+@@ -4,6 +4,7 @@
  #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
  #include <dt-bindings/soc/qcom,gsbi.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
 +#include <dt-bindings/interrupt-controller/arm-gic.h>
  
  / {
        model = "Qualcomm IPQ8064";
-@@ -279,5 +280,42 @@
-                       #clock-cells = <1>;
-                       #reset-cells = <1>;
+@@ -415,5 +416,42 @@
+                       status = "disabled";
                };
 +
 +              nss-gmac-common {