summaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.1
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2015-08-04 23:09:38 +0000
committerFelix Fietkau <nbd@openwrt.org>2015-08-04 23:09:38 +0000
commit4d1656e813336c0fb6f3eb8d0280637e93d0ce74 (patch)
tree0a78af3ad31687344f0f560693a81e403d19d0b0 /target/linux/ipq806x/patches-4.1
parent991e641a7651ae45bcd607a081c606b5b9f54053 (diff)
downloadmaster-31e0f0ae-4d1656e813336c0fb6f3eb8d0280637e93d0ce74.tar.gz
master-31e0f0ae-4d1656e813336c0fb6f3eb8d0280637e93d0ce74.tar.bz2
master-31e0f0ae-4d1656e813336c0fb6f3eb8d0280637e93d0ce74.zip
ipq806x: update bleeding-edge kernel from 4.0 to 4.1
Default kernel doesn't change and stays on 3.18 for now. Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> SVN-Revision: 46554
Diffstat (limited to 'target/linux/ipq806x/patches-4.1')
-rw-r--r--target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch46
-rw-r--r--target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch35
-rw-r--r--target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch160
-rw-r--r--target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch53
-rw-r--r--target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch263
-rw-r--r--target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch753
-rw-r--r--target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch260
-rw-r--r--target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch29
-rw-r--r--target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch87
-rw-r--r--target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch144
-rw-r--r--target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch192
-rw-r--r--target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch130
-rw-r--r--target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch164
-rw-r--r--target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch351
-rw-r--r--target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch206
-rw-r--r--target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch127
-rw-r--r--target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch271
-rw-r--r--target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch205
-rw-r--r--target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch435
-rw-r--r--target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch304
-rw-r--r--target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch100
-rw-r--r--target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch461
-rw-r--r--target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch62
-rw-r--r--target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch734
-rw-r--r--target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch105
-rw-r--r--target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch65
-rw-r--r--target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch64
-rw-r--r--target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch407
-rw-r--r--target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch52
-rw-r--r--target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch145
-rw-r--r--target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch210
31 files changed, 6620 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch
new file mode 100644
index 0000000000..c0ad74c492
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch
@@ -0,0 +1,46 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -14,6 +14,14 @@
+ };
+ };
+
++ alias {
++ serial0 = &uart4;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
+ soc {
+ pinmux@800000 {
+ i2c4_pins: i2c4_pinmux {
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -159,7 +159,7 @@
+
+ syscon-tcsr = <&tcsr>;
+
+- serial@12490000 {
++ uart2: serial@12490000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x12490000 0x1000>,
+ <0x12480000 0x1000>;
+@@ -197,7 +197,7 @@
+
+ syscon-tcsr = <&tcsr>;
+
+- serial@16340000 {
++ uart4: serial@16340000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16340000 0x1000>,
+ <0x16300000 0x1000>;
+@@ -234,7 +234,7 @@
+
+ syscon-tcsr = <&tcsr>;
+
+- serial@1a240000 {
++ uart5: serial@1a240000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x1a240000 0x1000>,
+ <0x1a200000 0x1000>;
diff --git a/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch
new file mode 100644
index 0000000000..34eb9c0dfa
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch
@@ -0,0 +1,35 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -78,13 +78,28 @@
+ reg = <0>;
+
+ partition@0 {
+- label = "rootfs";
+- reg = <0x0 0x1000000>;
++ label = "lowlevel_init";
++ reg = <0x0 0x1b0000>;
+ };
+
+ partition@1 {
+- label = "scratch";
+- reg = <0x1000000 0x1000000>;
++ label = "u-boot";
++ reg = <0x1b0000 0x80000>;
++ };
++
++ partition@2 {
++ label = "u-boot-env";
++ reg = <0x230000 0x40000>;
++ };
++
++ partition@3 {
++ label = "caldata";
++ reg = <0x270000 0x40000>;
++ };
++
++ partition@4 {
++ label = "firmware";
++ reg = <0x2b0000 0x1d50000>;
+ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch
new file mode 100644
index 0000000000..7d8c8e8a81
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch
@@ -0,0 +1,160 @@
+From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 7 Apr 2015 19:58:58 -0700
+Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree
+
+Add basic DB149 (IPQ806x based platform) device-tree. It supports UART,
+SATA, USB2, USB3 and NOR flash.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++
+ 2 files changed, 133 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -451,6 +451,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8084-ifc6540.dtb \
+ qcom-apq8084-mtp.dtb \
+ qcom-ipq8064-ap148.dtb \
++ qcom-ipq8064-db149.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb \
+ qcom-msm8974-sony-xperia-honami.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -0,0 +1,132 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++/ {
++ model = "Qualcomm IPQ8064/DB149";
++ compatible = "qcom,ipq8064-db149", "qcom,ipq8064";
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ alias {
++ serial0 = &uart2;
++ };
++
++ chosen {
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ i2c4_pins: i2c4_pinmux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ bias-disable;
++ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ drive-strength = <10>;
++ bias-none;
++ };
++ };
++ };
++
++ gsbi2: gsbi@12480000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ uart2: serial@12490000 {
++ status = "ok";
++ };
++ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi4: spi@1a280000 {
++ status = "ok";
++ spi-max-frequency = <50000000>;
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 0>;
++
++ flash: m25p80@0 {
++ compatible = "s25fl256s1";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++ m25p,fast-read;
++
++ partition@0 {
++ label = "lowlevel_init";
++ reg = <0x0 0x1b0000>;
++ };
++
++ partition@1 {
++ label = "u-boot";
++ reg = <0x1b0000 0x80000>;
++ };
++
++ partition@2 {
++ label = "u-boot-env";
++ reg = <0x230000 0x40000>;
++ };
++
++ partition@3 {
++ label = "caldata";
++ reg = <0x270000 0x40000>;
++ };
++
++ partition@4 {
++ label = "firmware";
++ reg = <0x2b0000 0x1d50000>;
++ };
++ };
++ };
++ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
++ };
++};
diff --git a/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
new file mode 100644
index 0000000000..6b296377b6
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
@@ -0,0 +1,53 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -46,15 +46,12 @@
+ serial@16340000 {
+ status = "ok";
+ };
+-
+- i2c4: i2c@16380000 {
+- status = "ok";
+-
+- clock-frequency = <200000>;
+-
+- pinctrl-0 = <&i2c4_pins>;
+- pinctrl-names = "default";
+- };
++ /*
++ * The i2c device on gsbi4 should not be enabled.
++ * On ipq806x designs gsbi4 i2c is meant for exclusive
++ * RPM usage. Turning this on in kernel manifests as
++ * i2c failure for the RPM.
++ */
+ };
+
+ gsbi5: gsbi@1a200000 {
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -807,7 +807,7 @@ static struct clk_rcg gsbi7_qup_src = {
+ .parent_names = gcc_pxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+- .flags = CLK_SET_PARENT_GATE,
++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
+ },
+ },
+ };
+@@ -823,7 +823,7 @@ static struct clk_branch gsbi7_qup_clk =
+ .parent_names = (const char *[]){ "gsbi7_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+- .flags = CLK_SET_RATE_PARENT,
++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+ },
+ };
+@@ -871,7 +871,7 @@ static struct clk_branch gsbi4_h_clk = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_h_clk",
+ .ops = &clk_branch_ops,
+- .flags = CLK_IS_ROOT,
++ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
+ },
+ },
+ };
diff --git a/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644
index 0000000000..41f91fae7c
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
@@ -0,0 +1,263 @@
+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
+
+--- /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.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644
index 0000000000..dbf16a303a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
@@ -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
+@@ -7599,6 +7599,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
+@@ -125,4 +125,13 @@ config PCIE_IPROC_PLATFORM
+ Say Y here if you want to use the Broadcom iProc PCIe controller
+ through the generic platform bus interface
+
++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
+@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-laye
+ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+ obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
+ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000000..e7de2bb097
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
@@ -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";
+@@ -109,5 +125,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
+@@ -30,6 +30,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";
+@@ -128,5 +152,26 @@
+ 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";
++ };
++
++ pcie2: pci@1b900000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 63 0>;
++ pinctrl-0 = <&pcie3_pins>;
++ pinctrl-names = "default";
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -4,6 +4,8 @@
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
++#include <include/dt-bindings/interrupt-controller/arm-gic.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -329,5 +331,128 @@
+ #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.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644
index 0000000000..e2d31354ed
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
@@ -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.
+
diff --git a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch
new file mode 100644
index 0000000000..be80f4c02f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch
@@ -0,0 +1,87 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -2,6 +2,7 @@
+
+ #include "skeleton.dtsi"
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
++#include <dt-bindings/mfd/qcom-rpm.h>
+ #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+@@ -92,6 +93,63 @@
+ reg-names = "lpass-lpaif";
+ };
+
++ rpm@108000 {
++ compatible = "qcom,rpm-ipq8064";
++ reg = <0x108000 0x1000>;
++ qcom,ipc = <&l2cc 0x8 2>;
++
++ interrupts = <0 19 0>,
++ <0 21 0>,
++ <0 22 0>;
++ interrupt-names = "ack",
++ "err",
++ "wakeup";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ smb208_s1a: smb208-s1a {
++ compatible = "qcom,rpm-smb208";
++ reg = <QCOM_RPM_SMB208_S1a>;
++
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1150000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++
++ };
++
++ smb208_s1b: smb208-s1b {
++ compatible = "qcom,rpm-smb208";
++ reg = <QCOM_RPM_SMB208_S1b>;
++
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1150000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++
++ smb208_s2a: smb208-s2a {
++ compatible = "qcom,rpm-smb208";
++ reg = <QCOM_RPM_SMB208_S2a>;
++
++ regulator-min-microvolt = < 800000>;
++ regulator-max-microvolt = <1250000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++
++ smb208_s2b: smb208-s2b {
++ compatible = "qcom,rpm-smb208";
++ reg = <QCOM_RPM_SMB208_S2b>;
++
++ regulator-min-microvolt = < 800000>;
++ regulator-max-microvolt = <1250000>;
++
++ qcom,switch-mode-frequency = <1200000>;
++ };
++ };
++
+ qcom_pinmux: pinmux@800000 {
+ compatible = "qcom,ipq8064-pinctrl";
+ reg = <0x800000 0x4000>;
+@@ -136,6 +194,12 @@
+ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+ };
+
++ l2cc: clock-controller@2011000 {
++ compatible = "qcom,kpss-gcc", "syscon";
++ reg = <0x2011000 0x1000>;
++ clock-output-names = "acpu_l2_aux";
++ };
++
+ saw0: regulator@2089000 {
+ compatible = "qcom,saw2";
+ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
diff --git a/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch
new file mode 100644
index 0000000000..36a92c858a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch
@@ -0,0 +1,144 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063051
+Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>,
+ Mark Rutland <mark.rutland@arm.com>, Russell King <linux@arm.linux.org.uk>,
+ Courtney Cavin <courtney.cavin@sonymobile.com>
+Date: Fri, 20 Mar 2015 23:45:20 -0700
+
+Krait CPUs have a handful of L2 cache controller registers that
+live behind a cp15 based indirection register. First you program
+the indirection register (l2cpselr) to point the L2 'window'
+register (l2cpdr) at what you want to read/write. Then you
+read/write the 'window' register to do what you want. The
+l2cpselr register is not banked per-cpu so we must lock around
+accesses to it to prevent other CPUs from re-pointing l2cpdr
+underneath us.
+
+Cc: Mark Rutland <mark.rutland@arm.com>
+Cc: Russell King <linux@arm.linux.org.uk>
+Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+arch/arm/common/Kconfig | 3 ++
+ arch/arm/common/Makefile | 1 +
+ arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++
+ arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
+ 4 files changed, 82 insertions(+)
+ create mode 100644 arch/arm/common/krait-l2-accessors.c
+ create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
+
+--- a/arch/arm/common/Kconfig
++++ b/arch/arm/common/Kconfig
+@@ -9,6 +9,9 @@ config DMABOUNCE
+ bool
+ select ZONE_DMA
+
++config KRAIT_L2_ACCESSORS
++ bool
++
+ config SHARP_LOCOMO
+ bool
+
+--- a/arch/arm/common/Makefile
++++ b/arch/arm/common/Makefile
+@@ -7,6 +7,7 @@ obj-y += firmware.o
+ obj-$(CONFIG_ICST) += icst.o
+ obj-$(CONFIG_SA1111) += sa1111.o
+ obj-$(CONFIG_DMABOUNCE) += dmabounce.o
++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
+ obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
+ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+--- /dev/null
++++ b/arch/arm/common/krait-l2-accessors.c
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 2011-2013, 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/spinlock.h>
++#include <linux/export.h>
++
++#include <asm/barrier.h>
++#include <asm/krait-l2-accessors.h>
++
++static DEFINE_RAW_SPINLOCK(krait_l2_lock);
++
++void krait_set_l2_indirect_reg(u32 addr, u32 val)
++{
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&krait_l2_lock, flags);
++ /*
++ * Select the L2 window by poking l2cpselr, then write to the window
++ * via l2cpdr.
++ */
++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
++ isb();
++ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
++ isb();
++
++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
++}
++EXPORT_SYMBOL(krait_set_l2_indirect_reg);
++
++u32 krait_get_l2_indirect_reg(u32 addr)
++{
++ u32 val;
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&krait_l2_lock, flags);
++ /*
++ * Select the L2 window by poking l2cpselr, then read from the window
++ * via l2cpdr.
++ */
++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
++ isb();
++ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
++
++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
++
++ return val;
++}
++EXPORT_SYMBOL(krait_get_l2_indirect_reg);
+--- /dev/null
++++ b/arch/arm/include/asm/krait-l2-accessors.h
+@@ -0,0 +1,20 @@
++/*
++ * Copyright (c) 2011-2013, 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.
++ */
++
++#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
++#define __ASMARM_KRAIT_L2_ACCESSORS_H
++
++extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
++extern u32 krait_get_l2_indirect_reg(u32 addr);
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch
new file mode 100644
index 0000000000..3a475fba3d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch
@@ -0,0 +1,192 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,02/13] clk: mux: Split out register accessors for reuse
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063111
+Message-Id: <1426920332-9340-3-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:21 -0700
+
+We want to reuse the logic in clk-mux.c for other clock drivers
+that don't use readl as register accessors. Fortunately, there
+really isn't much to the mux code besides the table indirection
+and quirk flags if you assume any bit shifting and masking has
+been done already. Pull that logic out into reusable functions
+that operate on an optional table and some flags so that other
+drivers can use the same logic.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+drivers/clk/clk-mux.c | 76 +++++++++++++++++++++++++++-----------------
+ include/linux/clk-provider.h | 9 ++++--
+ 2 files changed, 54 insertions(+), 31 deletions(-)
+
+--- a/drivers/clk/clk-mux.c
++++ b/drivers/clk/clk-mux.c
+@@ -29,35 +29,24 @@
+
+ #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+-static u8 clk_mux_get_parent(struct clk_hw *hw)
++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
++ unsigned int *table, unsigned long flags)
+ {
+- struct clk_mux *mux = to_clk_mux(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+- u32 val;
+
+- /*
+- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+- * to 0x7 (index starts at one)
+- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+- * val = 0x4 really means "bit 2, index starts at bit 0"
+- */
+- val = clk_readl(mux->reg) >> mux->shift;
+- val &= mux->mask;
+-
+- if (mux->table) {
++ if (table) {
+ int i;
+
+ for (i = 0; i < num_parents; i++)
+- if (mux->table[i] == val)
++ if (table[i] == val)
+ return i;
+ return -EINVAL;
+ }
+
+- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
++ if (val && (flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
++ if (val && (flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= num_parents)
+@@ -65,24 +54,53 @@ static u8 clk_mux_get_parent(struct clk_
+
+ return val;
+ }
++EXPORT_SYMBOL_GPL(clk_mux_get_parent);
+
+-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
++static u8 _clk_mux_get_parent(struct clk_hw *hw)
+ {
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+- unsigned long flags = 0;
+
+- if (mux->table)
+- index = mux->table[index];
++ /*
++ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
++ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
++ * to 0x7 (index starts at one)
++ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
++ * val = 0x4 really means "bit 2, index starts at bit 0"
++ */
++ val = clk_readl(mux->reg) >> mux->shift;
++ val &= mux->mask;
++
++ return clk_mux_get_parent(hw, val, mux->table, mux->flags);
++}
+
+- else {
+- if (mux->flags & CLK_MUX_INDEX_BIT)
+- index = 1 << index;
++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
++ unsigned long flags)
++{
++ unsigned int val = index;
+
+- if (mux->flags & CLK_MUX_INDEX_ONE)
+- index++;
++ if (table) {
++ val = table[val];
++ } else {
++ if (flags & CLK_MUX_INDEX_BIT)
++ val = 1 << index;
++
++ if (flags & CLK_MUX_INDEX_ONE)
++ val++;
+ }
+
++ return val;
++}
++EXPORT_SYMBOL_GPL(clk_mux_reindex);
++
++static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
++{
++ struct clk_mux *mux = to_clk_mux(hw);
++ u32 val;
++ unsigned long flags = 0;
++
++ index = clk_mux_reindex(index, mux->table, mux->flags);
++
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+@@ -102,21 +120,21 @@ static int clk_mux_set_parent(struct clk
+ }
+
+ const struct clk_ops clk_mux_ops = {
+- .get_parent = clk_mux_get_parent,
++ .get_parent = _clk_mux_get_parent,
+ .set_parent = clk_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+ };
+ EXPORT_SYMBOL_GPL(clk_mux_ops);
+
+ const struct clk_ops clk_mux_ro_ops = {
+- .get_parent = clk_mux_get_parent,
++ .get_parent = _clk_mux_get_parent,
+ };
+ EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
+
+ struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
+ {
+ struct clk_mux *mux;
+ struct clk *clk;
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -409,7 +409,7 @@ void clk_unregister_divider(struct clk *
+ struct clk_mux {
+ struct clk_hw hw;
+ void __iomem *reg;
+- u32 *table;
++ unsigned int *table;
+ u32 mask;
+ u8 shift;
+ u8 flags;
+@@ -425,6 +425,11 @@ struct clk_mux {
+ extern const struct clk_ops clk_mux_ops;
+ extern const struct clk_ops clk_mux_ro_ops;
+
++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
++ unsigned int *table, unsigned long flags);
++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
++ unsigned long flags);
++
+ struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+@@ -433,7 +438,7 @@ struct clk *clk_register_mux(struct devi
+ struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
+
+ void clk_unregister_mux(struct clk *clk);
+
diff --git a/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
new file mode 100644
index 0000000000..8063860985
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
@@ -0,0 +1,130 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during
+ set_rate
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063271
+Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:22 -0700
+
+If a clock is on and we call clk_set_rate() on it we may get into
+a situation where the clock temporarily increases in rate
+dramatically while we walk the tree and call .set_rate() ops. For
+example, consider a case where a PLL feeds into a divider.
+Initially the divider is set to divide by 1 and the PLL is
+running fairly slow (100MHz). The downstream consumer of the
+divider output can only handle rates =< 400 MHz, but the divider
+can only choose between divisors of 1 and 4.
+
+ +-----+ +----------------+
+ | PLL |-->| div 1 or div 4 |---> consumer device
+ +-----+ +----------------+
+
+To achieve a rate of 400MHz on the output of the divider, we
+would have to set the rate of the PLL to 1.6 GHz and then divide
+it by 4. The current code would set the PLL to 1.6GHz first while
+the divider is still set to 1, thus causing the downstream
+consumer of the clock to receive a few clock cycles of 1.6GHz
+clock (far beyond it's maximum acceptable rate). We should be
+changing the divider first before increasing the PLL rate to
+avoid this problem.
+
+Therefore, set the rate of any child clocks that are increasing
+in rate from their current rate so that they can increase their
+dividers if necessary. We assume that there isn't such a thing as
+minimum rate requirements.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
+ 1 file changed, 22 insertions(+), 12 deletions(-)
+
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -1743,21 +1743,24 @@ static struct clk_core *clk_propagate_ra
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+-static void clk_change_rate(struct clk_core *clk)
++static void
++clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
+ {
+ struct clk_core *child;
+ struct hlist_node *tmp;
+ unsigned long old_rate;
+- unsigned long best_parent_rate = 0;
+ bool skip_set_rate = false;
+ struct clk_core *old_parent;
+
+- old_rate = clk->rate;
++ hlist_for_each_entry(child, &clk->children, child_node) {
++ /* Skip children who will be reparented to another clock */
++ if (child->new_parent && child->new_parent != clk)
++ continue;
++ if (child->new_rate > child->rate)
++ clk_change_rate(child, clk->new_rate);
++ }
+
+- if (clk->new_parent)
+- best_parent_rate = clk->new_parent->rate;
+- else if (clk->parent)
+- best_parent_rate = clk->parent->rate;
++ old_rate = clk->rate;
+
+ if (clk->new_parent && clk->new_parent != clk->parent) {
+ old_parent = __clk_set_parent_before(clk, clk->new_parent);
+@@ -1783,7 +1786,7 @@ static void clk_change_rate(struct clk_c
+
+ trace_clk_set_rate_complete(clk, clk->new_rate);
+
+- clk->rate = clk_recalc(clk, best_parent_rate);
++ clk->rate = clk->new_rate;
+
+ if (clk->notifier_count && old_rate != clk->rate)
+ __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+@@ -1796,12 +1799,13 @@ static void clk_change_rate(struct clk_c
+ /* Skip children who will be reparented to another clock */
+ if (child->new_parent && child->new_parent != clk)
+ continue;
+- clk_change_rate(child);
++ if (child->new_rate != child->rate)
++ clk_change_rate(child, clk->new_rate);
+ }
+
+ /* handle the new child who might not be in clk->children yet */
+- if (clk->new_child)
+- clk_change_rate(clk->new_child);
++ if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
++ clk_change_rate(clk->new_child, clk->new_rate);
+ }
+
+ static int clk_core_set_rate_nolock(struct clk_core *clk,
+@@ -1810,6 +1814,7 @@ static int clk_core_set_rate_nolock(stru
+ struct clk_core *top, *fail_clk;
+ unsigned long rate = req_rate;
+ int ret = 0;
++ unsigned long parent_rate;
+
+ if (!clk)
+ return 0;
+@@ -1835,8 +1840,13 @@ static int clk_core_set_rate_nolock(stru
+ return -EBUSY;
+ }
+
++ if (top->parent)
++ parent_rate = top->parent->rate;
++ else
++ parent_rate = 0;
++
+ /* change the rates */
+- clk_change_rate(top);
++ clk_change_rate(top, parent_rate);
+
+ clk->req_rate = req_rate;
+
diff --git a/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch
new file mode 100644
index 0000000000..0cf4eb331e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch
@@ -0,0 +1,164 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,04/13] clk: Add safe switch hook
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063211
+Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:23 -0700
+
+Sometimes clocks can't accept their parent source turning off
+while the source is reprogrammed to a different rate. Most
+notably CPU clocks require a way to switch away from the current
+PLL they're running on, reprogram that PLL to a new rate, and
+then switch back to the PLL with the new rate once they're done.
+Add a hook that drivers can implement allowing them to return a
+'safe parent' that they can switch their parent to while the
+upstream source is reprogrammed to support this.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+This patch is good enough for Krait, but soon I'll need to
+support a "safe rate" where we ask a clock what rate it needs to be running
+at to be sure it's within voltage constraints. Right now safe parent
+handles that problem on Krait, but on other platforms it won't work.
+
+ drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------
+ include/linux/clk-provider.h | 1 +
+ 2 files changed, 54 insertions(+), 8 deletions(-)
+
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -56,9 +56,12 @@ struct clk_core {
+ struct clk_core **parents;
+ u8 num_parents;
+ u8 new_parent_index;
++ u8 safe_parent_index;
+ unsigned long rate;
+ unsigned long req_rate;
++ unsigned long old_rate;
+ unsigned long new_rate;
++ struct clk_core *safe_parent;
+ struct clk_core *new_parent;
+ struct clk_core *new_child;
+ unsigned long flags;
+@@ -1595,7 +1598,8 @@ out:
+ static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate,
+ struct clk_core *new_parent, u8 p_index)
+ {
+- struct clk_core *child;
++ struct clk_core *child, *parent;
++ struct clk_hw *parent_hw;
+
+ clk->new_rate = new_rate;
+ clk->new_parent = new_parent;
+@@ -1605,6 +1609,18 @@ static void clk_calc_subtree(struct clk_
+ if (new_parent && new_parent != clk->parent)
+ new_parent->new_child = clk;
+
++ if (clk->ops->get_safe_parent) {
++ parent_hw = clk->ops->get_safe_parent(clk->hw);
++ if (parent_hw) {
++ parent = parent_hw->core;
++ p_index = clk_fetch_parent_index(clk, parent);
++ clk->safe_parent_index = p_index;
++ clk->safe_parent = parent;
++ }
++ } else {
++ clk->safe_parent = NULL;
++ }
++
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ child->new_rate = clk_recalc(child, new_rate);
+ clk_calc_subtree(child, child->new_rate, NULL, 0);
+@@ -1709,14 +1725,43 @@ static struct clk_core *clk_propagate_ra
+ unsigned long event)
+ {
+ struct clk_core *child, *tmp_clk, *fail_clk = NULL;
++ struct clk_core *old_parent;
+ int ret = NOTIFY_DONE;
+
+- if (clk->rate == clk->new_rate)
++ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
+ return NULL;
+
++ switch (event) {
++ case PRE_RATE_CHANGE:
++ if (clk->safe_parent)
++ clk->ops->set_parent(clk->hw, clk->safe_parent_index);
++ clk->old_rate = clk->rate;
++ break;
++ case POST_RATE_CHANGE:
++ if (clk->safe_parent) {
++ old_parent = __clk_set_parent_before(clk,
++ clk->new_parent);
++ if (clk->ops->set_rate_and_parent) {
++ clk->ops->set_rate_and_parent(clk->hw,
++ clk->new_rate,
++ clk->new_parent ?
++ clk->new_parent->rate : 0,
++ clk->new_parent_index);
++ } else if (clk->ops->set_parent) {
++ clk->ops->set_parent(clk->hw,
++ clk->new_parent_index);
++ }
++ __clk_set_parent_after(clk, clk->new_parent,
++ old_parent);
++ }
++ break;
++ }
++
+ if (clk->notifier_count) {
+- ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
+- if (ret & NOTIFY_STOP_MASK)
++ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate)
++ ret = __clk_notify(clk, event, clk->old_rate,
++ clk->new_rate);
++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
+ fail_clk = clk;
+ }
+
+@@ -1762,7 +1807,8 @@ clk_change_rate(struct clk_core *clk, un
+
+ old_rate = clk->rate;
+
+- if (clk->new_parent && clk->new_parent != clk->parent) {
++ if (clk->new_parent && clk->new_parent != clk->parent &&
++ !clk->safe_parent) {
+ old_parent = __clk_set_parent_before(clk, clk->new_parent);
+ trace_clk_set_parent(clk, clk->new_parent);
+
+@@ -1788,9 +1834,6 @@ clk_change_rate(struct clk_core *clk, un
+
+ clk->rate = clk->new_rate;
+
+- if (clk->notifier_count && old_rate != clk->rate)
+- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+-
+ /*
+ * Use safe iteration, as change_rate can actually swap parents
+ * for certain clock types.
+@@ -1850,6 +1893,8 @@ static int clk_core_set_rate_nolock(stru
+
+ clk->req_rate = req_rate;
+
++ clk_propagate_rate_change(top, POST_RATE_CHANGE);
++
+ return ret;
+ }
+
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -183,6 +183,7 @@ struct clk_ops {
+ struct clk_hw **best_parent_hw);
+ int (*set_parent)(struct clk_hw *hw, u8 index);
+ u8 (*get_parent)(struct clk_hw *hw);
++ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw);
+ int (*set_rate)(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
+ int (*set_rate_and_parent)(struct clk_hw *hw,
diff --git a/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
new file mode 100644
index 0000000000..6fad6e8a0c
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
@@ -0,0 +1,351 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063261
+Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:24 -0700
+
+HFPLLs are the main frequency source for Krait CPU clocks. Add
+support for changing the rate of these PLLs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+I'd really like to get rid of __clk_hfpll_init_once() if possible...
+
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
+ 3 files changed, 308 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-hfpll.c
+ create mode 100644 drivers/clk/qcom/clk-hfpll.h
+
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+
+ obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-hfpll.c
+@@ -0,0 +1,253 @@
++/*
++ * Copyright (c) 2013-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/kernel.h>
++#include <linux/export.h>
++#include <linux/regmap.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++
++#include "clk-regmap.h"
++#include "clk-hfpll.h"
++
++#define PLL_OUTCTRL BIT(0)
++#define PLL_BYPASSNL BIT(1)
++#define PLL_RESET_N BIT(2)
++
++/* Initialize a HFPLL at a given rate and enable it. */
++static void __clk_hfpll_init_once(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++
++ if (likely(h->init_done))
++ return;
++
++ /* Configure PLL parameters for integer mode. */
++ if (hd->config_val)
++ regmap_write(regmap, hd->config_reg, hd->config_val);
++ regmap_write(regmap, hd->m_reg, 0);
++ regmap_write(regmap, hd->n_reg, 1);
++
++ if (hd->user_reg) {
++ u32 regval = hd->user_val;
++ unsigned long rate;
++
++ rate = __clk_get_rate(hw->clk);
++
++ /* Pick the right VCO. */
++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
++ regval |= hd->user_vco_mask;
++ regmap_write(regmap, hd->user_reg, regval);
++ }
++
++ if (hd->droop_reg)
++ regmap_write(regmap, hd->droop_reg, hd->droop_val);
++
++ h->init_done = true;
++}
++
++static void __clk_hfpll_enable(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 val;
++
++ __clk_hfpll_init_once(hw);
++
++ /* Disable PLL bypass mode. */
++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
++
++ /*
++ * H/W requires a 5us delay between disabling the bypass and
++ * de-asserting the reset. Delay 10us just to be safe.
++ */
++ udelay(10);
++
++ /* De-assert active-low PLL reset. */
++ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
++
++ /* Wait for PLL to lock. */
++ if (hd->status_reg) {
++ do {
++ regmap_read(regmap, hd->status_reg, &val);
++ } while (!(val & BIT(hd->lock_bit)));
++ } else {
++ udelay(60);
++ }
++
++ /* Enable PLL output. */
++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
++}
++
++/* Enable an already-configured HFPLL. */
++static int clk_hfpll_enable(struct clk_hw *hw)
++{
++ unsigned long flags;
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 mode;
++
++ spin_lock_irqsave(&h->lock, flags);
++ regmap_read(regmap, hd->mode_reg, &mode);
++ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
++ __clk_hfpll_enable(hw);
++ spin_unlock_irqrestore(&h->lock, flags);
++
++ return 0;
++}
++
++static void __clk_hfpll_disable(struct clk_hfpll *h)
++{
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++
++ /*
++ * Disable the PLL output, disable test mode, enable the bypass mode,
++ * and assert the reset.
++ */
++ regmap_update_bits(regmap, hd->mode_reg,
++ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
++}
++
++static void clk_hfpll_disable(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ unsigned long flags;
++
++ spin_lock_irqsave(&h->lock, flags);
++ __clk_hfpll_disable(h);
++ spin_unlock_irqrestore(&h->lock, flags);
++}
++
++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ unsigned long rrate;
++
++ rate = clamp(rate, hd->min_rate, hd->max_rate);
++
++ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
++ if (rrate > hd->max_rate)
++ rrate -= *parent_rate;
++
++ return rrate;
++}
++
++/*
++ * For optimization reasons, assumes no downstream clocks are actively using
++ * it.
++ */
++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ unsigned long flags;
++ u32 l_val, val;
++ bool enabled;
++
++ l_val = rate / parent_rate;
++
++ spin_lock_irqsave(&h->lock, flags);
++
++ enabled = __clk_is_enabled(hw->clk);
++ if (enabled)
++ __clk_hfpll_disable(h);
++
++ /* Pick the right VCO. */
++ if (hd->user_reg && hd->user_vco_mask) {
++ regmap_read(regmap, hd->user_reg, &val);
++ if (rate <= hd->low_vco_max_rate)
++ val &= ~hd->user_vco_mask;
++ else
++ val |= hd->user_vco_mask;
++ regmap_write(regmap, hd->user_reg, val);
++ }
++
++ regmap_write(regmap, hd->l_reg, l_val);
++
++ if (enabled)
++ __clk_hfpll_enable(hw);
++
++ spin_unlock_irqrestore(&h->lock, flags);
++
++ return 0;
++}
++
++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 l_val;
++
++ regmap_read(regmap, hd->l_reg, &l_val);
++
++ return l_val * parent_rate;
++}
++
++static void clk_hfpll_init(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 mode, status;
++
++ regmap_read(regmap, hd->mode_reg, &mode);
++ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
++ __clk_hfpll_init_once(hw);
++ return;
++ }
++
++ if (hd->status_reg) {
++ regmap_read(regmap, hd->status_reg, &status);
++ if (!(status & BIT(hd->lock_bit))) {
++ WARN(1, "HFPLL %s is ON, but not locked!\n",
++ __clk_get_name(hw->clk));
++ clk_hfpll_disable(hw);
++ __clk_hfpll_init_once(hw);
++ }
++ }
++}
++
++static int hfpll_is_enabled(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 mode;
++
++ regmap_read(regmap, hd->mode_reg, &mode);
++ mode &= 0x7;
++ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
++}
++
++const struct clk_ops clk_ops_hfpll = {
++ .enable = clk_hfpll_enable,
++ .disable = clk_hfpll_disable,
++ .is_enabled = hfpll_is_enabled,
++ .round_rate = clk_hfpll_round_rate,
++ .set_rate = clk_hfpll_set_rate,
++ .recalc_rate = clk_hfpll_recalc_rate,
++ .init = clk_hfpll_init,
++};
++EXPORT_SYMBOL_GPL(clk_ops_hfpll);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-hfpll.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (c) 2013-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.
++ */
++#ifndef __QCOM_CLK_HFPLL_H__
++#define __QCOM_CLK_HFPLL_H__
++
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++#include "clk-regmap.h"
++
++struct hfpll_data {
++ u32 mode_reg;
++ u32 l_reg;
++ u32 m_reg;
++ u32 n_reg;
++ u32 user_reg;
++ u32 droop_reg;
++ u32 config_reg;
++ u32 status_reg;
++ u8 lock_bit;
++
++ u32 droop_val;
++ u32 config_val;
++ u32 user_val;
++ u32 user_vco_mask;
++ unsigned long low_vco_max_rate;
++
++ unsigned long min_rate;
++ unsigned long max_rate;
++};
++
++struct clk_hfpll {
++ struct hfpll_data const *d;
++ int init_done;
++
++ struct clk_regmap clkr;
++ spinlock_t lock;
++};
++
++#define to_clk_hfpll(_hw) \
++ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
++
++extern const struct clk_ops clk_ops_hfpll;
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch
new file mode 100644
index 0000000000..4056784287
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch
@@ -0,0 +1,206 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,06/13] clk: qcom: Add HFPLL driver
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063231
+Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
+Date: Fri, 20 Mar 2015 23:45:25 -0700
+
+On some devices (MSM8974 for example), the HFPLLs are
+instantiated within the Krait processor subsystem as separate
+register regions. Add a driver for these PLLs so that we can
+provide HFPLL clocks for use by the system.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++
+ drivers/clk/qcom/Kconfig | 8 ++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++
+ 4 files changed, 158 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
+ create mode 100644 drivers/clk/qcom/hfpll.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
+@@ -0,0 +1,40 @@
++High-Frequency PLL (HFPLL)
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: must be "qcom,hfpll"
++
++- reg:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: address and size of HPLL registers. An optional second
++ element specifies the address and size of the alias
++ register region.
++
++- clock-output-names:
++ Usage: required
++ Value type: <string>
++ Definition: Name of the PLL. Typically hfpllX where X is a CPU number
++ starting at 0. Otherwise hfpll_Y where Y is more specific
++ such as "l2".
++
++Example:
++
++1) An HFPLL for the L2 cache.
++
++ clock-controller@f9016000 {
++ compatible = "qcom,hfpll";
++ reg = <0xf9016000 0x30>;
++ clock-output-names = "hfpll_l2";
++ };
++
++2) An HFPLL for CPU0. This HFPLL has the alias register region.
++
++ clock-controller@f908a000 {
++ compatible = "qcom,hfpll";
++ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
++ clock-output-names = "hfpll0";
++ };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -97,3 +97,11 @@ config MSM_MMCC_8974
+ Support for the multimedia clock controller on msm8974 devices.
+ Say Y if you want to support multimedia devices such as display,
+ graphics, video encode/decode, camera, etc.
++
++config QCOM_HFPLL
++ tristate "High-Frequency PLL (HFPLL) Clock Controller"
++ depends on COMMON_CLK_QCOM
++ help
++ Support for the high-frequency PLLs present on Qualcomm devices.
++ Say Y if you want to support CPU frequency scaling on devices
++ such as MSM8974, APQ8084, etc.
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
+ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+--- /dev/null
++++ b/drivers/clk/qcom/hfpll.c
+@@ -0,0 +1,109 @@
++/*
++ * Copyright (c) 2013-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/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/regmap.h>
++
++#include "clk-regmap.h"
++#include "clk-hfpll.h"
++
++static const struct hfpll_data hdata = {
++ .mode_reg = 0x00,
++ .l_reg = 0x04,
++ .m_reg = 0x08,
++ .n_reg = 0x0c,
++ .user_reg = 0x10,
++ .config_reg = 0x14,
++ .config_val = 0x430405d,
++ .status_reg = 0x1c,
++ .lock_bit = 16,
++
++ .user_val = 0x8,
++ .user_vco_mask = 0x100000,
++ .low_vco_max_rate = 1248000000,
++ .min_rate = 537600000UL,
++ .max_rate = 2900000000UL,
++};
++
++static const struct of_device_id qcom_hfpll_match_table[] = {
++ { .compatible = "qcom,hfpll" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
++
++static const struct regmap_config hfpll_regmap_config = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = 0x30,
++ .fast_io = true,
++};
++
++static int qcom_hfpll_probe(struct platform_device *pdev)
++{
++ struct clk *clk;
++ struct resource *res;
++ struct device *dev = &pdev->dev;
++ void __iomem *base;
++ struct regmap *regmap;
++ struct clk_hfpll *h;
++ struct clk_init_data init = {
++ .parent_names = (const char *[]){ "xo" },
++ .num_parents = 1,
++ .ops = &clk_ops_hfpll,
++ };
++
++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
++ if (!h)
++ return -ENOMEM;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ if (of_property_read_string_index(dev->of_node, "clock-output-names",
++ 0, &init.name))
++ return -ENODEV;
++
++ h->d = &hdata;
++ h->clkr.hw.init = &init;
++ spin_lock_init(&h->lock);
++
++ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr);
++
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct platform_driver qcom_hfpll_driver = {
++ .probe = qcom_hfpll_probe,
++ .driver = {
++ .name = "qcom-hfpll",
++ .of_match_table = qcom_hfpll_match_table,
++ },
++};
++module_platform_driver(qcom_hfpll_driver);
++
++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-hfpll");
diff --git a/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
new file mode 100644
index 0000000000..ffcd462d12
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
@@ -0,0 +1,127 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063241
+Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:27 -0700
+
+Describe the HFPLLs present on IPQ806X devices.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -30,6 +30,7 @@
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+ #include "clk-branch.h"
++#include "clk-hfpll.h"
+ #include "reset.h"
+
+ static struct clk_pll pll0 = {
+@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = {
+ },
+ };
+
++static struct hfpll_data hfpll0_data = {
++ .mode_reg = 0x3200,
++ .l_reg = 0x3208,
++ .m_reg = 0x320c,
++ .n_reg = 0x3210,
++ .config_reg = 0x3204,
++ .status_reg = 0x321c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3214,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll0 = {
++ .d = &hfpll0_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll0",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
++};
++
++static struct hfpll_data hfpll1_data = {
++ .mode_reg = 0x3240,
++ .l_reg = 0x3248,
++ .m_reg = 0x324c,
++ .n_reg = 0x3250,
++ .config_reg = 0x3244,
++ .status_reg = 0x325c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3314,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll1 = {
++ .d = &hfpll1_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll1",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
++};
++
++static struct hfpll_data hfpll_l2_data = {
++ .mode_reg = 0x3300,
++ .l_reg = 0x3308,
++ .m_reg = 0x330c,
++ .n_reg = 0x3310,
++ .config_reg = 0x3304,
++ .status_reg = 0x331c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3314,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll_l2 = {
++ .d = &hfpll_l2_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll_l2",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
++};
++
++
+ static struct clk_pll pll14 = {
+ .l_reg = 0x31c4,
+ .m_reg = 0x31c8,
+@@ -2307,6 +2387,9 @@ static struct clk_regmap *gcc_ipq806x_cl
+ [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
+ [EBI2_CLK] = &ebi2_clk.clkr,
+ [EBI2_AON_CLK] = &ebi2_aon_clk.clkr,
++ [PLL9] = &hfpll0.clkr,
++ [PLL10] = &hfpll1.clkr,
++ [PLL12] = &hfpll_l2.clkr,
+ };
+
+ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
diff --git a/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch
new file mode 100644
index 0000000000..5fcc787c89
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch
@@ -0,0 +1,271 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,09/13] clk: qcom: Add support for Krait clocks
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063251
+Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>
+Date: Fri, 20 Mar 2015 23:45:28 -0700
+
+The Krait clocks are made up of a series of muxes and a divider
+that choose between a fixed rate clock and dedicated HFPLLs for
+each CPU. Instead of using mmio accesses to remux parents, the
+Krait implementation exposes the remux control via cp15
+registers. Support these clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+drivers/clk/qcom/Kconfig | 4 ++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-krait.h | 49 +++++++++++++
+ 4 files changed, 220 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-krait.c
+ create mode 100644 drivers/clk/qcom/clk-krait.h
+
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -105,3 +105,7 @@ config QCOM_HFPLL
+ Support for the high-frequency PLLs present on Qualcomm devices.
+ Say Y if you want to support CPU frequency scaling on devices
+ such as MSM8974, APQ8084, etc.
++
++config KRAIT_CLOCKS
++ bool
++ select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
+ clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.c
+@@ -0,0 +1,166 @@
++/*
++ * Copyright (c) 2013-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/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++
++#include <asm/krait-l2-accessors.h>
++
++#include "clk-krait.h"
++
++/* Secondary and primary muxes share the same cp15 register */
++static DEFINE_SPINLOCK(krait_clock_reg_lock);
++
++#define LPL_SHIFT 8
++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
++{
++ unsigned long flags;
++ u32 regval;
++
++ spin_lock_irqsave(&krait_clock_reg_lock, flags);
++ regval = krait_get_l2_indirect_reg(mux->offset);
++ regval &= ~(mux->mask << mux->shift);
++ regval |= (sel & mux->mask) << mux->shift;
++ if (mux->lpl) {
++ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
++ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
++ }
++ krait_set_l2_indirect_reg(mux->offset, regval);
++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
++
++ /* Wait for switch to complete. */
++ mb();
++ udelay(1);
++}
++
++static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
++{
++ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++ u32 sel;
++
++ sel = clk_mux_reindex(index, mux->parent_map, 0);
++ mux->en_mask = sel;
++ /* Don't touch mux if CPU is off as it won't work */
++ if (__clk_is_enabled(hw->clk))
++ __krait_mux_set_sel(mux, sel);
++ return 0;
++}
++
++static u8 krait_mux_get_parent(struct clk_hw *hw)
++{
++ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++ u32 sel;
++
++ sel = krait_get_l2_indirect_reg(mux->offset);
++ sel >>= mux->shift;
++ sel &= mux->mask;
++ mux->en_mask = sel;
++
++ return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
++}
++
++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw)
++{
++ int i;
++ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++ int num_parents = __clk_get_num_parents(hw->clk);
++
++ i = mux->safe_sel;
++ for (i = 0; i < num_parents; i++)
++ if (mux->safe_sel == mux->parent_map[i])
++ break;
++
++ return __clk_get_hw(clk_get_parent_by_index(hw->clk, i));
++}
++
++static int krait_mux_enable(struct clk_hw *hw)
++{
++ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++
++ __krait_mux_set_sel(mux, mux->en_mask);
++
++ return 0;
++}
++
++static void krait_mux_disable(struct clk_hw *hw)
++{
++ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
++
++ __krait_mux_set_sel(mux, mux->safe_sel);
++}
++
++const struct clk_ops krait_mux_clk_ops = {
++ .enable = krait_mux_enable,
++ .disable = krait_mux_disable,
++ .set_parent = krait_mux_set_parent,
++ .get_parent = krait_mux_get_parent,
++ .determine_rate = __clk_mux_determine_rate_closest,
++ .get_safe_parent = krait_mux_get_safe_parent,
++};
++EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
++
++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ *parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), rate * 2);
++ return DIV_ROUND_UP(*parent_rate, 2);
++}
++
++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct krait_div2_clk *d = to_krait_div2_clk(hw);
++ unsigned long flags;
++ u32 val;
++ u32 mask = BIT(d->width) - 1;
++
++ if (d->lpl)
++ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
++
++ spin_lock_irqsave(&krait_clock_reg_lock, flags);
++ val = krait_get_l2_indirect_reg(d->offset);
++ val &= ~mask;
++ krait_set_l2_indirect_reg(d->offset, val);
++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
++
++ return 0;
++}
++
++static unsigned long
++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
++{
++ struct krait_div2_clk *d = to_krait_div2_clk(hw);
++ u32 mask = BIT(d->width) - 1;
++ u32 div;
++
++ div = krait_get_l2_indirect_reg(d->offset);
++ div >>= d->shift;
++ div &= mask;
++ div = (div + 1) * 2;
++
++ return DIV_ROUND_UP(parent_rate, div);
++}
++
++const struct clk_ops krait_div2_clk_ops = {
++ .round_rate = krait_div2_round_rate,
++ .set_rate = krait_div2_set_rate,
++ .recalc_rate = krait_div2_recalc_rate,
++};
++EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.h
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (c) 2013, 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.
++ */
++
++#ifndef __QCOM_CLK_KRAIT_H
++#define __QCOM_CLK_KRAIT_H
++
++#include <linux/clk-provider.h>
++
++struct krait_mux_clk {
++ unsigned int *parent_map;
++ bool has_safe_parent;
++ u8 safe_sel;
++ u32 offset;
++ u32 mask;
++ u32 shift;
++ u32 en_mask;
++ bool lpl;
++
++ struct clk_hw hw;
++};
++
++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
++
++extern const struct clk_ops krait_mux_clk_ops;
++
++struct krait_div2_clk {
++ u32 offset;
++ u8 width;
++ u32 shift;
++ bool lpl;
++
++ struct clk_hw hw;
++};
++
++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
++
++extern const struct clk_ops krait_div2_clk_ops;
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
new file mode 100644
index 0000000000..db4388812b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
@@ -0,0 +1,205 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063201
+Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
+Date: Fri, 20 Mar 2015 23:45:29 -0700
+
+The ACC and GCC regions present in KPSSv1 contain registers to
+control clocks and power to each Krait CPU and L2. For CPUfreq
+purposes probe these devices and expose a mux clock that chooses
+between PXO and PLL8.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++
+ .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++
+ drivers/clk/qcom/Kconfig | 8 ++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++
+ 5 files changed, 139 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
+ create mode 100644 drivers/clk/qcom/kpss-xcc.c
+
+--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+@@ -21,10 +21,17 @@ PROPERTIES
+ the register region. An optional second element specifies
+ the base address and size of the alias register region.
+
++- clock-output-names:
++ Usage: optional
++ Value type: <string>
++ Definition: Name of the output clock. Typically acpuX_aux where X is a
++ CPU number starting at 0.
++
+ Example:
+
+ clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0x02088000 0x1000>,
+ <0x02008000 0x1000>;
++ clock-output-names = "acpu0_aux";
+ };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
+@@ -0,0 +1,28 @@
++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: should be one of:
++ "qcom,kpss-gcc"
++
++- reg:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: base address and size of the register region
++
++- clock-output-names:
++ Usage: required
++ Value type: <string>
++ Definition: Name of the output clock. Typically acpu_l2_aux indicating
++ an L2 cache auxiliary clock.
++
++Example:
++
++ l2cc: clock-controller@2011000 {
++ compatible = "qcom,kpss-gcc";
++ reg = <0x2011000 0x1000>;
++ clock-output-names = "acpu_l2_aux";
++ };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -106,6 +106,14 @@ config QCOM_HFPLL
+ Say Y if you want to support CPU frequency scaling on devices
+ such as MSM8974, APQ8084, etc.
+
++config KPSS_XCC
++ tristate "KPSS Clock Controller"
++ depends on COMMON_CLK_QCOM
++ help
++ Support for the Krait ACC and GCC clock controllers. Say Y
++ if you want to support CPU frequency scaling on devices such
++ as MSM8960, APQ8064, etc.
++
+ config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -23,4 +23,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
+ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+--- /dev/null
++++ b/drivers/clk/qcom/kpss-xcc.c
+@@ -0,0 +1,95 @@
++/* Copyright (c) 2014-2015, 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/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++
++static const char *aux_parents[] = {
++ "pll8_vote",
++ "pxo",
++};
++
++static unsigned int aux_parent_map[] = {
++ 3,
++ 0,
++};
++
++static const struct of_device_id kpss_xcc_match_table[] = {
++ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
++ { .compatible = "qcom,kpss-gcc" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
++
++static int kpss_xcc_driver_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *id;
++ struct clk *clk;
++ struct resource *res;
++ void __iomem *base;
++ const char *name;
++
++ id = of_match_device(kpss_xcc_match_table, &pdev->dev);
++ if (!id)
++ return -ENODEV;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ if (id->data) {
++ if (of_property_read_string_index(pdev->dev.of_node,
++ "clock-output-names", 0, &name))
++ return -ENODEV;
++ base += 0x14;
++ } else {
++ name = "acpu_l2_aux";
++ base += 0x28;
++ }
++
++ clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
++ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
++ 0, aux_parent_map, NULL);
++
++ platform_set_drvdata(pdev, clk);
++
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static int kpss_xcc_driver_remove(struct platform_device *pdev)
++{
++ clk_unregister_mux(platform_get_drvdata(pdev));
++ return 0;
++}
++
++static struct platform_driver kpss_xcc_driver = {
++ .probe = kpss_xcc_driver_probe,
++ .remove = kpss_xcc_driver_remove,
++ .driver = {
++ .name = "kpss-xcc",
++ .of_match_table = kpss_xcc_match_table,
++ },
++};
++module_platform_driver(kpss_xcc_driver);
++
++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:kpss-xcc");
diff --git a/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644
index 0000000000..59fc44f1cb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch
@@ -0,0 +1,435 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063121
+Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
+Date: Fri, 20 Mar 2015 23:45:30 -0700
+
+The Krait CPU clocks are made up of a primary mux and secondary
+mux for each CPU and the L2, controlled via cp15 accessors. For
+Kraits within KPSSv1 each secondary mux accepts a different aux
+source, but on KPSSv2 each secondary mux accepts the same aux
+source.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
+ drivers/clk/qcom/Kconfig | 8 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++
+ 4 files changed, 383 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+ create mode 100644 drivers/clk/qcom/krait-cc.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+@@ -0,0 +1,22 @@
++Krait Clock Controller
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: must be one of:
++ "qcom,krait-cc-v1"
++ "qcom,krait-cc-v2"
++
++- #clock-cells:
++ Usage: required
++ Value type: <u32>
++ Definition: must be 1
++
++Example:
++
++ kraitcc: clock-controller {
++ compatible = "qcom,krait-cc-v1";
++ #clock-cells = <1>;
++ };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -114,6 +114,14 @@ config KPSS_XCC
+ if you want to support CPU frequency scaling on devices such
+ as MSM8960, APQ8064, etc.
+
++config KRAITCC
++ tristate "Krait Clock Controller"
++ depends on COMMON_CLK_QCOM && ARM
++ select KRAIT_CLOCKS
++ help
++ Support for the Krait CPU clocks on Qualcomm devices.
++ Say Y if you want to support CPU frequency scaling.
++
+ config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
++obj-$(CONFIG_KRAITCC) += krait-cc.o
+--- /dev/null
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -0,0 +1,352 @@
++/* Copyright (c) 2013-2015, 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/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/slab.h>
++
++#include "clk-krait.h"
++
++static unsigned int sec_mux_map[] = {
++ 2,
++ 0,
++};
++
++static unsigned int pri_mux_map[] = {
++ 1,
++ 2,
++ 0,
++};
++
++static int
++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
++{
++ struct krait_div2_clk *div;
++ struct clk_init_data init = {
++ .num_parents = 1,
++ .ops = &krait_div2_clk_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ const char *p_names[1];
++ struct clk *clk;
++
++ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
++ if (!div)
++ return -ENOMEM;
++
++ div->width = 2;
++ div->shift = 6;
++ div->lpl = id >= 0;
++ div->offset = offset;
++ div->hw.init = &init;
++
++ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++ if (!init.name)
++ return -ENOMEM;
++
++ init.parent_names = p_names;
++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++ if (!p_names[0]) {
++ kfree(init.name);
++ return -ENOMEM;
++ }
++
++ clk = devm_clk_register(dev, &div->hw);
++ kfree(p_names[0]);
++ kfree(init.name);
++
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static int
++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
++ bool unique_aux)
++{
++ struct krait_mux_clk *mux;
++ static const char *sec_mux_list[] = {
++ "acpu_aux",
++ "qsb",
++ };
++ struct clk_init_data init = {
++ .parent_names = sec_mux_list,
++ .num_parents = ARRAY_SIZE(sec_mux_list),
++ .ops = &krait_mux_clk_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ struct clk *clk;
++
++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++ if (!mux)
++ return -ENOMEM;
++
++ mux->offset = offset;
++ mux->lpl = id >= 0;
++ mux->has_safe_parent = true;
++ mux->safe_sel = 2;
++ mux->mask = 0x3;
++ mux->shift = 2;
++ mux->parent_map = sec_mux_map;
++ mux->hw.init = &init;
++
++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++ if (!init.name)
++ return -ENOMEM;
++
++ if (unique_aux) {
++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
++ if (!sec_mux_list[0]) {
++ clk = ERR_PTR(-ENOMEM);
++ goto err_aux;
++ }
++ }
++
++ clk = devm_clk_register(dev, &mux->hw);
++
++ if (unique_aux)
++ kfree(sec_mux_list[0]);
++err_aux:
++ kfree(init.name);
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct clk *
++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
++{
++ struct krait_mux_clk *mux;
++ const char *p_names[3];
++ struct clk_init_data init = {
++ .parent_names = p_names,
++ .num_parents = ARRAY_SIZE(p_names),
++ .ops = &krait_mux_clk_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ struct clk *clk;
++
++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++ if (!mux)
++ return ERR_PTR(-ENOMEM);
++
++ mux->has_safe_parent = true;
++ mux->safe_sel = 0;
++ mux->mask = 0x3;
++ mux->shift = 0;
++ mux->offset = offset;
++ mux->lpl = id >= 0;
++ mux->parent_map = pri_mux_map;
++ mux->hw.init = &init;
++
++ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
++ if (!init.name)
++ return ERR_PTR(-ENOMEM);
++
++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++ if (!p_names[0]) {
++ clk = ERR_PTR(-ENOMEM);
++ goto err_p0;
++ }
++
++ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++ if (!p_names[1]) {
++ clk = ERR_PTR(-ENOMEM);
++ goto err_p1;
++ }
++
++ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++ if (!p_names[2]) {
++ clk = ERR_PTR(-ENOMEM);
++ goto err_p2;
++ }
++
++ clk = devm_clk_register(dev, &mux->hw);
++
++ kfree(p_names[2]);
++err_p2:
++ kfree(p_names[1]);
++err_p1:
++ kfree(p_names[0]);
++err_p0:
++ kfree(init.name);
++ return clk;
++}
++
++/* id < 0 for L2, otherwise id == physical CPU number */
++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
++{
++ int ret;
++ unsigned offset;
++ void *p = NULL;
++ const char *s;
++ struct clk *clk;
++
++ if (id >= 0) {
++ offset = 0x4501 + (0x1000 * id);
++ s = p = kasprintf(GFP_KERNEL, "%d", id);
++ if (!s)
++ return ERR_PTR(-ENOMEM);
++ } else {
++ offset = 0x500;
++ s = "_l2";
++ }
++
++ ret = krait_add_div(dev, id, s, offset);
++ if (ret) {
++ clk = ERR_PTR(ret);
++ goto err;
++ }
++
++ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
++ if (ret) {
++ clk = ERR_PTR(ret);
++ goto err;
++ }
++
++ clk = krait_add_pri_mux(dev, id, s, offset);
++err:
++ kfree(p);
++ return clk;
++}
++
++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
++{
++ unsigned int idx = clkspec->args[0];
++ struct clk **clks = data;
++
++ if (idx >= 5) {
++ pr_err("%s: invalid clock index %d\n", __func__, idx);
++ return ERR_PTR(-EINVAL);
++ }
++
++ return clks[idx] ? : ERR_PTR(-ENODEV);
++}
++
++static const struct of_device_id krait_cc_match_table[] = {
++ { .compatible = "qcom,krait-cc-v1", (void *)1UL },
++ { .compatible = "qcom,krait-cc-v2" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, krait_cc_match_table);
++
++static int krait_cc_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ const struct of_device_id *id;
++ unsigned long cur_rate, aux_rate;
++ int cpu;
++ struct clk *clk;
++ struct clk **clks;
++ struct clk *l2_pri_mux_clk;
++
++ id = of_match_device(krait_cc_match_table, dev);
++ if (!id)
++ return -ENODEV;
++
++ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
++ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ if (!id->data) {
++ clk = clk_register_fixed_factor(dev, "acpu_aux",
++ "gpll0_vote", 0, 1, 2);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++ }
++
++ /* Krait configurations have at most 4 CPUs and one L2 */
++ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
++ if (!clks)
++ return -ENOMEM;
++
++ for_each_possible_cpu(cpu) {
++ clk = krait_add_clks(dev, cpu, id->data);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++ clks[cpu] = clk;
++ }
++
++ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
++ if (IS_ERR(l2_pri_mux_clk))
++ return PTR_ERR(l2_pri_mux_clk);
++ clks[4] = l2_pri_mux_clk;
++
++ /*
++ * We don't want the CPU or L2 clocks to be turned off at late init
++ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
++ * refcount of these clocks. Any cpufreq/hotplug manager can assume
++ * that the clocks have already been prepared and enabled by the time
++ * they take over.
++ */
++ for_each_online_cpu(cpu) {
++ clk_prepare_enable(l2_pri_mux_clk);
++ WARN(clk_prepare_enable(clks[cpu]),
++ "Unable to turn on CPU%d clock", cpu);
++ }
++
++ /*
++ * Force reinit of HFPLLs and muxes to overwrite any potential
++ * incorrect configuration of HFPLLs and muxes by the bootloader.
++ * While at it, also make sure the cores are running at known rates
++ * and print the current rate.
++ *
++ * The clocks are set to aux clock rate first to make sure the
++ * secondary mux is not sourcing off of QSB. The rate is then set to
++ * two different rates to force a HFPLL reinit under all
++ * circumstances.
++ */
++ cur_rate = clk_get_rate(l2_pri_mux_clk);
++ aux_rate = 384000000;
++ if (cur_rate == 1) {
++ pr_info("L2 @ QSB rate. Forcing new rate.\n");
++ cur_rate = aux_rate;
++ }
++ clk_set_rate(l2_pri_mux_clk, aux_rate);
++ clk_set_rate(l2_pri_mux_clk, 2);
++ clk_set_rate(l2_pri_mux_clk, cur_rate);
++ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
++ for_each_possible_cpu(cpu) {
++ clk = clks[cpu];
++ cur_rate = clk_get_rate(clk);
++ if (cur_rate == 1) {
++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
++ cur_rate = aux_rate;
++ }
++ clk_set_rate(clk, aux_rate);
++ clk_set_rate(clk, 2);
++ clk_set_rate(clk, cur_rate);
++ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
++ }
++
++ of_clk_add_provider(dev->of_node, krait_of_get, clks);
++
++ return 0;
++}
++
++static struct platform_driver krait_cc_driver = {
++ .probe = krait_cc_probe,
++ .driver = {
++ .name = "krait-cc",
++ .of_match_table = krait_cc_match_table,
++ },
++};
++module_platform_driver(krait_cc_driver);
++
++MODULE_DESCRIPTION("Krait CPU Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:krait-cc");
diff --git a/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
new file mode 100644
index 0000000000..fd37c05425
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
@@ -0,0 +1,304 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063191
+Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
+Date: Fri, 20 Mar 2015 23:45:31 -0700
+
+Register a cpufreq-generic device whenever we detect that a
+"qcom,krait" compatible CPU is present in DT.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++
+ drivers/cpufreq/Kconfig.arm | 9 +
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++
+ 4 files changed, 252 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
+ create mode 100644 drivers/cpufreq/qcom-cpufreq.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
+@@ -0,0 +1,38 @@
++Qualcomm Process Voltage Scaling Tables
++
++The node name is required to be "qcom,pvs". There shall only be one
++such node present in the root of the tree.
++
++PROPERTIES
++
++- qcom,pvs-format-a or qcom,pvs-format-b:
++ Usage: required
++ Value type: <empty>
++ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
++ If qcom,pvs-format-a is used the table is two columns
++ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage,
++ and current in that order).
++
++- qcom,speedX-pvsY-bin-vZ:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
++ and version Z.
++Example:
++
++ qcom,pvs {
++ qcom,pvs-format-a;
++ qcom,speed0-pvs0-bin-v0 =
++ < 384000000 950000 >,
++ < 486000000 975000 >,
++ < 594000000 1000000 >,
++ < 702000000 1025000 >,
++ < 810000000 1075000 >,
++ < 918000000 1100000 >,
++ < 1026000000 1125000 >,
++ < 1134000000 1175000 >,
++ < 1242000000 1200000 >,
++ < 1350000000 1225000 >,
++ < 1458000000 1237500 >,
++ < 1512000000 1250000 >;
++ };
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -146,6 +146,15 @@ config ARM_OMAP2PLUS_CPUFREQ
+ depends on ARCH_OMAP2PLUS
+ default ARCH_OMAP2PLUS
+
++config ARM_QCOM_CPUFREQ
++ tristate "Qualcomm based"
++ depends on ARCH_QCOM
++ select PM_OPP
++ help
++ This adds the CPUFreq driver for Qualcomm SoC based boards.
++
++ If in doubt, say N.
++
+ config ARM_S3C_CPUFREQ
+ bool
+ help
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -66,6 +66,7 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += ki
+ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
+ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
+ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
+ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
+ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
+ obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
+--- /dev/null
++++ b/drivers/cpufreq/qcom-cpufreq.c
+@@ -0,0 +1,204 @@
++/* 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/cpu.h>
++#include <linux/err.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_opp.h>
++#include <linux/slab.h>
++#include <linux/cpufreq-dt.h>
++
++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
++{
++ void __iomem *base;
++ u32 pte_efuse;
++
++ *speed = *pvs = *pvs_ver = 0;
++
++ base = ioremap(0x007000c0, 4);
++ if (!base) {
++ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
++ return;
++ }
++
++ pte_efuse = readl_relaxed(base);
++ iounmap(base);
++
++ *speed = pte_efuse & 0xf;
++ if (*speed == 0xf)
++ *speed = (pte_efuse >> 4) & 0xf;
++
++ if (*speed == 0xf) {
++ *speed = 0;
++ pr_warn("Speed bin: Defaulting to %d\n", *speed);
++ } else {
++ pr_info("Speed bin: %d\n", *speed);
++ }
++
++ *pvs = (pte_efuse >> 10) & 0x7;
++ if (*pvs == 0x7)
++ *pvs = (pte_efuse >> 13) & 0x7;
++
++ if (*pvs == 0x7) {
++ *pvs = 0;
++ pr_warn("PVS bin: Defaulting to %d\n", *pvs);
++ } else {
++ pr_info("PVS bin: %d\n", *pvs);
++ }
++}
++
++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
++{
++ u32 pte_efuse, redundant_sel;
++ void __iomem *base;
++
++ *speed = 0;
++ *pvs = 0;
++ *pvs_ver = 0;
++
++ base = ioremap(0xfc4b80b0, 8);
++ if (!base) {
++ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
++ return;
++ }
++
++ pte_efuse = readl_relaxed(base);
++ redundant_sel = (pte_efuse >> 24) & 0x7;
++ *speed = pte_efuse & 0x7;
++ /* 4 bits of PVS are in efuse register bits 31, 8-6. */
++ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
++ *pvs_ver = (pte_efuse >> 4) & 0x3;
++
++ switch (redundant_sel) {
++ case 1:
++ *speed = (pte_efuse >> 27) & 0xf;
++ break;
++ case 2:
++ *pvs = (pte_efuse >> 27) & 0xf;
++ break;
++ }
++
++ /* Check SPEED_BIN_BLOW_STATUS */
++ if (pte_efuse & BIT(3)) {
++ pr_info("Speed bin: %d\n", *speed);
++ } else {
++ pr_warn("Speed bin not set. Defaulting to 0!\n");
++ *speed = 0;
++ }
++
++ /* Check PVS_BLOW_STATUS */
++ pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
++ if (pte_efuse) {
++ pr_info("PVS bin: %d\n", *pvs);
++ } else {
++ pr_warn("PVS bin not set. Defaulting to 0!\n");
++ *pvs = 0;
++ }
++
++ pr_info("PVS version: %d\n", *pvs_ver);
++ iounmap(base);
++}
++
++static int __init qcom_cpufreq_populate_opps(void)
++{
++ int len, rows, cols, i, k, speed, pvs, pvs_ver;
++ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
++ struct device_node *np;
++ struct device *dev;
++ int cpu = 0;
++
++ np = of_find_node_by_name(NULL, "qcom,pvs");
++ if (!np)
++ return -ENODEV;
++
++ if (of_property_read_bool(np, "qcom,pvs-format-a")) {
++ get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
++ cols = 2;
++ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
++ get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
++ cols = 3;
++ } else {
++ return -ENODEV;
++ }
++
++ snprintf(table_name, sizeof(table_name),
++ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
++
++ if (!of_find_property(np, table_name, &len))
++ return -EINVAL;
++
++ len /= sizeof(u32);
++ if (len % cols || len == 0)
++ return -EINVAL;
++
++ rows = len / cols;
++
++ for (i = 0, k = 0; i < rows; i++) {
++ u32 freq, volt;
++
++ of_property_read_u32_index(np, table_name, k++, &freq);
++ of_property_read_u32_index(np, table_name, k++, &volt);
++ while (k % cols)
++ k++; /* Skip uA entries if present */
++ for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
++ dev = get_cpu_device(cpu);
++ if (!dev)
++ return -ENODEV;
++ if (dev_pm_opp_add(dev, freq, volt))
++ pr_warn("failed to add OPP %u\n", freq);
++ }
++ }
++
++ return 0;
++}
++
++static int __init qcom_cpufreq_driver_init(void)
++{
++ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
++ struct platform_device_info devinfo = {
++ .name = "cpufreq-dt",
++ .data = &pdata,
++ .size_data = sizeof(pdata),
++ };
++ struct device *cpu_dev;
++ struct device_node *np;
++ int ret;
++
++ cpu_dev = get_cpu_device(0);
++ if (!cpu_dev)
++ return -ENODEV;
++
++ np = of_node_get(cpu_dev->of_node);
++ if (!np)
++ return -ENOENT;
++
++ if (!of_device_is_compatible(np, "qcom,krait")) {
++ of_node_put(np);
++ return -ENODEV;
++ }
++ of_node_put(np);
++
++ ret = qcom_cpufreq_populate_opps();
++ if (ret)
++ return ret;
++
++ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
++}
++module_init(qcom_cpufreq_driver_init);
++
++MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
new file mode 100644
index 0000000000..46feec366d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
@@ -0,0 +1,100 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -25,6 +25,11 @@
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
++ clocks = <&kraitcc 0>;
++ clock-names = "cpu";
++ clock-latency = <100000>;
++ core-supply = <&smb208_s2a>;
++ voltage-tolerance = <5>;
+ };
+
+ cpu@1 {
+@@ -35,11 +40,24 @@
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc1>;
+ qcom,saw = <&saw1>;
++ clocks = <&kraitcc 1>;
++ clock-names = "cpu";
++ clock-latency = <100000>;
++ core-supply = <&smb208_s2b>;
+ };
+
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
++ clocks = <&kraitcc 4>;
++ clock-names = "cache";
++ cache-points-kHz = <
++ /* kHz uV CPU kHz */
++ 1200000 1150000 1200000
++ 1000000 1100000 600000
++ 384000 1100000 384000
++ >;
++ vdd_dig-supply = <&smb208_s1a>;
+ };
+ };
+
+@@ -195,6 +213,46 @@
+ };
+ };
+
++ kraitcc: clock-controller {
++ compatible = "qcom,krait-cc-v1";
++ #clock-cells = <1>;
++ };
++
++ qcom,pvs {
++ qcom,pvs-format-a;
++ qcom,speed0-pvs0-bin-v0 =
++ < 1400000000 1250000 >,
++ < 1200000000 1200000 >,
++ < 1000000000 1150000 >,
++ < 800000000 1100000 >,
++ < 600000000 1050000 >,
++ < 384000000 1000000 >;
++
++ qcom,speed0-pvs1-bin-v0 =
++ < 1400000000 1175000 >,
++ < 1200000000 1125000 >,
++ < 1000000000 1075000 >,
++ < 800000000 1025000 >,
++ < 600000000 975000 >,
++ < 384000000 925000 >;
++
++ qcom,speed0-pvs2-bin-v0 =
++ < 1400000000 1125000 >,
++ < 1200000000 1075000 >,
++ < 1000000000 1025000 >,
++ < 800000000 995000 >,
++ < 600000000 925000 >,
++ < 384000000 875000 >;
++
++ qcom,speed0-pvs3-bin-v0 =
++ < 1400000000 1050000 >,
++ < 1200000000 1000000 >,
++ < 1000000000 950000 >,
++ < 800000000 900000 >,
++ < 600000000 850000 >,
++ < 384000000 800000 >;
++ };
++
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -310,11 +368,13 @@
+ acc0: clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu0_aux";
+ };
+
+ acc1: clock-controller@2098000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu1_aux";
+ };
+
+ l2cc: clock-controller@2011000 {
diff --git a/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
new file mode 100644
index 0000000000..521adc5c1e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
@@ -0,0 +1,461 @@
+From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 30 May 2014 16:36:11 -0700
+Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
+
+Krait processors have individual clocks for each CPU that can
+scale independently from one another. cpufreq-cpu0 is fairly
+close to this, but assumes that there is only one clock for all
+CPUs. Add a driver to support the Krait configuration.
+
+TODO: Merge into cpufreq-cpu0? Or make generic?
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+ drivers/cpufreq/Kconfig | 13 +++
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 204 insertions(+)
+ create mode 100644 drivers/cpufreq/cpufreq-krait.c
+
+--- a/drivers/cpufreq/Kconfig
++++ b/drivers/cpufreq/Kconfig
+@@ -198,6 +198,19 @@ config CPUFREQ_DT
+
+ If in doubt, say N.
+
++config GENERIC_CPUFREQ_KRAIT
++ tristate "Krait cpufreq driver"
++ depends on HAVE_CLK && OF
++ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
++ depends on !CPU_THERMAL || THERMAL
++ select PM_OPP
++ help
++ This adds a generic cpufreq driver for CPU0 frequency management.
++ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
++ systems which share clock and voltage across all CPUs.
++
++ If in doubt, say N.
++
+ if X86
+ source "drivers/cpufreq/Kconfig.x86"
+ endif
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)
+ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
+
+ obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o
+
+ ##################################################################################
+ # x86 drivers.
+--- /dev/null
++++ b/drivers/cpufreq/cpufreq-krait.c
+@@ -0,0 +1,390 @@
++/*
++ * Copyright (C) 2012 Freescale Semiconductor, Inc.
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * The OPP code in function krait_set_target() is reused from
++ * drivers/cpufreq/omap-cpufreq.c
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/clk.h>
++#include <linux/cpu.h>
++#include <linux/cpu_cooling.h>
++#include <linux/cpufreq.h>
++#include <linux/cpumask.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/pm_opp.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++static unsigned int transition_latency;
++static unsigned int voltage_tolerance; /* in percentage */
++
++static struct device *cpu_dev;
++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks);
++static DEFINE_PER_CPU(struct regulator *, krait_supply_core);
++static struct cpufreq_frequency_table *freq_table;
++static struct thermal_cooling_device *cdev;
++
++struct cache_points {
++ unsigned long cache_freq;
++ unsigned int cache_volt;
++ unsigned long cpu_freq;
++};
++
++static struct regulator *krait_l2_reg;
++static struct clk *krait_l2_clk;
++static struct cache_points *krait_l2_points;
++static int nr_krait_l2_points;
++
++static int krait_parse_cache_points(struct device *dev,
++ struct device_node *of_node)
++{
++ const struct property *prop;
++ const __be32 *val;
++ int nr, i;
++
++ prop = of_find_property(of_node, "cache-points-kHz", NULL);
++ if (!prop)
++ return -ENODEV;
++ if (!prop->value)
++ return -ENODATA;
++
++ /*
++ * Each OPP is a set of tuples consisting of frequency and
++ * cpu-frequency like <freq-kHz volt-uV freq-kHz>.
++ */
++ nr = prop->length / sizeof(u32);
++ if (nr % 3) {
++ dev_err(dev, "%s: Invalid cache points\n", __func__);
++ return -EINVAL;
++ }
++ nr /= 3;
++
++ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points),
++ GFP_KERNEL);
++ if (!krait_l2_points)
++ return -ENOMEM;
++ nr_krait_l2_points = nr;
++
++ for (i = 0, val = prop->value; i < nr; i++) {
++ unsigned long cache_freq = be32_to_cpup(val++) * 1000;
++ unsigned int cache_volt = be32_to_cpup(val++);
++ unsigned long cpu_freq = be32_to_cpup(val++) * 1000;
++
++ krait_l2_points[i].cache_freq = cache_freq;
++ krait_l2_points[i].cache_volt = cache_volt;
++ krait_l2_points[i].cpu_freq = cpu_freq;
++ }
++
++ return 0;
++}
++
++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index)
++{
++ struct dev_pm_opp *opp;
++ unsigned long volt = 0, volt_old = 0, tol = 0;
++ unsigned long freq, max_cpu_freq = 0;
++ unsigned int old_freq, new_freq;
++ long freq_Hz, freq_exact;
++ int ret, i;
++ struct clk *cpu_clk;
++ struct regulator *core;
++ unsigned int cpu;
++
++ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu);
++
++ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
++ if (freq_Hz <= 0)
++ freq_Hz = freq_table[index].frequency * 1000;
++
++ freq_exact = freq_Hz;
++ new_freq = freq_Hz / 1000;
++ old_freq = clk_get_rate(cpu_clk) / 1000;
++
++ core = per_cpu(krait_supply_core, policy->cpu);
++
++ rcu_read_lock();
++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
++ if (IS_ERR(opp)) {
++ rcu_read_unlock();
++ pr_err("failed to find OPP for %ld\n", freq_Hz);
++ return PTR_ERR(opp);
++ }
++ volt = dev_pm_opp_get_voltage(opp);
++ rcu_read_unlock();
++ tol = volt * voltage_tolerance / 100;
++ volt_old = regulator_get_voltage(core);
++
++ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
++ old_freq / 1000, volt_old ? volt_old / 1000 : -1,
++ new_freq / 1000, volt ? volt / 1000 : -1);
++
++ /* scaling up? scale voltage before frequency */
++ if (new_freq > old_freq) {
++ ret = regulator_set_voltage_tol(core, volt, tol);
++ if (ret) {
++ pr_err("failed to scale voltage up: %d\n", ret);
++ return ret;
++ }
++ }
++
++ ret = clk_set_rate(cpu_clk, freq_exact);
++ if (ret) {
++ pr_err("failed to set clock rate: %d\n", ret);
++ return ret;
++ }
++
++ /* scaling down? scale voltage after frequency */
++ if (new_freq < old_freq) {
++ ret = regulator_set_voltage_tol(core, volt, tol);
++ if (ret) {
++ pr_err("failed to scale voltage down: %d\n", ret);
++ clk_set_rate(cpu_clk, old_freq * 1000);
++ }
++ }
++
++ for_each_possible_cpu(cpu) {
++ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu));
++ max_cpu_freq = max(max_cpu_freq, freq);
++ }
++
++ for (i = 0; i < nr_krait_l2_points; i++) {
++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
++ if (krait_l2_reg) {
++ ret = regulator_set_voltage_tol(krait_l2_reg,
++ krait_l2_points[i].cache_volt,
++ tol);
++ if (ret) {
++ pr_err("failed to scale l2 voltage: %d\n",
++ ret);
++ }
++ }
++ ret = clk_set_rate(krait_l2_clk,
++ krait_l2_points[i].cache_freq);
++ if (ret)
++ pr_err("failed to scale l2 clk: %d\n", ret);
++ break;
++ }
++
++ }
++
++ return ret;
++}
++
++static int krait_cpufreq_init(struct cpufreq_policy *policy)
++{
++ int ret;
++
++ policy->clk = per_cpu(krait_cpu_clks, policy->cpu);
++
++ ret = cpufreq_table_validate_and_show(policy, freq_table);
++ if (ret) {
++ pr_err("%s: invalid frequency table: %d\n", __func__, ret);
++ return ret;
++ }
++
++ policy->cpuinfo.transition_latency = transition_latency;
++
++ return 0;
++}
++
++static struct cpufreq_driver krait_cpufreq_driver = {
++ .flags = CPUFREQ_STICKY,
++ .verify = cpufreq_generic_frequency_table_verify,
++ .target_index = krait_set_target,
++ .get = cpufreq_generic_get,
++ .init = krait_cpufreq_init,
++ .name = "generic_krait",
++ .attr = cpufreq_generic_attr,
++};
++
++static int krait_cpufreq_probe(struct platform_device *pdev)
++{
++ struct device_node *np, *cache;
++ int ret, i;
++ unsigned int cpu;
++ struct device *dev;
++ struct clk *clk;
++ struct regulator *core;
++ unsigned long freq_Hz, freq, max_cpu_freq;
++ struct dev_pm_opp *opp;
++ unsigned long volt, tol;
++
++ cpu_dev = get_cpu_device(0);
++ if (!cpu_dev) {
++ pr_err("failed to get krait device\n");
++ return -ENODEV;
++ }
++
++ np = of_node_get(cpu_dev->of_node);
++ if (!np) {
++ pr_err("failed to find krait node\n");
++ return -ENOENT;
++ }
++
++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
++ if (ret) {
++ pr_err("failed to init cpufreq table: %d\n", ret);
++ goto out_put_node;
++ }
++
++ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
++
++ if (of_property_read_u32(np, "clock-latency", &transition_latency))
++ transition_latency = CPUFREQ_ETERNAL;
++
++ cache = of_find_next_cache_node(np);
++ if (cache) {
++ struct device_node *vdd;
++
++ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0);
++ if (vdd) {
++ krait_l2_reg = regulator_get(NULL, vdd->name);
++ if (IS_ERR(krait_l2_reg)) {
++ pr_warn("failed to get l2 vdd_dig supply\n");
++ krait_l2_reg = NULL;
++ }
++ of_node_put(vdd);
++ }
++
++ krait_l2_clk = of_clk_get(cache, 0);
++ if (!IS_ERR(krait_l2_clk)) {
++ ret = krait_parse_cache_points(&pdev->dev, cache);
++ if (ret)
++ clk_put(krait_l2_clk);
++ }
++ if (IS_ERR(krait_l2_clk) || ret)
++ krait_l2_clk = NULL;
++ }
++
++ for_each_possible_cpu(cpu) {
++ dev = get_cpu_device(cpu);
++ if (!dev) {
++ pr_err("failed to get krait device\n");
++ ret = -ENOENT;
++ goto out_free_table;
++ }
++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(clk)) {
++ ret = PTR_ERR(clk);
++ goto out_free_table;
++ }
++ core = devm_regulator_get(dev, "core");
++ if (IS_ERR(core)) {
++ pr_debug("failed to get core regulator\n");
++ ret = PTR_ERR(core);
++ goto out_free_table;
++ }
++ per_cpu(krait_supply_core, cpu) = core;
++
++ freq_Hz = clk_get_rate(clk);
++
++ rcu_read_lock();
++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
++ if (IS_ERR(opp)) {
++ rcu_read_unlock();
++ pr_err("failed to find OPP for %ld\n", freq_Hz);
++ ret = PTR_ERR(opp);
++ goto out_free_table;
++ }
++ volt = dev_pm_opp_get_voltage(opp);
++ rcu_read_unlock();
++
++ tol = volt * voltage_tolerance / 100;
++ ret = regulator_set_voltage_tol(core, volt, tol);
++ if (ret) {
++ pr_err("failed to scale voltage up: %d\n", ret);
++ goto out_free_table;
++ }
++ ret = regulator_enable(core);
++ if (ret) {
++ pr_err("failed to enable regulator: %d\n", ret);
++ goto out_free_table;
++ }
++ max_cpu_freq = max(max_cpu_freq, freq);
++ }
++
++ for (i = 0; i < nr_krait_l2_points; i++) {
++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
++ if (krait_l2_reg) {
++ ret = regulator_set_voltage_tol(krait_l2_reg,
++ krait_l2_points[i].cache_volt,
++ tol);
++ if (ret)
++ pr_err("failed to scale l2 voltage: %d\n",
++ ret);
++ ret = regulator_enable(krait_l2_reg);
++ if (ret)
++ pr_err("failed to enable l2 voltage: %d\n",
++ ret);
++ }
++ break;
++ }
++
++ }
++
++ ret = cpufreq_register_driver(&krait_cpufreq_driver);
++ if (ret) {
++ pr_err("failed register driver: %d\n", ret);
++ goto out_free_table;
++ }
++ of_node_put(np);
++
++ /*
++ * For now, just loading the cooling device;
++ * thermal DT code takes care of matching them.
++ */
++ for_each_possible_cpu(cpu) {
++ dev = get_cpu_device(cpu);
++ np = of_node_get(dev->of_node);
++ if (of_find_property(np, "#cooling-cells", NULL)) {
++ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu));
++ if (IS_ERR(cdev))
++ pr_err("running cpufreq without cooling device: %ld\n",
++ PTR_ERR(cdev));
++ }
++ of_node_put(np);
++ }
++
++ return 0;
++
++out_free_table:
++ regulator_put(krait_l2_reg);
++ clk_put(krait_l2_clk);
++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
++out_put_node:
++ of_node_put(np);
++ return ret;
++}
++
++static int krait_cpufreq_remove(struct platform_device *pdev)
++{
++ cpufreq_cooling_unregister(cdev);
++ cpufreq_unregister_driver(&krait_cpufreq_driver);
++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
++ clk_put(krait_l2_clk);
++ regulator_put(krait_l2_reg);
++
++ return 0;
++}
++
++static struct platform_driver krait_cpufreq_platdrv = {
++ .driver = {
++ .name = "cpufreq-krait",
++ .owner = THIS_MODULE,
++ },
++ .probe = krait_cpufreq_probe,
++ .remove = krait_cpufreq_remove,
++};
++module_platform_driver(krait_cpufreq_platdrv);
++
++MODULE_DESCRIPTION("Krait CPUfreq driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/cpufreq/qcom-cpufreq.c
++++ b/drivers/cpufreq/qcom-cpufreq.c
+@@ -168,11 +168,8 @@ static int __init qcom_cpufreq_populate_
+
+ static int __init qcom_cpufreq_driver_init(void)
+ {
+- struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
+ struct platform_device_info devinfo = {
+- .name = "cpufreq-dt",
+- .data = &pdata,
+- .size_data = sizeof(pdata),
++ .name = "cpufreq-krait",
+ };
+ struct device *cpu_dev;
+ struct device_node *np;
diff --git a/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
new file mode 100644
index 0000000000..51ebc193b0
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
@@ -0,0 +1,62 @@
+From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Wed, 29 Apr 2015 15:21:46 -0700
+Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom
+
+ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
+on most ARM architectures. This automatically calculate ZRELADDR by
+masking PHYS_OFFSET with 0xf8000000.
+
+However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware
+network accelerators, and the bootloader removes this section from the
+layout passed from the ATAGS (when used).
+
+For newer bootloader, when DT is used, this is not a problem, we just
+reserve this memory in the device tree. But if the bootloader doesn't
+have DT support, then ATAGS have to be used. In this case, the ARM
+decompressor will position the kernel in this low mem, which will not be
+in the RAM section mapped by the bootloader, which means the kernel will
+freeze in the middle of the boot process trying to map the memory.
+
+As a work around, this patch allows disabling AUTO_ZRELADDR when
+ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders
+which don't support device-tree, which is the case on certain early
+IPQ806x based designs.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/Kconfig | 2 +-
+ arch/arm/Makefile | 2 ++
+ arch/arm/mach-qcom/Makefile.boot | 1 +
+ 3 files changed, 4 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/mach-qcom/Makefile.boot
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -320,7 +320,7 @@ config ARCH_MULTIPLATFORM
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_HAS_SG_CHAIN
+ select ARM_PATCH_PHYS_VIRT
+- select AUTO_ZRELADDR
++ select AUTO_ZRELADDR if !ARCH_QCOM
+ select CLKSRC_OF
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -240,9 +240,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac
+ else
+ MACHINE :=
+ endif
++ifeq ($(CONFIG_ARCH_QCOM),)
+ ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
+ MACHINE :=
+ endif
++endif
+
+ machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
+ platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y)))
+--- /dev/null
++++ b/arch/arm/mach-qcom/Makefile.boot
+@@ -0,0 +1 @@
++zreladdr-y+= 0x42208000
diff --git a/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
new file mode 100644
index 0000000000..dfe716953f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
@@ -0,0 +1,734 @@
+From 2fbb18f85826a9ba308fedb2cf90d3a661a39fd7 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 27 Mar 2015 00:16:14 -0700
+Subject: [PATCH] clk: qcom: Add support for NSS/GMAC clocks and resets
+
+Add the NSS/GMAC clocks and the TCM clock and NSS resets.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 594 ++++++++++++++++++++++++++-
+ drivers/clk/qcom/gcc-ipq806x.c.rej | 50 +++
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 2 +
+ include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++
+ 4 files changed, 688 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/clk/qcom/gcc-ipq806x.c.rej
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -220,12 +220,47 @@ static struct clk_regmap pll14_vote = {
+ },
+ };
+
++#define NSS_PLL_RATE(f, _l, _m, _n, i) \
++ { \
++ .freq = f, \
++ .l = _l, \
++ .m = _m, \
++ .n = _n, \
++ .ibits = i, \
++ }
++
++static struct pll_freq_tbl pll18_freq_tbl[] = {
++ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625),
++ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625),
++};
++
++static struct clk_pll pll18 = {
++ .l_reg = 0x31a4,
++ .m_reg = 0x31a8,
++ .n_reg = 0x31ac,
++ .config_reg = 0x31b4,
++ .mode_reg = 0x31a0,
++ .status_reg = 0x31b8,
++ .status_bit = 16,
++ .post_div_shift = 16,
++ .post_div_width = 1,
++ .freq_tbl = pll18_freq_tbl,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll18",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
+ enum {
+ P_PXO,
+ P_PLL8,
+ P_PLL3,
+ P_PLL0,
+ P_CXO,
++ P_PLL14,
++ P_PLL18,
+ };
+
+ static const struct parent_map gcc_pxo_pll8_map[] = {
+@@ -277,6 +312,22 @@ static const char *gcc_pxo_pll8_pll0_map
+ "pll0_vote",
+ };
+
++static const struct parent_map gcc_pxo_pll8_pll14_pll18_pll0_map[] = {
++ { P_PXO, 0 },
++ { P_PLL8, 4 },
++ { P_PLL0, 2 },
++ { P_PLL14, 5 },
++ { P_PLL18, 1 },
++};
++
++static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = {
++ "pxo",
++ "pll8_vote",
++ "pll0_vote",
++ "pll14",
++ "pll18",
++};
++
+ static struct freq_tbl clk_tbl_gsbi_uart[] = {
+ { 1843200, P_PLL8, 2, 6, 625 },
+ { 3686400, P_PLL8, 2, 12, 625 },
+@@ -2282,6 +2333,472 @@ static struct clk_branch ebi2_aon_clk =
+ },
+ };
+
++static const struct freq_tbl clk_tbl_gmac[] = {
++ { 133000000, P_PLL0, 1, 50, 301 },
++ { 266000000, P_PLL0, 1, 127, 382 },
++ { }
++};
++
++static struct clk_dyn_rcg gmac_core1_src = {
++ .ns_reg[0] = 0x3cac,
++ .ns_reg[1] = 0x3cb0,
++ .md_reg[0] = 0x3ca4,
++ .md_reg[1] = 0x3ca8,
++ .bank_reg = 0x3ca0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3ca0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core1_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core1_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 4,
++ .hwcg_reg = 0x3cb4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cb4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core1_clk",
++ .parent_names = (const char *[]){
++ "gmac_core1_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core2_src = {
++ .ns_reg[0] = 0x3ccc,
++ .ns_reg[1] = 0x3cd0,
++ .md_reg[0] = 0x3cc4,
++ .md_reg[1] = 0x3cc8,
++ .bank_reg = 0x3ca0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3cc0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core2_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core2_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 5,
++ .hwcg_reg = 0x3cd4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cd4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core2_clk",
++ .parent_names = (const char *[]){
++ "gmac_core2_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core3_src = {
++ .ns_reg[0] = 0x3cec,
++ .ns_reg[1] = 0x3cf0,
++ .md_reg[0] = 0x3ce4,
++ .md_reg[1] = 0x3ce8,
++ .bank_reg = 0x3ce0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3ce0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core3_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core3_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 6,
++ .hwcg_reg = 0x3cf4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cf4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core3_clk",
++ .parent_names = (const char *[]){
++ "gmac_core3_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core4_src = {
++ .ns_reg[0] = 0x3d0c,
++ .ns_reg[1] = 0x3d10,
++ .md_reg[0] = 0x3d04,
++ .md_reg[1] = 0x3d08,
++ .bank_reg = 0x3d00,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3d00,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core4_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core4_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 7,
++ .hwcg_reg = 0x3d14,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3d14,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core4_clk",
++ .parent_names = (const char *[]){
++ "gmac_core4_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_nss_tcm[] = {
++ { 266000000, P_PLL0, 3, 0, 0 },
++ { 400000000, P_PLL0, 2, 0, 0 },
++ { }
++};
++
++static struct clk_dyn_rcg nss_tcm_src = {
++ .ns_reg[0] = 0x3dc4,
++ .ns_reg[1] = 0x3dc8,
++ .bank_reg = 0x3dc0,
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss_tcm,
++ .clkr = {
++ .enable_reg = 0x3dc0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "nss_tcm_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch nss_tcm_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 14,
++ .clkr = {
++ .enable_reg = 0x3dd0,
++ .enable_mask = BIT(6) | BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "nss_tcm_clk",
++ .parent_names = (const char *[]){
++ "nss_tcm_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_nss[] = {
++ { 110000000, P_PLL18, 1, 1, 5 },
++ { 275000000, P_PLL18, 2, 0, 0 },
++ { 550000000, P_PLL18, 1, 0, 0 },
++ { 733000000, P_PLL18, 1, 0, 0 },
++ { }
++};
++
++static struct clk_dyn_rcg ubi32_core1_src_clk = {
++ .ns_reg[0] = 0x3d2c,
++ .ns_reg[1] = 0x3d30,
++ .md_reg[0] = 0x3d24,
++ .md_reg[1] = 0x3d28,
++ .bank_reg = 0x3d20,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss,
++ .clkr = {
++ .enable_reg = 0x3d20,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "ubi32_core1_src_clk",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
++ },
++ },
++};
++
++static struct clk_dyn_rcg ubi32_core2_src_clk = {
++ .ns_reg[0] = 0x3d4c,
++ .ns_reg[1] = 0x3d50,
++ .md_reg[0] = 0x3d44,
++ .md_reg[1] = 0x3d48,
++ .bank_reg = 0x3d40,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss,
++ .clkr = {
++ .enable_reg = 0x3d40,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "ubi32_core2_src_clk",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
++ },
++ },
++};
++
+ static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [PLL0] = &pll0.clkr,
+ [PLL0_VOTE] = &pll0_vote,
+@@ -2291,6 +2808,7 @@ static struct clk_regmap *gcc_ipq806x_cl
+ [PLL8_VOTE] = &pll8_vote,
+ [PLL14] = &pll14.clkr,
+ [PLL14_VOTE] = &pll14_vote,
++ [PLL18] = &pll18.clkr,
+ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
+ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
+ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
+@@ -2390,6 +2908,18 @@ static struct clk_regmap *gcc_ipq806x_cl
+ [PLL9] = &hfpll0.clkr,
+ [PLL10] = &hfpll1.clkr,
+ [PLL12] = &hfpll_l2.clkr,
++ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr,
++ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr,
++ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr,
++ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr,
++ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr,
++ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr,
++ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr,
++ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr,
++ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr,
++ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
++ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
++ [NSSTCM_CLK] = &nss_tcm_clk.clkr,
+ };
+
+ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
+@@ -2508,6 +3038,48 @@ static const struct qcom_reset_map gcc_i
+ [USB30_1_PHY_RESET] = { 0x3b58, 0 },
+ [NSSFB0_RESET] = { 0x3b60, 6 },
+ [NSSFB1_RESET] = { 0x3b60, 7 },
++ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3},
++ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 },
++ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 },
++ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 },
++ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 },
++ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 },
++ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 },
++ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 },
++ [GMAC_CORE1_RESET] = { 0x3cbc, 0 },
++ [GMAC_CORE2_RESET] = { 0x3cdc, 0 },
++ [GMAC_CORE3_RESET] = { 0x3cfc, 0 },
++ [GMAC_CORE4_RESET] = { 0x3d1c, 0 },
++ [GMAC_AHB_RESET] = { 0x3e24, 0 },
++ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 },
++ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 },
++ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 },
++ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 },
++ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 },
++ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 },
++ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 },
++ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 },
++ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 },
++ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 },
++ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 },
++ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 },
++ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 },
++ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 },
++ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 },
++ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 },
++ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 },
++ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 },
++ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 },
++ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 },
++ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 },
++ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 },
++ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 },
++ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 },
++ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 },
++ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 },
++ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 },
++ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 },
++ [NSS_SRDS_N_RESET] = { 0x3b60, 28 },
+ };
+
+ static const struct regmap_config gcc_ipq806x_regmap_config = {
+@@ -2536,6 +3108,8 @@ static int gcc_ipq806x_probe(struct plat
+ {
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
++ struct regmap *regmap;
++ int ret;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000);
+@@ -2546,7 +3120,25 @@ static int gcc_ipq806x_probe(struct plat
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+- return qcom_cc_probe(pdev, &gcc_ipq806x_desc);
++ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc);
++ if (ret)
++ return ret;
++
++ regmap = dev_get_regmap(dev, NULL);
++ if (!regmap)
++ return -ENODEV;
++
++ /* Setup PLL18 static bits */
++ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400);
++ regmap_write(regmap, 0x31b0, 0x3080);
++
++ /* Set GMAC footswitch sleep/wakeup values */
++ regmap_write(regmap, 0x3cb8, 8);
++ regmap_write(regmap, 0x3cd8, 8);
++ regmap_write(regmap, 0x3cf8, 8);
++ regmap_write(regmap, 0x3d18, 8);
++
++ return 0;
+ }
+
+ static int gcc_ipq806x_remove(struct platform_device *pdev)
+--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
+@@ -129,4 +129,47 @@
+ #define USB30_1_PHY_RESET 112
+ #define NSSFB0_RESET 113
+ #define NSSFB1_RESET 114
++#define UBI32_CORE1_CLKRST_CLAMP_RESET 115
++#define UBI32_CORE1_CLAMP_RESET 116
++#define UBI32_CORE1_AHB_RESET 117
++#define UBI32_CORE1_AXI_RESET 118
++#define UBI32_CORE2_CLKRST_CLAMP_RESET 119
++#define UBI32_CORE2_CLAMP_RESET 120
++#define UBI32_CORE2_AHB_RESET 121
++#define UBI32_CORE2_AXI_RESET 122
++#define GMAC_CORE1_RESET 123
++#define GMAC_CORE2_RESET 124
++#define GMAC_CORE3_RESET 125
++#define GMAC_CORE4_RESET 126
++#define GMAC_AHB_RESET 127
++#define NSS_CH0_RST_RX_CLK_N_RESET 128
++#define NSS_CH0_RST_TX_CLK_N_RESET 129
++#define NSS_CH0_RST_RX_125M_N_RESET 130
++#define NSS_CH0_HW_RST_RX_125M_N_RESET 131
++#define NSS_CH0_RST_TX_125M_N_RESET 132
++#define NSS_CH1_RST_RX_CLK_N_RESET 133
++#define NSS_CH1_RST_TX_CLK_N_RESET 134
++#define NSS_CH1_RST_RX_125M_N_RESET 135
++#define NSS_CH1_HW_RST_RX_125M_N_RESET 136
++#define NSS_CH1_RST_TX_125M_N_RESET 137
++#define NSS_CH2_RST_RX_CLK_N_RESET 138
++#define NSS_CH2_RST_TX_CLK_N_RESET 139
++#define NSS_CH2_RST_RX_125M_N_RESET 140
++#define NSS_CH2_HW_RST_RX_125M_N_RESET 141
++#define NSS_CH2_RST_TX_125M_N_RESET 142
++#define NSS_CH3_RST_RX_CLK_N_RESET 143
++#define NSS_CH3_RST_TX_CLK_N_RESET 144
++#define NSS_CH3_RST_RX_125M_N_RESET 145
++#define NSS_CH3_HW_RST_RX_125M_N_RESET 146
++#define NSS_CH3_RST_TX_125M_N_RESET 147
++#define NSS_RST_RX_250M_125M_N_RESET 148
++#define NSS_RST_TX_250M_125M_N_RESET 149
++#define NSS_QSGMII_TXPI_RST_N_RESET 150
++#define NSS_QSGMII_CDR_RST_N_RESET 151
++#define NSS_SGMII2_CDR_RST_N_RESET 152
++#define NSS_SGMII3_CDR_RST_N_RESET 153
++#define NSS_CAL_PRBS_RST_N_RESET 154
++#define NSS_LCKDT_RST_N_RESET 155
++#define NSS_SRDS_N_RESET 156
++
+ #endif
+--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -289,5 +289,7 @@
+ #define UBI32_CORE1_CLK 279
+ #define UBI32_CORE2_CLK 280
+ #define EBI2_AON_CLK 281
++#define NSSTCM_CLK_SRC 282
++#define NSSTCM_CLK 283
+
+ #endif
diff --git a/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch b/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch
new file mode 100644
index 0000000000..3711e1282b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch
@@ -0,0 +1,105 @@
+From 4f09499bc1d9bb095caccbcd73ff951ee631e521 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 8 May 2015 15:42:40 -0700
+Subject: [PATCH 1/8] stmmac: add phy-handle support to the platform layer
+
+On stmmac driver, PHY specification in device-tree was done using the
+non-standard property "snps,phy-addr". Specifying a PHY on a different
+MDIO bus that the one within the stmmac controller doesn't seem to be
+possible when device-tree is used.
+
+This change adds support for the phy-handle property, as specified in
+Documentation/devicetree/bindings/net/ethernet.txt.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++--------
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 ++++-
+ include/linux/stmmac.h | 1 +
+ 3 files changed, 24 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -52,6 +52,7 @@
+ #include "stmmac_ptp.h"
+ #include "stmmac.h"
+ #include <linux/reset.h>
++#include <linux/of_mdio.h>
+
+ #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
+
+@@ -816,18 +817,25 @@ static int stmmac_init_phy(struct net_de
+ priv->speed = 0;
+ priv->oldduplex = -1;
+
+- if (priv->plat->phy_bus_name)
+- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
+- priv->plat->phy_bus_name, priv->plat->bus_id);
+- else
+- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
+- priv->plat->bus_id);
+-
+- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
+- priv->plat->phy_addr);
+- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt);
++ if (priv->plat->phy_node) {
++ phydev = of_phy_connect(dev, priv->plat->phy_node,
++ &stmmac_adjust_link, 0, interface);
++ } else {
++ if (priv->plat->phy_bus_name)
++ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
++ priv->plat->phy_bus_name, priv->plat->bus_id);
++ else
++ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
++ priv->plat->bus_id);
++
++ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
++ priv->plat->phy_addr);
++ pr_debug("stmmac_init_phy: trying to attach to %s\n",
++ phy_id_fmt);
+
+- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface);
++ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link,
++ interface);
++ }
+
+ if (IS_ERR(phydev)) {
+ pr_err("%s: Could not attach to PHY\n", dev->name);
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -28,6 +28,7 @@
+ #include <linux/of.h>
+ #include <linux/of_net.h>
+ #include <linux/of_device.h>
++#include <linux/of_mdio.h>
+
+ #include "stmmac.h"
+ #include "stmmac_platform.h"
+@@ -168,13 +169,16 @@ static int stmmac_probe_config_dt(struct
+ /* Default to phy auto-detection */
+ plat->phy_addr = -1;
+
++ /* If we find a phy-handle property, use it as the PHY */
++ plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
++
+ /* "snps,phy-addr" is not a standard property. Mark it as deprecated
+ * and warn of its use. Remove this when phy node support is added.
+ */
+ if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
+ dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
+
+- if (plat->phy_bus_name)
++ if (plat->phy_node || plat->phy_bus_name)
+ plat->mdio_bus_data = NULL;
+ else
+ plat->mdio_bus_data =
+--- a/include/linux/stmmac.h
++++ b/include/linux/stmmac.h
+@@ -99,6 +99,7 @@ struct plat_stmmacenet_data {
+ int phy_addr;
+ int interface;
+ struct stmmac_mdio_bus_data *mdio_bus_data;
++ struct device_node *phy_node;
+ struct stmmac_dma_cfg *dma_cfg;
+ int clk_csr;
+ int has_gmac;
diff --git a/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch b/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch
new file mode 100644
index 0000000000..37f1002131
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch
@@ -0,0 +1,65 @@
+From 0149d275415cd1b2382ce94e5eb32641590097d0 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 8 May 2015 15:57:12 -0700
+Subject: [PATCH 2/8] stmmac: move error path at the end of
+ stmmac_probe_config_dt()
+
+We will want to do additional clean-up on certain errors. Therefore,
+this change moves the error path at the end of the function for better
+code readability.
+
+This patch doesn't change anything functionally.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 22 ++++++++++++++++------
+ 1 file changed, 16 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -130,13 +130,18 @@ static int stmmac_probe_config_dt(struct
+ struct device_node *np = pdev->dev.of_node;
+ struct stmmac_dma_cfg *dma_cfg;
+ const struct of_device_id *device;
++ int ret;
+
+- if (!np)
+- return -ENODEV;
++ if (!np) {
++ ret = -ENODEV;
++ goto err;
++ }
+
+ device = of_match_device(stmmac_dt_ids, &pdev->dev);
+- if (!device)
+- return -ENODEV;
++ if (!device) {
++ ret = -ENODEV;
++ goto err;
++ }
+
+ if (device->data) {
+ const struct stmmac_of_data *data = device->data;
+@@ -236,8 +241,10 @@ static int stmmac_probe_config_dt(struct
+ if (of_find_property(np, "snps,pbl", NULL)) {
+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
+ GFP_KERNEL);
+- if (!dma_cfg)
+- return -ENOMEM;
++ if (!dma_cfg) {
++ ret = -ENOMEM;
++ goto err;
++ }
+ plat->dma_cfg = dma_cfg;
+ of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
+ dma_cfg->fixed_burst =
+@@ -255,6 +262,9 @@ static int stmmac_probe_config_dt(struct
+ }
+
+ return 0;
++
++err:
++ return ret;
+ }
+ #else
+ static int stmmac_probe_config_dt(struct platform_device *pdev,
diff --git a/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch b/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch
new file mode 100644
index 0000000000..a05875bca5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch
@@ -0,0 +1,64 @@
+From 3a95f75867be562cb919ff23a738f70357188fbd Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 8 May 2015 16:02:03 -0700
+Subject: [PATCH 3/8] stmmac: add fixed-link device-tree support
+
+In case DT is used, this change adds the ability to the stmmac driver to
+detect a fixed-link PHY, instanciate it, and use it during
+phy_connect().
+
+Fixed link PHYs DT usage is described in:
+Documentation/devicetree/bindings/net/fixed-link.txt
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 12 +++++++++++-
+ 2 files changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -856,7 +856,7 @@ static int stmmac_init_phy(struct net_de
+ * device as well.
+ * Note: phydev->phy_id is the result of reading the UID PHY registers.
+ */
+- if (phydev->phy_id == 0) {
++ if (!priv->plat->phy_node && phydev->phy_id == 0) {
+ phy_disconnect(phydev);
+ return -ENODEV;
+ }
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -177,6 +177,14 @@ static int stmmac_probe_config_dt(struct
+ /* If we find a phy-handle property, use it as the PHY */
+ plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
+
++ /* If phy-handle is not specified, check if we have a fixed-phy */
++ if (!plat->phy_node && of_phy_is_fixed_link(np)) {
++ if ((of_phy_register_fixed_link(np) < 0))
++ return -ENODEV;
++
++ plat->phy_node = of_node_get(np);
++ }
++
+ /* "snps,phy-addr" is not a standard property. Mark it as deprecated
+ * and warn of its use. Remove this when phy node support is added.
+ */
+@@ -243,7 +251,7 @@ static int stmmac_probe_config_dt(struct
+ GFP_KERNEL);
+ if (!dma_cfg) {
+ ret = -ENOMEM;
+- goto err;
++ goto err2;
+ }
+ plat->dma_cfg = dma_cfg;
+ of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
+@@ -263,6 +271,8 @@ static int stmmac_probe_config_dt(struct
+
+ return 0;
+
++err2:
++ of_node_put(np);
+ err:
+ return ret;
+ }
diff --git a/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch b/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch
new file mode 100644
index 0000000000..7fd72be5a8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch
@@ -0,0 +1,407 @@
+From 69fb970ad3fe05af7cb99ea78230c69c7ca0d03b Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 8 May 2015 16:10:22 -0700
+Subject: [PATCH 4/8] stmmac: add ipq806x glue layer
+
+The ethernet controller available in IPQ806x is a Synopsys DesignWare
+Gigabit MAC IP core, already supported by the stmmac driver.
+
+This glue layer implements some platform specific settings required to
+get the controller working on an IPQ806x based platform.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/Kconfig | 1 +
+ drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c | 324 +++++++++++++++++++++
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 +
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 +
+ 5 files changed, 328 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c
+
+--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
+@@ -16,6 +16,7 @@ if STMMAC_ETH
+ config STMMAC_PLATFORM
+ tristate "STMMAC Platform bus support"
+ depends on STMMAC_ETH
++ select MFD_SYSCON
+ default y
+ ---help---
+ This selects the platform specific bus support for the stmmac driver.
+--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
+@@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethto
+
+ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
+ stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \
+- dwmac-sti.o dwmac-socfpga.o dwmac-rk.o
++ dwmac-sti.o dwmac-socfpga.o dwmac-rk.o dwmac-ipq806x.o
+
+ obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
+ stmmac-pci-objs:= stmmac_pci.o
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -43,6 +43,7 @@ static const struct of_device_id stmmac_
+ { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data},
+ { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data},
+ { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
++ { .compatible = "qcom,ipq806x-gmac", .data = &ipq806x_gmac_data },
+ { .compatible = "st,spear600-gmac"},
+ { .compatible = "snps,dwmac-3.610"},
+ { .compatible = "snps,dwmac-3.70a"},
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
+@@ -25,5 +25,6 @@ extern const struct stmmac_of_data stih4
+ extern const struct stmmac_of_data stid127_dwmac_data;
+ extern const struct stmmac_of_data socfpga_gmac_data;
+ extern const struct stmmac_of_data rk3288_gmac_data;
++extern const struct stmmac_of_data ipq806x_gmac_data;
+
+ #endif /* __STMMAC_PLATFORM_H__ */
+--- /dev/null
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+@@ -0,0 +1,343 @@
++/*
++ * Qualcomm Atheros IPQ806x GMAC glue layer
++ *
++ * Copyright (C) 2015 The Linux Foundation
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/phy.h>
++#include <linux/regmap.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/of_net.h>
++#include <linux/mfd/syscon.h>
++#include <linux/stmmac.h>
++#include <linux/of_mdio.h>
++
++#include "stmmac_platform.h"
++
++#define NSS_COMMON_CLK_GATE 0x8
++#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x)
++#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2))
++#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2))
++#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x)
++#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x)
++
++#define NSS_COMMON_CLK_DIV0 0xC
++#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8)
++#define NSS_COMMON_CLK_DIV_MASK 0x7f
++
++#define NSS_COMMON_CLK_SRC_CTRL 0x14
++#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (1 << x)
++/* Mode is coded on 1 bit but is different depending on the MAC ID:
++ * MAC0: QSGMII=0 RGMII=1
++ * MAC1: QSGMII=0 SGMII=0 RGMII=1
++ * MAC2 & MAC3: QSGMII=0 SGMII=1
++ */
++#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1
++#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0)
++
++#define NSS_COMMON_MACSEC_CTL 0x28
++#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x)
++
++#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4))
++#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19)
++#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16)
++#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8
++#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0
++#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f
++
++#define NSS_COMMON_CLK_DIV_RGMII_1000 1
++#define NSS_COMMON_CLK_DIV_RGMII_100 9
++#define NSS_COMMON_CLK_DIV_RGMII_10 99
++#define NSS_COMMON_CLK_DIV_SGMII_1000 0
++#define NSS_COMMON_CLK_DIV_SGMII_100 4
++#define NSS_COMMON_CLK_DIV_SGMII_10 49
++
++#define QSGMII_PCS_MODE_CTL 0x68
++#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7)
++
++#define QSGMII_PCS_CAL_LCKDT_CTL 0x120
++#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19)
++
++/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */
++#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \
++ (0x13c + (4 * (x - 2))))
++#define QSGMII_PHY_CDR_EN BIT(0)
++#define QSGMII_PHY_RX_FRONT_EN BIT(1)
++#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2)
++#define QSGMII_PHY_TX_DRIVER_EN BIT(3)
++#define QSGMII_PHY_QSGMII_EN BIT(7)
++#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12
++#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7
++#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18
++#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3
++#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20
++#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3
++#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22
++#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3
++#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28
++#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf
++
++struct ipq806x_gmac {
++ struct platform_device *pdev;
++ struct regmap *nss_common;
++ struct regmap *qsgmii_csr;
++ uint32_t id;
++ struct clk *core_clk;
++ phy_interface_t phy_mode;
++};
++
++static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed)
++{
++ struct device *dev = &gmac->pdev->dev;
++ int div;
++
++ switch (speed) {
++ case SPEED_1000:
++ div = NSS_COMMON_CLK_DIV_SGMII_1000;
++ break;
++
++ case SPEED_100:
++ div = NSS_COMMON_CLK_DIV_SGMII_100;
++ break;
++
++ case SPEED_10:
++ div = NSS_COMMON_CLK_DIV_SGMII_10;
++ break;
++
++ default:
++ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed);
++ return -EINVAL;
++ }
++
++ return div;
++}
++
++static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed)
++{
++ struct device *dev = &gmac->pdev->dev;
++ int div;
++
++ switch (speed) {
++ case SPEED_1000:
++ div = NSS_COMMON_CLK_DIV_RGMII_1000;
++ break;
++
++ case SPEED_100:
++ div = NSS_COMMON_CLK_DIV_RGMII_100;
++ break;
++
++ case SPEED_10:
++ div = NSS_COMMON_CLK_DIV_RGMII_10;
++ break;
++
++ default:
++ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed);
++ return -EINVAL;
++ }
++
++ return div;
++}
++
++static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
++{
++ uint32_t clk_bits, val;
++ int div;
++
++ switch (gmac->phy_mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ div = get_clk_div_rgmii(gmac, speed);
++ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
++ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
++ break;
++
++ case PHY_INTERFACE_MODE_SGMII:
++ div = get_clk_div_sgmii(gmac, speed);
++ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) |
++ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id);
++ break;
++
++ default:
++ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n",
++ phy_modes(gmac->phy_mode));
++ return -EINVAL;
++ }
++
++ /* Disable the clocks */
++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
++ val &= ~clk_bits;
++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
++
++ /* Set the divider */
++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val);
++ val &= ~(NSS_COMMON_CLK_DIV_MASK
++ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id));
++ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id);
++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val);
++
++ /* Enable the clock back */
++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
++ val |= clk_bits;
++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
++
++ return 0;
++}
++
++static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
++{
++ struct device *dev = &gmac->pdev->dev;
++
++ gmac->phy_mode = of_get_phy_mode(dev->of_node);
++ if (gmac->phy_mode < 0) {
++ dev_err(dev, "missing phy mode property\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
++ dev_err(dev, "missing qcom id property\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ /* The GMACs are called 1 to 4 in the documentation, but to simplify the
++ * code and keep it consistent with the Linux convention, we'll number
++ * them from 0 to 3 here.
++ */
++ if (gmac->id < 0 || gmac->id > 3) {
++ dev_err(dev, "invalid gmac id\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ gmac->core_clk = devm_clk_get(dev, "stmmaceth");
++ if (IS_ERR(gmac->core_clk)) {
++ dev_err(dev, "missing stmmaceth clk property\n");
++ return gmac->core_clk;
++ }
++ clk_set_rate(gmac->core_clk, 266000000);
++
++ /* Setup the register map for the nss common registers */
++ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node,
++ "qcom,nss-common");
++ if (IS_ERR(gmac->nss_common)) {
++ dev_err(dev, "missing nss-common node\n");
++ return gmac->nss_common;
++ }
++
++ /* Setup the register map for the qsgmii csr registers */
++ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node,
++ "qcom,qsgmii-csr");
++ if (IS_ERR(gmac->qsgmii_csr)) {
++ dev_err(dev, "missing qsgmii-csr node\n");
++ return gmac->qsgmii_csr;
++ }
++
++ return NULL;
++}
++
++static void *ipq806x_gmac_setup(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct ipq806x_gmac *gmac;
++ int val;
++ void *err;
++
++ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
++ if (!gmac)
++ return ERR_PTR(-ENOMEM);
++
++ gmac->pdev = pdev;
++
++ err = ipq806x_gmac_of_parse(gmac);
++ if (err) {
++ dev_err(dev, "device tree parsing error\n");
++ return err;
++ }
++
++ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
++ QSGMII_PCS_CAL_LCKDT_CTL_RST);
++
++ /* Inter frame gap is set to 12 */
++ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET |
++ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET;
++ /* We also initiate an AXI low power exit request */
++ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ;
++ switch (gmac->phy_mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
++ break;
++ default:
++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
++ phy_modes(gmac->phy_mode));
++ return NULL;
++ }
++ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
++
++ /* Configure the clock src according to the mode */
++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val);
++ val &= ~NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
++ switch (gmac->phy_mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) <<
++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
++ break;
++ default:
++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
++ phy_modes(gmac->phy_mode));
++ return NULL;
++ }
++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
++
++ /* Enable PTP clock */
++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
++ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id);
++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
++
++ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) {
++ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id),
++ QSGMII_PHY_CDR_EN |
++ QSGMII_PHY_RX_FRONT_EN |
++ QSGMII_PHY_RX_SIGNAL_DETECT_EN |
++ QSGMII_PHY_TX_DRIVER_EN |
++ QSGMII_PHY_QSGMII_EN |
++ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET |
++ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET |
++ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET |
++ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET |
++ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET);
++ }
++
++ return gmac;
++}
++
++static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed)
++{
++ struct ipq806x_gmac *gmac = priv;
++
++ ipq806x_gmac_set_speed(gmac, speed);
++}
++
++const struct stmmac_of_data ipq806x_gmac_data = {
++ .has_gmac = 1,
++ .setup = ipq806x_gmac_setup,
++ .fix_mac_speed = ipq806x_gmac_fix_mac_speed,
++};
diff --git a/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch b/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch
new file mode 100644
index 0000000000..3144fa36fe
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch
@@ -0,0 +1,52 @@
+From 0f9605d9409b77a89daef91cc68239fc2ff50457 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 8 May 2015 16:51:25 -0700
+Subject: [PATCH 5/8] net: stmmac: ipq806x: document device tree bindings
+
+Add the device tree bindings documentation for the QCA IPQ806x
+variant of the Synopsys DesignWare MAC.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ .../devicetree/bindings/net/ipq806x-dwmac.txt | 35 ++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/ipq806x-dwmac.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/ipq806x-dwmac.txt
+@@ -0,0 +1,35 @@
++* IPQ806x DWMAC Ethernet controller
++
++The device inherits all the properties of the dwmac/stmmac devices
++described in the file net/stmmac.txt with the following changes.
++
++Required properties:
++
++- compatible: should be "qcom,ipq806x-gmac" along with "snps,dwmac"
++ and any applicable more detailed version number
++ described in net/stmmac.txt
++
++- qcom,nss-common: should contain a phandle to a syscon device mapping the
++ nss-common registers.
++
++- qcom,qsgmii-csr: should contain a phandle to a syscon device mapping the
++ qsgmii-csr registers.
++
++Example:
++
++ gmac: ethernet@37000000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37000000 0x200000>;
++ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE1_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE1_RESET>;
++ reset-names = "stmmaceth";
++ };
diff --git a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
new file mode 100644
index 0000000000..20d850287c
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
@@ -0,0 +1,145 @@
+From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Mon, 11 May 2015 16:32:09 -0700
+Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++-
+ arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++
+ 2 files changed, 85 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -14,8 +14,9 @@
+ };
+ };
+
+- alias {
++ aliases {
+ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
+ };
+
+ chosen {
+@@ -54,6 +55,15 @@
+ bias-none;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -139,5 +149,33 @@
+ pinctrl-0 = <&pcie2_pins>;
+ pinctrl-names = "default";
+ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
++ pinctrl-0 = <&mdio0_pins>;
++ pinctrl-names = "default";
++
++ phy0: ethernet-phy@0 {
++ device_type = "ethernet-phy";
++ reg = <0>;
++ qca,ar8327-initvals = <
++ 0x00004 0x7600000 /* PAD0_MODE */
++ 0x00008 0x1000000 /* PAD5_MODE */
++ 0x0000c 0x80 /* PAD6_MODE */
++ 0x000e4 0xaa545 /* MAC_POWER_SEL */
++ 0x000e0 0xc74164de /* SGMII_CTRL */
++ 0x0007c 0x4e /* PORT0_STATUS */
++ 0x00094 0x4e /* PORT6_STATUS */
++ >;
++ };
++
++ phy4: ethernet-phy@4 {
++ device_type = "ethernet-phy";
++ reg = <4>;
++ };
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -16,6 +16,7 @@
+
+ alias {
+ serial0 = &uart2;
++ mdio-gpio0 = &mdio0;
+ };
+
+ chosen {
+@@ -62,6 +63,15 @@
+ bias-none;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -173,5 +183,44 @@
+ pinctrl-0 = <&pcie3_pins>;
+ pinctrl-names = "default";
+ };
++
++ mdio0: mdio {
++ compatible = "virtual,mdio-gpio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
++
++ pinctrl-0 = <&mdio0_pins>;
++ pinctrl-names = "default";
++
++ phy0: ethernet-phy@0 {
++ device_type = "ethernet-phy";
++ reg = <0>;
++ qca,ar8327-initvals = <
++ 0x00004 0x7600000 /* PAD0_MODE */
++ 0x00008 0x1000000 /* PAD5_MODE */
++ 0x0000c 0x80 /* PAD6_MODE */
++ 0x000e4 0xaa545 /* MAC_POWER_SEL */
++ 0x000e0 0xc74164de /* SGMII_CTRL */
++ 0x0007c 0x4e /* PORT0_STATUS */
++ 0x00094 0x4e /* PORT6_STATUS */
++ >;
++ };
++
++ phy4: ethernet-phy@4 {
++ device_type = "ethernet-phy";
++ reg = <4>;
++ };
++
++ phy6: ethernet-phy@6 {
++ device_type = "ethernet-phy";
++ reg = <6>;
++ };
++
++ phy7: ethernet-phy@7 {
++ device_type = "ethernet-phy";
++ reg = <7>;
++ };
++ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000000..de68678d36
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,210 @@
+From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Mon, 11 May 2015 12:29:18 -0700
+Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++
+ 3 files changed, 160 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -64,6 +64,16 @@
+ bias-disable;
+ };
+ };
++
++ rgmii2_pins: rgmii2_pins {
++ mux {
++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++ function = "rgmii2";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -177,5 +187,26 @@
+ reg = <4>;
+ };
+ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ phy-handle = <&phy4>;
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -72,6 +72,14 @@
+ bias-disable;
+ };
+ };
++
++ rgmii0_pins: rgmii0_pins {
++ mux {
++ pins = "gpio2", "gpio66";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -222,5 +230,40 @@
+ reg = <7>;
+ };
+ };
++
++ gmac0: ethernet@37000000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ qcom,id = <0>;
++ phy-handle = <&phy4>;
++
++ pinctrl-0 = <&rgmii0_pins>;
++ pinctrl-names = "default";
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <1>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++ phy-handle = <&phy6>;
++ };
++
++ gmac3: ethernet@37600000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <3>;
++ phy-handle = <&phy7>;
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -578,5 +578,91 @@
+ status = "disabled";
+ };
+
++ nss_common: syscon@03000000 {
++ compatible = "syscon";
++ reg = <0x03000000 0x0000FFFF>;
++ };
++
++ qsgmii_csr: syscon@1bb00000 {
++ compatible = "syscon";
++ reg = <0x1bb00000 0x000001FF>;
++ };
++
++ gmac0: ethernet@37000000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37000000 0x200000>;
++ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE1_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE1_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac1: ethernet@37200000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37200000 0x200000>;
++ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE2_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE2_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac2: ethernet@37400000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37400000 0x200000>;
++ interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE3_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE3_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
++ gmac3: ethernet@37600000 {
++ device_type = "network";
++ compatible = "qcom,ipq806x-gmac";
++ reg = <0x37600000 0x200000>;
++ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++
++ qcom,nss-common = <&nss_common>;
++ qcom,qsgmii-csr = <&qsgmii_csr>;
++
++ clocks = <&gcc GMAC_CORE4_CLK>;
++ clock-names = "stmmaceth";
++
++ resets = <&gcc GMAC_CORE4_RESET>;
++ reset-names = "stmmaceth";
++
++ status = "disabled";
++ };
++
+ };
+ };