aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x
diff options
context:
space:
mode:
authorJames <>2015-09-26 12:29:31 +0100
committerJames <>2015-09-26 12:29:31 +0100
commit626d9efa74685720020e816f3a917b7591d3cf7a (patch)
treed22eef73ae82287b30a1140decb4fc806d39d621 /target/linux/ipq806x
downloadtrunk-47048-626d9efa74685720020e816f3a917b7591d3cf7a.tar.gz
trunk-47048-626d9efa74685720020e816f3a917b7591d3cf7a.tar.bz2
trunk-47048-626d9efa74685720020e816f3a917b7591d3cf7a.zip
trunk-47048
Diffstat (limited to 'target/linux/ipq806x')
-rw-r--r--target/linux/ipq806x/Makefile22
-rw-r--r--target/linux/ipq806x/base-files.mk3
-rw-r--r--target/linux/ipq806x/base-files/etc/inittab4
-rw-r--r--target/linux/ipq806x/base-files/etc/uci-defaults/leds26
-rwxr-xr-xtarget/linux/ipq806x/base-files/etc/uci-defaults/network39
-rw-r--r--target/linux/ipq806x/base-files/lib/ipq806x.sh46
-rw-r--r--target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh12
-rw-r--r--target/linux/ipq806x/base-files/lib/upgrade/platform.sh30
-rw-r--r--target/linux/ipq806x/config-3.18444
-rw-r--r--target/linux/ipq806x/config-4.1449
-rw-r--r--target/linux/ipq806x/image/Makefile124
-rw-r--r--target/linux/ipq806x/modules.mk32
-rw-r--r--target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch522
-rw-r--r--target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch376
-rw-r--r--target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch56
-rw-r--r--target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch67
-rw-r--r--target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch48
-rw-r--r--target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch50
-rw-r--r--target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch46
-rw-r--r--target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch19
-rw-r--r--target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch160
-rw-r--r--target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch53
-rw-r--r--target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch14
-rw-r--r--target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch167
-rw-r--r--target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch234
-rw-r--r--target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch26
-rw-r--r--target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch34
-rw-r--r--target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch82
-rw-r--r--target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch841
-rw-r--r--target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch36
-rw-r--r--target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch277
-rw-r--r--target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch511
-rw-r--r--target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch126
-rw-r--r--target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch249
-rw-r--r--target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch65
-rw-r--r--target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch263
-rw-r--r--target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch753
-rw-r--r--target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch266
-rw-r--r--target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch29
-rw-r--r--target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch278
-rw-r--r--target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch80
-rw-r--r--target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch654
-rw-r--r--target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch71
-rw-r--r--target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch247
-rw-r--r--target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch42
-rw-r--r--target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch58
-rw-r--r--target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch25
-rw-r--r--target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch87
-rw-r--r--target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch55
-rw-r--r--target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch120
-rw-r--r--target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch115
-rw-r--r--target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch144
-rw-r--r--target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch192
-rw-r--r--target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch129
-rw-r--r--target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch170
-rw-r--r--target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch351
-rw-r--r--target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch206
-rw-r--r--target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch127
-rw-r--r--target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch271
-rw-r--r--target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch205
-rw-r--r--target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch435
-rw-r--r--target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch304
-rw-r--r--target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch100
-rw-r--r--target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch461
-rw-r--r--target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch70
-rw-r--r--target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch27
-rw-r--r--target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch62
-rw-r--r--target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch51
-rw-r--r--target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch47
-rw-r--r--target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch76
-rw-r--r--target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch958
-rw-r--r--target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch42
-rw-r--r--target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch74
-rw-r--r--target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch84
-rw-r--r--target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch2024
-rw-r--r--target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch82
-rw-r--r--target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch51
-rw-r--r--target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch79
-rw-r--r--target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch11
-rw-r--r--target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch62
-rw-r--r--target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch387
-rw-r--r--target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch13
-rw-r--r--target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch733
-rw-r--r--target/linux/ipq806x/patches-3.18/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch105
-rw-r--r--target/linux/ipq806x/patches-3.18/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch65
-rw-r--r--target/linux/ipq806x/patches-3.18/703-stmmac-add-fixed-link-device-tree-support.patch64
-rw-r--r--target/linux/ipq806x/patches-3.18/704-stmmac-add-ipq806x-glue-layer.patch427
-rw-r--r--target/linux/ipq806x/patches-3.18/705-net-stmmac-ipq806x-document-device-tree-bindings.patch52
-rw-r--r--target/linux/ipq806x/patches-3.18/706-net-stmmac-create-one-debugfs-dir-per-net-device.patch171
-rw-r--r--target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch146
-rw-r--r--target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch212
-rw-r--r--target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch35
-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.patch19
-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/024-ap148-add-memory-node.patch14
-rw-r--r--target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch167
-rw-r--r--target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch234
-rw-r--r--target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch26
-rw-r--r--target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch33
-rw-r--r--target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch82
-rw-r--r--target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch841
-rw-r--r--target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch36
-rw-r--r--target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch277
-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.patch266
-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/114-pcie-add-ctlr-init.patch278
-rw-r--r--target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch80
-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/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch76
-rw-r--r--target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch962
-rw-r--r--target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch42
-rw-r--r--target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch84
-rw-r--r--target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch2024
-rw-r--r--target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch82
-rw-r--r--target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch51
-rw-r--r--target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch76
-rw-r--r--target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch11
-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/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch387
-rw-r--r--target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch13
-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.patch146
-rw-r--r--target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch212
-rw-r--r--target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch35
-rw-r--r--target/linux/ipq806x/profiles/default.mk19
-rw-r--r--target/linux/ipq806x/profiles/netgear.mk20
148 files changed, 30301 insertions, 0 deletions
diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile
new file mode 100644
index 0000000..45f83dd
--- /dev/null
+++ b/target/linux/ipq806x/Makefile
@@ -0,0 +1,22 @@
+# Copyright (c) 2013 The Linux Foundation. All rights reserved.
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=arm
+BOARD:=ipq806x
+BOARDNAME:=Qualcomm Atheros IPQ806X
+FEATURES:=squashfs nand ubifs
+CPU_TYPE:=cortex-a7
+MAINTAINER:=John Crispin <blogic@openwrt.org>
+
+KERNEL_PATCHVER:=3.18
+
+KERNELNAME:=zImage Image dtbs
+
+include $(INCLUDE_DIR)/target.mk
+DEFAULT_PACKAGES += \
+ kmod-leds-gpio kmod-gpio-button-hotplug swconfig \
+ kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform
+
+
+$(eval $(call BuildTarget))
diff --git a/target/linux/ipq806x/base-files.mk b/target/linux/ipq806x/base-files.mk
new file mode 100644
index 0000000..fdd2c71
--- /dev/null
+++ b/target/linux/ipq806x/base-files.mk
@@ -0,0 +1,3 @@
+define Package/base-files/install-target
+ rm -f $(1)/etc/config/network
+endef
diff --git a/target/linux/ipq806x/base-files/etc/inittab b/target/linux/ipq806x/base-files/etc/inittab
new file mode 100644
index 0000000..19a6e11
--- /dev/null
+++ b/target/linux/ipq806x/base-files/etc/inittab
@@ -0,0 +1,4 @@
+# Copyright (c) 2013 The Linux Foundation. All rights reserved.
+::sysinit:/etc/init.d/rcS S boot
+::shutdown:/etc/init.d/rcS K shutdown
+ttyMSM0::askfirst:/bin/ash --login
diff --git a/target/linux/ipq806x/base-files/etc/uci-defaults/leds b/target/linux/ipq806x/base-files/etc/uci-defaults/leds
new file mode 100644
index 0000000..362c5b6
--- /dev/null
+++ b/target/linux/ipq806x/base-files/etc/uci-defaults/leds
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+
+. /lib/functions/uci-defaults.sh
+. /lib/ipq806x.sh
+
+board=$(ipq806x_board_name)
+
+case "$board" in
+r7500)
+ ucidef_set_led_usbdev "usb1" "USB 1" "r7500:white:usb1" "1-1"
+ ucidef_set_led_usbdev "usb2" "USB 2" "r7500:white:usb3" "3-1"
+ ucidef_set_led_netdev "wan" "WAN" "r7500:white:wan" "eth0"
+ ucidef_set_led_ide_disk "esata" "eSATA" "r7500:amber:esata"
+ ucidef_set_led_default "wps" "WPS" "r7500:white:wps" "0"
+ ucidef_set_led_default "rfkill" "rfkill" "r7500:white:rfkill" "0"
+ ;;
+*)
+ ;;
+esac
+
+ucidef_commit_leds
+
+exit 0
diff --git a/target/linux/ipq806x/base-files/etc/uci-defaults/network b/target/linux/ipq806x/base-files/etc/uci-defaults/network
new file mode 100755
index 0000000..7742b73
--- /dev/null
+++ b/target/linux/ipq806x/base-files/etc/uci-defaults/network
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (c) 2015 The Linux Foundation. All rights reserved.
+# Copyright (C) 2011 OpenWrt.org
+#
+
+[ -e /etc/config/network ] && exit 0
+
+touch /etc/config/network
+
+. /lib/functions/uci-defaults.sh
+. /lib/ipq806x.sh
+
+ucidef_set_interface_loopback
+
+board=$(ipq806x_board_name)
+
+case "$board" in
+ap148 |\
+r7500)
+ ucidef_set_interfaces_lan_wan "eth1" "eth0"
+ ucidef_add_switch "switch0" "1" "1"
+ ucidef_add_switch_vlan "switch0" "1" "6 1 2 3 4"
+ ucidef_add_switch_vlan "switch0" "2" "0 5"
+ ;;
+db149)
+ ucidef_set_interfaces_lan_wan "eth1 eth2 eth3" "eth0"
+ ucidef_add_switch "switch0" "1" "1"
+ ucidef_add_switch_vlan "switch0" "1" "6 1 2 3 4"
+ ucidef_add_switch_vlan "switch0" "2" "0 5"
+ ;;
+*)
+ echo "Unsupported hardware. Network interfaces not intialized"
+ ;;
+esac
+
+uci commit network
+
+exit 0
diff --git a/target/linux/ipq806x/base-files/lib/ipq806x.sh b/target/linux/ipq806x/base-files/lib/ipq806x.sh
new file mode 100644
index 0000000..5b27bde
--- /dev/null
+++ b/target/linux/ipq806x/base-files/lib/ipq806x.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+# Copyright (C) 2011 OpenWrt.org
+#
+
+IPQ806X_BOARD_NAME=
+IPQ806X_MODEL=
+
+ipq806x_board_detect() {
+ local machine
+ local name
+
+ machine=$(cat /proc/device-tree/model)
+
+ case "$machine" in
+ *"AP148")
+ name="ap148"
+ ;;
+ *"DB149")
+ name="db149"
+ ;;
+ *"R7500")
+ name="r7500"
+ ;;
+ esac
+
+ [ -z "$name" ] && name="unknown"
+
+ [ -z "$IPQ806X_BOARD_NAME" ] && IPQ806X_BOARD_NAME="$name"
+ [ -z "$IPQ806X_MODEL" ] && IPQ806X_MODEL="$machine"
+
+ [ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/"
+
+ echo "$IPQ806X_BOARD_NAME" > /tmp/sysinfo/board_name
+ echo "$IPQ806X_MODEL" > /tmp/sysinfo/model
+}
+
+ipq806x_board_name() {
+ local name
+
+ [ -f /tmp/sysinfo/board_name ] && name=$(cat /tmp/sysinfo/board_name)
+ [ -z "$name" ] && name="unknown"
+
+ echo "$name"
+}
diff --git a/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh
new file mode 100644
index 0000000..785f1eb
--- /dev/null
+++ b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+#
+
+do_ipq806x() {
+ . /lib/ipq806x.sh
+
+ ipq806x_board_detect
+}
+
+boot_hook_add preinit_main do_ipq806x
diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
new file mode 100644
index 0000000..c0e19a1
--- /dev/null
+++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
@@ -0,0 +1,30 @@
+. /lib/ipq806x.sh
+
+PART_NAME=firmware
+
+platform_check_image() {
+ local board=$(ipq806x_board_name)
+
+ case "$board" in
+ AP148 |\
+ r7500)
+ nand_do_platform_check $board $1
+ return $?;
+ ;;
+ *)
+ return 1;
+ esac
+}
+
+platform_pre_upgrade() {
+ local board=$(ipq806x_board_name)
+
+ case "$board" in
+ AP148 |\
+ r7500)
+ nand_do_upgrade "$1"
+ ;;
+ esac
+}
+
+# use default for platform_do_upgrade()
diff --git a/target/linux/ipq806x/config-3.18 b/target/linux/ipq806x/config-3.18
new file mode 100644
index 0000000..cb3dcfc
--- /dev/null
+++ b/target/linux/ipq806x/config-3.18
@@ -0,0 +1,444 @@
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_APM_EMULATION is not set
+CONFIG_APQ_GCC_8084=y
+CONFIG_APQ_MMCC_8084=y
+CONFIG_AR8216_PHY=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MSM8960=y
+CONFIG_ARCH_MSM8974=y
+CONFIG_ARCH_MSM8X60=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_ARM=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
+# CONFIG_ARM_ATAG_DTB_COMPAT is not set
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_ARM_QCOM_CPUFREQ=y
+# CONFIG_ARM_SP805_WATCHDOG is not set
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_AT803X_PHY=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_BOUNCE=y
+CONFIG_BUILD_BIN2C=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_CLEANCACHE=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_QCOM=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_COMPACTION=y
+CONFIG_COREDUMP=y
+# CONFIG_CPUFREQ_DT is not set
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+# CONFIG_CPU_THERMAL is not set
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+# CONFIG_CRC32_SARWATE is not set
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
+# CONFIG_CRYPTO_SHA512_ARM_NEON is not set
+CONFIG_CRYPTO_XZ=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_UART_8250 is not set
+# CONFIG_DEBUG_UART_PL01X is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DTC=y
+CONFIG_DWMAC_IPQ806X=y
+# CONFIG_DW_DMAC_CORE is not set
+# CONFIG_DW_DMAC_PCI is not set
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_ETHERNET_PACKET_MANGLE=y
+CONFIG_FIXED_PHY=y
+CONFIG_FREEZER=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_CPUFREQ_KRAIT=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIO_MSM_V2 is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SMP=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_UID16=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_HWMON=y
+CONFIG_HWSPINLOCK=y
+CONFIG_HWSPINLOCK_QCOM=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_HZ_FIXED=0
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_QUP=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_API=y
+CONFIG_IOMMU_HELPER=y
+CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_IPQ_GCC_806X=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_KPSS_XCC=y
+CONFIG_KRAITCC=y
+CONFIG_KRAIT_CLOCKS=y
+CONFIG_KRAIT_L2_ACCESSORS=y
+# CONFIG_LEDS_REGULATOR is not set
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+CONFIG_LIBFDT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MDIO_GPIO=y
+CONFIG_MFD_QCOM_RPM=y
+# CONFIG_MFD_SPMI_PMIC is not set
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MIGRATION=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MSM_GCC_8660=y
+CONFIG_MSM_GCC_8960=y
+CONFIG_MSM_GCC_8974=y
+CONFIG_MSM_IOMMU=y
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_IMPA7 is not set
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_QCOM=y
+CONFIG_MTD_QCOM_SMEM_PARTS=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPLIT_FIRMWARE=y
+CONFIG_MTD_SPLIT_FIT_FW=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEON=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_ADDRESS_PCI=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IOMMU=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_MSI=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PHYLIB=y
+# CONFIG_PHY_QCOM_APQ8064_SATA is not set
+CONFIG_PHY_QCOM_IPQ806X_SATA=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_APQ8064=y
+# CONFIG_PINCTRL_APQ8084 is not set
+CONFIG_PINCTRL_IPQ8064=y
+CONFIG_PINCTRL_MSM=y
+# CONFIG_PINCTRL_MSM8960 is not set
+CONFIG_PINCTRL_MSM8X74=y
+# CONFIG_PL330_DMA is not set
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_OPP=y
+CONFIG_PM_SLEEP=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_BRCMSTB is not set
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_GPIO_RESTART is not set
+# CONFIG_POWER_RESET_LTC2952 is not set
+CONFIG_POWER_RESET_MSM=y
+# CONFIG_POWER_RESET_SYSCON is not set
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_RCU=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_QCOM_ADM=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_WDT=y
+CONFIG_RAS=y
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_RCU_CPU_STALL_VERBOSE=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RD_GZIP=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_QCOM_RPM=y
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_CMOS is not set
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_QUP=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+# CONFIG_STMMAC_DA is not set
+CONFIG_STMMAC_DEBUG_FS=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_STOP_MACHINE=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_STATS=y
+CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_XZ=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_UID16=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_OF=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_VFIO is not set
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL_TI is not set
+# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
+CONFIG_XPS=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/ipq806x/config-4.1 b/target/linux/ipq806x/config-4.1
new file mode 100644
index 0000000..4ead455
--- /dev/null
+++ b/target/linux/ipq806x/config-4.1
@@ -0,0 +1,449 @@
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_APM_EMULATION is not set
+CONFIG_APQ_GCC_8084=y
+CONFIG_APQ_MMCC_8084=y
+CONFIG_AR8216_PHY=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MSM8960=y
+CONFIG_ARCH_MSM8974=y
+CONFIG_ARCH_MSM8X60=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_ARCH_QCOM=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_ARM=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
+# CONFIG_ARM_ATAG_DTB_COMPAT is not set
+CONFIG_ARM_CCI=y
+CONFIG_ARM_CCI400_COMMON=y
+CONFIG_ARM_CCI400_PMU=y
+# CONFIG_ARM_CPUIDLE is not set
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_ARM_QCOM_CPUFREQ=y
+# CONFIG_ARM_SMMU is not set
+# CONFIG_ARM_SP805_WATCHDOG is not set
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_AT803X_PHY=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_BOUNCE=y
+CONFIG_BUILD_BIN2C=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_CLEANCACHE=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_QCOM=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_COMPACTION=y
+CONFIG_COREDUMP=y
+# CONFIG_CPUFREQ_DT is not set
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+# CONFIG_CPU_THERMAL is not set
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+# CONFIG_CRC32_SARWATE is not set
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_XZ=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_UART_8250 is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DEVMEM=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DTC=y
+# CONFIG_DW_DMAC_PCI is not set
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_ETHERNET_PACKET_MANGLE=y
+CONFIG_FIXED_PHY=y
+CONFIG_FREEZER=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CPUFREQ_KRAIT=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIO_MSM_V2 is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_HAVE_ARCH_BITREVERSE=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SMP=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_UID16=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_HSU_DMA_PCI is not set
+CONFIG_HWMON=y
+CONFIG_HWSPINLOCK=y
+CONFIG_HWSPINLOCK_QCOM=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_HZ_FIXED=0
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_QUP=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_HELPER=y
+# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_IPQ_GCC_806X=y
+# CONFIG_IPQ_LCC_806X is not set
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_KPSS_XCC=y
+CONFIG_KRAITCC=y
+CONFIG_KRAIT_CLOCKS=y
+CONFIG_KRAIT_L2_ACCESSORS=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+CONFIG_LIBFDT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MDIO_GPIO=y
+CONFIG_MFD_QCOM_RPM=y
+# CONFIG_MFD_SPMI_PMIC is not set
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MIGRATION=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MSM_GCC_8660=y
+# CONFIG_MSM_GCC_8916 is not set
+CONFIG_MSM_GCC_8960=y
+CONFIG_MSM_GCC_8974=y
+# CONFIG_MSM_LCC_8960 is not set
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_QCOM=y
+CONFIG_MTD_QCOM_SMEM_PARTS=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPLIT_FIRMWARE=y
+CONFIG_MTD_SPLIT_FIT_FW=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEON=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_ADDRESS_PCI=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+# CONFIG_PCIE_IPROC is not set
+CONFIG_PCIE_PME=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DEBUG=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_MSI=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_PHYLIB=y
+# CONFIG_PHY_QCOM_APQ8064_SATA is not set
+CONFIG_PHY_QCOM_IPQ806X_SATA=y
+# CONFIG_PHY_QCOM_UFS is not set
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_APQ8064=y
+# CONFIG_PINCTRL_APQ8084 is not set
+CONFIG_PINCTRL_IPQ8064=y
+CONFIG_PINCTRL_MSM=y
+# CONFIG_PINCTRL_MSM8916 is not set
+# CONFIG_PINCTRL_MSM8960 is not set
+CONFIG_PINCTRL_MSM8X74=y
+# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set
+# CONFIG_PL330_DMA is not set
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_OPP=y
+CONFIG_PM_SLEEP=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_BRCMSTB is not set
+CONFIG_POWER_RESET_MSM=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_RCU=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_QCOM_ADM=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_WDT=y
+CONFIG_RAS=y
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RD_GZIP=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_CMOS is not set
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_QUP=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_SRCU=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_STOP_MACHINE=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_STATS=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_XZ=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_UID16=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_OF=y
+CONFIG_VDSO=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL_TI is not set
+# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
+CONFIG_XPS=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
new file mode 100644
index 0000000..c59e6d4
--- /dev/null
+++ b/target/linux/ipq806x/image/Makefile
@@ -0,0 +1,124 @@
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+UBIFS_OPTS = -m 2048 -e 124KiB -c 4096 -U -F
+UBINIZE_OPTS = -m 2048 -p 128KiB
+
+KERNEL_LOADADDR := 0x42208000
+
+define Image/Prepare
+ $(CP) $(LINUX_DIR)/vmlinux $(KDIR)/$(IMG_PREFIX)-vmlinux.elf
+ mkimage -A arm -O linux -T filesystem -C none \
+ -a $(KERNEL_LOADADDR) -e $(KERNEL_LOADADDR) \
+ -n 'ARM OpenWrt fakeroot' \
+ -s $(KDIR_TMP)/root.dummy-uImage.tmp
+ echo -ne '\xff' > $(KDIR_TMP)/root.dummy
+ cat $(KDIR_TMP)/root.dummy $(KDIR_TMP)/root.dummy-uImage.tmp > $(KDIR)/root.dummy
+endef
+
+define Image/BuildKernel
+ $(CP) $(KDIR)/$(IMG_PREFIX)-vmlinux.elf $(BIN_DIR)
+endef
+
+define Image/Build/squashfs
+ cp $(KDIR)/root.squashfs $(KDIR)/root.squashfs-raw
+ $(call prepare_generic_squashfs,$(KDIR)/root.squashfs)
+endef
+
+define Image/Build
+ $(call Image/Build/$(1),$(1))
+ dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-$(1)-root.img bs=2k conv=sync
+endef
+
+define Build/append-dtb
+ cat $(DTS_DIR)/$(DEVICE_DTS).dtb >> $@
+endef
+
+define Build/append-file
+ cat $(1) >> $@
+endef
+
+define Device/Default
+ PROFILES := Default
+ KERNEL_INITRAMFS_PREFIX := $$(IMG_PREFIX)-$(1)-initramfs
+ DEVICE_DTS :=
+ KERNEL_PREFIX := $$(IMAGE_PREFIX)
+ IMAGES :=
+ KERNEL_IN_UBI :=
+endef
+DEVICE_VARS += DEVICE_DTS KERNEL_IN_UBI
+
+define Device/LegacyImage
+ KERNEL_SUFFIX := -uImage
+ KERNEL = kernel-bin | append-dtb | uImage none
+ KERNEL_NAME := zImage
+endef
+
+define Device/FitImage
+ KERNEL_SUFFIX := -fit-uImage.itb
+ KERNEL = kernel-bin | gzip | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb
+ KERNEL_NAME := Image
+endef
+
+define Device/UbiFit
+ KERNEL_IN_UBI := 1
+ IMAGES := nand-factory.ubi nand-sysupgrade.tar
+ IMAGE/nand-factory.ubi := append-ubi
+ IMAGE/nand-sysupgrade.tar := sysupgrade-nand
+endef
+
+define Device/DniImage
+ PROFILES += $$(DEVICE_NAME)
+ FILESYSTEMS := squashfs
+ KERNEL_SUFFIX := -uImage
+ KERNEL_INITRAMFS := kernel-bin | append-dtb | uImage none
+ KERNEL = kernel-bin | append-dtb | pad-to $$$$(($$(KERNEL_SIZE)-2*64-1)) | uImage none | append-file $(KDIR)/root.dummy
+ KERNEL_NAME := zImage
+ NETGEAR_BOARD_ID :=
+ NETGEAR_HW_ID :=
+ BOARD_NAME :=
+ IMAGES := factory.img sysupgrade.tar
+ IMAGE/factory.img := append-kernel | append-ubi | netgear-dni
+ IMAGE/sysupgrade.tar = sysupgrade-nand
+endef
+DEVICE_VARS += KERNEL_SIZE NETGEAR_BOARD_ID NETGEAR_HW_ID DEVICE_BLOCK_SIZE DEVICE_PAGE_SIZE BOARD_NAME
+
+define Device/AP148
+ $(call Device/FitImage)
+ $(call Device/UbiFit)
+ DEVICE_DTS := qcom-ipq8064-ap148
+ BLOCKSIZE := 128KiB
+ PAGESIZE := 2048
+endef
+
+define Device/AP148-legacy
+ $(call Device/LegacyImage)
+ $(call Device/UbiFit)
+ DEVICE_DTS := qcom-ipq8064-ap148
+ BLOCKSIZE := 128KiB
+ PAGESIZE := 2048
+ BOARD_NAME := AP148
+endef
+
+define Device/DB149
+ $(call Device/FitImage)
+ DEVICE_DTS := qcom-ipq8064-db149
+ KERNEL_INSTALL := 1
+endef
+
+define Device/R7500
+ $(call Device/DniImage)
+ DEVICE_DTS := qcom-ipq8064-r7500
+ KERNEL_SIZE := 2097152
+ NETGEAR_BOARD_ID := R7500
+ NETGEAR_HW_ID := 29764841+0+128+256+3x3+4x4
+ BLOCKSIZE := 128KiB
+ PAGESIZE := 2048
+ BOARD_NAME := r7500
+endef
+
+TARGET_DEVICES += AP148 AP148-legacy DB149 R7500
+
+$(eval $(call BuildImage))
diff --git a/target/linux/ipq806x/modules.mk b/target/linux/ipq806x/modules.mk
new file mode 100644
index 0000000..04a2d4f
--- /dev/null
+++ b/target/linux/ipq806x/modules.mk
@@ -0,0 +1,32 @@
+define KernelPackage/usb-phy-qcom-dwc3
+ TITLE:=DWC3 USB QCOM PHY driver
+ DEPENDS:=@TARGET_ipq806x
+ KCONFIG:= CONFIG_PHY_QCOM_DWC3
+ FILES:= $(LINUX_DIR)/drivers/phy/phy-qcom-dwc3.ko
+ AUTOLOAD:=$(call AutoLoad,45,phy-qcom-dwc3,1)
+ $(call AddDepends/usb)
+endef
+
+define KernelPackage/usb-phy-qcom-dwc3/description
+ This driver provides support for the integrated DesignWare
+ USB3 IP Core within the QCOM SoCs.
+endef
+
+$(eval $(call KernelPackage,usb-phy-qcom-dwc3))
+
+
+define KernelPackage/usb-dwc3-qcom
+ TITLE:=DWC3 USB QCOM controller driver
+ DEPENDS:=@TARGET_ipq806x +kmod-usb-dwc3 +kmod-usb-phy-qcom-dwc3
+ KCONFIG:= CONFIG_USB_DWC3_QCOM
+ FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-qcom.ko
+ AUTOLOAD:=$(call AutoLoad,53,dwc3-qcom,1)
+ $(call AddDepends/usb)
+endef
+
+define KernelPackage/usb-dwc3-qcom/description
+ This driver provides support for the integrated DesignWare
+ USB3 IP Core within the QCOM SoCs.
+endef
+
+$(eval $(call KernelPackage,usb-dwc3-qcom))
diff --git a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch
new file mode 100644
index 0000000..e110bf7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch
@@ -0,0 +1,522 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: spi: qup: Add DMA capabilities
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 4432401
+Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
+To: Mark Brown <broonie@kernel.org>
+Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
+ Daniel Sneddon <dsneddon@codeaurora.org>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ "Ivan T. Ivanov" <iivanov@mm-sol.com>,
+ linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 16:06:21 -0500
+
+This patch adds DMA capabilities to the spi-qup driver. If DMA channels are
+present, the QUP will use DMA instead of block mode for transfers to/from SPI
+peripherals for transactions larger than the length of a block.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 +
+ drivers/spi/spi-qup.c | 361 ++++++++++++++++++--
+ 2 files changed, 350 insertions(+), 21 deletions(-)
+
+--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -27,6 +27,11 @@ Optional properties:
+ - spi-max-frequency: Specifies maximum SPI clock frequency,
+ Units - Hz. Definition as per
+ Documentation/devicetree/bindings/spi/spi-bus.txt
++- dmas : Two DMA channel specifiers following the convention outlined
++ in bindings/dma/dma.txt
++- dma-names: Names for the dma channels, if present. There must be at
++ least one channel named "tx" for transmit and named "rx" for
++ receive.
+ - num-cs: total number of chipselects
+ - cs-gpios: should specify GPIOs used for chipselects.
+ The gpios will be referred to as reg = <index> in the SPI child
+@@ -51,6 +56,10 @@ Example:
+ clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
+ clock-names = "core", "iface";
+
++ dmas = <&blsp2_bam 2>,
++ <&blsp2_bam 3>;
++ dma-names = "rx", "tx";
++
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi8_default>;
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -22,6 +22,8 @@
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
+
+ #define QUP_CONFIG 0x0000
+ #define QUP_STATE 0x0004
+@@ -116,6 +118,8 @@
+
+ #define SPI_NUM_CHIPSELECTS 4
+
++#define SPI_MAX_XFER (SZ_64K - 64)
++
+ /* high speed mode is when bus rate is greater then 26MHz */
+ #define SPI_HS_MIN_RATE 26000000
+ #define SPI_MAX_RATE 50000000
+@@ -143,6 +147,17 @@ struct spi_qup {
+ int tx_bytes;
+ int rx_bytes;
+ int qup_v1;
++
++ int use_dma;
++
++ struct dma_chan *rx_chan;
++ struct dma_slave_config rx_conf;
++ struct dma_chan *tx_chan;
++ struct dma_slave_config tx_conf;
++ dma_addr_t rx_dma;
++ dma_addr_t tx_dma;
++ void *dummy;
++ atomic_t dma_outstanding;
+ };
+
+
+@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp
+ }
+ }
+
++static void qup_dma_callback(void *data)
++{
++ struct spi_qup *controller = data;
++
++ if (atomic_dec_and_test(&controller->dma_outstanding))
++ complete(&controller->done);
++}
++
++static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer)
++{
++ struct dma_async_tx_descriptor *rxd, *txd;
++ dma_cookie_t rx_cookie, tx_cookie;
++ u32 xfer_len, rx_align = 0, tx_align = 0, n_words;
++ struct scatterlist tx_sg[2], rx_sg[2];
++ int ret = 0;
++ u32 bytes_to_xfer = xfer->len;
++ u32 offset = 0;
++ u32 rx_nents = 0, tx_nents = 0;
++ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0;
++
++
++ if (xfer->rx_buf) {
++ rx_dma = dma_map_single(controller->dev, xfer->rx_buf,
++ xfer->len, DMA_FROM_DEVICE);
++
++ if (dma_mapping_error(controller->dev, rx_dma)) {
++ ret = -ENOMEM;
++ return ret;
++ }
++
++ /* check to see if we need dummy buffer for leftover bytes */
++ rx_align = xfer->len % controller->in_blk_sz;
++ if (rx_align) {
++ rx_dummy_dma = dma_map_single(controller->dev,
++ controller->dummy, controller->in_fifo_sz,
++ DMA_FROM_DEVICE);
++
++ if (dma_mapping_error(controller->dev, rx_dummy_dma)) {
++ ret = -ENOMEM;
++ goto err_map_rx_dummy;
++ }
++ }
++ }
++
++ if (xfer->tx_buf) {
++ tx_dma = dma_map_single(controller->dev,
++ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
++
++ if (dma_mapping_error(controller->dev, tx_dma)) {
++ ret = -ENOMEM;
++ goto err_map_tx;
++ }
++
++ /* check to see if we need dummy buffer for leftover bytes */
++ tx_align = xfer->len % controller->out_blk_sz;
++ if (tx_align) {
++ memcpy(controller->dummy + SZ_1K,
++ xfer->tx_buf + xfer->len - tx_align,
++ tx_align);
++ memset(controller->dummy + SZ_1K + tx_align, 0,
++ controller->out_blk_sz - tx_align);
++
++ tx_dummy_dma = dma_map_single(controller->dev,
++ controller->dummy + SZ_1K,
++ controller->out_blk_sz, DMA_TO_DEVICE);
++
++ if (dma_mapping_error(controller->dev, tx_dummy_dma)) {
++ ret = -ENOMEM;
++ goto err_map_tx_dummy;
++ }
++ }
++ }
++
++ atomic_set(&controller->dma_outstanding, 0);
++
++ while (bytes_to_xfer > 0) {
++ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER);
++ n_words = DIV_ROUND_UP(xfer_len, controller->w_size);
++
++ /* write out current word count to controller */
++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++
++ reinit_completion(&controller->done);
++
++ if (xfer->tx_buf) {
++ /* recalc align for each transaction */
++ tx_align = xfer_len % controller->out_blk_sz;
++
++ if (tx_align)
++ tx_nents = 2;
++ else
++ tx_nents = 1;
++
++ /* initialize scatterlists */
++ sg_init_table(tx_sg, tx_nents);
++ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align;
++ sg_dma_address(&tx_sg[0]) = tx_dma + offset;
++
++ /* account for non block size transfer */
++ if (tx_align) {
++ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz;
++ sg_dma_address(&tx_sg[1]) = tx_dummy_dma;
++ }
++
++ txd = dmaengine_prep_slave_sg(controller->tx_chan,
++ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0);
++ if (!txd) {
++ ret = -ENOMEM;
++ goto err_unmap;
++ }
++
++ atomic_inc(&controller->dma_outstanding);
++
++ txd->callback = qup_dma_callback;
++ txd->callback_param = controller;
++
++ tx_cookie = dmaengine_submit(txd);
++
++ dma_async_issue_pending(controller->tx_chan);
++ }
++
++ if (xfer->rx_buf) {
++ /* recalc align for each transaction */
++ rx_align = xfer_len % controller->in_blk_sz;
++
++ if (rx_align)
++ rx_nents = 2;
++ else
++ rx_nents = 1;
++
++ /* initialize scatterlists */
++ sg_init_table(rx_sg, rx_nents);
++ sg_dma_address(&rx_sg[0]) = rx_dma + offset;
++ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align;
++
++ /* account for non block size transfer */
++ if (rx_align) {
++ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz;
++ sg_dma_address(&rx_sg[1]) = rx_dummy_dma;
++ }
++
++ rxd = dmaengine_prep_slave_sg(controller->rx_chan,
++ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0);
++ if (!rxd) {
++ ret = -ENOMEM;
++ goto err_unmap;
++ }
++
++ atomic_inc(&controller->dma_outstanding);
++
++ rxd->callback = qup_dma_callback;
++ rxd->callback_param = controller;
++
++ rx_cookie = dmaengine_submit(rxd);
++
++ dma_async_issue_pending(controller->rx_chan);
++ }
++
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set EXECUTE state\n");
++ goto err_unmap;
++ }
++
++ if (!wait_for_completion_timeout(&controller->done,
++ msecs_to_jiffies(1000))) {
++ ret = -ETIMEDOUT;
++
++ /* clear out all the DMA transactions */
++ if (xfer->tx_buf)
++ dmaengine_terminate_all(controller->tx_chan);
++ if (xfer->rx_buf)
++ dmaengine_terminate_all(controller->rx_chan);
++
++ goto err_unmap;
++ }
++
++ if (rx_align)
++ memcpy(xfer->rx_buf + offset + xfer->len - rx_align,
++ controller->dummy, rx_align);
++
++ /* adjust remaining bytes to transfer */
++ bytes_to_xfer -= xfer_len;
++ offset += xfer_len;
++
++
++ /* reset mini-core state so we can program next transaction */
++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++ dev_err(controller->dev, "cannot set RESET state\n");
++ goto err_unmap;
++ }
++ }
++
++ ret = 0;
++
++err_unmap:
++ if (tx_align)
++ dma_unmap_single(controller->dev, tx_dummy_dma,
++ controller->out_fifo_sz, DMA_TO_DEVICE);
++err_map_tx_dummy:
++ if (xfer->tx_buf)
++ dma_unmap_single(controller->dev, tx_dma, xfer->len,
++ DMA_TO_DEVICE);
++err_map_tx:
++ if (rx_align)
++ dma_unmap_single(controller->dev, rx_dummy_dma,
++ controller->in_fifo_sz, DMA_FROM_DEVICE);
++err_map_rx_dummy:
++ if (xfer->rx_buf)
++ dma_unmap_single(controller->dev, rx_dma, xfer->len,
++ DMA_FROM_DEVICE);
++
++ return ret;
++}
++
+ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ {
+ struct spi_qup *controller = dev_id;
+@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i
+ error = -EIO;
+ }
+
+- if (opflags & QUP_OP_IN_SERVICE_FLAG)
+- spi_qup_fifo_read(controller, xfer);
++ if (!controller->use_dma) {
++ if (opflags & QUP_OP_IN_SERVICE_FLAG)
++ spi_qup_fifo_read(controller, xfer);
+
+- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+- spi_qup_fifo_write(controller, xfer);
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++ spi_qup_fifo_write(controller, xfer);
++ }
+
+ spin_lock_irqsave(&controller->lock, flags);
+ controller->error = error;
+@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+ u32 config, iomode, mode;
+ int ret, n_words, w_size;
++ size_t dma_align = dma_get_cache_alignment();
++ u32 dma_available = 0;
+
+ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+ dev_err(controller->dev, "too big size for loopback %d > %d\n",
+@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_
+ n_words = xfer->len / w_size;
+ controller->w_size = w_size;
+
++ if (controller->rx_chan &&
++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align))
++ dma_available = 1;
++
+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+ mode = QUP_IO_M_MODE_FIFO;
+ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_
+ /* must be zero for FIFO */
+ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+- } else {
++ controller->use_dma = 0;
++ } else if (!dma_available) {
+ mode = QUP_IO_M_MODE_BLOCK;
+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+ /* must be zero for BLOCK and BAM */
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ controller->use_dma = 0;
++ } else {
++ mode = QUP_IO_M_MODE_DMOV;
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ controller->use_dma = 1;
+ }
+
+ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+ /* Set input and output transfer mode */
+ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++
++ if (!controller->use_dma)
++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++ else
++ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
++
+ iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+ iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+
+@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_
+ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
+ config |= xfer->bits_per_word - 1;
+ config |= QUP_CONFIG_SPI_MODE;
++
++ if (controller->use_dma) {
++ if (!xfer->tx_buf)
++ config |= QUP_CONFIG_NO_OUTPUT;
++ if (!xfer->rx_buf)
++ config |= QUP_CONFIG_NO_INPUT;
++ }
++
+ writel_relaxed(config, controller->base + QUP_CONFIG);
+
+ /* only write to OPERATIONAL_MASK when register is present */
+@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s
+ controller->tx_bytes = 0;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+- dev_warn(controller->dev, "cannot set RUN state\n");
+- goto exit;
+- }
++ if (controller->use_dma) {
++ ret = spi_qup_do_dma(controller, xfer);
++ } else {
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set RUN state\n");
++ goto exit;
++ }
+
+- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
+- dev_warn(controller->dev, "cannot set PAUSE state\n");
+- goto exit;
+- }
++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
++ dev_warn(controller->dev, "cannot set PAUSE state\n");
++ goto exit;
++ }
+
+- spi_qup_fifo_write(controller, xfer);
++ spi_qup_fifo_write(controller, xfer);
+
+- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+- dev_warn(controller->dev, "cannot set EXECUTE state\n");
+- goto exit;
+- }
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set EXECUTE state\n");
++ goto exit;
++ }
+
+- if (!wait_for_completion_timeout(&controller->done, timeout))
+- ret = -ETIMEDOUT;
++ if (!wait_for_completion_timeout(&controller->done, timeout))
++ ret = -ETIMEDOUT;
++ }
+ exit:
+ spi_qup_set_state(controller, QUP_STATE_RESET);
+ spin_lock_irqsave(&controller->lock, flags);
+@@ -554,6 +817,7 @@ static int spi_qup_probe(struct platform
+ master->transfer_one = spi_qup_transfer_one;
+ master->dev.of_node = pdev->dev.of_node;
+ master->auto_runtime_pm = true;
++ master->dma_alignment = dma_get_cache_alignment();
+
+ platform_set_drvdata(pdev, master);
+
+@@ -619,6 +883,56 @@ static int spi_qup_probe(struct platform
+ QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
+ base + QUP_ERROR_FLAGS_EN);
+
++ /* allocate dma resources, if available */
++ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
++ if (controller->rx_chan) {
++ controller->tx_chan =
++ dma_request_slave_channel(&pdev->dev, "tx");
++
++ if (!controller->tx_chan) {
++ dev_err(&pdev->dev, "Failed to allocate dma tx chan");
++ dma_release_channel(controller->rx_chan);
++ }
++
++ /* set DMA parameters */
++ controller->rx_conf.device_fc = 1;
++ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO;
++ controller->rx_conf.src_maxburst = controller->in_blk_sz;
++
++ controller->tx_conf.device_fc = 1;
++ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO;
++ controller->tx_conf.dst_maxburst = controller->out_blk_sz;
++
++ if (dmaengine_slave_config(controller->rx_chan,
++ &controller->rx_conf)) {
++ dev_err(&pdev->dev, "failed to configure RX channel\n");
++
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ } else if (dmaengine_slave_config(controller->tx_chan,
++ &controller->tx_conf)) {
++ dev_err(&pdev->dev, "failed to configure TX channel\n");
++
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ }
++
++ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE,
++ GFP_KERNEL);
++
++ if (!controller->dummy) {
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ }
++ }
++
++
+ writel_relaxed(0, base + SPI_CONFIG);
+ writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
+
+@@ -731,6 +1045,11 @@ static int spi_qup_remove(struct platfor
+ if (ret)
+ return ret;
+
++ if (controller->rx_chan)
++ dma_release_channel(controller->rx_chan);
++ if (controller->tx_chan)
++ dma_release_channel(controller->tx_chan);
++
+ clk_disable_unprepare(controller->cclk);
+ clk_disable_unprepare(controller->iclk);
+
diff --git a/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch
new file mode 100644
index 0000000..62ee5b4
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch
@@ -0,0 +1,376 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3] spi: qup: Fix incorrect block transfers
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 5007321
+Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org>
+To: Mark Brown <broonie@kernel.org>
+Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
+ linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org,
+ "Ivan T. Ivanov" <iivanov@mm-sol.com>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>
+Date: Tue, 30 Sep 2014 16:21:28 -0500
+
+This patch fixes a number of errors with the QUP block transfer mode. Errors
+manifested themselves as input underruns, output overruns, and timed out
+transactions.
+
+The block mode does not require the priming that occurs in FIFO mode. At the
+moment that the QUP is placed into the RUN state, the QUP will immediately raise
+an interrupt if the request is a write. Therefore, there is no need to prime
+the pump.
+
+In addition, the block transfers require that whole blocks of data are
+read/written at a time. The last block of data that completes a transaction may
+contain less than a full blocks worth of data.
+
+Each block of data results in an input/output service interrupt accompanied with
+a input/output block flag set. Additional block reads/writes require clearing
+of the service flag. It is ok to check for additional blocks of data in the
+ISR, but you have to ack every block you transfer. Imbalanced acks result in
+early return from complete transactions with pending interrupts that still have
+to be ack'd. The next transaction can be affected by these interrupts.
+Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set.
+
+Changes from v2:
+- Added in additional completion check so that transaction done is not
+ prematurely signaled.
+- Fixed various review comments.
+
+Changes from v1:
+- Split out read/write block function.
+- Removed extraneous checks for transfer length
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 148 insertions(+), 53 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -82,6 +82,8 @@
+ #define QUP_IO_M_MODE_BAM 3
+
+ /* QUP_OPERATIONAL fields */
++#define QUP_OP_IN_BLOCK_READ_REQ BIT(13)
++#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12)
+ #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
+ #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
+ #define QUP_OP_IN_SERVICE_FLAG BIT(9)
+@@ -147,6 +149,7 @@ struct spi_qup {
+ int tx_bytes;
+ int rx_bytes;
+ int qup_v1;
++ int mode;
+
+ int use_dma;
+
+@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_
+ return 0;
+ }
+
+-
+-static void spi_qup_fifo_read(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++static void spi_qup_fill_read_buffer(struct spi_qup *controller,
++ struct spi_transfer *xfer, u32 data)
+ {
+ u8 *rx_buf = xfer->rx_buf;
+- u32 word, state;
+- int idx, shift, w_size;
+-
+- w_size = controller->w_size;
+-
+- while (controller->rx_bytes < xfer->len) {
+-
+- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
+- break;
+-
+- word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+-
+- if (!rx_buf) {
+- controller->rx_bytes += w_size;
+- continue;
+- }
++ int idx, shift;
+
+- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++ if (rx_buf)
++ for (idx = 0; idx < controller->w_size; idx++) {
+ /*
+ * The data format depends on bytes per SPI word:
+ * 4 bytes: 0x12345678
+@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi
+ * 1 byte : 0x00000012
+ */
+ shift = BITS_PER_BYTE;
+- shift *= (w_size - idx - 1);
+- rx_buf[controller->rx_bytes] = word >> shift;
++ shift *= (controller->w_size - idx - 1);
++ rx_buf[controller->rx_bytes + idx] = data >> shift;
++ }
++
++ controller->rx_bytes += controller->w_size;
++}
++
++static void spi_qup_prepare_write_data(struct spi_qup *controller,
++ struct spi_transfer *xfer, u32 *data)
++{
++ const u8 *tx_buf = xfer->tx_buf;
++ u32 val;
++ int idx;
++
++ *data = 0;
++
++ if (tx_buf)
++ for (idx = 0; idx < controller->w_size; idx++) {
++ val = tx_buf[controller->tx_bytes + idx];
++ *data |= val << (BITS_PER_BYTE * (3 - idx));
+ }
++
++ controller->tx_bytes += controller->w_size;
++}
++
++static void spi_qup_fifo_read(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ u32 data;
++
++ /* clear service request */
++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++ while (controller->rx_bytes < xfer->len) {
++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++ QUP_OP_IN_FIFO_NOT_EMPTY))
++ break;
++
++ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
++
++ spi_qup_fill_read_buffer(controller, xfer, data);
+ }
+ }
+
+ static void spi_qup_fifo_write(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++ struct spi_transfer *xfer)
+ {
+- const u8 *tx_buf = xfer->tx_buf;
+- u32 word, state, data;
+- int idx, w_size;
++ u32 data;
+
+- w_size = controller->w_size;
++ /* clear service request */
++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
+
+ while (controller->tx_bytes < xfer->len) {
+
+- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+- if (state & QUP_OP_OUT_FIFO_FULL)
++ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
++ QUP_OP_OUT_FIFO_FULL)
+ break;
+
+- word = 0;
+- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
++ spi_qup_prepare_write_data(controller, xfer, &data);
++ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO);
+
+- if (!tx_buf) {
+- controller->tx_bytes += w_size;
+- break;
+- }
++ }
++}
+
+- data = tx_buf[controller->tx_bytes];
+- word |= data << (BITS_PER_BYTE * (3 - idx));
+- }
++static void spi_qup_block_read(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ u32 data;
++ u32 reads_per_blk = controller->in_blk_sz >> 2;
++ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size;
++ int i;
++
++ do {
++ /* ACK by clearing service flag */
++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++ /* transfer up to a block size of data in a single pass */
++ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) {
++
++ /* read data and fill up rx buffer */
++ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
++ spi_qup_fill_read_buffer(controller, xfer, data);
++ }
++
++ /* check to see if next block is ready */
++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++ QUP_OP_IN_BLOCK_READ_REQ))
++ break;
+
+- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
+- }
++ } while (num_words);
++
++ /*
++ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
++ * reads, it has to be cleared again at the very end
++ */
++ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
++ QUP_OP_MAX_INPUT_DONE_FLAG)
++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++}
++
++static void spi_qup_block_write(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ u32 data;
++ u32 writes_per_blk = controller->out_blk_sz >> 2;
++ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size;
++ int i;
++
++ do {
++ /* ACK by clearing service flag */
++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++ controller->base + QUP_OPERATIONAL);
++
++ /* transfer up to a block size of data in a single pass */
++ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) {
++
++ /* swizzle the bytes for output and write out */
++ spi_qup_prepare_write_data(controller, xfer, &data);
++ writel_relaxed(data,
++ controller->base + QUP_OUTPUT_FIFO);
++ }
++
++ /* check to see if next block is ready */
++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++ QUP_OP_OUT_BLOCK_WRITE_REQ))
++ break;
++
++ } while (num_words);
+ }
+
+ static void qup_dma_callback(void *data)
+@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i
+
+ writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
+ writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
+- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+
+ if (!xfer) {
++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+ dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
+ qup_err, spi_err, opflags);
+ return IRQ_HANDLED;
+@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i
+ }
+
+ if (!controller->use_dma) {
+- if (opflags & QUP_OP_IN_SERVICE_FLAG)
+- spi_qup_fifo_read(controller, xfer);
++ if (opflags & QUP_OP_IN_SERVICE_FLAG) {
++ if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
++ spi_qup_block_read(controller, xfer);
++ else
++ spi_qup_fifo_read(controller, xfer);
++ }
+
+- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+- spi_qup_fifo_write(controller, xfer);
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) {
++ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ)
++ spi_qup_block_write(controller, xfer);
++ else
++ spi_qup_fifo_write(controller, xfer);
++ }
+ }
+
+ spin_lock_irqsave(&controller->lock, flags);
+@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+ controller->xfer = xfer;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (controller->rx_bytes == xfer->len || error)
++ if ((controller->rx_bytes == xfer->len &&
++ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
+ complete(&controller->done);
+
+ return IRQ_HANDLED;
+@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- u32 config, iomode, mode;
++ u32 config, iomode;
+ int ret, n_words, w_size;
+ size_t dma_align = dma_get_cache_alignment();
+ u32 dma_available = 0;
+@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_
+ dma_available = 1;
+
+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+- mode = QUP_IO_M_MODE_FIFO;
++ controller->mode = QUP_IO_M_MODE_FIFO;
+ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+ /* must be zero for FIFO */
+@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+ controller->use_dma = 0;
+ } else if (!dma_available) {
+- mode = QUP_IO_M_MODE_BLOCK;
++ controller->mode = QUP_IO_M_MODE_BLOCK;
+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+ /* must be zero for BLOCK and BAM */
+@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ controller->use_dma = 0;
+ } else {
+- mode = QUP_IO_M_MODE_DMOV;
++ controller->mode = QUP_IO_M_MODE_DMOV;
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ controller->use_dma = 1;
+@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_
+ else
+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+
+- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+
+ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+
+@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s
+ goto exit;
+ }
+
+- spi_qup_fifo_write(controller, xfer);
++ if (controller->mode == QUP_IO_M_MODE_FIFO)
++ spi_qup_fifo_write(controller, xfer);
+
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+ dev_warn(controller->dev, "cannot set EXECUTE state\n");
+@@ -741,6 +836,7 @@ exit:
+ if (!ret)
+ ret = controller->error;
+ spin_unlock_irqrestore(&controller->lock, flags);
++
+ return ret;
+ }
+
diff --git a/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch
new file mode 100644
index 0000000..7052227
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch
@@ -0,0 +1,56 @@
+From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 15 Jan 2015 17:56:02 -0800
+Subject: [PATCH] spi: qup: Ensure done detection
+
+This patch fixes an issue where a SPI transaction has completed, but the done
+condition is missed. This occurs because at the time of interrupt the
+MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks
+of data from the FIFO, the last portion of data comes in.
+
+The opflags read at the beginning of the irq handler no longer matches the
+current opflag state. To get around this condition, the block read function
+should update the opflags so that done detection is correct after the return.
+
+Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp
+ }
+
+ static void spi_qup_block_read(struct spi_qup *controller,
+- struct spi_transfer *xfer)
++ struct spi_transfer *xfer, u32 *opflags)
+ {
+ u32 data;
+ u32 reads_per_blk = controller->in_blk_sz >> 2;
+@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp
+
+ /*
+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
+- * reads, it has to be cleared again at the very end
++ * reads, it has to be cleared again at the very end. However, be sure
++ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
++ * present and this is used to determine if transaction is complete
+ */
+- if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
+- QUP_OP_MAX_INPUT_DONE_FLAG)
++ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
+
+@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ if (!controller->use_dma) {
+ if (opflags & QUP_OP_IN_SERVICE_FLAG) {
+ if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
+- spi_qup_block_read(controller, xfer);
++ spi_qup_block_read(controller, xfer, &opflags);
+ else
+ spi_qup_fifo_read(controller, xfer);
+ }
diff --git a/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch
new file mode 100644
index 0000000..68489a8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch
@@ -0,0 +1,67 @@
+From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:19:30 -0800
+Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding
+
+MSM watchdog configuration happens in the same register block as the
+timer, so we'll use the same binding as the existing timer.
+
+The qcom-wdt will now be probed when devicetree has an entry compatible
+with "qcom,kpss-timer" or "qcom-scss-timer".
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/watchdog/qcom-wdt.c
++++ b/drivers/watchdog/qcom-wdt.c
+@@ -20,9 +20,9 @@
+ #include <linux/reboot.h>
+ #include <linux/watchdog.h>
+
+-#define WDT_RST 0x0
+-#define WDT_EN 0x8
+-#define WDT_BITE_TIME 0x24
++#define WDT_RST 0x38
++#define WDT_EN 0x40
++#define WDT_BITE_TIME 0x5C
+
+ struct qcom_wdt {
+ struct watchdog_device wdd;
+@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platfor
+ {
+ struct qcom_wdt *wdt;
+ struct resource *res;
++ struct device_node *np = pdev->dev.of_node;
++ u32 percpu_offset;
+ int ret;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platfor
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ /* We use CPU0's DGT for the watchdog */
++ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
++ percpu_offset = 0;
++
++ res->start += percpu_offset;
++ res->end += percpu_offset;
++
+ wdt->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platfo
+ }
+
+ static const struct of_device_id qcom_wdt_of_table[] = {
+- { .compatible = "qcom,kpss-wdt-msm8960", },
+- { .compatible = "qcom,kpss-wdt-apq8064", },
+- { .compatible = "qcom,kpss-wdt-ipq8064", },
++ { .compatible = "qcom,kpss-timer" },
++ { .compatible = "qcom,scss-timer" },
+ { },
+ };
+ MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
diff --git a/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch
new file mode 100644
index 0000000..ae96776
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch
@@ -0,0 +1,48 @@
+From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:27:39 -0800
+Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064
+
+Add the watchdog related entries to the Krait Processor Sub-system
+(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock
+description of SLEEP_CLK, which will do for now.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -60,6 +60,14 @@
+ };
+ };
+
++ clocks {
++ sleep_clk: sleep_clk {
++ compatible = "fixed-clock";
++ clock-frequency = <32768>;
++ #clock-cells = <0>;
++ };
++ };
++
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -89,10 +97,14 @@
+ compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ interrupts = <1 1 0x301>,
+ <1 2 0x301>,
+- <1 3 0x301>;
++ <1 3 0x301>,
++ <1 4 0x301>,
++ <1 5 0x301>;
+ reg = <0x0200a000 0x100>;
+ clock-frequency = <25000000>,
+ <32768>;
++ clocks = <&sleep_clk>;
++ clock-names = "sleep";
+ cpu-offset = <0x80000>;
+ };
+
diff --git a/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch
new file mode 100644
index 0000000..6876768
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch
@@ -0,0 +1,50 @@
+From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:36:27 -0800
+Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc
+
+The watchdog has been reworked to use the same DT node as the timer.
+This change is updating the device tree doc accordingly.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+--- a/Documentation/devicetree/bindings/arm/msm/timer.txt
++++ b/Documentation/devicetree/bindings/arm/msm/timer.txt
+@@ -9,11 +9,17 @@ Properties:
+ "qcom,scss-timer" - scorpion subsystem
+
+ - interrupts : Interrupts for the the debug timer, the first general purpose
+- timer, and optionally a second general purpose timer in that
+- order.
++ timer, and optionally a second general purpose timer, and
++ optionally as well, 2 watchdog interrupts, in that order.
+
+ - reg : Specifies the base address of the timer registers.
+
++- clocks: Reference to the parent clocks, one per output clock. The parents
++ must appear in the same order as the clock names.
++
++- clock-names: The name of the clocks as free-form strings. They should be in
++ the same order as the clocks.
++
+ - clock-frequency : The frequency of the debug timer and the general purpose
+ timer(s) in Hz in that order.
+
+@@ -29,9 +35,13 @@ Example:
+ compatible = "qcom,scss-timer", "qcom,msm-timer";
+ interrupts = <1 1 0x301>,
+ <1 2 0x301>,
+- <1 3 0x301>;
++ <1 3 0x301>,
++ <1 4 0x301>,
++ <1 5 0x301>;
+ reg = <0x0200a000 0x100>;
+ clock-frequency = <19200000>,
+ <32768>;
++ clocks = <&sleep_clk>;
++ clock-names = "sleep";
+ cpu-offset = <0x40000>;
+ };
diff --git a/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch
new file mode 100644
index 0000000..a61481e
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -140,7 +140,7 @@
+ ranges;
+ status = "disabled";
+
+- serial@12490000 {
++ uart2: serial@12490000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x12490000 0x1000>,
+ <0x12480000 0x1000>;
+@@ -175,7 +175,7 @@
+ ranges;
+ status = "disabled";
+
+- serial@16340000 {
++ uart4: serial@16340000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16340000 0x1000>,
+ <0x16300000 0x1000>;
+@@ -209,7 +209,7 @@
+ ranges;
+ status = "disabled";
+
+- 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-3.18/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch
new file mode 100644
index 0000000..bfdb30f
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -77,15 +77,7 @@
+ spi-max-frequency = <50000000>;
+ reg = <0>;
+
+- partition@0 {
+- label = "rootfs";
+- reg = <0x0 0x1000000>;
+- };
+-
+- partition@1 {
+- label = "scratch";
+- reg = <0x1000000 0x1000000>;
+- };
++ linux,part-probe = "qcom-smem";
+ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch b/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch
new file mode 100644
index 0000000..bd6ec1e
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch
@@ -0,0 +1,160 @@
+From a32d6e7c8fca6371a2614924b89981bc912b6378 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 | 257 +++++++++++++++++++++++++++++++
+ 2 files changed, 258 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -360,6 +360,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-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
new file mode 100644
index 0000000..319859b
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -794,7 +794,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,
+ },
+ },
+ };
+@@ -810,7 +810,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,
+ },
+ },
+ };
+@@ -858,7 +858,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-3.18/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch
new file mode 100644
index 0000000..f026ed9
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch
@@ -0,0 +1,14 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,6 +4,11 @@
+ model = "Qualcomm IPQ8064/AP148";
+ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
+
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
diff --git a/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch b/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch
new file mode 100644
index 0000000..04f35b7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch
@@ -0,0 +1,167 @@
+From fb7737e949e31d8a71acee6bbb670f32dbd2a2c0 Mon Sep 17 00:00:00 2001
+From: Suman Anna <s-anna@ti.com>
+Date: Wed, 4 Mar 2015 20:01:14 -0600
+Subject: [PATCH] hwspinlock/core: add device tree support
+
+This patch adds a new OF-friendly API of_hwspin_lock_get_id()
+for hwspinlock clients to use/request locks from a hwspinlock
+device instantiated through a device-tree blob. This new API
+can be used by hwspinlock clients to get the id for a specific
+lock using the phandle + args specifier, so that it can be
+requested using the available hwspin_lock_request_specific()
+API.
+
+Signed-off-by: Suman Anna <s-anna@ti.com>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+[small comment clarification]
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ Documentation/hwspinlock.txt | 10 +++++
+ drivers/hwspinlock/hwspinlock_core.c | 79 ++++++++++++++++++++++++++++++++++++
+ include/linux/hwspinlock.h | 7 ++++
+ 3 files changed, 96 insertions(+)
+
+--- a/Documentation/hwspinlock.txt
++++ b/Documentation/hwspinlock.txt
+@@ -48,6 +48,16 @@ independent, drivers.
+ ids for predefined purposes.
+ Should be called from a process context (might sleep).
+
++ int of_hwspin_lock_get_id(struct device_node *np, int index);
++ - retrieve the global lock id for an OF phandle-based specific lock.
++ This function provides a means for DT users of a hwspinlock module
++ to get the global lock id of a specific hwspinlock, so that it can
++ be requested using the normal hwspin_lock_request_specific() API.
++ The function returns a lock id number on success, -EPROBE_DEFER if
++ the hwspinlock device is not yet registered with the core, or other
++ error values.
++ Should be called from a process context (might sleep).
++
+ int hwspin_lock_free(struct hwspinlock *hwlock);
+ - free a previously-assigned hwspinlock; returns 0 on success, or an
+ appropriate error code on failure (e.g. -EINVAL if the hwspinlock
+--- a/drivers/hwspinlock/hwspinlock_core.c
++++ b/drivers/hwspinlock/hwspinlock_core.c
+@@ -27,6 +27,7 @@
+ #include <linux/hwspinlock.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/mutex.h>
++#include <linux/of.h>
+
+ #include "hwspinlock_internal.h"
+
+@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *
+ }
+ EXPORT_SYMBOL_GPL(__hwspin_unlock);
+
++/**
++ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
++ * @bank: the hwspinlock device bank
++ * @hwlock_spec: hwlock specifier as found in the device tree
++ *
++ * This is a simple translation function, suitable for hwspinlock platform
++ * drivers that only has a lock specifier length of 1.
++ *
++ * Returns a relative index of the lock within a specified bank on success,
++ * or -EINVAL on invalid specifier cell count.
++ */
++static inline int
++of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)
++{
++ if (WARN_ON(hwlock_spec->args_count != 1))
++ return -EINVAL;
++
++ return hwlock_spec->args[0];
++}
++
++/**
++ * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock
++ * @np: device node from which to request the specific hwlock
++ * @index: index of the hwlock in the list of values
++ *
++ * This function provides a means for DT users of the hwspinlock module to
++ * get the global lock id of a specific hwspinlock using the phandle of the
++ * hwspinlock device, so that it can be requested using the normal
++ * hwspin_lock_request_specific() API.
++ *
++ * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
++ * device is not yet registered, -EINVAL on invalid args specifier value or an
++ * appropriate error as returned from the OF parsing of the DT client node.
++ */
++int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++ struct of_phandle_args args;
++ struct hwspinlock *hwlock;
++ struct radix_tree_iter iter;
++ void **slot;
++ int id;
++ int ret;
++
++ ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,
++ &args);
++ if (ret)
++ return ret;
++
++ /* Find the hwspinlock device: we need its base_id */
++ ret = -EPROBE_DEFER;
++ rcu_read_lock();
++ radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {
++ hwlock = radix_tree_deref_slot(slot);
++ if (unlikely(!hwlock))
++ continue;
++
++ if (hwlock->bank->dev->of_node == args.np) {
++ ret = 0;
++ break;
++ }
++ }
++ rcu_read_unlock();
++ if (ret < 0)
++ goto out;
++
++ id = of_hwspin_lock_simple_xlate(&args);
++ if (id < 0 || id >= hwlock->bank->num_locks) {
++ ret = -EINVAL;
++ goto out;
++ }
++ id += hwlock->bank->base_id;
++
++out:
++ of_node_put(args.np);
++ return ret ? ret : id;
++}
++EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);
++
+ static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
+ {
+ struct hwspinlock *tmp;
+--- a/include/linux/hwspinlock.h
++++ b/include/linux/hwspinlock.h
+@@ -26,6 +26,7 @@
+ #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */
+
+ struct device;
++struct device_node;
+ struct hwspinlock;
+ struct hwspinlock_device;
+ struct hwspinlock_ops;
+@@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspin
+ struct hwspinlock *hwspin_lock_request(void);
+ struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
+ int hwspin_lock_free(struct hwspinlock *hwlock);
++int of_hwspin_lock_get_id(struct device_node *np, int index);
+ int hwspin_lock_get_id(struct hwspinlock *hwlock);
+ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
+ unsigned long *);
+@@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock *
+ {
+ }
+
++static inline int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++ return 0;
++}
++
+ static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)
+ {
+ return 0;
diff --git a/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch b/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch
new file mode 100644
index 0000000..581b199
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch
@@ -0,0 +1,234 @@
+From 19a0f61224d2d91860fa8291ab63cb104ee86bdd Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Tue, 24 Mar 2015 10:11:05 -0700
+Subject: [PATCH] hwspinlock: qcom: Add support for Qualcomm HW Mutex block
+
+Add driver for Qualcomm Hardware Mutex block found in many Qualcomm
+SoCs.
+
+Based on initial effort by Kumar Gala <galak@codeaurora.org>
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Jeffrey Hugo <jhugo@codeaurora.org>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/Kconfig | 12 +++
+ drivers/hwspinlock/Makefile | 1 +
+ drivers/hwspinlock/qcom_hwspinlock.c | 181 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 194 insertions(+)
+ create mode 100644 drivers/hwspinlock/qcom_hwspinlock.c
+
+--- a/drivers/hwspinlock/Kconfig
++++ b/drivers/hwspinlock/Kconfig
+@@ -18,6 +18,18 @@ config HWSPINLOCK_OMAP
+
+ If unsure, say N.
+
++config HWSPINLOCK_QCOM
++ tristate "Qualcomm Hardware Spinlock device"
++ depends on ARCH_QCOM
++ select HWSPINLOCK
++ select MFD_SYSCON
++ help
++ Say y here to support the Qualcomm Hardware Mutex functionality, which
++ provides a synchronisation mechanism for the various processors on
++ the SoC.
++
++ If unsure, say N.
++
+ config HSEM_U8500
+ tristate "STE Hardware Semaphore functionality"
+ depends on ARCH_U8500
+--- a/drivers/hwspinlock/Makefile
++++ b/drivers/hwspinlock/Makefile
+@@ -4,4 +4,5 @@
+
+ obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
+ obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
++obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
+ obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
+--- /dev/null
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -0,0 +1,181 @@
++/*
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2015, Sony Mobile Communications AB
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++
++#include "hwspinlock_internal.h"
++
++#define QCOM_MUTEX_APPS_PROC_ID 1
++#define QCOM_MUTEX_NUM_LOCKS 32
++
++static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
++{
++ struct regmap_field *field = lock->priv;
++ u32 lock_owner;
++ int ret;
++
++ ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
++ if (ret)
++ return ret;
++
++ ret = regmap_field_read(field, &lock_owner);
++ if (ret)
++ return ret;
++
++ return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
++}
++
++static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
++{
++ struct regmap_field *field = lock->priv;
++ u32 lock_owner;
++ int ret;
++
++ ret = regmap_field_read(field, &lock_owner);
++ if (ret) {
++ pr_err("%s: unable to query spinlock owner\n", __func__);
++ return;
++ }
++
++ if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
++ pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
++ __func__, lock_owner);
++ }
++
++ ret = regmap_field_write(field, 0);
++ if (ret)
++ pr_err("%s: failed to unlock spinlock\n", __func__);
++}
++
++static const struct hwspinlock_ops qcom_hwspinlock_ops = {
++ .trylock = qcom_hwspinlock_trylock,
++ .unlock = qcom_hwspinlock_unlock,
++};
++
++static const struct of_device_id qcom_hwspinlock_of_match[] = {
++ { .compatible = "qcom,sfpb-mutex" },
++ { .compatible = "qcom,tcsr-mutex" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
++
++static int qcom_hwspinlock_probe(struct platform_device *pdev)
++{
++ struct hwspinlock_device *bank;
++ struct device_node *syscon;
++ struct reg_field field;
++ struct regmap *regmap;
++ size_t array_size;
++ u32 stride;
++ u32 base;
++ int ret;
++ int i;
++
++ syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
++ if (!syscon) {
++ dev_err(&pdev->dev, "no syscon property\n");
++ return -ENODEV;
++ }
++
++ regmap = syscon_node_to_regmap(syscon);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no offset in syscon\n");
++ return -EINVAL;
++ }
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no stride syscon\n");
++ return -EINVAL;
++ }
++
++ array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
++ bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
++ if (!bank)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, bank);
++
++ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
++ field.reg = base + i * stride;
++ field.lsb = 0;
++ field.msb = 32;
++
++ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
++ regmap, field);
++ }
++
++ pm_runtime_enable(&pdev->dev);
++
++ ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops,
++ 0, QCOM_MUTEX_NUM_LOCKS);
++ if (ret)
++ pm_runtime_disable(&pdev->dev);
++
++ return ret;
++}
++
++static int qcom_hwspinlock_remove(struct platform_device *pdev)
++{
++ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = hwspin_lock_unregister(bank);
++ if (ret) {
++ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
++ return ret;
++ }
++
++ pm_runtime_disable(&pdev->dev);
++
++ return 0;
++}
++
++static struct platform_driver qcom_hwspinlock_driver = {
++ .probe = qcom_hwspinlock_probe,
++ .remove = qcom_hwspinlock_remove,
++ .driver = {
++ .name = "qcom_hwspinlock",
++ .of_match_table = qcom_hwspinlock_of_match,
++ },
++};
++
++static int __init qcom_hwspinlock_init(void)
++{
++ return platform_driver_register(&qcom_hwspinlock_driver);
++}
++/* board init code might need to reserve hwspinlocks for predefined purposes */
++postcore_initcall(qcom_hwspinlock_init);
++
++static void __exit qcom_hwspinlock_exit(void)
++{
++ platform_driver_unregister(&qcom_hwspinlock_driver);
++}
++module_exit(qcom_hwspinlock_exit);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");
diff --git a/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch b/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch
new file mode 100644
index 0000000..70d5a58
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch
@@ -0,0 +1,26 @@
+From bd5717a4632cdecafe82d03de7dcb3b1876e2828 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Fri, 26 Jun 2015 14:47:21 -0700
+Subject: [PATCH] hwspinlock: qcom: Correct msb in regmap_field
+
+msb of the regmap_field was mistakenly given the value 32, to set all bits
+in the regmap update mask; although incorrect this worked until 921cc294,
+where the mask calculation was corrected.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/qcom_hwspinlock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwspinlock/qcom_hwspinlock.c
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -123,7 +123,7 @@ static int qcom_hwspinlock_probe(struct
+ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
+ field.reg = base + i * stride;
+ field.lsb = 0;
+- field.msb = 32;
++ field.msb = 31;
+
+ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
+ regmap, field);
diff --git a/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..4a35325
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
@@ -0,0 +1,34 @@
+From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:45:26 -0700
+Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts
+
+Add one new node to the ipq806x.dtsi file to declare & register the
+hardware spinlock devices. This mechanism is required to be used by
+other drivers such as SMEM.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -291,5 +291,17 @@
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
++
++ sfpb_mutex_block: syscon@1200600 {
++ compatible = "syscon";
++ reg = <0x01200600 0x100>;
++ };
+ };
++
++ sfpb_mutex: sfpb-mutex {
++ compatible = "qcom,sfpb-mutex";
++ syscon = <&sfpb_mutex_block 4 4>;
++
++ #hwlock-cells = <1>;
++ };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch
new file mode 100644
index 0000000..d22db22
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202201
+Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com>
+To: Rob Herring <robh+dt@kernel.org>, Pawel Moll <pawel.moll@arm.com>,
+ Mark Rutland <mark.rutland@arm.com>,
+ Ian Campbell <ijc+devicetree@hellion.org.uk>,
+ Kumar Gala <galak@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>,
+ Andry Gross <agross@codeaurora.org>
+Cc: <devicetree@vger.kernel.org>,
+ linux-arm-msm <linux-arm-msm@vger.kernel.org>,
+ <linux-kernel@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:57 -0700
+
+Add device tree binding documentation for the Qualcom Shared Memory
+manager.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+
+---
+Changes since v1:
+- None
+
+ .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+@@ -0,0 +1,49 @@
++Qualcomm Shared Memory binding
++
++This binding describes the Qualcomm Shared Memory, used to share data between
++various subsystems and OSes in Qualcomm platforms.
++
++- compatible:
++ Usage: required
++ Value type: <stringlist>
++ Definition: must be:
++ "qcom,smem"
++
++- memory-region:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: handle to memory reservation for main smem memory region.
++
++- reg:
++ Usage: optional
++ Value type: <prop-encoded-array>
++ Definition: base address and size pair for any additional memory areas
++ of the shared memory.
++
++- hwspinlocks:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: reference to a hwspinlock used to protect allocations from
++ the shared memory
++
++= EXAMPLE
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ smem_region: smem@fa00000 {
++ reg = <0xfa00000 0x200000>;
++ no-map;
++ };
++ };
++
++ smem@fa00000 {
++ compatible = "qcom,smem";
++
++ memory-region = <&smem_region>;
++ reg = <0xfc428000 0x4000>;
++
++ hwlocks = <&tcsr_mutex 3>;
++ };
diff --git a/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch
new file mode 100644
index 0000000..c8cff1a
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch
@@ -0,0 +1,841 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202211
+Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com>
+To: Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+ David Brown <davidb@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>
+Cc: <linux-kernel@vger.kernel.org>, <linux-arm-msm@vger.kernel.org>,
+ <linux-soc@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:58 -0700
+
+The Shared Memory Manager driver implements an interface for allocating
+and accessing items in the memory area shared among all of the
+processors in a Qualcomm platform.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Tested-by: Andy Gross <agross@codeaurora.org>
+
+---
+Changes since v1:
+- ioremapping the regions nocache
+- improved documentation of the two regions of partitions
+- corrected free space check in private allocator
+
+ drivers/soc/qcom/Kconfig | 7 +
+ drivers/soc/qcom/Makefile | 1 +
+ drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/soc/qcom/smem.h | 14 +
+ 4 files changed, 790 insertions(+)
+ create mode 100644 drivers/soc/qcom/smem.c
+ create mode 100644 include/linux/soc/qcom/smem.h
+
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -9,3 +9,10 @@ config QCOM_GSBI
+ functions for connecting the underlying serial UART, SPI, and I2C
+ devices to the output pins.
+
++config QCOM_SMEM
++ tristate "Qualcomm Shared Memory Manager (SMEM)"
++ depends on ARCH_QCOM
++ help
++ Say y here to enable support for the Qualcomm Shared Memory Manager.
++ The driver provides an interface to items in a heap shared among all
++ processors in a Qualcomm platform.
+--- a/drivers/soc/qcom/Makefile
++++ b/drivers/soc/qcom/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
++obj-$(CONFIG_QCOM_SMEM) += smem.o
+--- /dev/null
++++ b/drivers/soc/qcom/smem.c
+@@ -0,0 +1,768 @@
++/*
++ * Copyright (c) 2015, Sony Mobile Communications AB.
++ * Copyright (c) 2012-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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/soc/qcom/smem.h>
++
++/*
++ * The Qualcomm shared memory system is a allocate only heap structure that
++ * consists of one of more memory areas that can be accessed by the processors
++ * in the SoC.
++ *
++ * All systems contains a global heap, accessible by all processors in the SoC,
++ * with a table of contents data structure (@smem_header) at the beginning of
++ * the main shared memory block.
++ *
++ * The global header contains metadata for allocations as well as a fixed list
++ * of 512 entries (@smem_global_entry) that can be initialized to reference
++ * parts of the shared memory space.
++ *
++ *
++ * In addition to this global heap a set of "private" heaps can be set up at
++ * boot time with access restrictions so that only certain processor pairs can
++ * access the data.
++ *
++ * These partitions are referenced from an optional partition table
++ * (@smem_ptable), that is found 4kB from the end of the main smem region. The
++ * partition table entries (@smem_ptable_entry) lists the involved processors
++ * (or hosts) and their location in the main shared memory region.
++ *
++ * Each partition starts with a header (@smem_partition_header) that identifies
++ * the partition and holds properties for the two internal memory regions. The
++ * two regions are cached and non-cached memory respectively. Each region
++ * contain a link list of allocation headers (@smem_private_entry) followed by
++ * their data.
++ *
++ * Items in the non-cached region are allocated from the start of the partition
++ * while items in the cached region are allocated from the end. The free area
++ * is hence the region between the cached and non-cached offsets.
++ *
++ *
++ * To synchronize allocations in the shared memory heaps a remote spinlock must
++ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
++ * platforms.
++ *
++ */
++
++/**
++ * struct smem_proc_comm - proc_comm communication struct (legacy)
++ * @command: current command to be executed
++ * @status: status of the currently requested command
++ * @params: parameters to the command
++ */
++struct smem_proc_comm {
++ u32 command;
++ u32 status;
++ u32 params[2];
++};
++
++/**
++ * struct smem_global_entry - entry to reference smem items on the heap
++ * @allocated: boolean to indicate if this entry is used
++ * @offset: offset to the allocated space
++ * @size: size of the allocated space, 8 byte aligned
++ * @aux_base: base address for the memory region used by this unit, or 0 for
++ * the default region. bits 0,1 are reserved
++ */
++struct smem_global_entry {
++ u32 allocated;
++ u32 offset;
++ u32 size;
++ u32 aux_base; /* bits 1:0 reserved */
++};
++#define AUX_BASE_MASK 0xfffffffc
++
++/**
++ * struct smem_header - header found in beginning of primary smem region
++ * @proc_comm: proc_comm communication interface (legacy)
++ * @version: array of versions for the various subsystems
++ * @initialized: boolean to indicate that smem is initialized
++ * @free_offset: index of the first unallocated byte in smem
++ * @available: number of bytes available for allocation
++ * @reserved: reserved field, must be 0
++ * toc: array of references to items
++ */
++struct smem_header {
++ struct smem_proc_comm proc_comm[4];
++ u32 version[32];
++ u32 initialized;
++ u32 free_offset;
++ u32 available;
++ u32 reserved;
++ struct smem_global_entry toc[];
++};
++
++/**
++ * struct smem_ptable_entry - one entry in the @smem_ptable list
++ * @offset: offset, within the main shared memory region, of the partition
++ * @size: size of the partition
++ * @flags: flags for the partition (currently unused)
++ * @host0: first processor/host with access to this partition
++ * @host1: second processor/host with access to this partition
++ * @reserved: reserved entries for later use
++ */
++struct smem_ptable_entry {
++ u32 offset;
++ u32 size;
++ u32 flags;
++ u16 host0;
++ u16 host1;
++ u32 reserved[8];
++};
++
++/**
++ * struct smem_ptable - partition table for the private partitions
++ * @magic: magic number, must be SMEM_PTABLE_MAGIC
++ * @version: version of the partition table
++ * @num_entries: number of partitions in the table
++ * @reserved: for now reserved entries
++ * @entry: list of @smem_ptable_entry for the @num_entries partitions
++ */
++struct smem_ptable {
++ u32 magic;
++ u32 version;
++ u32 num_entries;
++ u32 reserved[5];
++ struct smem_ptable_entry entry[];
++};
++#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$TOC" */
++
++/**
++ * struct smem_partition_header - header of the partitions
++ * @magic: magic number, must be SMEM_PART_MAGIC
++ * @host0: first processor/host with access to this partition
++ * @host1: second processor/host with access to this partition
++ * @size: size of the partition
++ * @offset_free_uncached: offset to the first free byte of uncached memory in
++ * this partition
++ * @offset_free_cached: offset to the first free byte of cached memory in this
++ * partition
++ * @reserved: for now reserved entries
++ */
++struct smem_partition_header {
++ u32 magic;
++ u16 host0;
++ u16 host1;
++ u32 size;
++ u32 offset_free_uncached;
++ u32 offset_free_cached;
++ u32 reserved[3];
++};
++#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */
++
++/**
++ * struct smem_private_entry - header of each item in the private partition
++ * @canary: magic number, must be SMEM_PRIVATE_CANARY
++ * @item: identifying number of the smem item
++ * @size: size of the data, including padding bytes
++ * @padding_data: number of bytes of padding of data
++ * @padding_hdr: number of bytes of padding between the header and the data
++ * @reserved: for now reserved entry
++ */
++struct smem_private_entry {
++ u16 canary;
++ u16 item;
++ u32 size; /* includes padding bytes */
++ u16 padding_data;
++ u16 padding_hdr;
++ u32 reserved;
++};
++#define SMEM_PRIVATE_CANARY 0xa5a5
++
++/*
++ * Item 3 of the global heap contains an array of versions for the various
++ * software components in the SoC. We verify that the boot loader version is
++ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
++ */
++#define SMEM_ITEM_VERSION 3
++#define SMEM_MASTER_SBL_VERSION_INDEX 7
++#define SMEM_EXPECTED_VERSION 11
++
++/*
++ * The first 8 items are only to be allocated by the boot loader while
++ * initializing the heap.
++ */
++#define SMEM_ITEM_LAST_FIXED 8
++
++/* Highest accepted item number, for both global and private heaps */
++#define SMEM_ITEM_LAST 512
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS 0
++
++/* Max number of processors/hosts in a system */
++#define SMEM_HOST_COUNT 7
++
++/**
++ * struct smem_region - representation of a chunk of memory used for smem
++ * @aux_base: identifier of aux_mem base
++ * @virt_base: virtual base address of memory with this aux_mem identifier
++ * @size: size of the memory region
++ */
++struct smem_region {
++ u32 aux_base;
++ void __iomem *virt_base;
++ size_t size;
++};
++
++/**
++ * struct qcom_smem - device data for the smem device
++ * @dev: device pointer
++ * @hwlock: reference to a hwspinlock
++ * @partitions: list of pointers to partitions affecting the current
++ * processor/host
++ * @num_regions: number of @regions
++ * @regions: list of the memory regions defining the shared memory
++ */
++struct qcom_smem {
++ struct device *dev;
++
++ struct hwspinlock *hwlock;
++
++ struct smem_partition_header *partitions[SMEM_HOST_COUNT];
++
++ unsigned num_regions;
++ struct smem_region regions[0];
++};
++
++/* Pointer to the one and only smem handle */
++static struct qcom_smem *__smem;
++
++/* Timeout (ms) for the trylock of remote spinlocks */
++#define HWSPINLOCK_TIMEOUT 1000
++
++static int qcom_smem_alloc_private(struct qcom_smem *smem,
++ unsigned host,
++ unsigned item,
++ size_t size)
++{
++ struct smem_partition_header *phdr;
++ struct smem_private_entry *hdr;
++ size_t alloc_size;
++ void *p;
++
++ /* We're not going to find it if there's no matching partition */
++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++ return -ENOENT;
++
++ phdr = smem->partitions[host];
++
++ p = (void *)phdr + sizeof(*phdr);
++ while (p < (void *)phdr + phdr->offset_free_uncached) {
++ hdr = p;
++
++ if (hdr->canary != SMEM_PRIVATE_CANARY) {
++ dev_err(smem->dev,
++ "Found invalid canary in host %d partition\n",
++ host);
++ return -EINVAL;
++ }
++
++ if (hdr->item == item)
++ return -EEXIST;
++
++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++ }
++
++ /* Check that we don't grow into the cached region */
++ alloc_size = sizeof(*hdr) + ALIGN(size, 8);
++ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) {
++ dev_err(smem->dev, "Out of memory\n");
++ return -ENOSPC;
++ }
++
++ hdr = p;
++ hdr->canary = SMEM_PRIVATE_CANARY;
++ hdr->item = item;
++ hdr->size = ALIGN(size, 8);
++ hdr->padding_data = hdr->size - size;
++ hdr->padding_hdr = 0;
++
++ /*
++ * Ensure the header is written before we advance the free offset, so
++ * that remote processors that does not take the remote spinlock still
++ * gets a consistent view of the linked list.
++ */
++ wmb();
++ phdr->offset_free_uncached += alloc_size;
++
++ return 0;
++}
++
++static int qcom_smem_alloc_global(struct qcom_smem *smem,
++ unsigned item,
++ size_t size)
++{
++ struct smem_header *header;
++ struct smem_global_entry *entry;
++
++ if (WARN_ON(item >= SMEM_ITEM_LAST))
++ return -EINVAL;
++
++ header = smem->regions[0].virt_base;
++ entry = &header->toc[item];
++ if (entry->allocated)
++ return -EEXIST;
++
++ size = ALIGN(size, 8);
++ if (WARN_ON(size > header->available))
++ return -ENOMEM;
++
++ entry->offset = header->free_offset;
++ entry->size = size;
++
++ /*
++ * Ensure the header is consistent before we mark the item allocated,
++ * so that remote processors will get a consistent view of the item
++ * even though they do not take the spinlock on read.
++ */
++ wmb();
++ entry->allocated = 1;
++
++ header->free_offset += size;
++ header->available -= size;
++
++ return 0;
++}
++
++/**
++ * qcom_smem_alloc - allocate space for a smem item
++ * @host: remote processor id, or -1
++ * @item: smem item handle
++ * @size: number of bytes to be allocated
++ *
++ * Allocate space for a given smem item of size @size, given that the item is
++ * not yet allocated.
++ */
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
++{
++ unsigned long flags;
++ int ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ if (item < SMEM_ITEM_LAST_FIXED) {
++ dev_err(__smem->dev,
++ "Rejecting allocation of static entry %d\n", item);
++ return -EINVAL;
++ }
++
++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++ HWSPINLOCK_TIMEOUT,
++ &flags);
++ if (ret)
++ return ret;
++
++ ret = qcom_smem_alloc_private(__smem, host, item, size);
++ if (ret == -ENOENT)
++ ret = qcom_smem_alloc_global(__smem, item, size);
++
++ hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++
++ return ret;
++}
++EXPORT_SYMBOL(qcom_smem_alloc);
++
++static int qcom_smem_get_global(struct qcom_smem *smem,
++ unsigned item,
++ void **ptr,
++ size_t *size)
++{
++ struct smem_header *header;
++ struct smem_region *area;
++ struct smem_global_entry *entry;
++ u32 aux_base;
++ unsigned i;
++
++ if (WARN_ON(item >= SMEM_ITEM_LAST))
++ return -EINVAL;
++
++ header = smem->regions[0].virt_base;
++ entry = &header->toc[item];
++ if (!entry->allocated)
++ return -ENXIO;
++
++ if (ptr != NULL) {
++ aux_base = entry->aux_base & AUX_BASE_MASK;
++
++ for (i = 0; i < smem->num_regions; i++) {
++ area = &smem->regions[i];
++
++ if (area->aux_base == aux_base || !aux_base) {
++ *ptr = area->virt_base + entry->offset;
++ break;
++ }
++ }
++ }
++ if (size != NULL)
++ *size = entry->size;
++
++ return 0;
++}
++
++static int qcom_smem_get_private(struct qcom_smem *smem,
++ unsigned host,
++ unsigned item,
++ void **ptr,
++ size_t *size)
++{
++ struct smem_partition_header *phdr;
++ struct smem_private_entry *hdr;
++ void *p;
++
++ /* We're not going to find it if there's no matching partition */
++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++ return -ENOENT;
++
++ phdr = smem->partitions[host];
++
++ p = (void *)phdr + sizeof(*phdr);
++ while (p < (void *)phdr + phdr->offset_free_uncached) {
++ hdr = p;
++
++ if (hdr->canary != SMEM_PRIVATE_CANARY) {
++ dev_err(smem->dev,
++ "Found invalid canary in host %d partition\n",
++ host);
++ return -EINVAL;
++ }
++
++ if (hdr->item == item) {
++ if (ptr != NULL)
++ *ptr = p + sizeof(*hdr) + hdr->padding_hdr;
++
++ if (size != NULL)
++ *size = hdr->size - hdr->padding_data;
++
++ return 0;
++ }
++
++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++ }
++
++ return -ENOENT;
++}
++
++/**
++ * qcom_smem_get - resolve ptr of size of a smem item
++ * @host: the remote processor, or -1
++ * @item: smem item handle
++ * @ptr: pointer to be filled out with address of the item
++ * @size: pointer to be filled out with size of the item
++ *
++ * Looks up pointer and size of a smem item.
++ */
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size)
++{
++ unsigned long flags;
++ int ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++ HWSPINLOCK_TIMEOUT,
++ &flags);
++ if (ret)
++ return ret;
++
++ ret = qcom_smem_get_private(__smem, host, item, ptr, size);
++ if (ret == -ENOENT)
++ ret = qcom_smem_get_global(__smem, item, ptr, size);
++
++ hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++ return ret;
++
++}
++EXPORT_SYMBOL(qcom_smem_get);
++
++/**
++ * qcom_smem_get_free_space - retrieve amont of free space in a partition
++ * @host: the remote processor identifing a partition, or -1
++ *
++ * To be used by smem clients as a quick way to determine if any new
++ * allocations has been made.
++ */
++int qcom_smem_get_free_space(unsigned host)
++{
++ struct smem_partition_header *phdr;
++ struct smem_header *header;
++ unsigned ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
++ phdr = __smem->partitions[host];
++ ret = phdr->offset_free_uncached;
++ } else {
++ header = __smem->regions[0].virt_base;
++ ret = header->available;
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(qcom_smem_get_free_space);
++
++static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
++{
++ unsigned *versions;
++ size_t size;
++ int ret;
++
++ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION,
++ (void **)&versions, &size);
++ if (ret < 0) {
++ dev_err(smem->dev, "Unable to read the version item\n");
++ return -ENOENT;
++ }
++
++ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
++ dev_err(smem->dev, "Version item is too small\n");
++ return -EINVAL;
++ }
++
++ return versions[SMEM_MASTER_SBL_VERSION_INDEX];
++}
++
++static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
++ unsigned local_host)
++{
++ struct smem_partition_header *header;
++ struct smem_ptable_entry *entry;
++ struct smem_ptable *ptable;
++ unsigned remote_host;
++ int i;
++
++ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024;
++ if (ptable->magic != SMEM_PTABLE_MAGIC)
++ return 0;
++
++ if (ptable->version != 1) {
++ dev_err(smem->dev,
++ "Unsupported partition header version %d\n",
++ ptable->version);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < ptable->num_entries; i++) {
++ entry = &ptable->entry[i];
++
++ if (entry->host0 != local_host && entry->host1 != local_host)
++ continue;
++
++ if (!entry->offset)
++ continue;
++
++ if (!entry->size)
++ continue;
++
++ if (entry->host0 == local_host)
++ remote_host = entry->host1;
++ else
++ remote_host = entry->host0;
++
++ if (smem->partitions[remote_host]) {
++ dev_err(smem->dev,
++ "Already found a partition for host %d\n",
++ remote_host);
++ return -EINVAL;
++ }
++
++ header = smem->regions[0].virt_base + entry->offset;
++
++ if (header->magic != SMEM_PART_MAGIC) {
++ dev_err(smem->dev,
++ "Partition %d has invalid magic\n", i);
++ return -EINVAL;
++ }
++
++ if (header->host0 != local_host && header->host1 != local_host) {
++ dev_err(smem->dev,
++ "Partition %d hosts are invalid\n", i);
++ return -EINVAL;
++ }
++
++ if (header->host0 != remote_host && header->host1 != remote_host) {
++ dev_err(smem->dev,
++ "Partition %d hosts are invalid\n", i);
++ return -EINVAL;
++ }
++
++ if (header->size != entry->size) {
++ dev_err(smem->dev,
++ "Partition %d has invalid size\n", i);
++ return -EINVAL;
++ }
++
++ if (header->offset_free_uncached > header->size) {
++ dev_err(smem->dev,
++ "Partition %d has invalid free pointer\n", i);
++ return -EINVAL;
++ }
++
++ smem->partitions[remote_host] = header;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_count_mem_regions(struct platform_device *pdev)
++{
++ struct resource *res;
++ int num_regions = 0;
++ int i;
++
++ for (i = 0; i < pdev->num_resources; i++) {
++ res = &pdev->resource[i];
++
++ if (resource_type(res) == IORESOURCE_MEM)
++ num_regions++;
++ }
++
++ return num_regions;
++}
++
++static int qcom_smem_probe(struct platform_device *pdev)
++{
++ struct smem_header *header;
++ struct device_node *np;
++ struct qcom_smem *smem;
++ struct resource *res;
++ struct resource r;
++ size_t array_size;
++ int num_regions = 0;
++ int hwlock_id;
++ u32 version;
++ int ret;
++ int i;
++
++ num_regions = qcom_smem_count_mem_regions(pdev) + 1;
++
++ array_size = num_regions * sizeof(struct smem_region);
++ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL);
++ if (!smem)
++ return -ENOMEM;
++
++ smem->dev = &pdev->dev;
++ smem->num_regions = num_regions;
++
++ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
++ if (!np) {
++ dev_err(&pdev->dev, "No memory-region specified\n");
++ return -EINVAL;
++ }
++
++ ret = of_address_to_resource(np, 0, &r);
++ of_node_put(np);
++ if (ret)
++ return ret;
++
++ smem->regions[0].aux_base = (u32)r.start;
++ smem->regions[0].size = resource_size(&r);
++ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev,
++ r.start,
++ resource_size(&r));
++ if (!smem->regions[0].virt_base)
++ return -ENOMEM;
++
++ for (i = 1; i < num_regions; i++) {
++ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1);
++
++ smem->regions[i].aux_base = (u32)res->start;
++ smem->regions[i].size = resource_size(res);
++ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev,
++ res->start,
++ resource_size(res));
++ if (!smem->regions[i].virt_base)
++ return -ENOMEM;
++ }
++
++ header = smem->regions[0].virt_base;
++ if (header->initialized != 1 || header->reserved) {
++ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n");
++ return -EINVAL;
++ }
++
++ version = qcom_smem_get_sbl_version(smem);
++ if (version >> 16 != SMEM_EXPECTED_VERSION) {
++ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version);
++ return -EINVAL;
++ }
++
++ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
++ if (ret < 0)
++ return ret;
++
++ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
++ if (hwlock_id < 0) {
++ dev_err(&pdev->dev, "failed to retrieve hwlock\n");
++ return hwlock_id;
++ }
++
++ smem->hwlock = hwspin_lock_request_specific(hwlock_id);
++ if (!smem->hwlock)
++ return -ENXIO;
++
++ __smem = smem;
++
++ return 0;
++}
++
++static int qcom_smem_remove(struct platform_device *pdev)
++{
++ hwspin_lock_free(__smem->hwlock);
++ __smem = NULL;
++
++ return 0;
++}
++
++static const struct of_device_id qcom_smem_of_match[] = {
++ { .compatible = "qcom,smem" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
++
++static struct platform_driver qcom_smem_driver = {
++ .probe = qcom_smem_probe,
++ .remove = qcom_smem_remove,
++ .driver = {
++ .name = "qcom_smem",
++ .of_match_table = qcom_smem_of_match,
++ .suppress_bind_attrs = true,
++ },
++};
++
++static int __init qcom_smem_init(void)
++{
++ return platform_driver_register(&qcom_smem_driver);
++}
++arch_initcall(qcom_smem_init);
++
++static void __exit qcom_smem_exit(void)
++{
++ platform_driver_unregister(&qcom_smem_driver);
++}
++module_exit(qcom_smem_exit)
++
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
++MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
++MODULE_LICENSE("GPLv2");
+--- /dev/null
++++ b/include/linux/soc/qcom/smem.h
+@@ -0,0 +1,14 @@
++#ifndef __QCOM_SMEM_H__
++#define __QCOM_SMEM_H__
++
++struct device_node;
++struct qcom_smem;
++
++#define QCOM_SMEM_HOST_ANY -1
++
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size);
++
++int qcom_smem_get_free_space(unsigned host);
++
++#endif
diff --git a/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..b2b9a74
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
@@ -0,0 +1,36 @@
+From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 14 Aug 2015 11:17:20 -0700
+Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts
+
+SMEM is used on IPQ806x to store various board related information such
+as boot device and flash partition layout. We'll declare it as a device
+so we can make use of it thanks to the new SMEM soc driver.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -54,7 +54,7 @@
+ no-map;
+ };
+
+- smem@41000000 {
++ smem: smem@41000000 {
+ reg = <0x41000000 0x200000>;
+ no-map;
+ };
+@@ -304,4 +304,10 @@
+
+ #hwlock-cells = <1>;
+ };
++
++ smem {
++ compatible = "qcom,smem";
++ memory-region = <&smem>;
++ hwlocks = <&sfpb_mutex 3>;
++ };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
new file mode 100644
index 0000000..b2c8cd5
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
@@ -0,0 +1,277 @@
+From 0501f76b138cf1dc11a313bb7a094da524b79337 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:53:14 -0700
+Subject: [PATCH 3/3] mtd: add SMEM parser for QCOM platforms
+
+On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
+used to store partition layout. This new parser can now be used to read
+SMEM and use it to register an MTD layout according to its content.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/mtd/Kconfig | 7 ++
+ drivers/mtd/Makefile | 1 +
+ drivers/mtd/qcom_smem_part.c | 231 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 239 insertions(+)
+ create mode 100644 drivers/mtd/qcom_smem_part.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -200,6 +200,13 @@ config MTD_MYLOADER_PARTS
+ You will still need the parsing functions to be called by the driver
+ for your particular device. It won't happen automatically.
+
++config MTD_QCOM_SMEM_PARTS
++ tristate "QCOM SMEM partitioning support"
++ depends on QCOM_SMEM
++ help
++ This provides partitions parser for QCOM devices using SMEM
++ such as IPQ806x.
++
+ comment "User Modules And Translation Layers"
+
+ #
+--- /dev/null
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -0,0 +1,231 @@
++/*
++ * Copyright (c) 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/device.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/spi/spi.h>
++#include <linux/module.h>
++
++#include <linux/soc/qcom/smem.h>
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS 0
++
++/* SMEM items index */
++#define SMEM_AARM_PARTITION_TABLE 9
++#define SMEM_BOOT_FLASH_TYPE 421
++#define SMEM_BOOT_FLASH_BLOCK_SIZE 424
++
++/* SMEM Flash types */
++#define SMEM_FLASH_NAND 2
++#define SMEM_FLASH_SPI 6
++
++#define SMEM_PART_NAME_SZ 16
++#define SMEM_PARTS_MAX 32
++
++struct smem_partition {
++ char name[SMEM_PART_NAME_SZ];
++ __le32 start;
++ __le32 size;
++ __le32 attr;
++};
++
++struct smem_partition_table {
++ u8 magic[8];
++ __le32 version;
++ __le32 len;
++ struct smem_partition parts[SMEM_PARTS_MAX];
++};
++
++/* SMEM Magic values in partition table */
++static const u8 SMEM_PTABLE_MAGIC[] = {
++ 0xaa, 0x73, 0xee, 0x55,
++ 0xdb, 0xbd, 0x5e, 0xe3,
++};
++
++static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
++ (void **) smem_blksz, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read flash blksz from SMEM\n");
++ return -ENOENT;
++ }
++
++ if (size != sizeof(**smem_blksz)) {
++ pr_err("Invalid flash blksz size in SMEM\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_get_flash_type(u64 **smem_flash_type)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
++ (void **) smem_flash_type, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read flash type from SMEM\n");
++ return -ENOENT;
++ }
++
++ if (size != sizeof(**smem_flash_type)) {
++ pr_err("Invalid flash type size in SMEM\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
++ (void **) pparts, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read partition table from SMEM\n");
++ return -ENOENT;
++ }
++
++ return 0;
++}
++
++static int of_dev_node_match(struct device *dev, void *data)
++{
++ return dev->of_node == data;
++}
++
++static bool is_spi_device(struct device_node *np)
++{
++ struct device *dev;
++
++ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
++ if (!dev)
++ return false;
++
++ put_device(dev);
++ return true;
++}
++
++static int parse_qcom_smem_partitions(struct mtd_info *master,
++ struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct smem_partition_table *smem_parts;
++ u64 *smem_flash_type, *smem_blksz;
++ struct mtd_partition *mtd_parts;
++ struct device_node *of_node = data->of_node;
++ int i, ret;
++
++ /*
++ * SMEM will only store the partition table of the boot device.
++ * If this is not the boot device, do not return any partition.
++ */
++ ret = qcom_smem_get_flash_type(&smem_flash_type);
++ if (ret < 0)
++ return ret;
++
++ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
++ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
++ return 0;
++
++ /*
++ * Just for sanity purpose, make sure the block size in SMEM matches the
++ * block size of the MTD device
++ */
++ ret = qcom_smem_get_flash_blksz(&smem_blksz);
++ if (ret < 0)
++ return ret;
++
++ if (*smem_blksz != master->erasesize) {
++ pr_err("SMEM block size differs from MTD block size\n");
++ return -EINVAL;
++ }
++
++ /* Get partition pointer from SMEM */
++ ret = qcom_smem_get_flash_partitions(&smem_parts);
++ if (ret < 0)
++ return ret;
++
++ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
++ sizeof(SMEM_PTABLE_MAGIC))) {
++ pr_err("SMEM partition magic invalid\n");
++ return -EINVAL;
++ }
++
++ /* Allocate and populate the mtd structures */
++ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
++ GFP_KERNEL);
++ if (!mtd_parts)
++ return -ENOMEM;
++
++ for (i = 0; i < smem_parts->len; i++) {
++ struct smem_partition *s_part = &smem_parts->parts[i];
++ struct mtd_partition *m_part = &mtd_parts[i];
++
++ m_part->name = s_part->name;
++ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
++ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
++
++ /*
++ * The last SMEM partition may have its size marked as
++ * something like 0xffffffff, which means "until the end of the
++ * flash device". In this case, truncate it.
++ */
++ if (m_part->offset + m_part->size > master->size)
++ m_part->size = master->size - m_part->offset;
++ }
++
++ *pparts = mtd_parts;
++
++ return smem_parts->len;
++}
++
++static struct mtd_part_parser qcom_smem_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_qcom_smem_partitions,
++ .name = "qcom-smem",
++};
++
++static int __init qcom_smem_parser_init(void)
++{
++ register_mtd_parser(&qcom_smem_parser);
++ return 0;
++}
++
++static void __exit qcom_smem_parser_exit(void)
++{
++ deregister_mtd_parser(&qcom_smem_parser);
++}
++
++module_init(qcom_smem_parser_init);
++module_exit(qcom_smem_parser_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+ obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
diff --git a/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
new file mode 100644
index 0000000..25803b8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
@@ -0,0 +1,511 @@
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -256,4 +256,15 @@ config PHY_STIH41X_USB
+ Enable this to support the USB transceiver that is part of
+ STMicroelectronics STiH41x SoC series.
+
++config PHY_QCOM_DWC3
++ tristate "QCOM DWC3 USB PHY support"
++ depends on ARCH_QCOM
++ depends on HAS_IOMEM
++ depends on OF
++ select GENERIC_PHY
++ help
++ This option enables support for the Synopsis PHYs present inside the
++ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS
++ PHY controllers.
++
+ endmenu
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) +=
+ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
+ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
+ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
++obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o
+--- /dev/null
++++ b/drivers/phy/phy-qcom-dwc3.c
+@@ -0,0 +1,483 @@
++/* Copyright (c) 2013-2014, Code Aurora Forum. 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/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++/**
++ * USB QSCRATCH Hardware registers
++ */
++#define QSCRATCH_GENERAL_CFG (0x08)
++#define HSUSB_PHY_CTRL_REG (0x10)
++
++/* PHY_CTRL_REG */
++#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24)
++#define HSUSB_CTRL_USB2_SUSPEND BIT(23)
++#define HSUSB_CTRL_UTMI_CLK_EN BIT(21)
++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20)
++#define HSUSB_CTRL_USE_CLKCORE BIT(18)
++#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17)
++#define HSUSB_CTRL_COMMONONN BIT(11)
++#define HSUSB_CTRL_ID_HV_CLAMP BIT(9)
++#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8)
++#define HSUSB_CTRL_CLAMP_EN BIT(7)
++#define HSUSB_CTRL_RETENABLEN BIT(1)
++#define HSUSB_CTRL_POR BIT(0)
++
++/* QSCRATCH_GENERAL_CFG */
++#define HSUSB_GCFG_XHCI_REV BIT(2)
++
++/**
++ * USB QSCRATCH Hardware registers
++ */
++#define SSUSB_PHY_CTRL_REG (0x00)
++#define SSUSB_PHY_PARAM_CTRL_1 (0x04)
++#define SSUSB_PHY_PARAM_CTRL_2 (0x08)
++#define CR_PROTOCOL_DATA_IN_REG (0x0c)
++#define CR_PROTOCOL_DATA_OUT_REG (0x10)
++#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
++#define CR_PROTOCOL_CAP_DATA_REG (0x18)
++#define CR_PROTOCOL_READ_REG (0x1c)
++#define CR_PROTOCOL_WRITE_REG (0x20)
++
++/* PHY_CTRL_REG */
++#define SSUSB_CTRL_REF_USE_PAD BIT(28)
++#define SSUSB_CTRL_TEST_POWERDOWN BIT(27)
++#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24)
++#define SSUSB_CTRL_SS_PHY_EN BIT(8)
++#define SSUSB_CTRL_SS_PHY_RESET BIT(7)
++
++/* SSPHY control registers */
++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane)
++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane)
++
++/* RX OVRD IN HI bits */
++#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
++#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
++#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
++#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700
++#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8
++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
++#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018
++#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2)
++#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003
++
++/* TX OVRD DRV LO register bits */
++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
++#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80
++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7
++#define TX_OVRD_DRV_LO_EN BIT(14)
++
++struct qcom_dwc3_usb_phy {
++ void __iomem *base;
++ struct device *dev;
++ struct phy *phy;
++
++ int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
++ int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
++
++ struct clk *xo_clk;
++ struct clk *ref_clk;
++};
++
++/**
++ * Write register and read back masked value to confirm it is written
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @offset - register offset.
++ * @mask - register bitmask specifying what should be updated
++ * @val - value to write.
++ */
++static inline void qcom_dwc3_phy_write_readback(
++ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
++ const u32 mask, u32 val)
++{
++ u32 write_val, tmp = readl(phy_dwc3->base + offset);
++
++ tmp &= ~mask; /* retain other bits */
++ write_val = tmp | val;
++
++ writel(write_val, phy_dwc3->base + offset);
++
++ /* Read back to see if val was written */
++ tmp = readl(phy_dwc3->base + offset);
++ tmp &= mask; /* clear other bits */
++
++ if (tmp != val)
++ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
++ val, offset);
++}
++
++static int wait_for_latch(void __iomem *addr)
++{
++ u32 retry = 10;
++
++ while (true) {
++ if (!readl(addr))
++ break;
++
++ if (--retry == 0)
++ return -ETIMEDOUT;
++
++ usleep_range(10, 20);
++ }
++
++ return 0;
++}
++
++/**
++ * Write SSPHY register
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to write.
++ * @val - value to write.
++ */
++static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
++{
++ int ret;
++
++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++ if (ret)
++ goto err_wait;
++
++ writel(val, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
++
++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
++ if (ret)
++ goto err_wait;
++
++ writel(0x1, base + CR_PROTOCOL_WRITE_REG);
++
++ ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
++
++err_wait:
++ return ret;
++}
++
++/**
++ * Read SSPHY register.
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to read.
++ */
++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
++{
++ int ret;
++ bool first_read = true;
++
++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++ if (ret)
++ goto err_wait;
++
++ /*
++ * Due to hardware bug, first read of SSPHY register might be
++ * incorrect. Hence as workaround, SW should perform SSPHY register
++ * read twice, but use only second read and ignore first read.
++ */
++retry:
++ writel(0x1, base + CR_PROTOCOL_READ_REG);
++
++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++ if (ret)
++ goto err_wait;
++
++ if (first_read) {
++ readl(base + CR_PROTOCOL_DATA_OUT_REG);
++ first_read = false;
++ goto retry;
++ }
++
++ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
++
++err_wait:
++ return ret;
++}
++
++static int qcom_dwc3_phy_power_on(struct phy *phy)
++{
++ int ret;
++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++ ret = clk_prepare_enable(phy_dwc3->xo_clk);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(phy_dwc3->ref_clk);
++ if (ret)
++ clk_disable_unprepare(phy_dwc3->xo_clk);
++
++ return ret;
++}
++
++static int qcom_dwc3_phy_power_off(struct phy *phy)
++{
++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++ clk_disable_unprepare(phy_dwc3->ref_clk);
++ clk_disable_unprepare(phy_dwc3->xo_clk);
++
++ return 0;
++}
++
++static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++ u32 val;
++
++ /*
++ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
++ * enable clamping, and disable RETENTION (power-on default is ENABLED)
++ */
++ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
++ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
++ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
++ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
++ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
++
++ /* use core clock if external reference is not present */
++ if (!phy_dwc3->xo_clk)
++ val |= HSUSB_CTRL_USE_CLKCORE;
++
++ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
++ usleep_range(2000, 2200);
++
++ /* Disable (bypass) VBUS and ID filters */
++ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
++
++ return 0;
++}
++
++static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++ int ret;
++ u32 data = 0;
++
++ /* reset phy */
++ data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++ writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
++ phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++ usleep_range(2000, 2200);
++ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++ /* clear REF_PAD if we don't have XO clk */
++ if (!phy_dwc3->xo_clk)
++ data &= ~SSUSB_CTRL_REF_USE_PAD;
++ else
++ data |= SSUSB_CTRL_REF_USE_PAD;
++
++ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++ msleep(30);
++
++ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
++ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++ /*
++ * Fix RX Equalization setting as follows
++ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
++ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
++ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
++ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
++ */
++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
++ if (ret)
++ goto err_phy_trans;
++
++ data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
++ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
++ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
++ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
++ data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++ SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
++ if (ret)
++ goto err_phy_trans;
++
++ /*
++ * Set EQ and TX launch amplitudes as follows
++ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
++ * LANE0.TX_OVRD_DRV_LO.EN set to 1.
++ */
++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
++ if (ret)
++ goto err_phy_trans;
++
++ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
++ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
++ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
++ data |= 0x7f;
++ data |= TX_OVRD_DRV_LO_EN;
++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
++ if (ret)
++ goto err_phy_trans;
++
++ /*
++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
++ * TX_FULL_SWING [26:20] amplitude to 127
++ * TX_DEEMPH_3_5DB [13:8] to 22
++ * LOS_BIAS [2:0] to 0x5
++ */
++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
++ 0x07f03f07, 0x07f01605);
++
++err_phy_trans:
++ return ret;
++}
++
++static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++ /* Sequence to put SSPHY in low power state:
++ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
++ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
++ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
++ * 4. Disable SSPHY ref clk
++ */
++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++ SSUSB_CTRL_SS_PHY_EN, 0x0);
++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++ SSUSB_CTRL_REF_USE_PAD, 0x0);
++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++ 0x0, SSUSB_CTRL_TEST_POWERDOWN);
++
++ return 0;
++}
++
++static int qcom_dwc3_phy_init(struct phy *phy)
++{
++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++ if (phy_dwc3->phy_init)
++ return phy_dwc3->phy_init(phy_dwc3);
++
++ return 0;
++}
++
++static int qcom_dwc3_phy_exit(struct phy *phy)
++{
++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++ if (phy_dwc3->phy_exit)
++ return qcom_dwc3_ss_phy_exit(phy_dwc3);
++
++ return 0;
++}
++
++static struct phy_ops qcom_dwc3_phy_ops = {
++ .init = qcom_dwc3_phy_init,
++ .exit = qcom_dwc3_phy_exit,
++ .power_on = qcom_dwc3_phy_power_on,
++ .power_off = qcom_dwc3_phy_power_off,
++ .owner = THIS_MODULE,
++};
++
++static const struct of_device_id qcom_dwc3_phy_table[] = {
++ { .compatible = "qcom,dwc3-hs-usb-phy", },
++ { .compatible = "qcom,dwc3-ss-usb-phy", },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
++
++static int qcom_dwc3_phy_probe(struct platform_device *pdev)
++{
++ struct qcom_dwc3_usb_phy *phy_dwc3;
++ struct phy_provider *phy_provider;
++ struct resource *res;
++
++ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
++ if (!phy_dwc3)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, phy_dwc3);
++
++ phy_dwc3->dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
++ if (IS_ERR(phy_dwc3->base))
++ return PTR_ERR(phy_dwc3->base);
++
++ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
++ if (IS_ERR(phy_dwc3->ref_clk)) {
++ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
++ return PTR_ERR(phy_dwc3->ref_clk);
++ }
++
++ if (of_device_is_compatible(pdev->dev.of_node,
++ "qcom,dwc3-hs-usb-phy")) {
++ clk_set_rate(phy_dwc3->ref_clk, 60000000);
++ phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
++ } else if (of_device_is_compatible(pdev->dev.of_node,
++ "qcom,dwc3-ss-usb-phy")) {
++ phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
++ phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
++ clk_set_rate(phy_dwc3->ref_clk, 125000000);
++ } else {
++ dev_err(phy_dwc3->dev, "Unknown phy\n");
++ return -EINVAL;
++ }
++
++ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
++ if (IS_ERR(phy_dwc3->xo_clk)) {
++ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
++ phy_dwc3->xo_clk = NULL;
++ }
++
++ phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops,
++ NULL);
++
++ if (IS_ERR(phy_dwc3->phy))
++ return PTR_ERR(phy_dwc3->phy);
++
++ phy_set_drvdata(phy_dwc3->phy, phy_dwc3);
++
++ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
++ of_phy_simple_xlate);
++
++ if (IS_ERR(phy_provider))
++ return PTR_ERR(phy_provider);
++
++ return 0;
++}
++
++static struct platform_driver qcom_dwc3_phy_driver = {
++ .probe = qcom_dwc3_phy_probe,
++ .driver = {
++ .name = "qcom-dwc3-usb-phy",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_dwc3_phy_table,
++ },
++};
++
++module_platform_driver(qcom_dwc3_phy_driver);
++
++MODULE_ALIAS("platform:phy-qcom-dwc3");
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
diff --git a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
new file mode 100644
index 0000000..e2d03d4
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
@@ -0,0 +1,126 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -91,5 +91,29 @@
+ 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";
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -296,6 +296,91 @@
+ compatible = "syscon";
+ reg = <0x01200600 0x100>;
+ };
++
++ hs_phy_1: phy@100f8800 {
++ compatible = "qcom,dwc3-hs-usb-phy";
++ reg = <0x100f8800 0x30>;
++ clocks = <&gcc USB30_1_UTMI_CLK>;
++ clock-names = "ref";
++ #phy-cells = <0>;
++
++ status = "disabled";
++ };
++
++ ss_phy_1: phy@100f8830 {
++ compatible = "qcom,dwc3-ss-usb-phy";
++ reg = <0x100f8830 0x30>;
++ clocks = <&gcc USB30_1_MASTER_CLK>;
++ clock-names = "ref";
++ #phy-cells = <0>;
++
++ status = "disabled";
++ };
++
++ hs_phy_0: phy@110f8800 {
++ compatible = "qcom,dwc3-hs-usb-phy";
++ reg = <0x110f8800 0x30>;
++ clocks = <&gcc USB30_0_UTMI_CLK>;
++ clock-names = "ref";
++ #phy-cells = <0>;
++
++ status = "disabled";
++ };
++
++ ss_phy_0: phy@110f8830 {
++ compatible = "qcom,dwc3-ss-usb-phy";
++ reg = <0x110f8830 0x30>;
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "ref";
++ #phy-cells = <0>;
++
++ status = "disabled";
++ };
++
++ usb3_0: usb30@0 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "core";
++
++ ranges;
++
++ status = "disabled";
++
++ dwc3@11000000 {
++ compatible = "snps,dwc3";
++ reg = <0x11000000 0xcd00>;
++ interrupts = <0 110 0x4>;
++ phys = <&hs_phy_0>, <&ss_phy_0>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
++
++ usb3_1: usb30@1 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_1_MASTER_CLK>;
++ clock-names = "core";
++
++ ranges;
++
++ status = "disabled";
++
++ dwc3@10000000 {
++ compatible = "snps,dwc3";
++ reg = <0x10000000 0xcd00>;
++ interrupts = <0 205 0x4>;
++ phys = <&hs_phy_1>, <&ss_phy_1>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
++
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch b/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch
new file mode 100644
index 0000000..752f3f7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch
@@ -0,0 +1,249 @@
+--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+@@ -6,7 +6,8 @@ configuration settings. The mode settin
+ the 4 GSBI IOs.
+
+ Required properties:
+-- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
++- compatible: Should contain "qcom,gsbi-v1.0.0"
++- cell-index: Should contain the GSBI index
+ - reg: Address range for GSBI registers
+ - clocks: required clock
+ - clock-names: must contain "iface" entry
+@@ -16,6 +17,8 @@ Required properties:
+ Optional properties:
+ - qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference
+ dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
++- syscon-tcsr: indicates phandle of TCSR syscon node. Required if child uses
++ dma.
+
+ Required properties if child node exists:
+ - #address-cells: Must be 1
+@@ -39,6 +42,7 @@ Example for APQ8064:
+
+ gsbi4@16300000 {
+ compatible = "qcom,gsbi-v1.0.0";
++ cell-index = <4>;
+ reg = <0x16300000 0x100>;
+ clocks = <&gcc GSBI4_H_CLK>;
+ clock-names = "iface";
+@@ -48,6 +52,8 @@ Example for APQ8064:
+ qcom,mode = <GSBI_PROT_I2C_UART>;
+ qcom,crci = <GSBI_CRCI_QUP>;
+
++ syscon-tcsr = <&tcsr>;
++
+ /* child nodes go under here */
+
+ i2c_qup4: i2c@16380000 {
+@@ -76,3 +82,9 @@ Example for APQ8064:
+ };
+ };
+
++ tcsr: syscon@1a400000 {
++ compatible = "qcom,apq8064-tcsr", "syscon";
++ reg = <0x1a400000 0x100>;
++ };
++
++
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -4,6 +4,7 @@
+ config QCOM_GSBI
+ tristate "QCOM General Serial Bus Interface"
+ depends on ARCH_QCOM
++ select MFD_SYSCON
+ help
+ Say y here to enable GSBI support. The GSBI provides control
+ functions for connecting the underlying serial UART, SPI, and I2C
+--- a/drivers/soc/qcom/qcom_gsbi.c
++++ b/drivers/soc/qcom/qcom_gsbi.c
+@@ -18,22 +18,129 @@
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
+
+ #define GSBI_CTRL_REG 0x0000
+ #define GSBI_PROTOCOL_SHIFT 4
++#define MAX_GSBI 12
++
++#define TCSR_ADM_CRCI_BASE 0x70
++
++struct crci_config {
++ u32 num_rows;
++ const u32 (*array)[MAX_GSBI];
++};
++
++static const u32 crci_ipq8064[][MAX_GSBI] = {
++ {
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++ {
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++};
++
++static const struct crci_config config_ipq8064 = {
++ .num_rows = ARRAY_SIZE(crci_ipq8064),
++ .array = crci_ipq8064,
++};
++
++static const unsigned int crci_apq8064[][MAX_GSBI] = {
++ {
++ 0x001800, 0x006000, 0x000030, 0x0000c0,
++ 0x000300, 0x000400, 0x000000, 0x000000,
++ 0x000000, 0x000000, 0x000000, 0x000000
++ },
++ {
++ 0x000000, 0x000000, 0x000000, 0x000000,
++ 0x000000, 0x000020, 0x0000c0, 0x000000,
++ 0x000000, 0x000000, 0x000000, 0x000000
++ },
++};
++
++static const struct crci_config config_apq8064 = {
++ .num_rows = ARRAY_SIZE(crci_apq8064),
++ .array = crci_apq8064,
++};
++
++static const unsigned int crci_msm8960[][MAX_GSBI] = {
++ {
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000400, 0x000000, 0x000000,
++ 0x000000, 0x000000, 0x000000, 0x000000
++ },
++ {
++ 0x000000, 0x000000, 0x000000, 0x000000,
++ 0x000000, 0x000020, 0x0000c0, 0x000300,
++ 0x001800, 0x006000, 0x000000, 0x000000
++ },
++};
++
++static const struct crci_config config_msm8960 = {
++ .num_rows = ARRAY_SIZE(crci_msm8960),
++ .array = crci_msm8960,
++};
++
++static const unsigned int crci_msm8660[][MAX_GSBI] = {
++ { /* ADM 0 - B */
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++ { /* ADM 0 - B */
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++ { /* ADM 1 - A */
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++ { /* ADM 1 - B */
++ 0x000003, 0x00000c, 0x000030, 0x0000c0,
++ 0x000300, 0x000c00, 0x003000, 0x00c000,
++ 0x030000, 0x0c0000, 0x300000, 0xc00000
++ },
++};
++
++static const struct crci_config config_msm8660 = {
++ .num_rows = ARRAY_SIZE(crci_msm8660),
++ .array = crci_msm8660,
++};
+
+ struct gsbi_info {
+ struct clk *hclk;
+ u32 mode;
+ u32 crci;
++ struct regmap *tcsr;
++};
++
++static const struct of_device_id tcsr_dt_match[] = {
++ { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
++ { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
++ { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
++ { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
++ { },
+ };
+
+ static int gsbi_probe(struct platform_device *pdev)
+ {
+ struct device_node *node = pdev->dev.of_node;
++ struct device_node *tcsr_node;
++ const struct of_device_id *match;
+ struct resource *res;
+ void __iomem *base;
+ struct gsbi_info *gsbi;
++ int i;
++ u32 mask, gsbi_num;
++ const struct crci_config *config = NULL;
+
+ gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
+
+@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_de
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
++ /* get the tcsr node and setup the config and regmap */
++ gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
++
++ if (!IS_ERR(gsbi->tcsr)) {
++ tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
++ if (tcsr_node) {
++ match = of_match_node(tcsr_dt_match, tcsr_node);
++ if (match)
++ config = match->data;
++ else
++ dev_warn(&pdev->dev, "no matching TCSR\n");
++
++ of_node_put(tcsr_node);
++ }
++ }
++
++ if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
++ dev_err(&pdev->dev, "missing cell-index\n");
++ return -EINVAL;
++ }
++
++ if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
++ dev_err(&pdev->dev, "invalid cell-index\n");
++ return -EINVAL;
++ }
++
+ if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
+ dev_err(&pdev->dev, "missing mode configuration\n");
+ return -EINVAL;
+@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_de
+ writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
+ base + GSBI_CTRL_REG);
+
++ /*
++ * modify tcsr to reflect mode and ADM CRCI mux
++ * Each gsbi contains a pair of bits, one for RX and one for TX
++ * SPI mode requires both bits cleared, otherwise they are set
++ */
++ if (config) {
++ for (i = 0; i < config->num_rows; i++) {
++ mask = config->array[i][gsbi_num - 1];
++
++ if (gsbi->mode == GSBI_PROT_SPI)
++ regmap_update_bits(gsbi->tcsr,
++ TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
++ else
++ regmap_update_bits(gsbi->tcsr,
++ TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
++
++ }
++ }
++
+ /* make sure the gsbi control write is not reordered */
+ wmb();
+
diff --git a/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch b/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch
new file mode 100644
index 0000000..322e1d9
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch
@@ -0,0 +1,65 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -132,6 +132,7 @@
+
+ gsbi2: gsbi@12480000 {
+ compatible = "qcom,gsbi-v1.0.0";
++ cell-index = <2>;
+ reg = <0x12480000 0x100>;
+ clocks = <&gcc GSBI2_H_CLK>;
+ clock-names = "iface";
+@@ -140,6 +141,8 @@
+ ranges;
+ status = "disabled";
+
++ syscon-tcsr = <&tcsr>;
++
+ uart2: serial@12490000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x12490000 0x1000>,
+@@ -167,6 +170,7 @@
+
+ gsbi4: gsbi@16300000 {
+ compatible = "qcom,gsbi-v1.0.0";
++ cell-index = <4>;
+ reg = <0x16300000 0x100>;
+ clocks = <&gcc GSBI4_H_CLK>;
+ clock-names = "iface";
+@@ -175,6 +179,8 @@
+ ranges;
+ status = "disabled";
+
++ syscon-tcsr = <&tcsr>;
++
+ uart4: serial@16340000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16340000 0x1000>,
+@@ -201,6 +207,7 @@
+
+ gsbi5: gsbi@1a200000 {
+ compatible = "qcom,gsbi-v1.0.0";
++ cell-index = <5>;
+ reg = <0x1a200000 0x100>;
+ clocks = <&gcc GSBI5_H_CLK>;
+ clock-names = "iface";
+@@ -209,6 +216,8 @@
+ ranges;
+ status = "disabled";
+
++ syscon-tcsr = <&tcsr>;
++
+ uart5: serial@1a240000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x1a240000 0x1000>,
+@@ -279,6 +288,11 @@
+ status = "disabled";
+ };
+
++ tcsr: syscon@1a400000 {
++ compatible = "qcom,tcsr-ipq8064", "syscon";
++ reg = <0x1a400000 0x100>;
++ };
++
+ qcom,ssbi@500000 {
+ compatible = "qcom,ssbi";
+ reg = <0x00500000 0x1000>;
diff --git a/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644
index 0000000..41f91fa
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644
index 0000000..a0c9c7c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -7127,6 +7127,13 @@ L: linux-pci@vger.kernel.org
+ S: Maintained
+ F: drivers/pci/host/*spear*
+
++PCIE DRIVER FOR QUALCOMM MSM
++M: Stanimir Varbanov <svarbanov@mm-sol.com>
++L: linux-pci@vger.kernel.org
++L: linux-arm-msm@vger.kernel.org
++S: Maintained
++F: drivers/pci/host/*qcom*
++
+ PCMCIA SUBSYSTEM
+ P: Linux PCMCIA Team
+ L: linux-pcmcia@lists.infradead.org
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -91,4 +91,13 @@ config PCI_XGENE
+ There are 5 internal PCIe ports available. Each port is GEN3 capable
+ and have varied lanes from x1 to x8.
+
++config PCIE_QCOM
++ bool "Qualcomm PCIe controller"
++ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
++ select PCIE_DW
++ select PCIEPORTBUS
++ help
++ Say Y here to enable PCIe controller support on Qualcomm SoCs. The
++ PCIe controller use Designware core plus Qualcomm specific hardware
++ wrappers.
+ endmenu
+--- /dev/null
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -0,0 +1,677 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_gpio.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/phy/phy.h>
++#include <linux/regulator/consumer.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "pcie-designware.h"
++
++#define PCIE20_PARF_PHY_CTRL 0x40
++#define PCIE20_PARF_PHY_REFCLK 0x4C
++#define PCIE20_PARF_DBI_BASE_ADDR 0x168
++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
++
++#define PCIE20_ELBI_SYS_CTRL 0x04
++#define PCIE20_ELBI_SYS_STTS 0x08
++#define XMLH_LINK_UP BIT(10)
++
++#define PCIE20_CAP 0x70
++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
++
++#define PERST_DELAY_MIN_US 1000
++#define PERST_DELAY_MAX_US 1005
++
++#define LINKUP_DELAY_MIN_US 5000
++#define LINKUP_DELAY_MAX_US 5100
++#define LINKUP_RETRIES_COUNT 20
++
++#define PCIE_V0 0 /* apq8064 */
++#define PCIE_V1 1 /* apq8084 */
++
++struct qcom_pcie_resources_v0 {
++ struct clk *iface_clk;
++ struct clk *core_clk;
++ struct clk *phy_clk;
++ struct reset_control *pci_reset;
++ struct reset_control *axi_reset;
++ struct reset_control *ahb_reset;
++ struct reset_control *por_reset;
++ struct reset_control *phy_reset;
++ struct regulator *vdda;
++ struct regulator *vdda_phy;
++ struct regulator *vdda_refclk;
++};
++
++struct qcom_pcie_resources_v1 {
++ struct clk *iface;
++ struct clk *aux;
++ struct clk *master_bus;
++ struct clk *slave_bus;
++ struct reset_control *core;
++ struct regulator *vdda;
++};
++
++union pcie_resources {
++ struct qcom_pcie_resources_v0 v0;
++ struct qcom_pcie_resources_v1 v1;
++};
++
++struct qcom_pcie {
++ struct pcie_port pp;
++ struct device *dev;
++ union pcie_resources res;
++ void __iomem *parf;
++ void __iomem *dbi;
++ void __iomem *elbi;
++ struct phy *phy;
++ struct gpio_desc *reset;
++ unsigned int version;
++};
++
++#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp)
++
++static inline void
++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
++{
++ u32 val = readl(addr);
++
++ val &= ~clear_mask;
++ val |= set_mask;
++ writel(val, addr);
++}
++
++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
++{
++ int val, active_low;
++
++ if (IS_ERR_OR_NULL(pcie->reset))
++ return;
++
++ active_low = gpiod_is_active_low(pcie->reset);
++
++ if (assert)
++ val = !!active_low;
++ else
++ val = !active_low;
++
++ gpiod_set_value(pcie->reset, val);
++
++ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
++}
++
++static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
++{
++ qcom_ep_reset_assert_deassert(pcie, 1);
++}
++
++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
++{
++ qcom_ep_reset_assert_deassert(pcie, 0);
++}
++
++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
++{
++ struct pcie_port *pp = arg;
++
++ return dw_handle_msi_irq(pp);
++}
++
++static int qcom_pcie_link_up(struct pcie_port *pp)
++{
++ struct qcom_pcie *pcie = to_qcom_pcie(pp);
++ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
++
++ return val & BIT(29) ? 1 : 0;
++}
++
++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++
++ reset_control_assert(res->pci_reset);
++ reset_control_assert(res->axi_reset);
++ reset_control_assert(res->ahb_reset);
++ reset_control_assert(res->por_reset);
++ reset_control_assert(res->pci_reset);
++ clk_disable_unprepare(res->iface_clk);
++ clk_disable_unprepare(res->core_clk);
++ clk_disable_unprepare(res->phy_clk);
++ regulator_disable(res->vdda);
++ regulator_disable(res->vdda_phy);
++ regulator_disable(res->vdda_refclk);
++}
++
++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++
++ reset_control_assert(res->core);
++ clk_disable_unprepare(res->slave_bus);
++ clk_disable_unprepare(res->master_bus);
++ clk_disable_unprepare(res->iface);
++ clk_disable_unprepare(res->aux);
++ regulator_disable(res->vdda);
++}
++
++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++ struct device *dev = pcie->dev;
++ int ret;
++
++ ret = regulator_enable(res->vdda);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda regulator\n");
++ return ret;
++ }
++
++ ret = regulator_enable(res->vdda_refclk);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda_refclk regulator\n");
++ goto err_refclk;
++ }
++
++ ret = regulator_enable(res->vdda_phy);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda_phy regulator\n");
++ goto err_vdda_phy;
++ }
++
++ ret = clk_prepare_enable(res->iface_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable iface clock\n");
++ goto err_iface;
++ }
++
++ ret = clk_prepare_enable(res->core_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable core clock\n");
++ goto err_clk_core;
++ }
++
++ ret = clk_prepare_enable(res->phy_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable phy clock\n");
++ goto err_clk_phy;
++ }
++
++ ret = reset_control_deassert(res->ahb_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert ahb reset\n");
++ goto err_reset_ahb;
++ }
++
++ return 0;
++
++err_reset_ahb:
++ clk_disable_unprepare(res->phy_clk);
++err_clk_phy:
++ clk_disable_unprepare(res->core_clk);
++err_clk_core:
++ clk_disable_unprepare(res->iface_clk);
++err_iface:
++ regulator_disable(res->vdda_phy);
++err_vdda_phy:
++ regulator_disable(res->vdda_refclk);
++err_refclk:
++ regulator_disable(res->vdda);
++ return ret;
++}
++
++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++ struct device *dev = pcie->dev;
++ int ret;
++
++ ret = reset_control_deassert(res->core);
++ if (ret) {
++ dev_err(dev, "cannot deassert core reset\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(res->aux);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable aux clock\n");
++ goto err_res;
++ }
++
++ ret = clk_prepare_enable(res->iface);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable iface clock\n");
++ goto err_aux;
++ }
++
++ ret = clk_prepare_enable(res->master_bus);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable master_bus clock\n");
++ goto err_iface;
++ }
++
++ ret = clk_prepare_enable(res->slave_bus);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable slave_bus clock\n");
++ goto err_master;
++ }
++
++ ret = regulator_enable(res->vdda);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda regulator\n");
++ goto err_slave;
++ }
++
++ return 0;
++
++err_slave:
++ clk_disable_unprepare(res->slave_bus);
++err_master:
++ clk_disable_unprepare(res->master_bus);
++err_iface:
++ clk_disable_unprepare(res->iface);
++err_aux:
++ clk_disable_unprepare(res->aux);
++err_res:
++ reset_control_assert(res->core);
++
++ return ret;
++}
++
++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++ struct device *dev = pcie->dev;
++
++ res->vdda = devm_regulator_get(dev, "vdda");
++ if (IS_ERR(res->vdda))
++ return PTR_ERR(res->vdda);
++
++ res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
++ if (IS_ERR(res->vdda_phy))
++ return PTR_ERR(res->vdda_phy);
++
++ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
++ if (IS_ERR(res->vdda_refclk))
++ return PTR_ERR(res->vdda_refclk);
++
++ res->iface_clk = devm_clk_get(dev, "iface");
++ if (IS_ERR(res->iface_clk))
++ return PTR_ERR(res->iface_clk);
++
++ res->core_clk = devm_clk_get(dev, "core");
++ if (IS_ERR(res->core_clk))
++ return PTR_ERR(res->core_clk);
++
++ res->phy_clk = devm_clk_get(dev, "phy");
++ if (IS_ERR(res->phy_clk))
++ return PTR_ERR(res->phy_clk);
++
++ res->pci_reset = devm_reset_control_get(dev, "pci");
++ if (IS_ERR(res->pci_reset))
++ return PTR_ERR(res->pci_reset);
++
++ res->axi_reset = devm_reset_control_get(dev, "axi");
++ if (IS_ERR(res->axi_reset))
++ return PTR_ERR(res->axi_reset);
++
++ res->ahb_reset = devm_reset_control_get(dev, "ahb");
++ if (IS_ERR(res->ahb_reset))
++ return PTR_ERR(res->ahb_reset);
++
++ res->por_reset = devm_reset_control_get(dev, "por");
++ if (IS_ERR(res->por_reset))
++ return PTR_ERR(res->por_reset);
++
++ res->phy_reset = devm_reset_control_get(dev, "phy");
++ if (IS_ERR(res->phy_reset))
++ return PTR_ERR(res->phy_reset);
++
++ return 0;
++}
++
++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++ struct device *dev = pcie->dev;
++
++ res->vdda = devm_regulator_get(dev, "vdda");
++ if (IS_ERR(res->vdda))
++ return PTR_ERR(res->vdda);
++
++ res->iface = devm_clk_get(dev, "iface");
++ if (IS_ERR(res->iface))
++ return PTR_ERR(res->iface);
++
++ res->aux = devm_clk_get(dev, "aux");
++ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER)
++ return -EPROBE_DEFER;
++ else if (IS_ERR(res->aux))
++ res->aux = NULL;
++
++ res->master_bus = devm_clk_get(dev, "master_bus");
++ if (IS_ERR(res->master_bus))
++ return PTR_ERR(res->master_bus);
++
++ res->slave_bus = devm_clk_get(dev, "slave_bus");
++ if (IS_ERR(res->slave_bus))
++ return PTR_ERR(res->slave_bus);
++
++ res->core = devm_reset_control_get(dev, "core");
++ if (IS_ERR(res->core))
++ return PTR_ERR(res->core);
++
++ return 0;
++}
++
++static int qcom_pcie_enable_link_training(struct pcie_port *pp)
++{
++ struct qcom_pcie *pcie = to_qcom_pcie(pp);
++ struct device *dev = pp->dev;
++ int retries;
++ u32 val;
++
++ /* enable link training */
++ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
++
++ /* wait for up to 100ms for the link to come up */
++ retries = LINKUP_RETRIES_COUNT;
++ do {
++ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
++ if (val & XMLH_LINK_UP)
++ break;
++ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
++ } while (retries--);
++
++ if (retries < 0 || !dw_pcie_link_up(pp)) {
++ dev_err(dev, "link initialization failed\n");
++ return -ENXIO;
++ }
++
++ return 0;
++}
++
++static void qcom_pcie_host_init_v1(struct pcie_port *pp)
++{
++ struct qcom_pcie *pcie = to_qcom_pcie(pp);
++ int ret;
++
++ qcom_ep_reset_assert(pcie);
++
++ ret = qcom_pcie_enable_resources_v1(pcie);
++ if (ret)
++ return;
++
++ /* change DBI base address */
++ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
++
++ if (IS_ENABLED(CONFIG_PCI_MSI))
++ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
++ 0, BIT(31));
++
++ ret = phy_init(pcie->phy);
++ if (ret)
++ goto err_res;
++
++ ret = phy_power_on(pcie->phy);
++ if (ret)
++ goto err_phy;
++
++ dw_pcie_setup_rc(pp);
++
++ if (IS_ENABLED(CONFIG_PCI_MSI))
++ dw_pcie_msi_init(pp);
++
++ qcom_ep_reset_deassert(pcie);
++
++ ret = qcom_pcie_enable_link_training(pp);
++ if (ret)
++ goto err;
++
++ return;
++
++err:
++ qcom_ep_reset_assert(pcie);
++ phy_power_off(pcie->phy);
++err_phy:
++ phy_exit(pcie->phy);
++err_res:
++ qcom_pcie_disable_resources_v1(pcie);
++}
++
++static void qcom_pcie_host_init_v0(struct pcie_port *pp)
++{
++ struct qcom_pcie *pcie = to_qcom_pcie(pp);
++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++ struct device *dev = pcie->dev;
++ int ret;
++
++ qcom_ep_reset_assert(pcie);
++
++ ret = qcom_pcie_enable_resources_v0(pcie);
++ if (ret)
++ return;
++
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
++
++ /* enable external reference clock */
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
++
++ ret = reset_control_deassert(res->phy_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert phy reset\n");
++ return;
++ }
++
++ ret = reset_control_deassert(res->pci_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert pci reset\n");
++ return;
++ }
++
++ ret = reset_control_deassert(res->por_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert por reset\n");
++ return;
++ }
++
++ ret = reset_control_deassert(res->axi_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert axi reset\n");
++ return;
++ }
++
++ /* wait 150ms for clock acquisition */
++ usleep_range(10000, 15000);
++
++ dw_pcie_setup_rc(pp);
++
++ if (IS_ENABLED(CONFIG_PCI_MSI))
++ dw_pcie_msi_init(pp);
++
++ qcom_ep_reset_deassert(pcie);
++
++ ret = qcom_pcie_enable_link_training(pp);
++ if (ret)
++ goto err;
++
++ return;
++err:
++ qcom_ep_reset_assert(pcie);
++ qcom_pcie_disable_resources_v0(pcie);
++}
++
++static void qcom_pcie_host_init(struct pcie_port *pp)
++{
++ struct qcom_pcie *pcie = to_qcom_pcie(pp);
++
++ if (pcie->version == PCIE_V0)
++ return qcom_pcie_host_init_v0(pp);
++ else
++ return qcom_pcie_host_init_v1(pp);
++}
++
++static int
++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val)
++{
++ /* the device class is not reported correctly from the register */
++ if (where == PCI_CLASS_REVISION && size == 4) {
++ *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
++ *val &= ~(0xffff << 16);
++ *val |= PCI_CLASS_BRIDGE_PCI << 16;
++ return PCIBIOS_SUCCESSFUL;
++ }
++
++ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
++ size, val);
++}
++
++static struct pcie_host_ops qcom_pcie_ops = {
++ .link_up = qcom_pcie_link_up,
++ .host_init = qcom_pcie_host_init,
++ .rd_own_conf = qcom_pcie_rd_own_conf,
++};
++
++static const struct of_device_id qcom_pcie_match[] = {
++ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
++ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
++ { }
++};
++
++static int qcom_pcie_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ const struct of_device_id *match;
++ struct resource *res;
++ struct qcom_pcie *pcie;
++ struct pcie_port *pp;
++ int ret;
++
++ match = of_match_node(qcom_pcie_match, dev->of_node);
++ if (!match)
++ return -ENXIO;
++
++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++ if (!pcie)
++ return -ENOMEM;
++
++ pcie->version = (unsigned int)match->data;
++
++ pcie->reset = devm_gpiod_get_optional(dev, "perst");
++ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
++ return PTR_ERR(pcie->reset);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
++ pcie->parf = devm_ioremap_resource(dev, res);
++ if (IS_ERR(pcie->parf))
++ return PTR_ERR(pcie->parf);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
++ pcie->dbi = devm_ioremap_resource(dev, res);
++ if (IS_ERR(pcie->dbi))
++ return PTR_ERR(pcie->dbi);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
++ pcie->elbi = devm_ioremap_resource(dev, res);
++ if (IS_ERR(pcie->elbi))
++ return PTR_ERR(pcie->elbi);
++
++ pcie->phy = devm_phy_optional_get(dev, "pciephy");
++ if (IS_ERR(pcie->phy))
++ return PTR_ERR(pcie->phy);
++
++ pcie->dev = dev;
++
++ if (pcie->version == PCIE_V0)
++ ret = qcom_pcie_get_resources_v0(pcie);
++ else
++ ret = qcom_pcie_get_resources_v1(pcie);
++
++ if (ret)
++ return ret;
++
++ pp = &pcie->pp;
++ pp->dev = dev;
++ pp->dbi_base = pcie->dbi;
++ pp->root_bus_nr = -1;
++ pp->ops = &qcom_pcie_ops;
++
++ if (IS_ENABLED(CONFIG_PCI_MSI)) {
++ pp->msi_irq = platform_get_irq_byname(pdev, "msi");
++ if (pp->msi_irq < 0) {
++ dev_err(dev, "cannot get msi irq\n");
++ return pp->msi_irq;
++ }
++
++ ret = devm_request_irq(dev, pp->msi_irq,
++ qcom_pcie_msi_irq_handler,
++ IRQF_SHARED, "qcom-pcie-msi", pp);
++ if (ret) {
++ dev_err(dev, "cannot request msi irq\n");
++ return ret;
++ }
++ }
++
++ ret = dw_pcie_host_init(pp);
++ if (ret) {
++ dev_err(dev, "cannot initialize host\n");
++ return ret;
++ }
++
++ platform_set_drvdata(pdev, pcie);
++
++ return 0;
++}
++
++static int qcom_pcie_remove(struct platform_device *pdev)
++{
++ struct qcom_pcie *pcie = platform_get_drvdata(pdev);
++
++ qcom_ep_reset_assert(pcie);
++ phy_power_off(pcie->phy);
++ phy_exit(pcie->phy);
++ if (pcie->version == PCIE_V0)
++ qcom_pcie_disable_resources_v0(pcie);
++ else
++ qcom_pcie_disable_resources_v1(pcie);
++
++ return 0;
++}
++
++static struct platform_driver qcom_pcie_driver = {
++ .probe = qcom_pcie_probe,
++ .remove = qcom_pcie_remove,
++ .driver = {
++ .name = "qcom-pcie",
++ .of_match_table = qcom_pcie_match,
++ },
++};
++
++module_platform_driver(qcom_pcie_driver);
++
++MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-pcie");
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+ obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000..d15f0ac
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,266 @@
+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
+@@ -35,6 +35,24 @@
+ bias-disable;
+ };
+
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
+ spi_pins: spi_pins {
+ mux {
+ pins = "gpio18", "gpio19", "gpio21";
+@@ -115,5 +133,19 @@
+ usb30@1 {
+ status = "ok";
+ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_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,33 @@
+ bias-disable;
+ };
+
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie2_pins: pcie2_pinmux {
++ mux {
++ pins = "gpio63";
++ function = "pcie3_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
+ spi_pins: spi_pins {
+ mux {
+ pins = "gpio18", "gpio19", "gpio21";
+@@ -128,5 +155,26 @@
+ usb30@1 {
+ status = "ok";
+ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie2: pci@1b900000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 63 0>;
++ pinctrl-0 = <&pcie2_pins>;
++ pinctrl-names = "default";
++ };
+ };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -3,6 +3,8 @@
+ #include "skeleton.dtsi"
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
++#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -311,6 +313,129 @@
+ reg = <0x01200600 0x100>;
+ };
+
++ pcie0: pci@1b500000 {
++ compatible = "qcom,pcie-v0";
++ reg = <0x1b500000 0x1000
++ 0x1b502000 0x80
++ 0x1b600000 0x100
++ 0x0ff00000 0x100000>;
++ reg-names = "dbi", "elbi", "parf", "config";
++ device_type = "pci";
++ linux,pci-domain = <0>;
++ bus-range = <0x00 0xff>;
++ num-lanes = <1>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++
++ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */
++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
++
++ interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_A_CLK>,
++ <&gcc PCIE_H_CLK>,
++ <&gcc PCIE_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++
++ resets = <&gcc PCIE_ACLK_RESET>,
++ <&gcc PCIE_HCLK_RESET>,
++ <&gcc PCIE_POR_RESET>,
++ <&gcc PCIE_PCI_RESET>,
++ <&gcc PCIE_PHY_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy";
++
++ status = "disabled";
++ };
++
++ pcie1: pci@1b700000 {
++ compatible = "qcom,pcie-v0";
++ reg = <0x1b700000 0x1000
++ 0x1b702000 0x80
++ 0x1b800000 0x100
++ 0x31f00000 0x100000>;
++ reg-names = "dbi", "elbi", "parf", "config";
++ device_type = "pci";
++ linux,pci-domain = <1>;
++ bus-range = <0x00 0xff>;
++ num-lanes = <1>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++
++ ranges = <0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */
++ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++
++ interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_1_A_CLK>,
++ <&gcc PCIE_1_H_CLK>,
++ <&gcc PCIE_1_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++
++ resets = <&gcc PCIE_1_ACLK_RESET>,
++ <&gcc PCIE_1_HCLK_RESET>,
++ <&gcc PCIE_1_POR_RESET>,
++ <&gcc PCIE_1_PCI_RESET>,
++ <&gcc PCIE_1_PHY_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy";
++
++ status = "disabled";
++ };
++
++ pcie2: pci@1b900000 {
++ compatible = "qcom,pcie-v0";
++ reg = <0x1b900000 0x1000
++ 0x1b902000 0x80
++ 0x1ba00000 0x100
++ 0x35f00000 0x100000>;
++ reg-names = "dbi", "elbi", "parf", "config";
++ device_type = "pci";
++ linux,pci-domain = <2>;
++ bus-range = <0x00 0xff>;
++ num-lanes = <1>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++
++ ranges = <0x81000000 0 0 0x35e00000 0 0x00100000 /* downstream I/O */
++ 0x82000000 0 0x00000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */
++
++ interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>;
++ interrupt-names = "msi";
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0 0 0 0x7>;
++ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++ clocks = <&gcc PCIE_2_A_CLK>,
++ <&gcc PCIE_2_H_CLK>,
++ <&gcc PCIE_2_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++
++ resets = <&gcc PCIE_2_ACLK_RESET>,
++ <&gcc PCIE_2_HCLK_RESET>,
++ <&gcc PCIE_2_POR_RESET>,
++ <&gcc PCIE_2_PCI_RESET>,
++ <&gcc PCIE_2_PHY_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy";
++
++ status = "disabled";
++ };
++
+ hs_phy_1: phy@100f8800 {
+ compatible = "qcom,dwc3-hs-usb-phy";
+ reg = <0x100f8800 0x30>;
diff --git a/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644
index 0000000..4f0e45f
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
+ select CLKSRC_OF
+ select PINCTRL
+ select QCOM_SCM if SMP
++ select PCI_DOMAINS if PCI
+ help
+ Support for Qualcomm's devicetree based systems.
+
diff --git a/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch
new file mode 100644
index 0000000..11c9810
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch
@@ -0,0 +1,278 @@
+--- a/drivers/pci/host/pcie-qcom.c
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -29,8 +29,53 @@
+
+ #include "pcie-designware.h"
+
++/* DBI registers */
++#define PCIE20_CAP 0x70
++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
++
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c
++
++#define PCIE20_PLR_IATU_VIEWPORT 0x900
++#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31)
++#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0)
++
++#define PCIE20_PLR_IATU_CTRL1 0x904
++#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0)
++#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0)
++
++#define PCIE20_PLR_IATU_CTRL2 0x908
++#define PCIE20_PLR_IATU_ENABLE BIT(31)
++
++#define PCIE20_PLR_IATU_LBAR 0x90C
++#define PCIE20_PLR_IATU_UBAR 0x910
++#define PCIE20_PLR_IATU_LAR 0x914
++#define PCIE20_PLR_IATU_LTAR 0x918
++#define PCIE20_PLR_IATU_UTAR 0x91c
++
++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000
++
++/* PARF registers */
++#define PCIE20_PARF_PCS_DEEMPH 0x34
++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0)
++
++#define PCIE20_PARF_PCS_SWING 0x38
++#define PCS_SWING_TX_SWING_FULL(x) (x << 8)
++#define PCS_SWING_TX_SWING_LOW(x) (x << 0)
++
+ #define PCIE20_PARF_PHY_CTRL 0x40
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16)
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16)
++
+ #define PCIE20_PARF_PHY_REFCLK 0x4C
++#define REF_SSP_EN BIT(16)
++#define REF_USE_PAD BIT(12)
++
++#define PCIE20_PARF_CONFIG_BITS 0x50
++#define PHY_RX0_EQ(x) (x << 24)
++
+ #define PCIE20_PARF_DBI_BASE_ADDR 0x168
+ #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
+ #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
+@@ -39,9 +84,6 @@
+ #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
+
+@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 {
+ struct clk *iface_clk;
+ struct clk *core_clk;
+ struct clk *phy_clk;
++ struct clk *aux_clk;
++ struct clk *ref_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 reset_control *ext_reset;
+ struct regulator *vdda;
+ struct regulator *vdda_phy;
+ struct regulator *vdda_refclk;
++ uint8_t phy_tx0_term_offset;
+ };
+
+ struct qcom_pcie_resources_v1 {
+@@ -156,10 +202,13 @@ static void qcom_pcie_disable_resources_
+ reset_control_assert(res->axi_reset);
+ reset_control_assert(res->ahb_reset);
+ reset_control_assert(res->por_reset);
+- reset_control_assert(res->pci_reset);
++ reset_control_assert(res->phy_reset);
++ reset_control_assert(res->ext_reset);
+ clk_disable_unprepare(res->iface_clk);
+ clk_disable_unprepare(res->core_clk);
+ clk_disable_unprepare(res->phy_clk);
++ clk_disable_unprepare(res->aux_clk);
++ clk_disable_unprepare(res->ref_clk);
+ regulator_disable(res->vdda);
+ regulator_disable(res->vdda_phy);
+ regulator_disable(res->vdda_refclk);
+@@ -201,6 +250,12 @@ static int qcom_pcie_enable_resources_v0
+ goto err_vdda_phy;
+ }
+
++ ret = reset_control_deassert(res->ext_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert ext reset\n");
++ goto err_reset_ext;
++ }
++
+ ret = clk_prepare_enable(res->iface_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable iface clock\n");
+@@ -219,6 +274,18 @@ static int qcom_pcie_enable_resources_v0
+ goto err_clk_phy;
+ }
+
++ ret = clk_prepare_enable(res->aux_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable aux clock\n");
++ goto err_clk_aux;
++ }
++
++ ret = clk_prepare_enable(res->ref_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable ref clock\n");
++ goto err_clk_ref;
++ }
++
+ ret = reset_control_deassert(res->ahb_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert ahb reset\n");
+@@ -228,12 +295,18 @@ static int qcom_pcie_enable_resources_v0
+ return 0;
+
+ err_reset_ahb:
++ clk_disable_unprepare(res->ref_clk);
++err_clk_ref:
++ clk_disable_unprepare(res->aux_clk);
++err_clk_aux:
+ 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:
++ reset_control_assert(res->ext_reset);
++err_reset_ext:
+ regulator_disable(res->vdda_phy);
+ err_vdda_phy:
+ regulator_disable(res->vdda_refclk);
+@@ -329,6 +402,14 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_clk))
+ return PTR_ERR(res->phy_clk);
+
++ res->aux_clk = devm_clk_get(dev, "aux");
++ if (IS_ERR(res->aux_clk))
++ return PTR_ERR(res->aux_clk);
++
++ res->ref_clk = devm_clk_get(dev, "ref");
++ if (IS_ERR(res->ref_clk))
++ return PTR_ERR(res->ref_clk);
++
+ res->pci_reset = devm_reset_control_get(dev, "pci");
+ if (IS_ERR(res->pci_reset))
+ return PTR_ERR(res->pci_reset);
+@@ -349,6 +430,14 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_reset))
+ return PTR_ERR(res->phy_reset);
+
++ res->ext_reset = devm_reset_control_get(dev, "ext");
++ if (IS_ERR(res->ext_reset))
++ return PTR_ERR(res->ext_reset);
++
++ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
++ &res->phy_tx0_term_offset))
++ res->phy_tx0_term_offset = 0;
++
+ return 0;
+ }
+
+@@ -461,6 +550,57 @@ err_res:
+ qcom_pcie_disable_resources_v1(pcie);
+ }
+
++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev)
++{
++ struct pcie_port *pp = &pcie->pp;
++
++ /*
++ * program and enable address translation region 0 (device config
++ * address space); region type config;
++ * axi config address range to device config address range
++ */
++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++ PCIE20_PLR_IATU_REGION_INDEX(0),
++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++ writel(pp->cfg0_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++ writel((pp->cfg0_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++ writel((pp->cfg0_mod_base + pp->cfg0_size - 1),
++ pcie->dbi + PCIE20_PLR_IATU_LAR);
++ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR);
++}
++
++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie)
++{
++ struct pcie_port *pp = &pcie->pp;
++
++ /*
++ * program and enable address translation region 2 (device resource
++ * address space); region type memory;
++ * axi device bar address range to device bar address range
++ */
++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++ PCIE20_PLR_IATU_REGION_INDEX(2),
++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++ writel(pp->mem_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++ writel((pp->mem_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++ writel(pp->mem_mod_base + pp->mem_size - 1,
++ pcie->dbi + PCIE20_PLR_IATU_LAR);
++ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++ writel(upper_32_bits(pp->mem_bus_addr),
++ pcie->dbi + PCIE20_PLR_IATU_UTAR);
++
++ /* 1K PCIE buffer setting */
++ writel(0x3, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
++}
++
+ static void qcom_pcie_host_init_v0(struct pcie_port *pp)
+ {
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
+@@ -476,9 +616,26 @@ static void qcom_pcie_host_init_v0(struc
+
+ 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));
++ /* Set Tx termination offset */
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL,
++ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
++ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
++
++ /* PARF programming */
++ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
++ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
++ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
++ pcie->parf + PCIE20_PARF_PCS_DEEMPH);
++ writel(PCS_SWING_TX_SWING_FULL(0x78) |
++ PCS_SWING_TX_SWING_LOW(0x78),
++ pcie->parf + PCIE20_PARF_PCS_SWING);
++ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
++
++ /* Enable reference clock */
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK,
++ REF_USE_PAD, REF_SSP_EN);
+
++ /* De-assert PHY, PCIe, POR and AXI resets */
+ ret = reset_control_deassert(res->phy_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert phy reset\n");
+@@ -517,6 +674,9 @@ static void qcom_pcie_host_init_v0(struc
+ if (ret)
+ goto err;
+
++ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR);
++ qcom_pcie_prog_viewport_mem2_outbound(pcie);
++
+ return;
+ err:
+ qcom_ep_reset_assert(pcie);
diff --git a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch
new file mode 100644
index 0000000..83c5f55
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch
@@ -0,0 +1,80 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -341,15 +341,21 @@
+
+ clocks = <&gcc PCIE_A_CLK>,
+ <&gcc PCIE_H_CLK>,
+- <&gcc PCIE_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_PHY_CLK>,
++ <&gcc PCIE_AUX_CLK>,
++ <&gcc PCIE_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_PHY_RESET>,
++ <&gcc PCIE_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
+@@ -382,15 +388,21 @@
+
+ clocks = <&gcc PCIE_1_A_CLK>,
+ <&gcc PCIE_1_H_CLK>,
+- <&gcc PCIE_1_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_1_PHY_CLK>,
++ <&gcc PCIE_1_AUX_CLK>,
++ <&gcc PCIE_1_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_1_PHY_RESET>,
++ <&gcc PCIE_1_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
+@@ -423,15 +435,21 @@
+
+ clocks = <&gcc PCIE_2_A_CLK>,
+ <&gcc PCIE_2_H_CLK>,
+- <&gcc PCIE_2_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_2_PHY_CLK>,
++ <&gcc PCIE_2_AUX_CLK>,
++ <&gcc PCIE_2_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_2_PHY_RESET>,
++ <&gcc PCIE_2_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
diff --git a/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch b/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch
new file mode 100644
index 0000000..d819142
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch
@@ -0,0 +1,654 @@
+From 58e214382bdd1eb48c5a3519182bddcb26edabad Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Wed, 26 Nov 2014 13:51:00 -0800
+Subject: [PATCH] mfd: qcom-rpm: Driver for the Qualcomm RPM
+
+Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960
+and 8064 based devices. The driver exposes resources that child drivers
+can operate on; to implementing regulator, clock and bus frequency
+drivers.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ drivers/mfd/Kconfig | 14 ++
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/qcom_rpm.c | 581 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/qcom_rpm.h | 13 +
+ 4 files changed, 609 insertions(+)
+ create mode 100644 drivers/mfd/qcom_rpm.c
+ create mode 100644 include/linux/mfd/qcom_rpm.h
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -567,6 +567,20 @@ config MFD_PM8921_CORE
+ Say M here if you want to include support for PM8921 chip as a module.
+ This will build a module called "pm8921-core".
+
++config MFD_QCOM_RPM
++ tristate "Qualcomm Resource Power Manager (RPM)"
++ depends on ARCH_QCOM && OF
++ help
++ If you say yes to this option, support will be included for the
++ Resource Power Manager system found in the Qualcomm 8660, 8960 and
++ 8064 based devices.
++
++ This is required to access many regulators, clocks and bus
++ frequencies controlled by the RPM on these devices.
++
++ Say M here if you want to include support for the Qualcomm RPM as a
++ module. This will build a module called "qcom_rpm".
++
+ config MFD_SPMI_PMIC
+ tristate "Qualcomm SPMI PMICs"
+ depends on ARCH_QCOM || COMPILE_TEST
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-
+ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
+ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
+ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
++obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
+ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
+ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+ obj-$(CONFIG_MFD_TPS65090) += tps65090.o
+--- /dev/null
++++ b/drivers/mfd/qcom_rpm.c
+@@ -0,0 +1,581 @@
++/*
++ * Copyright (c) 2014, Sony Mobile Communications AB.
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Author: Bjorn Andersson <bjorn.andersson@sonymobile.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 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/module.h>
++#include <linux/platform_device.h>
++#include <linux/of_platform.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/mfd/qcom_rpm.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regmap.h>
++
++#include <dt-bindings/mfd/qcom-rpm.h>
++
++struct qcom_rpm_resource {
++ unsigned target_id;
++ unsigned status_id;
++ unsigned select_id;
++ unsigned size;
++};
++
++struct qcom_rpm_data {
++ u32 version;
++ const struct qcom_rpm_resource *resource_table;
++ unsigned n_resources;
++};
++
++struct qcom_rpm {
++ struct device *dev;
++ struct regmap *ipc_regmap;
++ unsigned ipc_offset;
++ unsigned ipc_bit;
++
++ struct completion ack;
++ struct mutex lock;
++
++ void __iomem *status_regs;
++ void __iomem *ctrl_regs;
++ void __iomem *req_regs;
++
++ u32 ack_status;
++
++ const struct qcom_rpm_data *data;
++};
++
++#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4)
++#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4)
++#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4)
++
++#define RPM_REQUEST_TIMEOUT (5 * HZ)
++
++#define RPM_REQUEST_CONTEXT 3
++#define RPM_REQ_SELECT 11
++#define RPM_ACK_CONTEXT 15
++#define RPM_ACK_SELECTOR 23
++#define RPM_SELECT_SIZE 7
++
++#define RPM_NOTIFICATION BIT(30)
++#define RPM_REJECTED BIT(31)
++
++#define RPM_SIGNAL BIT(2)
++
++static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = {
++ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
++ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
++ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
++ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
++ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
++ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
++ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
++ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
++ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
++ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
++ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
++ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
++ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
++ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
++ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
++ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
++ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
++ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
++ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 },
++ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 },
++ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
++ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 },
++ [QCOM_RPM_PM8921_SMPS1] = { 116, 31, 30, 2 },
++ [QCOM_RPM_PM8921_SMPS2] = { 118, 33, 31, 2 },
++ [QCOM_RPM_PM8921_SMPS3] = { 120, 35, 32, 2 },
++ [QCOM_RPM_PM8921_SMPS4] = { 122, 37, 33, 2 },
++ [QCOM_RPM_PM8921_SMPS5] = { 124, 39, 34, 2 },
++ [QCOM_RPM_PM8921_SMPS6] = { 126, 41, 35, 2 },
++ [QCOM_RPM_PM8921_SMPS7] = { 128, 43, 36, 2 },
++ [QCOM_RPM_PM8921_SMPS8] = { 130, 45, 37, 2 },
++ [QCOM_RPM_PM8921_LDO1] = { 132, 47, 38, 2 },
++ [QCOM_RPM_PM8921_LDO2] = { 134, 49, 39, 2 },
++ [QCOM_RPM_PM8921_LDO3] = { 136, 51, 40, 2 },
++ [QCOM_RPM_PM8921_LDO4] = { 138, 53, 41, 2 },
++ [QCOM_RPM_PM8921_LDO5] = { 140, 55, 42, 2 },
++ [QCOM_RPM_PM8921_LDO6] = { 142, 57, 43, 2 },
++ [QCOM_RPM_PM8921_LDO7] = { 144, 59, 44, 2 },
++ [QCOM_RPM_PM8921_LDO8] = { 146, 61, 45, 2 },
++ [QCOM_RPM_PM8921_LDO9] = { 148, 63, 46, 2 },
++ [QCOM_RPM_PM8921_LDO10] = { 150, 65, 47, 2 },
++ [QCOM_RPM_PM8921_LDO11] = { 152, 67, 48, 2 },
++ [QCOM_RPM_PM8921_LDO12] = { 154, 69, 49, 2 },
++ [QCOM_RPM_PM8921_LDO13] = { 156, 71, 50, 2 },
++ [QCOM_RPM_PM8921_LDO14] = { 158, 73, 51, 2 },
++ [QCOM_RPM_PM8921_LDO15] = { 160, 75, 52, 2 },
++ [QCOM_RPM_PM8921_LDO16] = { 162, 77, 53, 2 },
++ [QCOM_RPM_PM8921_LDO17] = { 164, 79, 54, 2 },
++ [QCOM_RPM_PM8921_LDO18] = { 166, 81, 55, 2 },
++ [QCOM_RPM_PM8921_LDO19] = { 168, 83, 56, 2 },
++ [QCOM_RPM_PM8921_LDO20] = { 170, 85, 57, 2 },
++ [QCOM_RPM_PM8921_LDO21] = { 172, 87, 58, 2 },
++ [QCOM_RPM_PM8921_LDO22] = { 174, 89, 59, 2 },
++ [QCOM_RPM_PM8921_LDO23] = { 176, 91, 60, 2 },
++ [QCOM_RPM_PM8921_LDO24] = { 178, 93, 61, 2 },
++ [QCOM_RPM_PM8921_LDO25] = { 180, 95, 62, 2 },
++ [QCOM_RPM_PM8921_LDO26] = { 182, 97, 63, 2 },
++ [QCOM_RPM_PM8921_LDO27] = { 184, 99, 64, 2 },
++ [QCOM_RPM_PM8921_LDO28] = { 186, 101, 65, 2 },
++ [QCOM_RPM_PM8921_LDO29] = { 188, 103, 66, 2 },
++ [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 },
++ [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 },
++ [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 },
++ [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 },
++ [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 },
++ [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 },
++ [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 },
++ [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 },
++ [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 },
++ [QCOM_RPM_PM8821_SMPS1] = { 201, 116, 76, 2 },
++ [QCOM_RPM_PM8821_SMPS2] = { 203, 118, 77, 2 },
++ [QCOM_RPM_PM8821_LDO1] = { 205, 120, 78, 2 },
++ [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 },
++ [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 },
++ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 },
++ [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 },
++ [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 },
++ [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 },
++};
++
++static const struct qcom_rpm_data apq8064_template = {
++ .version = 3,
++ .resource_table = apq8064_rpm_resource_table,
++ .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table),
++};
++
++static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = {
++ [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 },
++ [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 },
++ [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 },
++ [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 },
++ [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 },
++ [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 },
++ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 },
++ [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 },
++ [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 },
++ [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 },
++ [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 },
++ [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 },
++ [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 },
++ [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 },
++ [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 },
++ [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 },
++ [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 },
++ [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 },
++ [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 },
++ [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 },
++ [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 },
++ [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 },
++ [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 },
++ [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 },
++ [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 },
++ [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 },
++ [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 },
++ [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 },
++ [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 },
++ [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 },
++ [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 },
++ [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 },
++ [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 },
++ [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 },
++ [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 },
++ [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 },
++ [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 },
++ [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 },
++ [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 },
++ [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 },
++ [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 },
++ [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 },
++ [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 },
++ [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 },
++ [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 },
++ [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 },
++ [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 },
++ [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 },
++ [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 },
++ [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 },
++ [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 },
++ [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 },
++ [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 },
++ [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 },
++ [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 },
++ [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 },
++ [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 },
++ [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 },
++ [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 },
++ [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 },
++ [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 },
++ [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 },
++ [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 },
++ [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 },
++ [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 },
++ [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 },
++ [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 },
++ [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 },
++ [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 },
++ [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 },
++ [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 },
++ [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 },
++ [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 },
++ [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 },
++};
++
++static const struct qcom_rpm_data msm8660_template = {
++ .version = 2,
++ .resource_table = msm8660_rpm_resource_table,
++ .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table),
++};
++
++static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = {
++ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
++ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
++ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
++ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
++ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
++ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
++ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
++ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
++ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
++ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
++ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
++ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
++ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
++ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
++ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
++ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
++ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
++ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 },
++ [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 },
++ [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 },
++ [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 },
++ [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 },
++ [QCOM_RPM_PM8921_SMPS1] = { 117, 31, 30, 2 },
++ [QCOM_RPM_PM8921_SMPS2] = { 119, 33, 31, 2 },
++ [QCOM_RPM_PM8921_SMPS3] = { 121, 35, 32, 2 },
++ [QCOM_RPM_PM8921_SMPS4] = { 123, 37, 33, 2 },
++ [QCOM_RPM_PM8921_SMPS5] = { 125, 39, 34, 2 },
++ [QCOM_RPM_PM8921_SMPS6] = { 127, 41, 35, 2 },
++ [QCOM_RPM_PM8921_SMPS7] = { 129, 43, 36, 2 },
++ [QCOM_RPM_PM8921_SMPS8] = { 131, 45, 37, 2 },
++ [QCOM_RPM_PM8921_LDO1] = { 133, 47, 38, 2 },
++ [QCOM_RPM_PM8921_LDO2] = { 135, 49, 39, 2 },
++ [QCOM_RPM_PM8921_LDO3] = { 137, 51, 40, 2 },
++ [QCOM_RPM_PM8921_LDO4] = { 139, 53, 41, 2 },
++ [QCOM_RPM_PM8921_LDO5] = { 141, 55, 42, 2 },
++ [QCOM_RPM_PM8921_LDO6] = { 143, 57, 43, 2 },
++ [QCOM_RPM_PM8921_LDO7] = { 145, 59, 44, 2 },
++ [QCOM_RPM_PM8921_LDO8] = { 147, 61, 45, 2 },
++ [QCOM_RPM_PM8921_LDO9] = { 149, 63, 46, 2 },
++ [QCOM_RPM_PM8921_LDO10] = { 151, 65, 47, 2 },
++ [QCOM_RPM_PM8921_LDO11] = { 153, 67, 48, 2 },
++ [QCOM_RPM_PM8921_LDO12] = { 155, 69, 49, 2 },
++ [QCOM_RPM_PM8921_LDO13] = { 157, 71, 50, 2 },
++ [QCOM_RPM_PM8921_LDO14] = { 159, 73, 51, 2 },
++ [QCOM_RPM_PM8921_LDO15] = { 161, 75, 52, 2 },
++ [QCOM_RPM_PM8921_LDO16] = { 163, 77, 53, 2 },
++ [QCOM_RPM_PM8921_LDO17] = { 165, 79, 54, 2 },
++ [QCOM_RPM_PM8921_LDO18] = { 167, 81, 55, 2 },
++ [QCOM_RPM_PM8921_LDO19] = { 169, 83, 56, 2 },
++ [QCOM_RPM_PM8921_LDO20] = { 171, 85, 57, 2 },
++ [QCOM_RPM_PM8921_LDO21] = { 173, 87, 58, 2 },
++ [QCOM_RPM_PM8921_LDO22] = { 175, 89, 59, 2 },
++ [QCOM_RPM_PM8921_LDO23] = { 177, 91, 60, 2 },
++ [QCOM_RPM_PM8921_LDO24] = { 179, 93, 61, 2 },
++ [QCOM_RPM_PM8921_LDO25] = { 181, 95, 62, 2 },
++ [QCOM_RPM_PM8921_LDO26] = { 183, 97, 63, 2 },
++ [QCOM_RPM_PM8921_LDO27] = { 185, 99, 64, 2 },
++ [QCOM_RPM_PM8921_LDO28] = { 187, 101, 65, 2 },
++ [QCOM_RPM_PM8921_LDO29] = { 189, 103, 66, 2 },
++ [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 },
++ [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 },
++ [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 },
++ [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 },
++ [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 },
++ [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 },
++ [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 },
++ [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 },
++ [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 },
++ [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 },
++ [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 },
++ [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 },
++ [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 },
++ [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 },
++};
++
++static const struct qcom_rpm_data msm8960_template = {
++ .version = 3,
++ .resource_table = msm8960_rpm_resource_table,
++ .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
++};
++
++static const struct of_device_id qcom_rpm_of_match[] = {
++ { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
++ { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
++ { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
++ { }
++};
++MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
++
++int qcom_rpm_write(struct qcom_rpm *rpm,
++ int state,
++ int resource,
++ u32 *buf, size_t count)
++{
++ const struct qcom_rpm_resource *res;
++ const struct qcom_rpm_data *data = rpm->data;
++ u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
++ int left;
++ int ret = 0;
++ int i;
++
++ if (WARN_ON(resource < 0 || resource >= data->n_resources))
++ return -EINVAL;
++
++ res = &data->resource_table[resource];
++ if (WARN_ON(res->size != count))
++ return -EINVAL;
++
++ mutex_lock(&rpm->lock);
++
++ for (i = 0; i < res->size; i++)
++ writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i));
++
++ bitmap_set((unsigned long *)sel_mask, res->select_id, 1);
++ for (i = 0; i < ARRAY_SIZE(sel_mask); i++) {
++ writel_relaxed(sel_mask[i],
++ RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i));
++ }
++
++ writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));
++
++ reinit_completion(&rpm->ack);
++ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
++
++ left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT);
++ if (!left)
++ ret = -ETIMEDOUT;
++ else if (rpm->ack_status & RPM_REJECTED)
++ ret = -EIO;
++
++ mutex_unlock(&rpm->lock);
++
++ return ret;
++}
++EXPORT_SYMBOL(qcom_rpm_write);
++
++static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev)
++{
++ struct qcom_rpm *rpm = dev;
++ u32 ack;
++ int i;
++
++ ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
++ for (i = 0; i < RPM_SELECT_SIZE; i++)
++ writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i));
++ writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
++
++ if (ack & RPM_NOTIFICATION) {
++ dev_warn(rpm->dev, "ignoring notification!\n");
++ } else {
++ rpm->ack_status = ack;
++ complete(&rpm->ack);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev)
++{
++ struct qcom_rpm *rpm = dev;
++
++ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
++ dev_err(rpm->dev, "RPM triggered fatal error\n");
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev)
++{
++ return IRQ_HANDLED;
++}
++
++static int qcom_rpm_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *match;
++ struct device_node *syscon_np;
++ struct resource *res;
++ struct qcom_rpm *rpm;
++ u32 fw_version[3];
++ int irq_wakeup;
++ int irq_ack;
++ int irq_err;
++ int ret;
++
++ rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL);
++ if (!rpm)
++ return -ENOMEM;
++
++ rpm->dev = &pdev->dev;
++ mutex_init(&rpm->lock);
++ init_completion(&rpm->ack);
++
++ irq_ack = platform_get_irq_byname(pdev, "ack");
++ if (irq_ack < 0) {
++ dev_err(&pdev->dev, "required ack interrupt missing\n");
++ return irq_ack;
++ }
++
++ irq_err = platform_get_irq_byname(pdev, "err");
++ if (irq_err < 0) {
++ dev_err(&pdev->dev, "required err interrupt missing\n");
++ return irq_err;
++ }
++
++ irq_wakeup = platform_get_irq_byname(pdev, "wakeup");
++ if (irq_wakeup < 0) {
++ dev_err(&pdev->dev, "required wakeup interrupt missing\n");
++ return irq_wakeup;
++ }
++
++ match = of_match_device(qcom_rpm_of_match, &pdev->dev);
++ rpm->data = match->data;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpm->status_regs))
++ return PTR_ERR(rpm->status_regs);
++ rpm->ctrl_regs = rpm->status_regs + 0x400;
++ rpm->req_regs = rpm->status_regs + 0x600;
++
++ syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
++ if (!syscon_np) {
++ dev_err(&pdev->dev, "no qcom,ipc node\n");
++ return -ENODEV;
++ }
++
++ rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
++ if (IS_ERR(rpm->ipc_regmap))
++ return PTR_ERR(rpm->ipc_regmap);
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
++ &rpm->ipc_offset);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no offset in qcom,ipc\n");
++ return -EINVAL;
++ }
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
++ &rpm->ipc_bit);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no bit in qcom,ipc\n");
++ return -EINVAL;
++ }
++
++ dev_set_drvdata(&pdev->dev, rpm);
++
++ fw_version[0] = readl(RPM_STATUS_REG(rpm, 0));
++ fw_version[1] = readl(RPM_STATUS_REG(rpm, 1));
++ fw_version[2] = readl(RPM_STATUS_REG(rpm, 2));
++ if (fw_version[0] != rpm->data->version) {
++ dev_err(&pdev->dev,
++ "RPM version %u.%u.%u incompatible with driver version %u",
++ fw_version[0],
++ fw_version[1],
++ fw_version[2],
++ rpm->data->version);
++ return -EFAULT;
++ }
++
++ dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
++ fw_version[1],
++ fw_version[2]);
++
++ ret = devm_request_irq(&pdev->dev,
++ irq_ack,
++ qcom_rpm_ack_interrupt,
++ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
++ "qcom_rpm_ack",
++ rpm);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to request ack interrupt\n");
++ return ret;
++ }
++
++ ret = irq_set_irq_wake(irq_ack, 1);
++ if (ret)
++ dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n");
++
++ ret = devm_request_irq(&pdev->dev,
++ irq_err,
++ qcom_rpm_err_interrupt,
++ IRQF_TRIGGER_RISING,
++ "qcom_rpm_err",
++ rpm);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to request err interrupt\n");
++ return ret;
++ }
++
++ ret = devm_request_irq(&pdev->dev,
++ irq_wakeup,
++ qcom_rpm_wakeup_interrupt,
++ IRQF_TRIGGER_RISING,
++ "qcom_rpm_wakeup",
++ rpm);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to request wakeup interrupt\n");
++ return ret;
++ }
++
++ ret = irq_set_irq_wake(irq_wakeup, 1);
++ if (ret)
++ dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n");
++
++ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
++}
++
++static int qcom_rpm_remove(struct platform_device *pdev)
++{
++ of_platform_depopulate(&pdev->dev);
++ return 0;
++}
++
++static struct platform_driver qcom_rpm_driver = {
++ .probe = qcom_rpm_probe,
++ .remove = qcom_rpm_remove,
++ .driver = {
++ .name = "qcom_rpm",
++ .of_match_table = qcom_rpm_of_match,
++ },
++};
++
++static int __init qcom_rpm_init(void)
++{
++ return platform_driver_register(&qcom_rpm_driver);
++}
++arch_initcall(qcom_rpm_init);
++
++static void __exit qcom_rpm_exit(void)
++{
++ platform_driver_unregister(&qcom_rpm_driver);
++}
++module_exit(qcom_rpm_exit)
++
++MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver");
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+--- /dev/null
++++ b/include/linux/mfd/qcom_rpm.h
+@@ -0,0 +1,13 @@
++#ifndef __QCOM_RPM_H__
++#define __QCOM_RPM_H__
++
++#include <linux/types.h>
++
++struct qcom_rpm;
++
++#define QCOM_RPM_ACTIVE_STATE 0
++#define QCOM_RPM_SLEEP_STATE 1
++
++int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count);
++
++#endif
diff --git a/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch
new file mode 100644
index 0000000..e5e9e4e
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch
@@ -0,0 +1,71 @@
+From 4d54b0adfa67476e6509bc8646b9dbac642e8a29 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Thu, 26 Mar 2015 11:29:26 -0700
+Subject: [PATCH] mfd: qcom_rpm: Add support for IPQ8064
+
+The IPQ8064 also includes an RPM following the same message structure as
+other chips. In addition, it supports a few new resource types to
+support the NSS fabric clocks and the SMB208/SMB209 regulators found on
+the reference boards.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ drivers/mfd/qcom_rpm.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 41 insertions(+)
+
+--- a/drivers/mfd/qcom_rpm.c
++++ b/drivers/mfd/qcom_rpm.c
+@@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm896
+ .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
+ };
+
++static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = {
++ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
++ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
++ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
++ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
++ [QCOM_RPM_NSS_FABRIC_0_CLK] = { 29, 13, 10, 1 },
++ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
++ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
++ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
++ [QCOM_RPM_NSS_FABRIC_1_CLK] = { 33, 17, 14, 1 },
++ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
++ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 2 },
++ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 3 },
++ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
++ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
++ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 2 },
++ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 3 },
++ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
++ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
++ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 2 },
++ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 3 },
++ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
++ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 2 },
++ [QCOM_RPM_CXO_BUFFERS] = { 209, 33, 31, 1 },
++ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 34, 32, 1 },
++ [QCOM_RPM_HDMI_SWITCH] = { 211, 35, 33, 1 },
++ [QCOM_RPM_DDR_DMM] = { 212, 36, 34, 2 },
++ [QCOM_RPM_VDDMIN_GPIO] = { 215, 40, 39, 1 },
++ [QCOM_RPM_SMB208_S1a] = { 216, 41, 90, 2 },
++ [QCOM_RPM_SMB208_S1b] = { 218, 43, 91, 2 },
++ [QCOM_RPM_SMB208_S2a] = { 220, 45, 92, 2 },
++ [QCOM_RPM_SMB208_S2b] = { 222, 47, 93, 2 },
++};
++
++static const struct qcom_rpm_data ipq806x_template = {
++ .version = 3,
++ .resource_table = ipq806x_rpm_resource_table,
++ .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table),
++};
++
+ static const struct of_device_id qcom_rpm_of_match[] = {
+ { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
+ { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
+ { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
++ { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
diff --git a/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch b/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch
new file mode 100644
index 0000000..a65d2c5
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch
@@ -0,0 +1,247 @@
+From aa0c4b815045420ea54d5ae5362f5a0190609d46 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Wed, 26 Nov 2014 13:50:59 -0800
+Subject: [PATCH] mfd: devicetree: bindings: Add Qualcomm RPM DT binding
+
+Add binding for the Qualcomm Resource Power Manager (RPM) found in 8660,
+8960 and 8064 based devices.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 70 ++++++++++
+ include/dt-bindings/mfd/qcom-rpm.h | 154 +++++++++++++++++++++
+ 2 files changed, 224 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+ create mode 100644 include/dt-bindings/mfd/qcom-rpm.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -0,0 +1,70 @@
++Qualcomm Resource Power Manager (RPM)
++
++This driver is used to interface with the Resource Power Manager (RPM) found in
++various Qualcomm platforms. The RPM allows each component in the system to vote
++for state of the system resources, such as clocks, regulators and bus
++frequencies.
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: must be one of:
++ "qcom,rpm-apq8064"
++ "qcom,rpm-msm8660"
++ "qcom,rpm-msm8960"
++
++- reg:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: base address and size of the RPM's message ram
++
++- interrupts:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: three entries specifying the RPM's:
++ 1. acknowledgement interrupt
++ 2. error interrupt
++ 3. wakeup interrupt
++
++- interrupt-names:
++ Usage: required
++ Value type: <string-array>
++ Definition: must be the three strings "ack", "err" and "wakeup", in order
++
++- #address-cells:
++ Usage: required
++ Value type: <u32>
++ Definition: must be 1
++
++- #size-cells:
++ Usage: required
++ Value type: <u32>
++ Definition: must be 0
++
++- qcom,ipc:
++ Usage: required
++ Value type: <prop-encoded-array>
++
++ Definition: three entries specifying the outgoing ipc bit used for
++ signaling the RPM:
++ - phandle to a syscon node representing the apcs registers
++ - u32 representing offset to the register within the syscon
++ - u32 representing the ipc bit within the register
++
++
++= EXAMPLE
++
++ #include <dt-bindings/mfd/qcom-rpm.h>
++
++ rpm@108000 {
++ compatible = "qcom,rpm-msm8960";
++ reg = <0x108000 0x1000>;
++ qcom,ipc = <&apcs 0x8 2>;
++
++ interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
++ interrupt-names = "ack", "err", "wakeup";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
+--- /dev/null
++++ b/include/dt-bindings/mfd/qcom-rpm.h
+@@ -0,0 +1,154 @@
++/*
++ * This header provides constants for the Qualcomm RPM bindings.
++ */
++
++#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H
++#define _DT_BINDINGS_MFD_QCOM_RPM_H
++
++/*
++ * Constants use to identify individual resources in the RPM.
++ */
++#define QCOM_RPM_APPS_FABRIC_ARB 1
++#define QCOM_RPM_APPS_FABRIC_CLK 2
++#define QCOM_RPM_APPS_FABRIC_HALT 3
++#define QCOM_RPM_APPS_FABRIC_IOCTL 4
++#define QCOM_RPM_APPS_FABRIC_MODE 5
++#define QCOM_RPM_APPS_L2_CACHE_CTL 6
++#define QCOM_RPM_CFPB_CLK 7
++#define QCOM_RPM_CXO_BUFFERS 8
++#define QCOM_RPM_CXO_CLK 9
++#define QCOM_RPM_DAYTONA_FABRIC_CLK 10
++#define QCOM_RPM_DDR_DMM 11
++#define QCOM_RPM_EBI1_CLK 12
++#define QCOM_RPM_HDMI_SWITCH 13
++#define QCOM_RPM_MMFPB_CLK 14
++#define QCOM_RPM_MM_FABRIC_ARB 15
++#define QCOM_RPM_MM_FABRIC_CLK 16
++#define QCOM_RPM_MM_FABRIC_HALT 17
++#define QCOM_RPM_MM_FABRIC_IOCTL 18
++#define QCOM_RPM_MM_FABRIC_MODE 19
++#define QCOM_RPM_PLL_4 20
++#define QCOM_RPM_PM8058_LDO0 21
++#define QCOM_RPM_PM8058_LDO1 22
++#define QCOM_RPM_PM8058_LDO2 23
++#define QCOM_RPM_PM8058_LDO3 24
++#define QCOM_RPM_PM8058_LDO4 25
++#define QCOM_RPM_PM8058_LDO5 26
++#define QCOM_RPM_PM8058_LDO6 27
++#define QCOM_RPM_PM8058_LDO7 28
++#define QCOM_RPM_PM8058_LDO8 29
++#define QCOM_RPM_PM8058_LDO9 30
++#define QCOM_RPM_PM8058_LDO10 31
++#define QCOM_RPM_PM8058_LDO11 32
++#define QCOM_RPM_PM8058_LDO12 33
++#define QCOM_RPM_PM8058_LDO13 34
++#define QCOM_RPM_PM8058_LDO14 35
++#define QCOM_RPM_PM8058_LDO15 36
++#define QCOM_RPM_PM8058_LDO16 37
++#define QCOM_RPM_PM8058_LDO17 38
++#define QCOM_RPM_PM8058_LDO18 39
++#define QCOM_RPM_PM8058_LDO19 40
++#define QCOM_RPM_PM8058_LDO20 41
++#define QCOM_RPM_PM8058_LDO21 42
++#define QCOM_RPM_PM8058_LDO22 43
++#define QCOM_RPM_PM8058_LDO23 44
++#define QCOM_RPM_PM8058_LDO24 45
++#define QCOM_RPM_PM8058_LDO25 46
++#define QCOM_RPM_PM8058_LVS0 47
++#define QCOM_RPM_PM8058_LVS1 48
++#define QCOM_RPM_PM8058_NCP 49
++#define QCOM_RPM_PM8058_SMPS0 50
++#define QCOM_RPM_PM8058_SMPS1 51
++#define QCOM_RPM_PM8058_SMPS2 52
++#define QCOM_RPM_PM8058_SMPS3 53
++#define QCOM_RPM_PM8058_SMPS4 54
++#define QCOM_RPM_PM8821_LDO1 55
++#define QCOM_RPM_PM8821_SMPS1 56
++#define QCOM_RPM_PM8821_SMPS2 57
++#define QCOM_RPM_PM8901_LDO0 58
++#define QCOM_RPM_PM8901_LDO1 59
++#define QCOM_RPM_PM8901_LDO2 60
++#define QCOM_RPM_PM8901_LDO3 61
++#define QCOM_RPM_PM8901_LDO4 62
++#define QCOM_RPM_PM8901_LDO5 63
++#define QCOM_RPM_PM8901_LDO6 64
++#define QCOM_RPM_PM8901_LVS0 65
++#define QCOM_RPM_PM8901_LVS1 66
++#define QCOM_RPM_PM8901_LVS2 67
++#define QCOM_RPM_PM8901_LVS3 68
++#define QCOM_RPM_PM8901_MVS 69
++#define QCOM_RPM_PM8901_SMPS0 70
++#define QCOM_RPM_PM8901_SMPS1 71
++#define QCOM_RPM_PM8901_SMPS2 72
++#define QCOM_RPM_PM8901_SMPS3 73
++#define QCOM_RPM_PM8901_SMPS4 74
++#define QCOM_RPM_PM8921_CLK1 75
++#define QCOM_RPM_PM8921_CLK2 76
++#define QCOM_RPM_PM8921_LDO1 77
++#define QCOM_RPM_PM8921_LDO2 78
++#define QCOM_RPM_PM8921_LDO3 79
++#define QCOM_RPM_PM8921_LDO4 80
++#define QCOM_RPM_PM8921_LDO5 81
++#define QCOM_RPM_PM8921_LDO6 82
++#define QCOM_RPM_PM8921_LDO7 83
++#define QCOM_RPM_PM8921_LDO8 84
++#define QCOM_RPM_PM8921_LDO9 85
++#define QCOM_RPM_PM8921_LDO10 86
++#define QCOM_RPM_PM8921_LDO11 87
++#define QCOM_RPM_PM8921_LDO12 88
++#define QCOM_RPM_PM8921_LDO13 89
++#define QCOM_RPM_PM8921_LDO14 90
++#define QCOM_RPM_PM8921_LDO15 91
++#define QCOM_RPM_PM8921_LDO16 92
++#define QCOM_RPM_PM8921_LDO17 93
++#define QCOM_RPM_PM8921_LDO18 94
++#define QCOM_RPM_PM8921_LDO19 95
++#define QCOM_RPM_PM8921_LDO20 96
++#define QCOM_RPM_PM8921_LDO21 97
++#define QCOM_RPM_PM8921_LDO22 98
++#define QCOM_RPM_PM8921_LDO23 99
++#define QCOM_RPM_PM8921_LDO24 100
++#define QCOM_RPM_PM8921_LDO25 101
++#define QCOM_RPM_PM8921_LDO26 102
++#define QCOM_RPM_PM8921_LDO27 103
++#define QCOM_RPM_PM8921_LDO28 104
++#define QCOM_RPM_PM8921_LDO29 105
++#define QCOM_RPM_PM8921_LVS1 106
++#define QCOM_RPM_PM8921_LVS2 107
++#define QCOM_RPM_PM8921_LVS3 108
++#define QCOM_RPM_PM8921_LVS4 109
++#define QCOM_RPM_PM8921_LVS5 110
++#define QCOM_RPM_PM8921_LVS6 111
++#define QCOM_RPM_PM8921_LVS7 112
++#define QCOM_RPM_PM8921_MVS 113
++#define QCOM_RPM_PM8921_NCP 114
++#define QCOM_RPM_PM8921_SMPS1 115
++#define QCOM_RPM_PM8921_SMPS2 116
++#define QCOM_RPM_PM8921_SMPS3 117
++#define QCOM_RPM_PM8921_SMPS4 118
++#define QCOM_RPM_PM8921_SMPS5 119
++#define QCOM_RPM_PM8921_SMPS6 120
++#define QCOM_RPM_PM8921_SMPS7 121
++#define QCOM_RPM_PM8921_SMPS8 122
++#define QCOM_RPM_PXO_CLK 123
++#define QCOM_RPM_QDSS_CLK 124
++#define QCOM_RPM_SFPB_CLK 125
++#define QCOM_RPM_SMI_CLK 126
++#define QCOM_RPM_SYS_FABRIC_ARB 127
++#define QCOM_RPM_SYS_FABRIC_CLK 128
++#define QCOM_RPM_SYS_FABRIC_HALT 129
++#define QCOM_RPM_SYS_FABRIC_IOCTL 130
++#define QCOM_RPM_SYS_FABRIC_MODE 131
++#define QCOM_RPM_USB_OTG_SWITCH 132
++#define QCOM_RPM_VDDMIN_GPIO 133
++
++/*
++ * Constants used to select force mode for regulators.
++ */
++#define QCOM_RPM_FORCE_MODE_NONE 0
++#define QCOM_RPM_FORCE_MODE_LPM 1
++#define QCOM_RPM_FORCE_MODE_HPM 2
++#define QCOM_RPM_FORCE_MODE_AUTO 3
++#define QCOM_RPM_FORCE_MODE_BYPASS 4
++
++#endif
diff --git a/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch b/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch
new file mode 100644
index 0000000..c8a9f3f
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch
@@ -0,0 +1,42 @@
+From 30bc3aa5c4ed3072bdff7d915772df1b91307ed4 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Thu, 26 Mar 2015 11:29:25 -0700
+Subject: [PATCH] mfd: devicetree: qcom_rpm: Document IPQ8064 resources
+
+The IPQ8064 SoC has several RPM-controlled resources, an NSS fabrick
+clock and four regulator resources. Provide definitions for them.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+[sboyd@codeaurora.org: Drop regulator part of binding]
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 1 +
+ include/dt-bindings/mfd/qcom-rpm.h | 6 ++++++
+ 2 files changed, 7 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -12,6 +12,7 @@ frequencies.
+ "qcom,rpm-apq8064"
+ "qcom,rpm-msm8660"
+ "qcom,rpm-msm8960"
++ "qcom,rpm-ipq8064"
+
+ - reg:
+ Usage: required
+--- a/include/dt-bindings/mfd/qcom-rpm.h
++++ b/include/dt-bindings/mfd/qcom-rpm.h
+@@ -141,6 +141,12 @@
+ #define QCOM_RPM_SYS_FABRIC_MODE 131
+ #define QCOM_RPM_USB_OTG_SWITCH 132
+ #define QCOM_RPM_VDDMIN_GPIO 133
++#define QCOM_RPM_NSS_FABRIC_0_CLK 134
++#define QCOM_RPM_NSS_FABRIC_1_CLK 135
++#define QCOM_RPM_SMB208_S1a 136
++#define QCOM_RPM_SMB208_S1b 137
++#define QCOM_RPM_SMB208_S2a 138
++#define QCOM_RPM_SMB208_S2b 139
+
+ /*
+ * Constants used to select force mode for regulators.
diff --git a/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch b/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch
new file mode 100644
index 0000000..e4f094c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch
@@ -0,0 +1,58 @@
+From 0f5bb5b5de3b18877373f746bdb85d8ea0efeedf Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Thu, 20 Nov 2014 13:41:25 -0600
+Subject: [PATCH] regulator: rpm: add support for RPM-controller SMB208
+
+The IPQ8064 reference boards make use of SMB208 regulators which are
+controlled by RPM. Implement support for these regulators in the RPM
+regulator driver.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/regulator/qcom_rpm-regulator.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+--- a/drivers/regulator/qcom_rpm-regulator.c
++++ b/drivers/regulator/qcom_rpm-regulator.c
+@@ -183,6 +183,13 @@ static const struct regulator_linear_ran
+ REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
+ };
+
++static const struct regulator_linear_range smb208_ranges[] = {
++ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
++ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
++ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
++ REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000),
++};
++
+ static const struct regulator_linear_range ncp_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
+ };
+@@ -559,6 +566,16 @@ static const struct qcom_rpm_reg pm8921_
+ .parts = &rpm8960_switch_parts,
+ };
+
++static const struct qcom_rpm_reg smb208_smps = {
++ .desc.linear_ranges = smb208_ranges,
++ .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges),
++ .desc.n_voltages = 235,
++ .desc.ops = &uV_ops,
++ .parts = &rpm8960_smps_parts,
++ .supports_force_mode_auto = false,
++ .supports_force_mode_bypass = false,
++};
++
+ static const struct of_device_id rpm_of_match[] = {
+ { .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo },
+ { .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo },
+@@ -578,6 +595,8 @@ static const struct of_device_id rpm_of_
+ { .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps },
+ { .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp },
+ { .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch },
++
++ { .compatible = "qcom,rpm-smb208", .data = &smb208_smps },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, rpm_of_match);
diff --git a/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch b/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch
new file mode 100644
index 0000000..48921a8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch
@@ -0,0 +1,25 @@
+From 803926825fa4db007437f76654e3e63bafb9b906 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Wed, 26 Nov 2014 13:51:01 -0800
+Subject: [PATCH] regulator: qcom-rpm: Add missing state flag in call to RPM
+
+This adds the missing state parameter to the call down to the RPM. This
+is currently hard coded to the active state, as that's all we're
+supporting at this moment.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ drivers/regulator/qcom_rpm-regulator.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/regulator/qcom_rpm-regulator.c
++++ b/drivers/regulator/qcom_rpm-regulator.c
+@@ -205,6 +205,7 @@ static int rpm_reg_write(struct qcom_rpm
+ vreg->val[req->word] |= value << req->shift;
+
+ return qcom_rpm_write(vreg->rpm,
++ QCOM_RPM_ACTIVE_STATE,
+ vreg->resource,
+ vreg->val,
+ vreg->parts->request_len);
diff --git a/target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch
new file mode 100644
index 0000000..7e4c5cb
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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/soc/qcom,gsbi.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+@@ -76,6 +77,63 @@
+ ranges;
+ compatible = "simple-bus";
+
++ 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>;
+@@ -120,6 +178,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-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch b/target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch
new file mode 100644
index 0000000..29f74b7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch
@@ -0,0 +1,55 @@
+From 6793b3cd5da817c4be218bd8632f07cf4d2b0d26 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 19 Nov 2014 14:48:59 +0100
+Subject: [PATCH] clk_mux: Fix set_parent doing the wrong thing when INDEX_BIT
+ && index >= 3
+
+If CLK_MUX_INDEX_BIT is set, then each bit turns on / off a single parent,
+so theoretically multiple parents could be enabled at the same time, but in
+practice only one bit should ever be 1. So to select parent 0, set
+the register (*) to 0x01, to select parent 1 set it 0x02, parent 2, 0x04,
+parent 3, 0x08, etc.
+
+But the current code does:
+
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+ index = (1 << ffs(index));
+
+Which means that:
+
+For an input index of 0, ffs returns 0, so we set the register
+to 0x01, ok.
+
+For an input index of 1, ffs returns 1, so we set the register
+to 0x02, ok.
+
+For an input index of 2, ffs returns 2, so we set the register
+to 0x04, ok.
+
+For an input index of 3, ffs returns 1, so we set the register
+to 0x02, not good!
+
+The code should simply be:
+
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+ index = 1 << index;
+
+Which always does the right thing, this commit fixes this.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Michael Turquette <mturquette@linaro.org>
+---
+ drivers/clk/clk-mux.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/clk/clk-mux.c
++++ b/drivers/clk/clk-mux.c
+@@ -77,7 +77,7 @@ static int clk_mux_set_parent(struct clk
+
+ else {
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+- index = (1 << ffs(index));
++ index = 1 << index;
+
+ if (mux->flags & CLK_MUX_INDEX_ONE)
+ index++;
diff --git a/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch b/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch
new file mode 100644
index 0000000..18972f3
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch
@@ -0,0 +1,120 @@
+From 15a02c1f6dd7c2bb150c61d00ffb33f584ff2288 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 19 Jan 2015 18:05:28 -0800
+Subject: [PATCH] clk: Add __clk_mux_determine_rate_closest
+
+Some clock drivers want to find the closest rate on the input of
+a mux instead of a rate that's less than or equal to the desired
+rate. Add a generic mux function to support this.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Tested-by: Kenneth Westfield <kwestfie@codeaurora.org>
+Signed-off-by: Michael Turquette <mturquette@linaro.org>
+---
+ drivers/clk/clk.c | 47 +++++++++++++++++++++++++++++++++++---------
+ include/linux/clk-provider.h | 8 +++++++-
+ 2 files changed, 45 insertions(+), 10 deletions(-)
+
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -695,14 +695,20 @@ struct clk *__clk_lookup(const char *nam
+ return NULL;
+ }
+
+-/*
+- * Helper for finding best parent to provide a given frequency. This can be used
+- * directly as a determine_rate callback (e.g. for a mux), or from a more
+- * complex clock that may combine a mux with other operations.
+- */
+-long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
+- unsigned long *best_parent_rate,
+- struct clk **best_parent_p)
++static bool mux_is_better_rate(unsigned long rate, unsigned long now,
++ unsigned long best, unsigned long flags)
++{
++ if (flags & CLK_MUX_ROUND_CLOSEST)
++ return abs(now - rate) < abs(best - rate);
++
++ return now <= rate && now > best;
++}
++
++static long
++clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate,
++ unsigned long *best_parent_rate,
++ struct clk **best_parent_p,
++ unsigned long flags)
+ {
+ struct clk *clk = hw->clk, *parent, *best_parent = NULL;
+ int i, num_parents;
+@@ -730,7 +736,7 @@ long __clk_mux_determine_rate(struct clk
+ parent_rate = __clk_round_rate(parent, rate);
+ else
+ parent_rate = __clk_get_rate(parent);
+- if (parent_rate <= rate && parent_rate > best) {
++ if (mux_is_better_rate(rate, parent_rate, best, flags)) {
+ best_parent = parent;
+ best = parent_rate;
+ }
+@@ -743,8 +749,31 @@ out:
+
+ return best;
+ }
++
++/*
++ * Helper for finding best parent to provide a given frequency. This can be used
++ * directly as a determine_rate callback (e.g. for a mux), or from a more
++ * complex clock that may combine a mux with other operations.
++ */
++long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *best_parent_rate,
++ struct clk **best_parent_p)
++{
++ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
++ best_parent_p, 0);
++}
+ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate);
+
++long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
++ unsigned long *best_parent_rate,
++ struct clk **best_parent_p)
++{
++ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
++ best_parent_p,
++ CLK_MUX_ROUND_CLOSEST);
++}
++EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
++
+ /*** clk api ***/
+
+ void __clk_unprepare(struct clk *clk)
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -382,6 +382,8 @@ struct clk *clk_register_divider_table(s
+ * register, and mask of mux bits are in higher 16-bit of this register.
+ * While setting the mux bits, higher 16-bit should also be updated to
+ * indicate changing mux bits.
++ * CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired
++ * frequency.
+ */
+ struct clk_mux {
+ struct clk_hw hw;
+@@ -396,7 +398,8 @@ struct clk_mux {
+ #define CLK_MUX_INDEX_ONE BIT(0)
+ #define CLK_MUX_INDEX_BIT BIT(1)
+ #define CLK_MUX_HIWORD_MASK BIT(2)
+-#define CLK_MUX_READ_ONLY BIT(3) /* mux setting cannot be changed */
++#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */
++#define CLK_MUX_ROUND_CLOSEST BIT(4)
+
+ extern const struct clk_ops clk_mux_ops;
+ extern const struct clk_ops clk_mux_ro_ops;
+@@ -554,6 +557,9 @@ struct clk *__clk_lookup(const char *nam
+ long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p);
++long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
++ unsigned long *best_parent_rate,
++ struct clk **best_parent_p);
+
+ /*
+ * FIXME clock api without lock protection
diff --git a/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch b/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch
new file mode 100644
index 0000000..790f25d
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch
@@ -0,0 +1,115 @@
+From 4e3c021fb995bcbb5d1f814d00584cb80eb904a8 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <k.kozlowski@samsung.com>
+Date: Mon, 5 Jan 2015 10:52:40 +0100
+Subject: [PATCH] clk: Add clk_unregister_{divider, gate, mux} to close memory
+ leak
+
+The common clk_register_{divider,gate,mux} functions allocated memory
+for internal data which wasn't freed anywhere. Drivers using these
+helpers could only unregister clocks but the memory would still leak.
+
+Add corresponding unregister functions which will release all resources.
+
+Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Michael Turquette <mturquette@linaro.org>
+---
+ drivers/clk/clk-divider.c | 16 ++++++++++++++++
+ drivers/clk/clk-gate.c | 16 ++++++++++++++++
+ drivers/clk/clk-mux.c | 16 ++++++++++++++++
+ include/linux/clk-provider.h | 4 ++++
+ 4 files changed, 52 insertions(+)
+
+--- a/drivers/clk/clk-divider.c
++++ b/drivers/clk/clk-divider.c
+@@ -461,3 +461,19 @@ struct clk *clk_register_divider_table(s
+ width, clk_divider_flags, table, lock);
+ }
+ EXPORT_SYMBOL_GPL(clk_register_divider_table);
++
++void clk_unregister_divider(struct clk *clk)
++{
++ struct clk_divider *div;
++ struct clk_hw *hw;
++
++ hw = __clk_get_hw(clk);
++ if (!hw)
++ return;
++
++ div = to_clk_divider(hw);
++
++ clk_unregister(clk);
++ kfree(div);
++}
++EXPORT_SYMBOL_GPL(clk_unregister_divider);
+--- a/drivers/clk/clk-gate.c
++++ b/drivers/clk/clk-gate.c
+@@ -162,3 +162,19 @@ struct clk *clk_register_gate(struct dev
+ return clk;
+ }
+ EXPORT_SYMBOL_GPL(clk_register_gate);
++
++void clk_unregister_gate(struct clk *clk)
++{
++ struct clk_gate *gate;
++ struct clk_hw *hw;
++
++ hw = __clk_get_hw(clk);
++ if (!hw)
++ return;
++
++ gate = to_clk_gate(hw);
++
++ clk_unregister(clk);
++ kfree(gate);
++}
++EXPORT_SYMBOL_GPL(clk_unregister_gate);
+--- a/drivers/clk/clk-mux.c
++++ b/drivers/clk/clk-mux.c
+@@ -177,3 +177,19 @@ struct clk *clk_register_mux(struct devi
+ NULL, lock);
+ }
+ EXPORT_SYMBOL_GPL(clk_register_mux);
++
++void clk_unregister_mux(struct clk *clk)
++{
++ struct clk_mux *mux;
++ struct clk_hw *hw;
++
++ hw = __clk_get_hw(clk);
++ if (!hw)
++ return;
++
++ mux = to_clk_mux(hw);
++
++ clk_unregister(clk);
++ kfree(mux);
++}
++EXPORT_SYMBOL_GPL(clk_unregister_mux);
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -294,6 +294,7 @@ struct clk *clk_register_gate(struct dev
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock);
++void clk_unregister_gate(struct clk *clk);
+
+ struct clk_div_table {
+ unsigned int val;
+@@ -361,6 +362,7 @@ struct clk *clk_register_divider_table(s
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock);
++void clk_unregister_divider(struct clk *clk);
+
+ /**
+ * struct clk_mux - multiplexer clock
+@@ -414,6 +416,8 @@ struct clk *clk_register_mux_table(struc
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+
++void clk_unregister_mux(struct clk *clk);
++
+ void of_fixed_factor_clk_setup(struct device_node *node);
+
+ /**
diff --git a/target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch
new file mode 100644
index 0000000..36a92c8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch
new file mode 100644
index 0000000..50022e6
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -390,7 +390,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;
+@@ -406,6 +406,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,
+@@ -414,7 +419,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-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
new file mode 100644
index 0000000..02d96ad
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
@@ -0,0 +1,129 @@
+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
+@@ -1476,21 +1476,23 @@ static struct clk *clk_propagate_rate_ch
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+-static void clk_change_rate(struct clk *clk)
++static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate)
+ {
+ struct clk *child;
+ struct hlist_node *tmp;
+ unsigned long old_rate;
+- unsigned long best_parent_rate = 0;
+ bool skip_set_rate = false;
+ struct clk *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);
+@@ -1510,7 +1512,7 @@ static void clk_change_rate(struct clk *
+ if (!skip_set_rate && clk->ops->set_rate)
+ clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_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);
+@@ -1523,12 +1525,13 @@ static void clk_change_rate(struct clk *
+ /* 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);
+ }
+
+ /**
+@@ -1556,6 +1559,7 @@ int clk_set_rate(struct clk *clk, unsign
+ {
+ struct clk *top, *fail_clk;
+ int ret = 0;
++ unsigned long parent_rate;
+
+ if (!clk)
+ return 0;
+@@ -1589,8 +1593,13 @@ int clk_set_rate(struct clk *clk, unsign
+ goto out;
+ }
+
++ 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);
+
+ out:
+ clk_prepare_unlock();
diff --git a/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch
new file mode 100644
index 0000000..227f8ce
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch
@@ -0,0 +1,170 @@
+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
+@@ -1350,7 +1350,8 @@ out:
+ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
+ struct clk *new_parent, u8 p_index)
+ {
+- struct clk *child;
++ struct clk *child, *parent;
++ struct clk_hw *parent_hw;
+
+ clk->new_rate = new_rate;
+ clk->new_parent = new_parent;
+@@ -1360,6 +1361,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->clk;
++ 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);
+@@ -1439,17 +1452,47 @@ out:
+ * so that in case of an error we can walk down the whole tree again and
+ * abort the change.
+ */
+-static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
++static struct clk *clk_propagate_rate_change(struct clk *clk,
++ unsigned long event)
+ {
+ struct clk *child, *tmp_clk, *fail_clk = NULL;
++ struct clk *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;
+ }
+
+@@ -1494,7 +1537,8 @@ static void clk_change_rate(struct clk *
+
+ 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);
+
+ if (clk->ops->set_rate_and_parent) {
+@@ -1514,9 +1558,6 @@ static void clk_change_rate(struct clk *
+
+ 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.
+@@ -1601,6 +1642,8 @@ int clk_set_rate(struct clk *clk, unsign
+ /* change the rates */
+ clk_change_rate(top, parent_rate);
+
++ clk_propagate_rate_change(top, POST_RATE_CHANGE);
++
+ out:
+ clk_prepare_unlock();
+
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -179,6 +179,7 @@ struct clk_ops {
+ struct clk **best_parent_clk);
+ 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,
+--- a/include/linux/clk-private.h
++++ b/include/linux/clk-private.h
+@@ -38,8 +38,11 @@ struct clk {
+ struct clk **parents;
+ u8 num_parents;
+ u8 new_parent_index;
++ u8 safe_parent_index;
+ unsigned long rate;
++ unsigned long old_rate;
+ unsigned long new_rate;
++ struct clk *safe_parent;
+ struct clk *new_parent;
+ struct clk *new_child;
+ unsigned long flags;
diff --git a/target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
new file mode 100644
index 0000000..701d5e7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
+ clk-qcom-y += clk-rcg.o
+ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.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-3.18/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch
new file mode 100644
index 0000000..a0b1d64
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -70,3 +70,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
+@@ -17,3 +17,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-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-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
new file mode 100644
index 0000000..7fd53d1
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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 = {
+@@ -102,6 +103,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,
+@@ -2261,6 +2341,9 @@ static struct clk_regmap *gcc_ipq806x_cl
+ [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
+ [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
+ [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_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-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch
new file mode 100644
index 0000000..63292e8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -78,3 +78,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
+@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
+ clk-qcom-y += clk-rcg.o
+ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.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-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
new file mode 100644
index 0000000..06b14d8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -79,6 +79,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
+@@ -18,4 +18,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-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-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644
index 0000000..98a09ac
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -87,6 +87,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
+@@ -20,3 +20,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-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
new file mode 100644
index 0000000..c3ca9b5
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -129,6 +129,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
+@@ -64,6 +64,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += o
+ obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o
+ obj-$(CONFIG_PXA27x) += 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-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
new file mode 100644
index 0000000..a3c3bbf
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -24,6 +24,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 {
+@@ -34,11 +39,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>;
+ };
+ };
+
+@@ -71,6 +89,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>;
+@@ -171,11 +229,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-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
new file mode 100644
index 0000000..6052b41
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -196,6 +196,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.
++
+ menu "x86 CPU frequency scaling drivers"
+ depends on X86
+ source "drivers/cpufreq/Kconfig.x86"
+--- 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 = 0;
++ 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 = 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-3.18/150-dmaengine-Rework-dma_chan_get.patch b/target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch
new file mode 100644
index 0000000..880e67c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch
@@ -0,0 +1,70 @@
+From d2f4f99db3e9ec8b063cf2e45704e2bb95428317 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 17 Nov 2014 14:41:58 +0100
+Subject: [PATCH] dmaengine: Rework dma_chan_get
+
+dma_chan_get uses a rather interesting error handling and code path.
+
+Change it to something more usual in the kernel.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/dmaengine.c | 36 +++++++++++++++++++-----------------
+ 1 file changed, 19 insertions(+), 17 deletions(-)
+
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -222,31 +222,33 @@ static void balance_ref_count(struct dma
+ */
+ static int dma_chan_get(struct dma_chan *chan)
+ {
+- int err = -ENODEV;
+ struct module *owner = dma_chan_to_owner(chan);
++ int ret;
+
++ /* The channel is already in use, update client count */
+ if (chan->client_count) {
+ __module_get(owner);
+- err = 0;
+- } else if (try_module_get(owner))
+- err = 0;
++ goto out;
++ }
+
+- if (err == 0)
+- chan->client_count++;
++ if (!try_module_get(owner))
++ return -ENODEV;
+
+ /* allocate upon first client reference */
+- if (chan->client_count == 1 && err == 0) {
+- int desc_cnt = chan->device->device_alloc_chan_resources(chan);
+-
+- if (desc_cnt < 0) {
+- err = desc_cnt;
+- chan->client_count = 0;
+- module_put(owner);
+- } else if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask))
+- balance_ref_count(chan);
+- }
+-
+- return err;
++ ret = chan->device->device_alloc_chan_resources(chan);
++ if (ret < 0)
++ goto err_out;
++
++ if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask))
++ balance_ref_count(chan);
++
++out:
++ chan->client_count++;
++ return 0;
++
++err_out:
++ module_put(owner);
++ return ret;
+ }
+
+ /**
diff --git a/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch b/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch
new file mode 100644
index 0000000..b24a10b
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch
@@ -0,0 +1,27 @@
+From 4f8ef9f4140cc286d7d1cf9237da7a7439e4fc0b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 17 Nov 2014 14:42:03 +0100
+Subject: [PATCH] dmaengine: Remove the need to declare device_control
+
+In order to migrate the drivers without triggering a BUG_ON for the converted
+drivers, which would cause bisectability issues, we need to remove that check
+before removing the device_control function entirely.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/dmaengine.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -814,8 +814,6 @@ int dma_async_device_register(struct dma
+ !device->device_prep_dma_sg);
+ BUG_ON(dma_has_cap(DMA_CYCLIC, device->cap_mask) &&
+ !device->device_prep_dma_cyclic);
+- BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) &&
+- !device->device_control);
+ BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) &&
+ !device->device_prep_interleaved_dma);
+
diff --git a/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch b/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch
new file mode 100644
index 0000000..6337259
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch
@@ -0,0 +1,62 @@
+From c4b54a648e682f678c338619df848233a6babc46 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 17 Nov 2014 14:41:59 +0100
+Subject: [PATCH] dmaengine: Make channel allocation callbacks optional
+
+Nowadays, some drivers don't have anything in there channel allocation
+callbacks anymore.
+
+Remove the BUG_ON if those callbacks aren't implemented, in order to allow
+drivers to not implement them.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/dmaengine.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -235,9 +235,11 @@ static int dma_chan_get(struct dma_chan
+ return -ENODEV;
+
+ /* allocate upon first client reference */
+- ret = chan->device->device_alloc_chan_resources(chan);
+- if (ret < 0)
+- goto err_out;
++ if (chan->device->device_alloc_chan_resources) {
++ ret = chan->device->device_alloc_chan_resources(chan);
++ if (ret < 0)
++ goto err_out;
++ }
+
+ if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask))
+ balance_ref_count(chan);
+@@ -259,11 +261,15 @@ err_out:
+ */
+ static void dma_chan_put(struct dma_chan *chan)
+ {
++ /* This channel is not in use, bail out */
+ if (!chan->client_count)
+- return; /* this channel failed alloc_chan_resources */
++ return;
++
+ chan->client_count--;
+ module_put(dma_chan_to_owner(chan));
+- if (chan->client_count == 0)
++
++ /* This channel is not in use anymore, free it */
++ if (!chan->client_count && chan->device->device_free_chan_resources)
+ chan->device->device_free_chan_resources(chan);
+ }
+
+@@ -817,8 +823,6 @@ int dma_async_device_register(struct dma
+ BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) &&
+ !device->device_prep_interleaved_dma);
+
+- BUG_ON(!device->device_alloc_chan_resources);
+- BUG_ON(!device->device_free_chan_resources);
+ BUG_ON(!device->device_tx_status);
+ BUG_ON(!device->device_issue_pending);
+ BUG_ON(!device->dev);
diff --git a/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch b/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch
new file mode 100644
index 0000000..ced452f
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch
@@ -0,0 +1,51 @@
+From 94a73e30dfe6722e9f4ef19f7892901d7d00eab1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 17 Nov 2014 14:42:00 +0100
+Subject: [PATCH] dmaengine: Introduce a device_config callback
+
+The fact that the channel configuration is done in device_control is rather
+misleading, since it's not really advertised as such, plus, the fact that the
+framework exposes a function of its own makes it not really intuitive, while
+we're losing the type checking whenever we pass that unsigned long argument.
+
+Add a device_config callback to dma_device, with a fallback on the old
+behaviour for now for existing drivers to opt in.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ include/linux/dmaengine.h | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/include/linux/dmaengine.h
++++ b/include/linux/dmaengine.h
+@@ -607,6 +607,8 @@ struct dma_tx_state {
+ * The function takes a buffer of size buf_len. The callback function will
+ * be called after period_len bytes have been transferred.
+ * @device_prep_interleaved_dma: Transfer expression in a generic way.
++ * @device_config: Pushes a new configuration to a channel, return 0 or an error
++ * code
+ * @device_control: manipulate all pending operations on a channel, returns
+ * zero or error code
+ * @device_tx_status: poll for transaction completion, the optional
+@@ -673,6 +675,9 @@ struct dma_device {
+ struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
+ struct dma_chan *chan, struct dma_interleaved_template *xt,
+ unsigned long flags);
++
++ int (*device_config)(struct dma_chan *chan,
++ struct dma_slave_config *config);
+ int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg);
+
+@@ -696,6 +701,9 @@ static inline int dmaengine_device_contr
+ static inline int dmaengine_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+ {
++ if (chan->device->device_config)
++ return chan->device->device_config(chan, config);
++
+ return dmaengine_device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long)config);
+ }
diff --git a/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch b/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch
new file mode 100644
index 0000000..422f7fb
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch
@@ -0,0 +1,47 @@
+From 7fa0cf462daa6f6121b332b87833d7f5bdb515c0 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 17 Nov 2014 14:42:02 +0100
+Subject: [PATCH] dmaengine: Add device_terminate_all callback
+
+Split out the terminate_all command from device_control to a dma_device
+callback. In order to preserve backward capability, still rely on
+device_control if no such callback has been implemented.
+
+Eventually, this will allow to create a generic dma_slave_caps callback.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ include/linux/dmaengine.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/include/linux/dmaengine.h
++++ b/include/linux/dmaengine.h
+@@ -611,6 +611,8 @@ struct dma_tx_state {
+ * code
+ * @device_control: manipulate all pending operations on a channel, returns
+ * zero or error code
++ * @device_terminate_all: Aborts all transfers on a channel. Returns 0
++ * or an error code
+ * @device_tx_status: poll for transaction completion, the optional
+ * txstate parameter can be supplied with a pointer to get a
+ * struct with auxiliary transfer status information, otherwise the call
+@@ -680,6 +682,7 @@ struct dma_device {
+ struct dma_slave_config *config);
+ int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg);
++ int (*device_terminate_all)(struct dma_chan *chan);
+
+ enum dma_status (*device_tx_status)(struct dma_chan *chan,
+ dma_cookie_t cookie,
+@@ -789,6 +792,9 @@ static inline int dma_get_slave_caps(str
+
+ static inline int dmaengine_terminate_all(struct dma_chan *chan)
+ {
++ if (chan->device->device_terminate_all)
++ return chan->device->device_terminate_all(chan);
++
+ return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0);
+ }
+
diff --git a/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
new file mode 100644
index 0000000..4f5c0ef
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
@@ -0,0 +1,76 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 6027361
+Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org>
+To: Vinod Koul <vinod.koul@intel.com>
+Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org,
+ linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+ linux-arm-kernel@lists.infradead.org, Kumar Gala <galak@codeaurora.org>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ Andy Gross <agross@codeaurora.org>
+Date: Tue, 17 Mar 2015 00:46:11 -0500
+
+This patch removes the crci information from the dma channel property. At least
+one client device requires using more than one CRCI value for a channel. This
+does not match the current binding and the crci information needs to be removed.
+
+Instead, the client device will provide this information via other means.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
+ 1 file changed, 6 insertions(+), 10 deletions(-)
+
+--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt
+@@ -4,8 +4,7 @@ Required properties:
+ - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960
+ - reg: Address range for DMA registers
+ - interrupts: Should contain one interrupt shared by all channels
+-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell
+- denotes CRCI (client rate control interface) flow control assignment.
++- #dma-cells: must be <1>. First cell denotes the channel number.
+ - clocks: Should contain the core clock and interface clock.
+ - clock-names: Must contain "core" for the core clock and "iface" for the
+ interface clock.
+@@ -22,7 +21,7 @@ Example:
+ compatible = "qcom,adm";
+ reg = <0x18300000 0x100000>;
+ interrupts = <0 170 0>;
+- #dma-cells = <2>;
++ #dma-cells = <1>;
+
+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+ clock-names = "core", "iface";
+@@ -35,15 +34,12 @@ Example:
+ qcom,ee = <0>;
+ };
+
+-DMA clients must use the format descripted in the dma.txt file, using a three
++DMA clients must use the format descripted in the dma.txt file, using a two
+ cell specifier for each channel.
+
+-Each dmas request consists of 3 cells:
++Each dmas request consists of two cells:
+ 1. phandle pointing to the DMA controller
+ 2. channel number
+- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0.
+- The CRCI is used for flow control. It identifies the peripheral device that
+- is the source/destination for the transferred data.
+
+ Example:
+
+@@ -56,7 +52,7 @@ Example:
+
+ cs-gpios = <&qcom_pinmux 20 0>;
+
+- dmas = <&adm_dma 6 9>,
+- <&adm_dma 5 10>;
++ dmas = <&adm_dma 6>,
++ <&adm_dma 5>;
+ dma-names = "rx", "tx";
+ };
diff --git a/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch
new file mode 100644
index 0000000..16d000e
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch
@@ -0,0 +1,958 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v6,2/2] dmaengine: Add ADM driver
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 6027351
+Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org>
+To: Vinod Koul <vinod.koul@intel.com>
+Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org,
+ linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+ linux-arm-kernel@lists.infradead.org, Kumar Gala <galak@codeaurora.org>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ Andy Gross <agross@codeaurora.org>
+Date: Tue, 17 Mar 2015 00:46:12 -0500
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8x60 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions. The controller also provides flow
+control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from peripherals
+and also incorporates CRCI (client rate control interface) flow control.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: sricharan <sricharan@codeaurora.org>
+
+---
+drivers/dma/Kconfig | 10 +
+ drivers/dma/Makefile | 1 +
+ drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 911 insertions(+)
+ create mode 100644 drivers/dma/qcom_adm.c
+
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -457,4 +457,14 @@ config QCOM_BAM_DMA
+ Enable support for the QCOM BAM DMA controller. This controller
+ provides DMA capabilities for a variety of on-chip devices.
+
++config QCOM_ADM
++ tristate "Qualcomm ADM support"
++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ ---help---
++ Enable support for the Qualcomm ADM DMA controller. This controller
++ provides DMA capabilities for both general purpose and on-chip
++ peripheral devices.
++
+ endif
+--- /dev/null
++++ b/drivers/dma/qcom_adm.c
+@@ -0,0 +1,896 @@
++/*
++ * 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/io.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/scatterlist.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_dma.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++#include <linux/dmaengine.h>
++
++#include "dmaengine.h"
++#include "virt-dma.h"
++
++/* ADM registers - calculated from channel number and security domain */
++#define ADM_CHAN_MULTI 0x4
++#define ADM_CI_MULTI 0x4
++#define ADM_CRCI_MULTI 0x4
++#define ADM_EE_MULTI 0x800
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee)
++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci))
++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan))
++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
++#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI)
++#define ADM_GP_CTL 0x3d8
++#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \
++ ADM_EE_OFFS(ee))
++
++/* channel status */
++#define ADM_CH_STATUS_VALID BIT(1)
++
++/* channel result */
++#define ADM_CH_RSLT_VALID BIT(31)
++#define ADM_CH_RSLT_ERR BIT(3)
++#define ADM_CH_RSLT_FLUSH BIT(2)
++#define ADM_CH_RSLT_TPD BIT(1)
++
++/* channel conf */
++#define ADM_CH_CONF_SHADOW_EN BIT(12)
++#define ADM_CH_CONF_MPU_DISABLE BIT(11)
++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9)
++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7)
++#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
++
++/* channel result conf */
++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1)
++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0)
++
++/* CRCI CTL */
++#define ADM_CRCI_CTL_MUX_SEL BIT(18)
++#define ADM_CRCI_CTL_RST BIT(17)
++
++/* CI configuration */
++#define ADM_CI_RANGE_END(x) (x << 24)
++#define ADM_CI_RANGE_START(x) (x << 16)
++#define ADM_CI_BURST_4_WORDS BIT(2)
++#define ADM_CI_BURST_8_WORDS BIT(3)
++
++/* GP CTL */
++#define ADM_GP_CTL_LP_EN BIT(12)
++#define ADM_GP_CTL_LP_CNT(x) (x << 8)
++
++/* Command pointer list entry */
++#define ADM_CPLE_LP BIT(31)
++#define ADM_CPLE_CMD_PTR_LIST BIT(29)
++
++/* Command list entry */
++#define ADM_CMD_LC BIT(31)
++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7)
++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3)
++
++#define ADM_CMD_TYPE_SINGLE 0x0
++#define ADM_CMD_TYPE_BOX 0x3
++
++#define ADM_CRCI_MUX_SEL BIT(4)
++#define ADM_DESC_ALIGN 8
++#define ADM_MAX_XFER (SZ_64K-1)
++#define ADM_MAX_ROWS (SZ_64K-1)
++#define ADM_MAX_CHANNELS 16
++
++struct adm_desc_hw_box {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 row_len;
++ u32 num_rows;
++ u32 row_offset;
++};
++
++struct adm_desc_hw_single {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 len;
++};
++
++struct adm_async_desc {
++ struct virt_dma_desc vd;
++ struct adm_device *adev;
++
++ size_t length;
++ enum dma_transfer_direction dir;
++ dma_addr_t dma_addr;
++ size_t dma_len;
++
++ void *cpl;
++ dma_addr_t cp_addr;
++ u32 crci;
++ u32 mux;
++ u32 blk_size;
++};
++
++struct adm_chan {
++ struct virt_dma_chan vc;
++ struct adm_device *adev;
++
++ /* parsed from DT */
++ u32 id; /* channel id */
++
++ struct adm_async_desc *curr_txd;
++ struct dma_slave_config slave;
++ struct list_head node;
++
++ int error;
++ int initialized;
++};
++
++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
++{
++ return container_of(common, struct adm_chan, vc.chan);
++}
++
++struct adm_device {
++ void __iomem *regs;
++ struct device *dev;
++ struct dma_device common;
++ struct device_dma_parameters dma_parms;
++ struct adm_chan *channels;
++
++ u32 ee;
++
++ struct clk *core_clk;
++ struct clk *iface_clk;
++
++ struct reset_control *clk_reset;
++ struct reset_control *c0_reset;
++ struct reset_control *c1_reset;
++ struct reset_control *c2_reset;
++ int irq;
++};
++
++/**
++ * adm_free_chan - Frees dma resources associated with the specific channel
++ *
++ * Free all allocated descriptors associated with this channel
++ *
++ */
++static void adm_free_chan(struct dma_chan *chan)
++{
++ /* free all queued descriptors */
++ vchan_free_chan_resources(to_virt_chan(chan));
++}
++
++/**
++ * adm_get_blksize - Get block size from burst value
++ *
++ */
++static int adm_get_blksize(unsigned int burst)
++{
++ int ret;
++
++ switch (burst) {
++ case 16:
++ case 32:
++ case 64:
++ case 128:
++ ret = ffs(burst>>4) - 1;
++ break;
++ case 192:
++ ret = 4;
++ break;
++ case 256:
++ ret = 5;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++/**
++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @crci: CRCI value
++ * @burst: Burst size of transaction
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg, u32 crci, u32 burst,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_box *box_desc = NULL;
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 rows, row_offset, crci_cmd;
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ crci_cmd = ADM_CMD_SRC_CRCI(crci);
++ row_offset = burst;
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ crci_cmd = ADM_CMD_DST_CRCI(crci);
++ row_offset = burst << 16;
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ while (remainder >= burst) {
++ box_desc = desc;
++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
++ box_desc->row_offset = row_offset;
++ box_desc->src_addr = *src;
++ box_desc->dst_addr = *dst;
++
++ rows = remainder / burst;
++ rows = min_t(u32, rows, ADM_MAX_ROWS);
++ box_desc->num_rows = rows << 16 | rows;
++ box_desc->row_len = burst << 16 | burst;
++
++ *incr_addr += burst * rows;
++ remainder -= burst * rows;
++ desc += sizeof(*box_desc);
++ }
++
++ /* if leftover bytes, do one single descriptor */
++ if (remainder) {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
++ single_desc->len = remainder;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ desc += sizeof(*single_desc);
++
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++ } else {
++ if (box_desc && sg_is_last(sg))
++ box_desc->cmd |= ADM_CMD_LC;
++ }
++
++ return desc;
++}
++
++/**
++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ do {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ single_desc->len = (remainder > ADM_MAX_XFER) ?
++ ADM_MAX_XFER : remainder;
++
++ remainder -= single_desc->len;
++ *incr_addr += single_desc->len;
++ desc += sizeof(*single_desc);
++ } while (remainder);
++
++ /* set last command if this is the end of the whole transaction */
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++
++ return desc;
++}
++
++/**
++ * adm_prep_slave_sg - Prep slave sg transaction
++ *
++ * @chan: dma channel
++ * @sgl: scatter gather list
++ * @sg_len: length of sg
++ * @direction: DMA transfer direction
++ * @flags: DMA flags
++ * @context: transfer context (unused)
++ */
++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
++ struct scatterlist *sgl, unsigned int sg_len,
++ enum dma_transfer_direction direction, unsigned long flags,
++ void *context)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++ struct scatterlist *sg;
++ u32 i, burst;
++ u32 single_count = 0, box_count = 0, crci = 0;
++ void *desc;
++ u32 *cple;
++ int blk_size = 0;
++
++ if (!is_slave_direction(direction)) {
++ dev_err(adev->dev, "invalid dma direction\n");
++ return NULL;
++ }
++
++ /*
++ * get burst value from slave configuration
++ */
++ burst = (direction == DMA_MEM_TO_DEV) ?
++ achan->slave.dst_maxburst :
++ achan->slave.src_maxburst;
++
++ /* if using flow control, validate burst and crci values */
++ if (achan->slave.device_fc) {
++
++ blk_size = adm_get_blksize(burst);
++ if (blk_size < 0) {
++ dev_err(adev->dev, "invalid burst value: %d\n",
++ burst);
++ return ERR_PTR(-EINVAL);
++ }
++
++ crci = achan->slave.slave_id & 0xf;
++ if (!crci || achan->slave.slave_id > 0x1f) {
++ dev_err(adev->dev, "invalid crci value\n");
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
++ /* iterate through sgs and compute allocation size of structures */
++ for_each_sg(sgl, sg, sg_len, i) {
++ if (achan->slave.device_fc) {
++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
++ ADM_MAX_ROWS);
++ if (sg_dma_len(sg) % burst)
++ single_count++;
++ } else {
++ single_count += DIV_ROUND_UP(sg_dma_len(sg),
++ ADM_MAX_XFER);
++ }
++ }
++
++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++ if (!async_desc)
++ return ERR_PTR(-ENOMEM);
++
++ if (crci)
++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
++ ADM_CRCI_CTL_MUX_SEL : 0;
++ async_desc->crci = crci;
++ async_desc->blk_size = blk_size;
++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
++ box_count * sizeof(struct adm_desc_hw_box) +
++ sizeof(*cple) + 2 * ADM_DESC_ALIGN;
++
++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len,
++ &async_desc->dma_addr, GFP_NOWAIT);
++
++ if (!async_desc->cpl) {
++ kfree(async_desc);
++ return ERR_PTR(-ENOMEM);
++ }
++
++ async_desc->adev = adev;
++
++ /* both command list entry and descriptors must be 8 byte aligned */
++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
++
++ /* init cmd list */
++ *cple = ADM_CPLE_LP;
++ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3;
++
++ for_each_sg(sgl, sg, sg_len, i) {
++ async_desc->length += sg_dma_len(sg);
++
++ if (achan->slave.device_fc)
++ desc = adm_process_fc_descriptors(achan, desc, sg, crci,
++ burst, direction);
++ else
++ desc = adm_process_non_fc_descriptors(achan, desc, sg,
++ direction);
++ }
++
++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++}
++
++/**
++ * adm_terminate_all - terminate all transactions on a channel
++ * @achan: adm dma channel
++ *
++ * Dequeues and frees all transactions, aborts current transaction
++ * No callbacks are done
++ *
++ */
++static int adm_terminate_all(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ unsigned long flags;
++ LIST_HEAD(head);
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ vchan_get_all_descriptors(&achan->vc, &head);
++
++ /* send flush command to terminate current transaction */
++ writel_relaxed(0x0,
++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ vchan_dma_desc_free_list(&achan->vc, &head);
++
++ return 0;
++}
++
++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flag;
++
++ spin_lock_irqsave(&achan->vc.lock, flag);
++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
++ spin_unlock_irqrestore(&achan->vc.lock, flag);
++
++ return 0;
++}
++
++/**
++ * adm_start_dma - start next transaction
++ * @achan - ADM dma channel
++ */
++static void adm_start_dma(struct adm_chan *achan)
++{
++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++
++ lockdep_assert_held(&achan->vc.lock);
++
++ if (!vd)
++ return;
++
++ list_del(&vd->node);
++
++ /* write next command list out to the CMD FIFO */
++ async_desc = container_of(vd, struct adm_async_desc, vd);
++ achan->curr_txd = async_desc;
++
++ /* reset channel error */
++ achan->error = 0;
++
++ if (!achan->initialized) {
++ /* enable interrupts */
++ writel(ADM_CH_CONF_SHADOW_EN |
++ ADM_CH_CONF_PERM_MPU_CONF |
++ ADM_CH_CONF_MPU_DISABLE |
++ ADM_CH_CONF_SEC_DOMAIN(adev->ee),
++ adev->regs + ADM_CH_CONF(achan->id));
++
++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ achan->initialized = 1;
++ }
++
++ /* set the crci block size if this transaction requires CRCI */
++ if (async_desc->crci) {
++ writel(async_desc->mux | async_desc->blk_size,
++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
++ }
++
++ /* make sure IRQ enable doesn't get reordered */
++ wmb();
++
++ /* write next command list out to the CMD FIFO */
++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
++}
++
++/**
++ * adm_dma_irq - irq handler for ADM controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t adm_dma_irq(int irq, void *data)
++{
++ struct adm_device *adev = data;
++ u32 srcs, i;
++ struct adm_async_desc *async_desc;
++ unsigned long flags;
++
++ srcs = readl_relaxed(adev->regs +
++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ struct adm_chan *achan = &adev->channels[i];
++ u32 status, result;
++
++ if (srcs & BIT(i)) {
++ status = readl_relaxed(adev->regs +
++ ADM_CH_STATUS_SD(i, adev->ee));
++
++ /* if no result present, skip */
++ if (!(status & ADM_CH_STATUS_VALID))
++ continue;
++
++ result = readl_relaxed(adev->regs +
++ ADM_CH_RSLT(i, adev->ee));
++
++ /* no valid results, skip */
++ if (!(result & ADM_CH_RSLT_VALID))
++ continue;
++
++ /* flag error if transaction was flushed or failed */
++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
++ achan->error = 1;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ async_desc = achan->curr_txd;
++
++ achan->curr_txd = NULL;
++
++ if (async_desc) {
++ vchan_cookie_complete(&async_desc->vd);
++
++ /* kick off next DMA */
++ adm_start_dma(achan);
++ }
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++ }
++ }
++
++ return IRQ_HANDLED;
++}
++
++/**
++ * adm_tx_status - returns status of transaction
++ * @chan: dma channel
++ * @cookie: transaction cookie
++ * @txstate: DMA transaction state
++ *
++ * Return status of dma transaction
++ */
++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++ struct dma_tx_state *txstate)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct virt_dma_desc *vd;
++ enum dma_status ret;
++ unsigned long flags;
++ size_t residue = 0;
++
++ ret = dma_cookie_status(chan, cookie, txstate);
++ if (ret == DMA_COMPLETE || !txstate)
++ return ret;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ vd = vchan_find_desc(&achan->vc, cookie);
++ if (vd)
++ residue = container_of(vd, struct adm_async_desc, vd)->length;
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ /*
++ * residue is either the full length if it is in the issued list, or 0
++ * if it is in progress. We have no reliable way of determining
++ * anything inbetween
++ */
++ dma_set_residue(txstate, residue);
++
++ if (achan->error)
++ return DMA_ERROR;
++
++ return ret;
++}
++
++/**
++ * adm_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Issues all pending transactions and starts DMA
++ */
++static void adm_issue_pending(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flags;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
++ adm_start_dma(achan);
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++}
++
++/**
++ * adm_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void adm_dma_free_desc(struct virt_dma_desc *vd)
++{
++ struct adm_async_desc *async_desc = container_of(vd,
++ struct adm_async_desc, vd);
++
++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len,
++ async_desc->cpl, async_desc->dma_addr);
++ kfree(async_desc);
++}
++
++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
++ u32 index)
++{
++ achan->id = index;
++ achan->adev = adev;
++
++ vchan_init(&achan->vc, &adev->common);
++ achan->vc.desc_free = adm_dma_free_desc;
++}
++
++static int adm_dma_probe(struct platform_device *pdev)
++{
++ struct adm_device *adev;
++ struct resource *iores;
++ int ret;
++ u32 i;
++
++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
++ if (!adev)
++ return -ENOMEM;
++
++ adev->dev = &pdev->dev;
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ adev->regs = devm_ioremap_resource(&pdev->dev, iores);
++ if (IS_ERR(adev->regs))
++ return PTR_ERR(adev->regs);
++
++ adev->irq = platform_get_irq(pdev, 0);
++ if (adev->irq < 0)
++ return adev->irq;
++
++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
++ if (ret) {
++ dev_err(adev->dev, "Execution environment unspecified\n");
++ return ret;
++ }
++
++ adev->core_clk = devm_clk_get(adev->dev, "core");
++ if (IS_ERR(adev->core_clk))
++ return PTR_ERR(adev->core_clk);
++
++ ret = clk_prepare_enable(adev->core_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable core clock\n");
++ return ret;
++ }
++
++ adev->iface_clk = devm_clk_get(adev->dev, "iface");
++ if (IS_ERR(adev->iface_clk)) {
++ ret = PTR_ERR(adev->iface_clk);
++ goto err_disable_core_clk;
++ }
++
++ ret = clk_prepare_enable(adev->iface_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++ goto err_disable_core_clk;
++ }
++
++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
++ if (IS_ERR(adev->clk_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 reset\n");
++ ret = PTR_ERR(adev->clk_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
++ if (IS_ERR(adev->c0_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
++ ret = PTR_ERR(adev->c0_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
++ if (IS_ERR(adev->c1_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
++ ret = PTR_ERR(adev->c1_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
++ if (IS_ERR(adev->c2_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
++ ret = PTR_ERR(adev->c2_reset);
++ goto err_disable_clks;
++ }
++
++ reset_control_assert(adev->clk_reset);
++ reset_control_assert(adev->c0_reset);
++ reset_control_assert(adev->c1_reset);
++ reset_control_assert(adev->c2_reset);
++
++ reset_control_deassert(adev->clk_reset);
++ reset_control_deassert(adev->c0_reset);
++ reset_control_deassert(adev->c1_reset);
++ reset_control_deassert(adev->c2_reset);
++
++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
++ sizeof(*adev->channels), GFP_KERNEL);
++
++ if (!adev->channels) {
++ ret = -ENOMEM;
++ goto err_disable_clks;
++ }
++
++ /* allocate and initialize channels */
++ INIT_LIST_HEAD(&adev->common.channels);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++)
++ adm_channel_init(adev, &adev->channels[i], i);
++
++ /* reset CRCIs */
++ for (i = 0; i < 16; i++)
++ writel(ADM_CRCI_CTL_RST, adev->regs +
++ ADM_CRCI_CTL(i, adev->ee));
++
++ /* configure client interfaces */
++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
++ adev->regs + ADM_GP_CTL);
++
++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++ 0, "adm_dma", adev);
++ if (ret)
++ goto err_disable_clks;
++
++ platform_set_drvdata(pdev, adev);
++
++ adev->common.dev = adev->dev;
++ adev->common.dev->dma_parms = &adev->dma_parms;
++
++ /* set capabilities */
++ dma_cap_zero(adev->common.cap_mask);
++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++ /* initialize dmaengine apis */
++ adev->common.device_free_chan_resources = adm_free_chan;
++ adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++ adev->common.device_issue_pending = adm_issue_pending;
++ adev->common.device_tx_status = adm_tx_status;
++ adev->common.device_terminate_all = adm_terminate_all;
++ adev->common.device_config = adm_slave_config;
++
++ ret = dma_async_device_register(&adev->common);
++ if (ret) {
++ dev_err(adev->dev, "failed to register dma async device\n");
++ goto err_disable_clks;
++ }
++
++ ret = of_dma_controller_register(pdev->dev.of_node,
++ of_dma_xlate_by_chan_id,
++ &adev->common);
++ if (ret)
++ goto err_unregister_dma;
++
++ return 0;
++
++err_unregister_dma:
++ dma_async_device_unregister(&adev->common);
++err_disable_clks:
++ clk_disable_unprepare(adev->iface_clk);
++err_disable_core_clk:
++ clk_disable_unprepare(adev->core_clk);
++
++ return ret;
++}
++
++static int adm_dma_remove(struct platform_device *pdev)
++{
++ struct adm_device *adev = platform_get_drvdata(pdev);
++ struct adm_chan *achan;
++ u32 i;
++
++ of_dma_controller_free(pdev->dev.of_node);
++ dma_async_device_unregister(&adev->common);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ achan = &adev->channels[i];
++
++ /* mask IRQs for this channel/EE pair */
++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ adm_terminate_all(&adev->channels[i].vc.chan);
++ }
++
++ devm_free_irq(adev->dev, adev->irq, adev);
++
++ clk_disable_unprepare(adev->core_clk);
++ clk_disable_unprepare(adev->iface_clk);
++
++ return 0;
++}
++
++static const struct of_device_id adm_of_match[] = {
++ { .compatible = "qcom,adm", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++
++static struct platform_driver adm_dma_driver = {
++ .probe = adm_dma_probe,
++ .remove = adm_dma_remove,
++ .driver = {
++ .name = "adm-dma-engine",
++ .of_match_table = adm_of_match,
++ },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/dma/Makefile
++++ b/drivers/dma/Makefile
+@@ -49,3 +49,4 @@ obj-y += xilinx/
+ obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o
+ obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
+ obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
diff --git a/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
new file mode 100644
index 0000000..657e36a
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
@@ -0,0 +1,42 @@
+From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Wed, 20 May 2015 15:41:07 +0530
+Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node
+
+This patch adds support for the ADM DMA on the IPQ8064 SOC
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -662,6 +662,26 @@
+ };
+ };
+
++ adm_dma: dma@18300000 {
++ compatible = "qcom,adm";
++ reg = <0x18300000 0x100000>;
++ interrupts = <0 170 0>;
++ #dma-cells = <1>;
++
++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
++ clock-names = "core", "iface";
++
++ resets = <&gcc ADM0_RESET>,
++ <&gcc ADM0_PBUS_RESET>,
++ <&gcc ADM0_C0_RESET>,
++ <&gcc ADM0_C1_RESET>,
++ <&gcc ADM0_C2_RESET>;
++ reset-names = "clk", "pbus", "c0", "c1", "c2";
++ qcom,ee = <0>;
++
++ status = "disabled";
++ };
++
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch b/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch
new file mode 100644
index 0000000..77d29d8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch
@@ -0,0 +1,74 @@
+From 4c385b25fab119144bffb255ad77712fe586ac10 Mon Sep 17 00:00:00 2001
+From: Archit Taneja <architt@codeaurora.org>
+Date: Thu, 2 Apr 2015 11:20:41 +0530
+Subject: [PATCH] clk: qcom: Add EBI2 clocks for IPQ806x
+
+The NAND controller within EBI2 requires EBI2_CLK and
+EBI2_ALWAYS_ON_CLK clocks. Create structs for these clocks so
+that they can be used by the NAND controller driver. Add an entry
+for EBI2_AON_CLK in the gcc-ipq806x DT binding document.
+
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 32 ++++++++++++++++++++++++++++
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 1 +
+ 2 files changed, 33 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -2239,6 +2239,36 @@ static struct clk_branch usb_fs1_h_clk =
+ },
+ };
+
++static struct clk_branch ebi2_clk = {
++ .hwcg_reg = 0x3b00,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fcc,
++ .halt_bit = 1,
++ .clkr = {
++ .enable_reg = 0x3b00,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "ebi2_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch ebi2_aon_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 0,
++ .clkr = {
++ .enable_reg = 0x3b00,
++ .enable_mask = BIT(8),
++ .hw.init = &(struct clk_init_data){
++ .name = "ebi2_always_on_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
+ static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [PLL0] = &pll0.clkr,
+ [PLL0_VOTE] = &pll0_vote,
+@@ -2341,6 +2371,8 @@ static struct clk_regmap *gcc_ipq806x_cl
+ [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
+ [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
+ [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,
+--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -289,5 +289,6 @@
+ #define UBI32_CORE2_CLK_SRC 278
+ #define UBI32_CORE1_CLK 279
+ #define UBI32_CORE2_CLK 280
++#define EBI2_AON_CLK 281
+
+ #endif
diff --git a/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
new file mode 100644
index 0000000..3fb4785
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
@@ -0,0 +1,84 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,
+ 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw
+ mode
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927081
+Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>
+Date: Mon, 3 Aug 2015 10:38:14 +0530
+
+Some controllers can access the factory bad block marker from OOB only
+when they read it in raw mode. When ECC is enabled, these controllers
+discard reading/writing bad block markers, preventing access to them
+altogether.
+
+The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
+This results in the nand driver's ecc->read_oob() op to be called, which
+works with ECC enabled.
+
+Create a new BBT option flag that tells nand_bbt to force the mode to
+MTD_OPS_RAW. This would result in the correct op being called for the
+underlying nand controller driver.
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+drivers/mtd/nand/nand_base.c | 6 +++++-
+ drivers/mtd/nand/nand_bbt.c | 6 +++++-
+ include/linux/mtd/bbm.h | 7 +++++++
+ 3 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -396,7 +396,11 @@ static int nand_default_block_markbad(st
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
+- ops.mode = MTD_OPS_PLACE_OOB;
++
++ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
++ ops.mode = MTD_OPS_RAW;
++ else
++ ops.mode = MTD_OPS_PLACE_OOB;
+
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -423,7 +423,11 @@ static int scan_block_fast(struct mtd_in
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+- ops.mode = MTD_OPS_PLACE_OOB;
++
++ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
++ ops.mode = MTD_OPS_RAW;
++ else
++ ops.mode = MTD_OPS_PLACE_OOB;
+
+ for (j = 0; j < numpages; j++) {
+ /*
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -116,6 +116,13 @@ struct nand_bbt_descr {
+ #define NAND_BBT_NO_OOB_BBM 0x00080000
+
+ /*
++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
++ * when in RAW access mode
++ */
++#define NAND_BBT_ACCESS_BBM_RAW 0x00100000
++
++/*
+ * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+ * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+ * in nand_chip.bbt_options.
diff --git a/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
new file mode 100644
index 0000000..6172f7d
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
@@ -0,0 +1,2024 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927101
+Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>
+Date: Mon, 3 Aug 2015 10:38:15 +0530
+
+The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx,
+MDM9x15 series.
+
+It exists as a sub block inside the IPs EBI2 (External Bus Interface 2)
+and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a
+broader interface for external slow peripheral devices such as LCD and
+NAND/NOR flash memory or SRAM like interfaces.
+
+We add support for the NAND controller found within EBI2. For the SoCs
+of our interest, we only use the NAND controller within EBI2. Therefore,
+it's safe for us to assume that the NAND controller is a standalone block
+within the SoC.
+
+The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND
+flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and
+16 bit correction/step) and RS ECC(4 bit correction/step) that covers main
+and spare data. The controller contains an internal 512 byte page buffer
+to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA
+for register read/write and data transfers. The controller performs page
+reads and writes at a codeword/step level of 512 bytes. It can support up
+to 2 external chips of different configurations.
+
+The driver prepares register read and write configuration descriptors for
+each codeword, followed by data descriptors to read or write data from the
+controller's internal buffer. It uses a single ADM DMA channel that we get
+via dmaengine API. The controller requires 2 ADM CRCIs for command and
+data flow control. These are passed via DT.
+
+The ecc layout used by the controller is syndrome like, but we can't use
+the standard syndrome ecc ops because of several reasons. First, the amount
+of data bytes covered by ecc isn't same in each step. Second, writing to
+free oob space requires us writing to the entire step in which the oob
+lies. This forces us to create our own ecc ops.
+
+One more difference is how the controller accesses the bad block marker.
+The controller ignores reading the marker when ECC is enabled. ECC needs
+to be explicity disabled to read or write to the bad block marker. For
+this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to
+read the factory provided bad block markers.
+
+v3:
+- Refactor dma functions for maximum reuse
+- Use dma_slave_confing on stack
+- optimize and clean upempty_page_fixup using memchr_inv
+- ensure portability with dma register reads using le32_* funcs
+- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves
+- fix handling of return values of dmaengine funcs
+- constify wherever possible
+- Remove dependency on ADM DMA in Kconfig
+- Misc fixes and clean ups
+
+v2:
+- Use new BBT flag that allows us to read BBM in raw mode
+- reduce memcpy-s in the driver
+- some refactor and clean ups because of above changes
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1921 insertions(+)
+ create mode 100644 drivers/mtd/nand/qcom_nandc.c
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -516,4 +516,11 @@ config MTD_NAND_XWAY
+ Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ to the External Bus Unit (EBU).
+
++config MTD_NAND_QCOM
++ tristate "Support for NAND on QCOM SoCs"
++ depends on ARCH_QCOM
++ help
++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND
++ controller. This controller is found on IPQ806x SoC.
++
+ endif # MTD_NAND
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nandc.c
+@@ -0,0 +1,1918 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/slab.h>
++#include <linux/bitops.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmaengine.h>
++#include <linux/module.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_mtd.h>
++#include <linux/delay.h>
++
++/* NANDc reg offsets */
++#define NAND_FLASH_CMD 0x00
++#define NAND_ADDR0 0x04
++#define NAND_ADDR1 0x08
++#define NAND_FLASH_CHIP_SELECT 0x0c
++#define NAND_EXEC_CMD 0x10
++#define NAND_FLASH_STATUS 0x14
++#define NAND_BUFFER_STATUS 0x18
++#define NAND_DEV0_CFG0 0x20
++#define NAND_DEV0_CFG1 0x24
++#define NAND_DEV0_ECC_CFG 0x28
++#define NAND_DEV1_ECC_CFG 0x2c
++#define NAND_DEV1_CFG0 0x30
++#define NAND_DEV1_CFG1 0x34
++#define NAND_READ_ID 0x40
++#define NAND_READ_STATUS 0x44
++#define NAND_DEV_CMD0 0xa0
++#define NAND_DEV_CMD1 0xa4
++#define NAND_DEV_CMD2 0xa8
++#define NAND_DEV_CMD_VLD 0xac
++#define SFLASHC_BURST_CFG 0xe0
++#define NAND_ERASED_CW_DETECT_CFG 0xe8
++#define NAND_ERASED_CW_DETECT_STATUS 0xec
++#define NAND_EBI2_ECC_BUF_CFG 0xf0
++#define FLASH_BUF_ACC 0x100
++
++#define NAND_CTRL 0xf00
++#define NAND_VERSION 0xf08
++#define NAND_READ_LOCATION_0 0xf20
++#define NAND_READ_LOCATION_1 0xf24
++
++/* dummy register offsets, used by write_reg_dma */
++#define NAND_DEV_CMD1_RESTORE 0xdead
++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef
++
++/* NAND_FLASH_CMD bits */
++#define PAGE_ACC BIT(4)
++#define LAST_PAGE BIT(5)
++
++/* NAND_FLASH_CHIP_SELECT bits */
++#define NAND_DEV_SEL 0
++#define DM_EN BIT(2)
++
++/* NAND_FLASH_STATUS bits */
++#define FS_OP_ERR BIT(4)
++#define FS_READY_BSY_N BIT(5)
++#define FS_MPU_ERR BIT(8)
++#define FS_DEVICE_STS_ERR BIT(16)
++#define FS_DEVICE_WP BIT(23)
++
++/* NAND_BUFFER_STATUS bits */
++#define BS_UNCORRECTABLE_BIT BIT(8)
++#define BS_CORRECTABLE_ERR_MSK 0x1f
++
++/* NAND_DEVn_CFG0 bits */
++#define DISABLE_STATUS_AFTER_WRITE 4
++#define CW_PER_PAGE 6
++#define UD_SIZE_BYTES 9
++#define ECC_PARITY_SIZE_BYTES_RS 19
++#define SPARE_SIZE_BYTES 23
++#define NUM_ADDR_CYCLES 27
++#define STATUS_BFR_READ 30
++#define SET_RD_MODE_AFTER_STATUS 31
++
++/* NAND_DEVn_CFG0 bits */
++#define DEV0_CFG1_ECC_DISABLE 0
++#define WIDE_FLASH 1
++#define NAND_RECOVERY_CYCLES 2
++#define CS_ACTIVE_BSY 5
++#define BAD_BLOCK_BYTE_NUM 6
++#define BAD_BLOCK_IN_SPARE_AREA 16
++#define WR_RD_BSY_GAP 17
++#define ENABLE_BCH_ECC 27
++
++/* NAND_DEV0_ECC_CFG bits */
++#define ECC_CFG_ECC_DISABLE 0
++#define ECC_SW_RESET 1
++#define ECC_MODE 4
++#define ECC_PARITY_SIZE_BYTES_BCH 8
++#define ECC_NUM_DATA_BYTES 16
++#define ECC_FORCE_CLK_OPEN 30
++
++/* NAND_DEV_CMD1 bits */
++#define READ_ADDR 0
++
++/* NAND_DEV_CMD_VLD bits */
++#define READ_START_VLD 0
++
++/* NAND_EBI2_ECC_BUF_CFG bits */
++#define NUM_STEPS 0
++
++/* NAND_ERASED_CW_DETECT_CFG bits */
++#define ERASED_CW_ECC_MASK 1
++#define AUTO_DETECT_RES 0
++#define MASK_ECC (1 << ERASED_CW_ECC_MASK)
++#define RESET_ERASED_DET (1 << AUTO_DETECT_RES)
++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES)
++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC)
++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC)
++
++/* NAND_ERASED_CW_DETECT_STATUS bits */
++#define PAGE_ALL_ERASED BIT(7)
++#define CODEWORD_ALL_ERASED BIT(6)
++#define PAGE_ERASED BIT(5)
++#define CODEWORD_ERASED BIT(4)
++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
++
++/* Version Mask */
++#define NAND_VERSION_MAJOR_MASK 0xf0000000
++#define NAND_VERSION_MAJOR_SHIFT 28
++#define NAND_VERSION_MINOR_MASK 0x0fff0000
++#define NAND_VERSION_MINOR_SHIFT 16
++
++/* NAND OP_CMDs */
++#define PAGE_READ 0x2
++#define PAGE_READ_WITH_ECC 0x3
++#define PAGE_READ_WITH_ECC_SPARE 0x4
++#define PROGRAM_PAGE 0x6
++#define PAGE_PROGRAM_WITH_ECC 0x7
++#define PROGRAM_PAGE_SPARE 0x9
++#define BLOCK_ERASE 0xa
++#define FETCH_ID 0xb
++#define RESET_DEVICE 0xd
++
++/*
++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
++ * the driver calls the chunks 'step' or 'codeword' interchangeably
++ */
++#define NANDC_STEP_SIZE 512
++
++/*
++ * the largest page size we support is 8K, this will have 16 steps/codewords
++ * of 512 bytes each
++ */
++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE)
++
++/* we read at most 3 registers per codeword scan */
++#define MAX_REG_RD (3 * MAX_NUM_STEPS)
++
++/* ECC modes */
++#define ECC_NONE BIT(0)
++#define ECC_RS_4BIT BIT(1)
++#define ECC_BCH_4BIT BIT(2)
++#define ECC_BCH_8BIT BIT(3)
++
++struct desc_info {
++ struct list_head list;
++
++ enum dma_transfer_direction dir;
++ struct scatterlist sgl;
++ struct dma_async_tx_descriptor *dma_desc;
++};
++
++/*
++ * holds the current register values that we want to write. acts as a contiguous
++ * chunk of memory which we use to write the controller registers through DMA.
++ */
++struct nandc_regs {
++ u32 cmd;
++ u32 addr0;
++ u32 addr1;
++ u32 chip_sel;
++ u32 exec;
++
++ u32 cfg0;
++ u32 cfg1;
++ u32 ecc_bch_cfg;
++
++ u32 clrflashstatus;
++ u32 clrreadstatus;
++
++ u32 cmd1;
++ u32 vld;
++
++ u32 orig_cmd1;
++ u32 orig_vld;
++
++ u32 ecc_buf_cfg;
++};
++
++/*
++ * @cmd_crci: ADM DMA CRCI for command flow control
++ * @data_crci: ADM DMA CRCI for data flow control
++ * @list: DMA descriptor list (list of desc_infos)
++ * @dma_done: completion param to denote end of last
++ * descriptor in the list
++ * @data_buffer: our local DMA buffer for page read/writes,
++ * used when we can't use the buffer provided
++ * by upper layers directly
++ * @buf_size/count/start: markers for chip->read_buf/write_buf functions
++ * @reg_read_buf: buffer for reading register data via DMA
++ * @reg_read_pos: marker for data read in reg_read_buf
++ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
++ * ecc/non-ecc mode for the current nand flash
++ * device
++ * @regs: a contiguous chunk of memory for DMA register
++ * writes
++ * @ecc_strength: 4 bit or 8 bit ecc, received via DT
++ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT
++ * @ecc_modes: supported ECC modes by the current controller,
++ * initialized via DT match data
++ * @cw_size: the number of bytes in a single step/codeword
++ * of a page, consisting of all data, ecc, spare
++ * and reserved bytes
++ * @cw_data: the number of bytes within a codeword protected
++ * by ECC
++ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used
++ * @status: value to be returned if NAND_CMD_STATUS command
++ * is executed
++ */
++struct qcom_nandc_data {
++ struct platform_device *pdev;
++ struct device *dev;
++
++ void __iomem *base;
++ struct resource *res;
++
++ struct clk *core_clk;
++ struct clk *aon_clk;
++
++ /* DMA stuff */
++ struct dma_chan *chan;
++ struct dma_slave_config slave_conf;
++ unsigned int cmd_crci;
++ unsigned int data_crci;
++ struct list_head list;
++ struct completion dma_done;
++
++ /* MTD stuff */
++ struct nand_chip chip;
++ struct mtd_info mtd;
++
++ /* local data buffer and markers */
++ u8 *data_buffer;
++ int buf_size;
++ int buf_count;
++ int buf_start;
++
++ /* local buffer to read back registers */
++ u32 *reg_read_buf;
++ int reg_read_pos;
++
++ /* required configs */
++ u32 cfg0, cfg1;
++ u32 cfg0_raw, cfg1_raw;
++ u32 ecc_buf_cfg;
++ u32 ecc_bch_cfg;
++ u32 clrflashstatus;
++ u32 clrreadstatus;
++ u32 sflashc_burst_cfg;
++ u32 cmd1, vld;
++
++ /* register state */
++ struct nandc_regs *regs;
++
++ /* things we get from DT */
++ int ecc_strength;
++ int bus_width;
++
++ u32 ecc_modes;
++
++ /* misc params */
++ int cw_size;
++ int cw_data;
++ bool use_ecc;
++ bool bch_enabled;
++ u8 status;
++ int last_command;
++};
++
++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset)
++{
++ return ioread32(this->base + offset);
++}
++
++static inline void nandc_write(struct qcom_nandc_data *this, int offset,
++ u32 val)
++{
++ iowrite32(val, this->base + offset);
++}
++
++/* helper to configure address register values */
++static void set_address(struct qcom_nandc_data *this, u16 column, int page)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nandc_regs *regs = this->regs;
++
++ if (chip->options & NAND_BUSWIDTH_16)
++ column >>= 1;
++
++ regs->addr0 = page << 16 | column;
++ regs->addr1 = page >> 16 & 0xff;
++}
++
++/*
++ * update_rw_regs: set up read/write register values, these will be
++ * written to the NAND controller registers via DMA
++ *
++ * @num_cw: number of steps for the read/write operation
++ * @read: read or write operation
++ */
++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read)
++{
++ struct nandc_regs *regs = this->regs;
++
++ if (read) {
++ if (this->use_ecc)
++ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
++ else
++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++ } else {
++ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
++ }
++
++ if (this->use_ecc) {
++ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) |
++ (num_cw - 1) << CW_PER_PAGE;
++
++ regs->cfg1 = this->cfg1;
++ regs->ecc_bch_cfg = this->ecc_bch_cfg;
++ } else {
++ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++ (num_cw - 1) << CW_PER_PAGE;
++
++ regs->cfg1 = this->cfg1_raw;
++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++ }
++
++ regs->ecc_buf_cfg = this->ecc_buf_cfg;
++ regs->clrflashstatus = this->clrflashstatus;
++ regs->clrreadstatus = this->clrreadstatus;
++ regs->exec = 1;
++}
++
++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off,
++ const void *vaddr, int size, bool flow_control)
++{
++ struct desc_info *desc;
++ struct dma_async_tx_descriptor *dma_desc;
++ struct scatterlist *sgl;
++ struct dma_slave_config slave_conf;
++ int r;
++
++ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++ if (!desc)
++ return -ENOMEM;
++
++ list_add_tail(&desc->list, &this->list);
++
++ sgl = &desc->sgl;
++
++ sg_init_one(sgl, vaddr, size);
++
++ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
++
++ r = dma_map_sg(this->dev, sgl, 1, desc->dir);
++ if (r == 0) {
++ r = -ENOMEM;
++ goto err;
++ }
++
++ memset(&slave_conf, 0x00, sizeof(slave_conf));
++
++ slave_conf.device_fc = flow_control;
++ if (read) {
++ slave_conf.src_maxburst = 16;
++ slave_conf.src_addr = this->res->start + reg_off;
++ slave_conf.slave_id = this->data_crci;
++ } else {
++ slave_conf.dst_maxburst = 16;
++ slave_conf.dst_addr = this->res->start + reg_off;
++ slave_conf.slave_id = this->cmd_crci;
++ }
++
++ r = dmaengine_slave_config(this->chan, &slave_conf);
++ if (r) {
++ dev_err(this->dev, "failed to configure dma channel\n");
++ goto err;
++ }
++
++ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0);
++ if (!dma_desc) {
++ dev_err(this->dev, "failed to prepare desc\n");
++ r = -EINVAL;
++ goto err;
++ }
++
++ desc->dma_desc = dma_desc;
++
++ return 0;
++err:
++ kfree(desc);
++
++ return r;
++}
++
++/*
++ * read_reg_dma: prepares a descriptor to read a given number of
++ * contiguous registers to the reg_read_buf pointer
++ *
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to read
++ */
++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++ bool flow_control = false;
++ void *vaddr;
++ int size;
++
++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
++ flow_control = true;
++
++ size = num_regs * sizeof(u32);
++ vaddr = this->reg_read_buf + this->reg_read_pos;
++ this->reg_read_pos += num_regs;
++
++ return prep_dma_desc(this, true, first, vaddr, size, flow_control);
++}
++
++/*
++ * write_reg_dma: prepares a descriptor to write a given number of
++ * contiguous registers
++ *
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to write
++ */
++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++ bool flow_control = false;
++ struct nandc_regs *regs = this->regs;
++ void *vaddr;
++ int size;
++
++ switch (first) {
++ case NAND_FLASH_CMD:
++ vaddr = &regs->cmd;
++ flow_control = true;
++ break;
++ case NAND_EXEC_CMD:
++ vaddr = &regs->exec;
++ break;
++ case NAND_FLASH_STATUS:
++ vaddr = &regs->clrflashstatus;
++ break;
++ case NAND_DEV0_CFG0:
++ vaddr = &regs->cfg0;
++ break;
++ case NAND_READ_STATUS:
++ vaddr = &regs->clrreadstatus;
++ break;
++ case NAND_DEV_CMD1:
++ vaddr = &regs->cmd1;
++ break;
++ case NAND_DEV_CMD1_RESTORE:
++ first = NAND_DEV_CMD1;
++ vaddr = &regs->orig_cmd1;
++ break;
++ case NAND_DEV_CMD_VLD:
++ vaddr = &regs->vld;
++ break;
++ case NAND_DEV_CMD_VLD_RESTORE:
++ first = NAND_DEV_CMD_VLD;
++ vaddr = &regs->orig_vld;
++ break;
++ case NAND_EBI2_ECC_BUF_CFG:
++ vaddr = &regs->ecc_buf_cfg;
++ break;
++ default:
++ dev_err(this->dev, "invalid starting register\n");
++ return -EINVAL;
++ }
++
++ size = num_regs * sizeof(u32);
++
++ return prep_dma_desc(this, false, first, vaddr, size, flow_control);
++}
++
++/*
++ * read_data_dma: prepares a DMA descriptor to transfer data from the
++ * controller's internal buffer to the buffer 'vaddr'
++ *
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: DMA transaction size in bytes
++ */
++static int read_data_dma(struct qcom_nandc_data *this, int reg_off,
++ const u8 *vaddr, int size)
++{
++ return prep_dma_desc(this, true, reg_off, vaddr, size, false);
++}
++
++/*
++ * write_data_dma: prepares a DMA descriptor to transfer data from
++ * 'vaddr' to the controller's internal buffer
++ *
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to read from
++ * @size: DMA transaction size in bytes
++ */
++static int write_data_dma(struct qcom_nandc_data *this, int reg_off,
++ const u8 *vaddr, int size)
++{
++ return prep_dma_desc(this, false, reg_off, vaddr, size, false);
++}
++
++/*
++ * helper to prepare dma descriptors to configure registers needed for reading a
++ * codeword/step in a page
++ */
++static void config_cw_read(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 3);
++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 2);
++ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1);
++}
++
++/*
++ * helpers to prepare dma descriptors used to configure registers needed for
++ * writing a codeword/step in a page
++ */
++static void config_cw_write_pre(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 3);
++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++}
++
++static void config_cw_write_post(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ write_reg_dma(this, NAND_FLASH_STATUS, 1);
++ write_reg_dma(this, NAND_READ_STATUS, 1);
++}
++
++/*
++ * the following functions are used within chip->cmdfunc() to perform different
++ * NAND_CMD_* commands
++ */
++
++/* sets up descriptors for NAND_CMD_PARAM */
++static int nandc_param(struct qcom_nandc_data *this)
++{
++ struct nandc_regs *regs = this->regs;
++
++ /*
++ * NAND_CMD_PARAM is called before we know much about the FLASH chip
++ * in use. we configure the controller to perform a raw read of 512
++ * bytes to read onfi params
++ */
++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++ regs->addr0 = 0;
++ regs->addr1 = 0;
++ regs->cfg0 = 0 << CW_PER_PAGE
++ | 512 << UD_SIZE_BYTES
++ | 5 << NUM_ADDR_CYCLES
++ | 0 << SPARE_SIZE_BYTES;
++
++ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | 17 << BAD_BLOCK_BYTE_NUM
++ | 1 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | 0 << WIDE_FLASH
++ | 1 << DEV0_CFG1_ECC_DISABLE;
++
++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++
++ /* configure CMD1 and VLD for ONFI param probing */
++ regs->vld = (this->vld & ~(1 << READ_START_VLD))
++ | 0 << READ_START_VLD;
++
++ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR))
++ | NAND_CMD_PARAM << READ_ADDR;
++
++ regs->exec = 1;
++
++ regs->orig_cmd1 = this->cmd1;
++ regs->orig_vld = this->vld;
++
++ write_reg_dma(this, NAND_DEV_CMD_VLD, 1);
++ write_reg_dma(this, NAND_DEV_CMD1, 1);
++
++ this->buf_count = 512;
++ memset(this->data_buffer, 0xff, this->buf_count);
++
++ config_cw_read(this);
++
++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count);
++
++ /* restore CMD1 and VLD regs */
++ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1);
++ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_ERASE1 */
++static int erase_block(struct qcom_nandc_data *this, int page_addr)
++{
++ struct nandc_regs *regs = this->regs;
++
++ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE;
++ regs->addr0 = page_addr;
++ regs->addr1 = 0;
++ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE);
++ regs->cfg1 = this->cfg1_raw;
++ regs->exec = 1;
++ regs->clrflashstatus = this->clrflashstatus;
++ regs->clrreadstatus = this->clrreadstatus;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 2);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ write_reg_dma(this, NAND_FLASH_STATUS, 1);
++ write_reg_dma(this, NAND_READ_STATUS, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_READID */
++static int read_id(struct qcom_nandc_data *this, int column)
++{
++ struct nandc_regs *regs = this->regs;
++
++ if (column == -1)
++ return 0;
++
++ regs->cmd = FETCH_ID;
++ regs->addr0 = column;
++ regs->addr1 = 0;
++ regs->chip_sel = DM_EN;
++ regs->exec = 1;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 4);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_READ_ID, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_RESET */
++static int reset(struct qcom_nandc_data *this)
++{
++ struct nandc_regs *regs = this->regs;
++
++ regs->cmd = RESET_DEVICE;
++ regs->exec = 1;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 1);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ return 0;
++}
++
++/* helpers to submit/free our list of dma descriptors */
++static void dma_callback(void *param)
++{
++ struct qcom_nandc_data *this = param;
++ struct completion *c = &this->dma_done;
++
++ complete(c);
++}
++
++static int submit_descs(struct qcom_nandc_data *this)
++{
++ struct completion *c = &this->dma_done;
++ struct desc_info *desc;
++ int r;
++
++ init_completion(c);
++
++ list_for_each_entry(desc, &this->list, list) {
++ /*
++ * we add a callback to the last descriptor in our list to
++ * notify completion of command
++ */
++ if (list_is_last(&desc->list, &this->list)) {
++ desc->dma_desc->callback = dma_callback;
++ desc->dma_desc->callback_param = this;
++ }
++
++ dmaengine_submit(desc->dma_desc);
++ }
++
++ dma_async_issue_pending(this->chan);
++
++ r = wait_for_completion_timeout(c, msecs_to_jiffies(500));
++ if (!r)
++ return -ETIMEDOUT;
++
++ return 0;
++}
++
++static void free_descs(struct qcom_nandc_data *this)
++{
++ struct desc_info *desc, *n;
++
++ list_for_each_entry_safe(desc, n, &this->list, list) {
++ list_del(&desc->list);
++ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir);
++ kfree(desc);
++ }
++}
++
++/* reset the register read buffer for next NAND operation */
++static void clear_read_regs(struct qcom_nandc_data *this)
++{
++ this->reg_read_pos = 0;
++ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf));
++}
++
++static void pre_command(struct qcom_nandc_data *this, int command)
++{
++ this->buf_count = 0;
++ this->buf_start = 0;
++ this->use_ecc = false;
++ this->last_command = command;
++
++ clear_read_regs(this);
++}
++
++/*
++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
++ * privately maintained status byte, this status byte can be read after
++ * NAND_CMD_STATUS is called
++ */
++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int num_cw;
++ int i;
++
++ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
++
++ for (i = 0; i < num_cw; i++) {
++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]);
++
++ if (flash_status & FS_MPU_ERR)
++ this->status &= ~NAND_STATUS_WP;
++
++ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
++ (flash_status & FS_DEVICE_STS_ERR)))
++ this->status |= NAND_STATUS_FAIL;
++ }
++}
++
++static void post_command(struct qcom_nandc_data *this, int command)
++{
++ switch (command) {
++ case NAND_CMD_READID:
++ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count);
++ break;
++ case NAND_CMD_PAGEPROG:
++ case NAND_CMD_ERASE1:
++ parse_erase_write_errors(this, command);
++ break;
++ default:
++ break;
++ }
++}
++
++/*
++ * Implements chip->cmdfunc. It's only used for a limited set of commands.
++ * The rest of the commands wouldn't be called by upper layers. For example,
++ * NAND_CMD_READOOB would never be called because we have our own versions
++ * of read_oob ops for nand_ecc_ctrl.
++ */
++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
++ int column, int page_addr)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ struct qcom_nandc_data *this = chip->priv;
++ bool wait = false;
++ int r = 0;
++
++ pre_command(this, command);
++
++ switch (command) {
++ case NAND_CMD_RESET:
++ r = reset(this);
++ wait = true;
++ break;
++
++ case NAND_CMD_READID:
++ this->buf_count = 4;
++ r = read_id(this, column);
++ wait = true;
++ break;
++
++ case NAND_CMD_PARAM:
++ r = nandc_param(this);
++ wait = true;
++ break;
++
++ case NAND_CMD_ERASE1:
++ r = erase_block(this, page_addr);
++ wait = true;
++ break;
++
++ case NAND_CMD_READ0:
++ /* we read the entire page for now */
++ WARN_ON(column != 0);
++
++ this->use_ecc = true;
++ set_address(this, 0, page_addr);
++ update_rw_regs(this, ecc->steps, true);
++ break;
++
++ case NAND_CMD_SEQIN:
++ WARN_ON(column != 0);
++ set_address(this, 0, page_addr);
++ break;
++
++ case NAND_CMD_PAGEPROG:
++ case NAND_CMD_STATUS:
++ case NAND_CMD_NONE:
++ default:
++ break;
++ }
++
++ if (r) {
++ dev_err(this->dev, "failure executing command %d\n",
++ command);
++ free_descs(this);
++ return;
++ }
++
++ if (wait) {
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev,
++ "failure submitting descs for command %d\n",
++ command);
++ }
++
++ free_descs(this);
++
++ post_command(this, command);
++}
++
++/*
++ * when using RS ECC, the NAND controller flags an error when reading an
++ * erased page. however, there are special characters at certain offsets when
++ * we read the erased page. we check here if the page is really empty. if so,
++ * we replace the magic characters with 0xffs
++ */
++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = ecc->steps;
++ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS];
++ int i, j;
++
++ /* if BCH is enabled, HW will take care of detecting erased pages */
++ if (this->bch_enabled || !this->use_ecc)
++ return false;
++
++ for (i = 0; i < cwperpage; i++) {
++ u8 *empty1, *empty2;
++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]);
++
++ /*
++ * an erased page flags an error in NAND_FLASH_STATUS, check if
++ * the page is erased by looking for 0x54s at offsets 3 and 175
++ * from the beginning of each codeword
++ */
++ if (!(flash_status & FS_OP_ERR))
++ break;
++
++ empty1 = &data_buf[3 + i * this->cw_data];
++ empty2 = &data_buf[175 + i * this->cw_data];
++
++ /*
++ * if the error wasn't because of an erased page, bail out and
++ * and let someone else do the error checking
++ */
++ if ((*empty1 == 0x54 && *empty2 == 0xff) ||
++ (*empty1 == 0xff && *empty2 == 0x54)) {
++ orig1[i] = *empty1;
++ orig2[i] = *empty2;
++
++ *empty1 = 0xff;
++ *empty2 = 0xff;
++ } else {
++ break;
++ }
++ }
++
++ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize))
++ goto not_empty;
++
++ /*
++ * tell the caller that the page was empty and is fixed up, so that
++ * parse_read_errors() doesn't think it's an error
++ */
++ return true;
++
++not_empty:
++ /* restore original values if not empty*/
++ for (j = 0; j < i; j++) {
++ data_buf[3 + j * this->cw_data] = orig1[j];
++ data_buf[175 + j * this->cw_data] = orig2[j];
++ }
++
++ return false;
++}
++
++struct read_stats {
++ __le32 flash;
++ __le32 buffer;
++ __le32 erased_cw;
++};
++
++/*
++ * reads back status registers set by the controller to notify page read
++ * errors. this is equivalent to what 'ecc->correct()' would do.
++ */
++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = ecc->steps;
++ unsigned int max_bitflips = 0;
++ int i;
++
++ for (i = 0; i < cwperpage; i++) {
++ int stat;
++ struct read_stats *buf;
++
++ buf = (struct read_stats *) (this->reg_read_buf + 3 * i);
++
++ buf->flash = le32_to_cpu(buf->flash);
++ buf->buffer = le32_to_cpu(buf->buffer);
++ buf->erased_cw = le32_to_cpu(buf->erased_cw);
++
++ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) {
++
++ /* ignore erased codeword errors */
++ if (this->bch_enabled) {
++ if ((buf->erased_cw & ERASED_CW) == ERASED_CW)
++ continue;
++ } else if (erased_page) {
++ continue;
++ }
++
++ if (buf->buffer & BS_UNCORRECTABLE_BIT) {
++ mtd->ecc_stats.failed++;
++ continue;
++ }
++ }
++
++ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK;
++ mtd->ecc_stats.corrected += stat;
++
++ max_bitflips = max_t(unsigned int, max_bitflips, stat);
++ }
++
++ return max_bitflips;
++}
++
++/*
++ * helper to perform the actual page read operation, used by ecc->read_page()
++ * and ecc->read_oob()
++ */
++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf,
++ u8 *oob_buf)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int i, r;
++
++ /* queue cmd descs for each codeword */
++ for (i = 0; i < ecc->steps; i++) {
++ int data_size, oob_size;
++
++ if (i == (ecc->steps - 1)) {
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++ } else {
++ data_size = this->cw_data;
++ oob_size = ecc->bytes;
++ }
++
++ config_cw_read(this);
++
++ if (data_buf)
++ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++ if (oob_buf)
++ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf,
++ oob_size);
++
++ if (data_buf)
++ data_buf += data_size;
++ if (oob_buf)
++ oob_buf += oob_size;
++ }
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failure to read page/oob\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/*
++ * a helper that copies the last step/codeword of a page (containing free oob)
++ * into our local buffer
++ */
++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int size;
++ int r;
++
++ clear_read_regs(this);
++
++ size = use_ecc ? this->cw_data : this->cw_size;
++
++ /* prepare a clean read buffer */
++ memset(this->data_buffer, 0xff, size);
++
++ this->use_ecc = use_ecc;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, true);
++
++ config_cw_read(this);
++
++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size);
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failed to copy last codeword\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/* implements ecc->read_page() */
++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
++ uint8_t *buf, int oob_required, int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ u8 *data_buf, *oob_buf = NULL;
++ bool erased_page;
++ int r;
++
++ data_buf = buf;
++ oob_buf = oob_required ? chip->oob_poi : NULL;
++
++ r = read_page_low(this, data_buf, oob_buf);
++ if (r) {
++ dev_err(this->dev, "failure to read page\n");
++ return r;
++ }
++
++ erased_page = empty_page_fixup(this, data_buf);
++
++ return parse_read_errors(this, erased_page);
++}
++
++/* implements ecc->read_oob() */
++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int r;
++
++ clear_read_regs(this);
++
++ this->use_ecc = true;
++ set_address(this, 0, page);
++ update_rw_regs(this, ecc->steps, true);
++
++ r = read_page_low(this, NULL, chip->oob_poi);
++ if (r)
++ dev_err(this->dev, "failure to read oob\n");
++
++ return r;
++}
++
++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */
++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int start, length;
++ int r;
++
++ /*
++ * configure registers for a raw page read, the address is set to the
++ * beginning of the last codeword, we don't care about reading ecc
++ * portion of oob, just the free stuff
++ */
++ r = copy_last_cw(this, false, page);
++ if (r)
++ return r;
++
++ /*
++ * reading raw oob has 2 parts, first the bad block byte, then the
++ * actual free oob region. perform a memcpy in two steps
++ */
++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++ memcpy(oob, this->data_buffer + start, length);
++
++ oob += length;
++
++ start = this->cw_data - (ecc->steps << 2) + 1;
++ length = ecc->steps << 2;
++
++ memcpy(oob, this->data_buffer + start, length);
++
++ return 0;
++}
++
++/* implements ecc->write_page() */
++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++ const uint8_t *buf, int oob_required)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ u8 *data_buf, *oob_buf;
++ int i, r = 0;
++
++ clear_read_regs(this);
++
++ data_buf = (u8 *) buf;
++ oob_buf = chip->oob_poi;
++
++ this->use_ecc = true;
++ update_rw_regs(this, ecc->steps, false);
++
++ for (i = 0; i < ecc->steps; i++) {
++ int data_size, oob_size;
++
++ if (i == (ecc->steps - 1)) {
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++ } else {
++ data_size = this->cw_data;
++ oob_size = ecc->bytes;
++ }
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++ /*
++ * we don't really need to write anything to oob for the
++ * first n - 1 codewords since these oob regions just
++ * contain ecc that's written by the controller itself
++ */
++ if (i == (ecc->steps - 1))
++ write_data_dma(this, FLASH_BUF_ACC + data_size,
++ oob_buf, oob_size);
++ config_cw_write_post(this);
++
++ data_buf += data_size;
++ oob_buf += oob_size;
++ }
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failure to write page\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/*
++ * implements ecc->write_oob()
++ *
++ * the NAND controller cannot write only data or only oob within a codeword,
++ * since ecc is calculated for the combined codeword. we first copy the
++ * entire contents for the last codeword(data + oob), replace the old oob
++ * with the new one in chip->oob_poi, and then write the entire codeword.
++ * this read-copy-write operation results in a slight perormance loss.
++ */
++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int free_boff;
++ int data_size, oob_size;
++ int r, status = 0;
++
++ r = copy_last_cw(this, true, page);
++ if (r)
++ return r;
++
++ clear_read_regs(this);
++
++ /* calculate the data and oob size for the last codeword/step */
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++
++ /*
++ * the location of spare data in the oob buffer, we could also use
++ * ecc->layout.oobfree here
++ */
++ free_boff = ecc->bytes * (ecc->steps - 1);
++
++ /* override new oob content to last codeword */
++ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size);
++
++ this->use_ecc = true;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, false);
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer,
++ data_size + oob_size);
++ config_cw_write_post(this);
++
++ r = submit_descs(this);
++
++ free_descs(this);
++
++ if (r) {
++ dev_err(this->dev, "failure to write oob\n");
++ return -EIO;
++ }
++
++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ status = chip->waitfunc(mtd, chip);
++
++ return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/* implements ecc->write_oob_raw(), used to write bad block marker flag */
++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd,
++ struct nand_chip *chip, int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int start, length;
++ int r, status = 0;
++
++ r = copy_last_cw(this, false, page);
++ if (r)
++ return r;
++
++ clear_read_regs(this);
++
++ /*
++ * writing raw oob has 2 parts, first the bad block region, then the
++ * actual free region
++ */
++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++ memcpy(this->data_buffer + start, oob, length);
++
++ oob += length;
++
++ start = this->cw_data - (ecc->steps << 2) + 1;
++ length = ecc->steps << 2;
++
++ memcpy(this->data_buffer + start, oob, length);
++
++ /* prepare write */
++ this->use_ecc = false;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, false);
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size);
++ config_cw_write_post(this);
++
++ r = submit_descs(this);
++
++ free_descs(this);
++
++ if (r) {
++ dev_err(this->dev, "failure to write updated oob\n");
++ return -EIO;
++ }
++
++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ status = chip->waitfunc(mtd, chip);
++
++ return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/*
++ * the three functions below implement chip->read_byte(), chip->read_buf()
++ * and chip->write_buf() respectively. these aren't used for
++ * reading/writing page data, they are used for smaller data like reading
++ * id, status etc
++ */
++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ uint8_t *buf = this->data_buffer;
++ uint8_t ret = 0x0;
++
++ if (this->last_command == NAND_CMD_STATUS) {
++ ret = this->status;
++
++ this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++
++ return ret;
++ }
++
++ if (this->buf_start < this->buf_count)
++ ret = buf[this->buf_start++];
++
++ return ret;
++}
++
++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++ memcpy(buf, this->data_buffer + this->buf_start, real_len);
++ this->buf_start += real_len;
++}
++
++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
++ int len)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++ memcpy(this->data_buffer + this->buf_start, buf, real_len);
++
++ this->buf_start += real_len;
++}
++
++/* we support only one external chip for now */
++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++
++ if (chipnr <= 0)
++ return;
++
++ dev_warn(this->dev, "invalid chip select\n");
++}
++
++/*
++ * NAND controller page layout info
++ *
++ * |-----------------------| |---------------------------------|
++ * | xx.......xx| | *********xx.......xx|
++ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx|
++ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx|
++ * | xx.......xx| | *********xx.......xx|
++ * |-----------------------| |---------------------------------|
++ * codeword 1,2..n-1 codeword n
++ * <---(528/532 Bytes)----> <-------(528/532 Bytes)---------->
++ *
++ * n = number of codewords in the page
++ * . = ECC bytes
++ * * = spare bytes
++ * x = unused/reserved bytes
++ *
++ * 2K page: n = 4, spare = 16 bytes
++ * 4K page: n = 8, spare = 32 bytes
++ * 8K page: n = 16, spare = 64 bytes
++ *
++ * the qcom nand controller operates at a sub page/codeword level. each
++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
++ * the number of ECC bytes vary based on the ECC strength and the bus width.
++ *
++ * the first n - 1 codewords contains 516 bytes of user data, the remaining
++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
++ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
++ *
++ * the layout described above is used by the controller when the ECC block is
++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are
++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout
++ * layouts defined below doesn't consider the positions occupied by the reserved
++ * bytes
++ *
++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width)
++ * in the last codeword is the position of bad block marker. the bad block
++ * marker cannot be accessed when ECC is enabled.
++ *
++ */
++
++/*
++ * Layouts for different page sizes and ecc modes. We skip the eccpos field
++ * since it isn't needed for this driver
++ */
++
++/* 2K page, 4 bit ECC */
++static struct nand_ecclayout layout_oob_64 = {
++ .eccbytes = 40,
++ .oobfree = {
++ { 30, 16 },
++ },
++};
++
++/* 4K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_128 = {
++ .eccbytes = 80,
++ .oobfree = {
++ { 70, 32 },
++ },
++};
++
++/* 4K page, 8 bit ECC, 8 bit bus width */
++static struct nand_ecclayout layout_oob_224_x8 = {
++ .eccbytes = 104,
++ .oobfree = {
++ { 91, 32 },
++ },
++};
++
++/* 4K page, 8 bit ECC, 16 bit bus width */
++static struct nand_ecclayout layout_oob_224_x16 = {
++ .eccbytes = 112,
++ .oobfree = {
++ { 98, 32 },
++ },
++};
++
++/* 8K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_256 = {
++ .eccbytes = 160,
++ .oobfree = {
++ { 151, 64 },
++ },
++};
++
++/*
++ * this is called before scan_ident, we do some minimal configurations so
++ * that reading ID and ONFI params work
++ */
++static void qcom_nandc_pre_init(struct qcom_nandc_data *this)
++{
++ /* kill onenand */
++ nandc_write(this, SFLASHC_BURST_CFG, 0);
++
++ /* enable ADM DMA */
++ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN);
++
++ /* save the original values of these registers */
++ this->cmd1 = nandc_read(this, NAND_DEV_CMD1);
++ this->vld = nandc_read(this, NAND_DEV_CMD_VLD);
++
++ /* initial status value */
++ this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++}
++
++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage;
++ bool wide_bus;
++
++ /* the nand controller fetches codewords/chunks of 512 bytes */
++ cwperpage = mtd->writesize >> 9;
++
++ ecc->strength = this->ecc_strength;
++
++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++ if (ecc->strength >= 8) {
++ /* 8 bit ECC defaults to BCH ECC on all platforms */
++ ecc->bytes = wide_bus ? 14 : 13;
++ } else {
++ /*
++ * if the controller supports BCH for 4 bit ECC, the controller
++ * uses lesser bytes for ECC. If RS is used, the ECC bytes is
++ * always 10 bytes
++ */
++ if (this->ecc_modes & ECC_BCH_4BIT)
++ ecc->bytes = wide_bus ? 8 : 7;
++ else
++ ecc->bytes = 10;
++ }
++
++ /* each step consists of 512 bytes of data */
++ ecc->size = NANDC_STEP_SIZE;
++
++ ecc->read_page = qcom_nandc_read_page;
++ ecc->read_oob = qcom_nandc_read_oob;
++ ecc->write_page = qcom_nandc_write_page;
++ ecc->write_oob = qcom_nandc_write_oob;
++
++ /*
++ * the bad block marker is readable only when we read the page with ECC
++ * disabled. all the ops above run with ECC enabled. We need raw read
++ * and write function for oob in order to access bad block marker.
++ */
++ ecc->read_oob_raw = qcom_nandc_read_oob_raw;
++ ecc->write_oob_raw = qcom_nandc_write_oob_raw;
++
++ switch (mtd->oobsize) {
++ case 64:
++ ecc->layout = &layout_oob_64;
++ break;
++ case 128:
++ ecc->layout = &layout_oob_128;
++ break;
++ case 224:
++ if (wide_bus)
++ ecc->layout = &layout_oob_224_x16;
++ else
++ ecc->layout = &layout_oob_224_x8;
++ break;
++ case 256:
++ ecc->layout = &layout_oob_256;
++ break;
++ default:
++ dev_err(this->dev, "unsupported NAND device, oobsize %d\n",
++ mtd->oobsize);
++ return -ENODEV;
++ }
++
++ ecc->mode = NAND_ECC_HW;
++
++ /* enable ecc by default */
++ this->use_ecc = true;
++
++ return 0;
++}
++
++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = mtd->writesize / ecc->size;
++ int spare_bytes, bad_block_byte;
++ bool wide_bus;
++ int ecc_mode = 0;
++
++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++ if (ecc->strength >= 8) {
++ this->cw_size = 532;
++
++ spare_bytes = wide_bus ? 0 : 2;
++
++ this->bch_enabled = true;
++ ecc_mode = 1;
++ } else {
++ this->cw_size = 528;
++
++ if (this->ecc_modes & ECC_BCH_4BIT) {
++ spare_bytes = wide_bus ? 2 : 4;
++
++ this->bch_enabled = true;
++ ecc_mode = 0;
++ } else {
++ spare_bytes = wide_bus ? 0 : 1;
++ }
++ }
++
++ /*
++ * DATA_UD_BYTES varies based on whether the read/write command protects
++ * spare data with ECC too. We protect spare data by default, so we set
++ * it to main + spare data, which are 512 and 4 bytes respectively.
++ */
++ this->cw_data = 516;
++
++ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1;
++
++ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE
++ | this->cw_data << UD_SIZE_BYTES
++ | 0 << DISABLE_STATUS_AFTER_WRITE
++ | 5 << NUM_ADDR_CYCLES
++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS
++ | 0 << STATUS_BFR_READ
++ | 1 << SET_RD_MODE_AFTER_STATUS
++ | spare_bytes << SPARE_SIZE_BYTES;
++
++ this->cfg1 = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | bad_block_byte << BAD_BLOCK_BYTE_NUM
++ | 0 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | wide_bus << WIDE_FLASH
++ | this->bch_enabled << ENABLE_BCH_ECC;
++
++ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
++ | this->cw_size << UD_SIZE_BYTES
++ | 5 << NUM_ADDR_CYCLES
++ | 0 << SPARE_SIZE_BYTES;
++
++ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | 17 << BAD_BLOCK_BYTE_NUM
++ | 1 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | wide_bus << WIDE_FLASH
++ | 1 << DEV0_CFG1_ECC_DISABLE;
++
++ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE
++ | 0 << ECC_SW_RESET
++ | this->cw_data << ECC_NUM_DATA_BYTES
++ | 1 << ECC_FORCE_CLK_OPEN
++ | ecc_mode << ECC_MODE
++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH;
++
++ this->ecc_buf_cfg = 0x203 << NUM_STEPS;
++
++ this->clrflashstatus = FS_READY_BSY_N;
++ this->clrreadstatus = 0xc0;
++
++ dev_dbg(this->dev,
++ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
++ this->cfg0, this->cfg1, this->ecc_buf_cfg,
++ this->ecc_bch_cfg, this->cw_size, this->cw_data,
++ ecc->strength, ecc->bytes, cwperpage);
++}
++
++static int qcom_nandc_alloc(struct qcom_nandc_data *this)
++{
++ int r;
++
++ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32));
++ if (r) {
++ dev_err(this->dev, "failed to set DMA mask\n");
++ return r;
++ }
++
++ /*
++ * we use the internal buffer for reading ONFI params, reading small
++ * data like ID and status, and preforming read-copy-write operations
++ * when writing to a codeword partially. 532 is the maximum possible
++ * size of a codeword for our nand controller
++ */
++ this->buf_size = 532;
++
++ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL);
++ if (!this->data_buffer)
++ return -ENOMEM;
++
++ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL);
++ if (!this->regs)
++ return -ENOMEM;
++
++ this->reg_read_buf = devm_kzalloc(this->dev,
++ MAX_REG_RD * sizeof(*this->reg_read_buf),
++ GFP_KERNEL);
++ if (!this->reg_read_buf)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&this->list);
++
++ this->chan = dma_request_slave_channel(this->dev, "rxtx");
++ if (!this->chan) {
++ dev_err(this->dev, "failed to request slave channel\n");
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void qcom_nandc_unalloc(struct qcom_nandc_data *this)
++{
++ dma_release_channel(this->chan);
++}
++
++static int qcom_nandc_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct device_node *np = this->dev->of_node;
++ struct mtd_part_parser_data ppdata = { .of_node = np };
++ int r;
++
++ mtd->priv = chip;
++ mtd->name = "qcom-nandc";
++ mtd->owner = THIS_MODULE;
++
++ chip->priv = this;
++
++ chip->cmdfunc = qcom_nandc_command;
++ chip->select_chip = qcom_nandc_select_chip;
++ chip->read_byte = qcom_nandc_read_byte;
++ chip->read_buf = qcom_nandc_read_buf;
++ chip->write_buf = qcom_nandc_write_buf;
++
++ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
++ if (this->bus_width == 16)
++ chip->options |= NAND_BUSWIDTH_16;
++
++ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW;
++ if (of_get_nand_on_flash_bbt(np))
++ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++ qcom_nandc_pre_init(this);
++
++ r = nand_scan_ident(mtd, 1, NULL);
++ if (r)
++ return r;
++
++ r = qcom_nandc_ecc_init(this);
++ if (r)
++ return r;
++
++ qcom_nandc_hw_post_init(this);
++
++ r = nand_scan_tail(mtd);
++ if (r)
++ return r;
++
++ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
++}
++
++static int qcom_nandc_parse_dt(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++ struct device_node *np = this->dev->of_node;
++ int r;
++
++ this->ecc_strength = of_get_nand_ecc_strength(np);
++ if (this->ecc_strength < 0) {
++ dev_warn(this->dev,
++ "incorrect ecc strength, setting to 4 bits/step\n");
++ this->ecc_strength = 4;
++ }
++
++ this->bus_width = of_get_nand_bus_width(np);
++ if (this->bus_width < 0) {
++ dev_warn(this->dev, "incorrect bus width, setting to 8\n");
++ this->bus_width = 8;
++ }
++
++ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci);
++ if (r) {
++ dev_err(this->dev, "command CRCI unspecified\n");
++ return r;
++ }
++
++ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci);
++ if (r) {
++ dev_err(this->dev, "data CRCI unspecified\n");
++ return r;
++ }
++
++ return 0;
++}
++
++static int qcom_nandc_probe(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this;
++ const struct of_device_id *match;
++ int r;
++
++ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
++ if (!this)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, this);
++
++ this->pdev = pdev;
++ this->dev = &pdev->dev;
++
++ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
++ if (!match) {
++ dev_err(&pdev->dev, "failed to match device\n");
++ return -ENODEV;
++ }
++
++ if (!match->data) {
++ dev_err(&pdev->dev, "failed to get device data\n");
++ return -ENODEV;
++ }
++
++ this->ecc_modes = (u32) match->data;
++
++ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ this->base = devm_ioremap_resource(&pdev->dev, this->res);
++ if (IS_ERR(this->base))
++ return PTR_ERR(this->base);
++
++ this->core_clk = devm_clk_get(&pdev->dev, "core");
++ if (IS_ERR(this->core_clk))
++ return PTR_ERR(this->core_clk);
++
++ this->aon_clk = devm_clk_get(&pdev->dev, "aon");
++ if (IS_ERR(this->aon_clk))
++ return PTR_ERR(this->aon_clk);
++
++ r = qcom_nandc_parse_dt(pdev);
++ if (r)
++ return r;
++
++ r = qcom_nandc_alloc(this);
++ if (r)
++ return r;
++
++ r = clk_prepare_enable(this->core_clk);
++ if (r)
++ goto err_core_clk;
++
++ r = clk_prepare_enable(this->aon_clk);
++ if (r)
++ goto err_aon_clk;
++
++ r = qcom_nandc_init(this);
++ if (r)
++ goto err_init;
++
++ return 0;
++
++err_init:
++ clk_disable_unprepare(this->aon_clk);
++err_aon_clk:
++ clk_disable_unprepare(this->core_clk);
++err_core_clk:
++ qcom_nandc_unalloc(this);
++
++ return r;
++}
++
++static int qcom_nandc_remove(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++
++ qcom_nandc_unalloc(this);
++
++ clk_disable_unprepare(this->aon_clk);
++ clk_disable_unprepare(this->core_clk);
++
++ return 0;
++}
++
++#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
++
++/*
++ * data will hold a struct pointer containing more differences once we support
++ * more IPs
++ */
++static const struct of_device_id qcom_nandc_of_match[] = {
++ { .compatible = "qcom,ebi2-nandc",
++ .data = (void *) EBI2_NANDC_ECC_MODES,
++ },
++ {}
++};
++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
++
++static struct platform_driver qcom_nandc_driver = {
++ .driver = {
++ .name = "qcom-nandc",
++ .of_match_table = qcom_nandc_of_match,
++ },
++ .probe = qcom_nandc_probe,
++ .remove = qcom_nandc_remove,
++};
++module_platform_driver(qcom_nandc_driver);
++
++MODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>");
++MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
+
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
new file mode 100644
index 0000000..6530eb1
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927141
+Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:16 +0530
+
+Add DT bindings document for the Qualcomm NAND controller driver.
+
+Cc: devicetree@vger.kernel.org
+
+v3:
+- Don't use '0x' when specifying nand controller address space
+- Add optional property for on-flash bbt usage
+
+Acked-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+@@ -0,0 +1,49 @@
++* Qualcomm NAND controller
++
++Required properties:
++- compatible: should be "qcom,ebi2-nand" for IPQ806x
++- reg: MMIO address range
++- clocks: must contain core clock and always on clock
++- clock-names: must contain "core" for the core clock and "aon" for the
++ always on clock
++- dmas: DMA specifier, consisting of a phandle to the ADM DMA
++ controller node and the channel number to be used for
++ NAND. Refer to dma.txt and qcom_adm.txt for more details
++- dma-names: must be "rxtx"
++- qcom,cmd-crci: must contain the ADM command type CRCI block instance
++ number specified for the NAND controller on the given
++ platform
++- qcom,data-crci: must contain the ADM data type CRCI block instance
++ number specified for the NAND controller on the given
++ platform
++
++Optional properties:
++- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen
++ as default
++
++- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8
++ bits. If not present, 4 is chosen as default
++- nand-on-flash-bbt: Create/use on-flash bad block table
++
++The device tree may optionally contain sub-nodes describing partitions of the
++address space. See partition.txt for more detail.
++
++Example:
++
++nand@1ac00000 {
++ compatible = "qcom,ebi2-nandc";
++ reg = <0x1ac00000 0x800>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&adm_dma 3>;
++ dma-names = "rxtx";
++ qcom,cmd-crci = <15>;
++ qcom,data-crci = <3>;
++
++ partition@0 {
++ ...
++ };
++};
diff --git a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..29e1592
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,51 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927121
+Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:17 +0530
+
+The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding
+compatible string.
+
+Cc: devicetree@vger.kernel.org
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -682,6 +682,22 @@
+ status = "disabled";
+ };
+
++ nand@1ac00000 {
++ compatible = "qcom,ebi2-nandc";
++ reg = <0x1ac00000 0x800>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&adm_dma 3>;
++ dma-names = "rxtx";
++ qcom,cmd-crci = <15>;
++ qcom,data-crci = <3>;
++
++ status = "disabled";
++ };
++
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
new file mode 100644
index 0000000..d62564c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
@@ -0,0 +1,79 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927091
+Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:18 +0530
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Cc: devicetree@vger.kernel.org
+
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -61,6 +61,31 @@
+ bias-none;
+ };
+ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -147,5 +172,19 @@
+ pinctrl-0 = <&pcie1_pins>;
+ pinctrl-names = "default";
+ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++ };
+ };
+ };
++
++&adm_dma {
++ status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
new file mode 100644
index 0000000..cae7f15
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -181,6 +181,8 @@
+
+ nand-ecc-strength = <4>;
+ nand-bus-width = <8>;
++
++ linux,part-probe = "qcom-smem";
+ };
+ };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
new file mode 100644
index 0000000..cb740de
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -311,7 +311,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-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch b/target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch
new file mode 100644
index 0000000..a9b83a1
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch
@@ -0,0 +1,387 @@
+From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo@openwrt.org>
+Date: Sun, 9 Aug 2015 13:02:38 +0200
+Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++
+ 2 files changed, 371 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -361,6 +361,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8084-mtp.dtb \
+ qcom-ipq8064-ap148.dtb \
+ qcom-ipq8064-db149.dtb \
++ qcom-ipq8064-r7500.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb \
+ qcom-msm8974-sony-xperia-honami.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+@@ -0,0 +1,362 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4 R7500";
++ compatible = "netgear,r7500", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0xe000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++ };
++
++ chosen {
++ bootargs = "rootfstype=squashfs noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ i2c4_pins: i2c4_pinmux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ bias-disable;
++ };
++
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ 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 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * 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.
++ */
++ };
++
++ 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";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1340000 {
++ label = "kernel";
++ reg = <0x1340000 0x0200000>;
++ };
++
++ ubi@1540000 {
++ label = "ubi";
++ reg = <0x1540000 0x1800000>;
++ };
++
++ netgear@2d40000 {
++ label = "netgear";
++ reg = <0x2d40000 0x0c00000>;
++ read-only;
++ };
++
++ reserve@3940000 {
++ label = "reserve";
++ reg = <0x3940000 0x46c0000>;
++ read-only;
++ };
++
++ firmware@1340000 {
++ label = "firmware";
++ reg = <0x1340000 0x1a00000>;
++ };
++
++ };
++
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ phy-handle = <&phy4>;
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 1>;
++ linux,code = <KEY_WLAN>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 1>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 1>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++
++ usb1 {
++ label = "r7500:amber:usb1";
++ gpios = <&qcom_pinmux 7 0>;
++ };
++
++ usb3 {
++ label = "r7500:amber:usb3";
++ gpios = <&qcom_pinmux 8 0>;
++ };
++
++ status {
++ label = "r7500:amber:status";
++ gpios = <&qcom_pinmux 9 0>;
++ };
++
++ internet {
++ label = "r7500:white:internet";
++ gpios = <&qcom_pinmux 22 0>;
++ };
++
++ wan {
++ label = "r7500:white:wan";
++ gpios = <&qcom_pinmux 23 0>;
++ };
++
++ wps {
++ label = "r7500:white:wps";
++ gpios = <&qcom_pinmux 24 0>;
++ };
++
++ esata {
++ label = "r7500:white:esata";
++ gpios = <&qcom_pinmux 26 0>;
++ };
++
++ power {
++ label = "r7500:white:power";
++ gpios = <&qcom_pinmux 53 0>;
++ default-state = "on";
++ };
++
++ rfkill {
++ label = "r7500:white:rfkill";
++ gpios = <&qcom_pinmux 64 0>;
++ };
++
++ wifi5g {
++ label = "r7500:white:wifi5g";
++ gpios = <&qcom_pinmux 67 0>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch
new file mode 100644
index 0000000..471a87b
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch
@@ -0,0 +1,13 @@
+--- a/drivers/mtd/qcom_smem_part.c
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -192,6 +192,10 @@ static int parse_qcom_smem_partitions(st
+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
+
++ /* "rootfs" conflicts with OpenWrt auto mounting */
++ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
++ m_part->name = "ubi";
++
+ /*
+ * The last SMEM partition may have its size marked as
+ * something like 0xffffffff, which means "until the end of the
diff --git a/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
new file mode 100644
index 0000000..22d28ac
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
@@ -0,0 +1,733 @@
+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
+@@ -209,11 +209,46 @@ 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,
++ },
++};
++
+ #define P_PXO 0
+ #define P_PLL8 1
+ #define P_PLL3 1
+ #define P_PLL0 2
+ #define P_CXO 2
++#define P_PLL14 3
++#define P_PLL18 4
+
+ static const u8 gcc_pxo_pll8_map[] = {
+ [P_PXO] = 0,
+@@ -264,6 +299,22 @@ static const char *gcc_pxo_pll8_pll0_map
+ "pll0_vote",
+ };
+
++static const u8 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 },
+@@ -2269,6 +2320,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,
+@@ -2277,6 +2794,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,
+@@ -2376,6 +2894,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[] = {
+@@ -2494,6 +3024,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 = {
+@@ -2522,6 +3094,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);
+@@ -2532,7 +3106,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/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -290,5 +290,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
+--- 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
diff --git a/target/linux/ipq806x/patches-3.18/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch b/target/linux/ipq806x/patches-3.18/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch
new file mode 100644
index 0000000..8e59b5c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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)
+
+@@ -818,18 +819,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
+@@ -27,6 +27,7 @@
+ #include <linux/of.h>
+ #include <linux/of_net.h>
+ #include <linux/of_device.h>
++#include <linux/of_mdio.h>
+ #include "stmmac.h"
+
+ static const struct of_device_id stmmac_dt_ids[] = {
+@@ -155,13 +156,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-3.18/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch b/target/linux/ipq806x/patches-3.18/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch
new file mode 100644
index 0000000..e8f9b5c
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -117,13 +117,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;
+@@ -219,8 +224,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 =
+@@ -235,6 +242,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-3.18/703-stmmac-add-fixed-link-device-tree-support.patch b/target/linux/ipq806x/patches-3.18/703-stmmac-add-fixed-link-device-tree-support.patch
new file mode 100644
index 0000000..3c20fe4
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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
+@@ -858,7 +858,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
+@@ -164,6 +164,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.
+ */
+@@ -226,7 +234,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);
+@@ -243,6 +251,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-3.18/704-stmmac-add-ipq806x-glue-layer.patch b/target/linux/ipq806x/patches-3.18/704-stmmac-add-ipq806x-glue-layer.patch
new file mode 100644
index 0000000..fa67404
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/704-stmmac-add-ipq806x-glue-layer.patch
@@ -0,0 +1,427 @@
+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
+ bool "STMMAC Platform bus support"
+ depends on STMMAC_ETH
++ select MFD_SYSCON
+ default y
+ ---help---
+ This selects the platform specific bus support for
+@@ -26,6 +27,15 @@ config STMMAC_PLATFORM
+
+ If unsure, say N.
+
++config DWMAC_IPQ806X
++ bool "QCA IPQ806x dwmac support"
++ depends on STMMAC_PLATFORM && ARCH_QCOM
++ help
++ Support for Ethernet controller on QCA IPQ806x SoC.
++
++ This selects the QCA IPQ806x SoC glue layer support for
++ the stmmac device driver.
++
+ config DWMAC_MESON
+ bool "Amlogic Meson dwmac support"
+ depends on STMMAC_PLATFORM && ARCH_MESON
+--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
+@@ -1,6 +1,7 @@
+ obj-$(CONFIG_STMMAC_ETH) += stmmac.o
+ stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
+ stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
++stmmac-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
+ stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o
+ stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
+ stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -46,6 +46,9 @@ static const struct of_device_id stmmac_
+ #ifdef CONFIG_DWMAC_SOCFPGA
+ { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
+ #endif
++#ifdef CONFIG_DWMAC_IPQ806X
++ { .compatible = "qcom,ipq806x-gmac", .data = &ipq806x_gmac_data },
++#endif
+ /* SoC specific glue layers should come before generic bindings */
+ { .compatible = "st,spear600-gmac"},
+ { .compatible = "snps,dwmac-3.610"},
+--- /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.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,
++};
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+@@ -137,6 +137,9 @@ void stmmac_disable_eee_mode(struct stmm
+ bool stmmac_eee_init(struct stmmac_priv *priv);
+
+ #ifdef CONFIG_STMMAC_PLATFORM
++#ifdef CONFIG_DWMAC_IPQ806X
++extern const struct stmmac_of_data ipq806x_gmac_data;
++#endif
+ #ifdef CONFIG_DWMAC_MESON
+ extern const struct stmmac_of_data meson6_dwmac_data;
+ #endif
diff --git a/target/linux/ipq806x/patches-3.18/705-net-stmmac-ipq806x-document-device-tree-bindings.patch b/target/linux/ipq806x/patches-3.18/705-net-stmmac-ipq806x-document-device-tree-bindings.patch
new file mode 100644
index 0000000..3144fa3
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/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-3.18/706-net-stmmac-create-one-debugfs-dir-per-net-device.patch b/target/linux/ipq806x/patches-3.18/706-net-stmmac-create-one-debugfs-dir-per-net-device.patch
new file mode 100644
index 0000000..50127fd
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/706-net-stmmac-create-one-debugfs-dir-per-net-device.patch
@@ -0,0 +1,171 @@
+From df944689d491e6af533173bf2ef448c3dd334f15 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Mon, 11 May 2015 15:15:25 -0700
+Subject: [PATCH 6/8] net: stmmac: create one debugfs dir per net-device
+
+stmmac DebugFS entries are currently global to the driver. As a result,
+having more than one stmmac device in the system creates the following
+error:
+* ERROR stmmaceth, debugfs create directory failed
+* stmmac_hw_setup: failed debugFS registration
+
+This also results in being able to access the debugfs information for
+the first registered device only.
+
+This patch changes the debugfs structure to have one sub-directory per
+net-device. Files under "/sys/kernel/debug/stmmaceth" will now show-up
+under /sys/kernel/debug/stmmaceth/ethN/.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 6 ++
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 76 ++++++++++++++++-------
+ 2 files changed, 59 insertions(+), 23 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+@@ -116,6 +116,12 @@ struct stmmac_priv {
+ int use_riwt;
+ int irq_wake;
+ spinlock_t ptp_lock;
++
++#ifdef CONFIG_DEBUG_FS
++ struct dentry *dbgfs_dir;
++ struct dentry *dbgfs_rings_status;
++ struct dentry *dbgfs_dma_cap;
++#endif
+ };
+
+ int stmmac_mdio_unregister(struct net_device *ndev);
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -119,7 +119,7 @@ static irqreturn_t stmmac_interrupt(int
+
+ #ifdef CONFIG_STMMAC_DEBUG_FS
+ static int stmmac_init_fs(struct net_device *dev);
+-static void stmmac_exit_fs(void);
++static void stmmac_exit_fs(struct net_device *dev);
+ #endif
+
+ #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
+@@ -1879,7 +1879,7 @@ static int stmmac_release(struct net_dev
+ netif_carrier_off(dev);
+
+ #ifdef CONFIG_STMMAC_DEBUG_FS
+- stmmac_exit_fs();
++ stmmac_exit_fs(dev);
+ #endif
+
+ stmmac_release_ptp(priv);
+@@ -2467,8 +2467,6 @@ static int stmmac_ioctl(struct net_devic
+
+ #ifdef CONFIG_STMMAC_DEBUG_FS
+ static struct dentry *stmmac_fs_dir;
+-static struct dentry *stmmac_rings_status;
+-static struct dentry *stmmac_dma_cap;
+
+ static void sysfs_display_ring(void *head, int size, int extend_desc,
+ struct seq_file *seq)
+@@ -2607,36 +2605,39 @@ static const struct file_operations stmm
+
+ static int stmmac_init_fs(struct net_device *dev)
+ {
+- /* Create debugfs entries */
+- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
++ struct stmmac_priv *priv = netdev_priv(dev);
++
++ /* Create per netdev entries */
++ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
+
+- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
+- pr_err("ERROR %s, debugfs create directory failed\n",
+- STMMAC_RESOURCE_NAME);
++ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
++ pr_err("ERROR %s/%s, debugfs create directory failed\n",
++ STMMAC_RESOURCE_NAME, dev->name);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report DMA RX/TX rings */
+- stmmac_rings_status = debugfs_create_file("descriptors_status",
+- S_IRUGO, stmmac_fs_dir, dev,
+- &stmmac_rings_status_fops);
++ priv->dbgfs_rings_status =
++ debugfs_create_file("descriptors_status", S_IRUGO,
++ priv->dbgfs_dir, dev,
++ &stmmac_rings_status_fops);
+
+- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
++ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
+ pr_info("ERROR creating stmmac ring debugfs file\n");
+- debugfs_remove(stmmac_fs_dir);
++ debugfs_remove_recursive(priv->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report the DMA HW features */
+- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
+- dev, &stmmac_dma_cap_fops);
++ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
++ priv->dbgfs_dir,
++ dev, &stmmac_dma_cap_fops);
+
+- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
++ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
+ pr_info("ERROR creating stmmac MMC debugfs file\n");
+- debugfs_remove(stmmac_rings_status);
+- debugfs_remove(stmmac_fs_dir);
++ debugfs_remove_recursive(priv->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+@@ -2644,11 +2645,11 @@ static int stmmac_init_fs(struct net_dev
+ return 0;
+ }
+
+-static void stmmac_exit_fs(void)
++static void stmmac_exit_fs(struct net_device *dev)
+ {
+- debugfs_remove(stmmac_rings_status);
+- debugfs_remove(stmmac_dma_cap);
+- debugfs_remove(stmmac_fs_dir);
++ struct stmmac_priv *priv = netdev_priv(dev);
++
++ debugfs_remove_recursive(priv->dbgfs_dir);
+ }
+ #endif /* CONFIG_STMMAC_DEBUG_FS */
+
+@@ -3032,6 +3033,21 @@ static int __init stmmac_init(void)
+ ret = stmmac_register_pci();
+ if (ret)
+ goto err_pci;
++
++#ifdef CONFIG_STMMAC_DEBUG_FS
++ /* Create debugfs main directory if it doesn't exist yet */
++ if (stmmac_fs_dir == NULL) {
++ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
++
++ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
++ pr_err("ERROR %s, debugfs create directory failed\n",
++ STMMAC_RESOURCE_NAME);
++
++ return -ENOMEM;
++ }
++ }
++#endif
++
+ return 0;
+ err_pci:
+ stmmac_unregister_platform();
+@@ -3042,6 +3058,9 @@ err:
+
+ static void __exit stmmac_exit(void)
+ {
++#ifdef CONFIG_STMMAC_DEBUG_FS
++ debugfs_remove_recursive(stmmac_fs_dir);
++#endif
+ stmmac_unregister_platform();
+ stmmac_unregister_pci();
+ }
diff --git a/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
new file mode 100644
index 0000000..fe28942
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
@@ -0,0 +1,146 @@
+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
+@@ -19,8 +19,9 @@
+ };
+ };
+
+- alias {
++ aliases {
+ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
+ };
+
+ chosen {
+@@ -86,6 +87,15 @@
+ bias-bus-hold;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -184,6 +194,34 @@
+
+ linux,part-probe = "qcom-smem";
+ };
++
++ 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 {
+@@ -65,6 +66,15 @@
+ bias-none;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -176,5 +186,44 @@
+ 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>;
++ };
++
++ 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-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000..3455d45
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,212 @@
+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
+@@ -96,6 +96,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 {
+@@ -222,6 +232,27 @@
+ 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
+@@ -75,6 +75,14 @@
+ bias-disable;
+ };
+ };
++
++ rgmii0_pins: rgmii0_pins {
++ mux {
++ pins = "gpio2", "gpio66";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -225,5 +233,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
+@@ -698,6 +698,92 @@
+ 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";
++ };
++
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch b/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch
new file mode 100644
index 0000000..68222ce
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch
@@ -0,0 +1,35 @@
+From 5bf2dabde1fa3af0c9082b42b6847ef3fd198b13 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo@openwrt.org>
+Date: Sun, 9 Aug 2015 12:53:55 +0200
+Subject: [PATCH] stmac: platform: add support for retreiving mac from mtd
+
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -284,6 +284,7 @@ static int stmmac_pltfr_probe(struct pla
+ struct stmmac_priv *priv = NULL;
+ struct plat_stmmacenet_data *plat_dat = NULL;
+ const char *mac = NULL;
++ u8 mtd_mac[ETH_ALEN] = { };
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(dev, res);
+@@ -313,6 +314,15 @@ static int stmmac_pltfr_probe(struct pla
+ pr_err("%s: main dt probe failed", __func__);
+ return ret;
+ }
++
++ if (!mac) {
++ ret = of_get_mac_address_mtd(dev->of_node, &mtd_mac);
++ if (ret == -EPROBE_DEFER)
++ return ret;
++
++ if (is_valid_ether_addr(&mtd_mac))
++ mac = mtd_mac;
++ }
+ }
+
+ /* Custom setup (if needed) */
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 0000000..c0ad74c
--- /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 0000000..bfdb30f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -77,15 +77,7 @@
+ spi-max-frequency = <50000000>;
+ reg = <0>;
+
+- partition@0 {
+- label = "rootfs";
+- reg = <0x0 0x1000000>;
+- };
+-
+- partition@1 {
+- label = "scratch";
+- reg = <0x1000000 0x1000000>;
+- };
++ linux,part-probe = "qcom-smem";
+ };
+ };
+ };
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 0000000..7d8c8e8
--- /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 0000000..6b29637
--- /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/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.1/024-ap148-add-memory-node.patch
new file mode 100644
index 0000000..f026ed9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/024-ap148-add-memory-node.patch
@@ -0,0 +1,14 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,6 +4,11 @@
+ model = "Qualcomm IPQ8064/AP148";
+ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
+
++ memory@0 {
++ reg = <0x42000000 0x1e000000>;
++ device_type = "memory";
++ };
++
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
diff --git a/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch b/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch
new file mode 100644
index 0000000..04f35b7
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch
@@ -0,0 +1,167 @@
+From fb7737e949e31d8a71acee6bbb670f32dbd2a2c0 Mon Sep 17 00:00:00 2001
+From: Suman Anna <s-anna@ti.com>
+Date: Wed, 4 Mar 2015 20:01:14 -0600
+Subject: [PATCH] hwspinlock/core: add device tree support
+
+This patch adds a new OF-friendly API of_hwspin_lock_get_id()
+for hwspinlock clients to use/request locks from a hwspinlock
+device instantiated through a device-tree blob. This new API
+can be used by hwspinlock clients to get the id for a specific
+lock using the phandle + args specifier, so that it can be
+requested using the available hwspin_lock_request_specific()
+API.
+
+Signed-off-by: Suman Anna <s-anna@ti.com>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+[small comment clarification]
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ Documentation/hwspinlock.txt | 10 +++++
+ drivers/hwspinlock/hwspinlock_core.c | 79 ++++++++++++++++++++++++++++++++++++
+ include/linux/hwspinlock.h | 7 ++++
+ 3 files changed, 96 insertions(+)
+
+--- a/Documentation/hwspinlock.txt
++++ b/Documentation/hwspinlock.txt
+@@ -48,6 +48,16 @@ independent, drivers.
+ ids for predefined purposes.
+ Should be called from a process context (might sleep).
+
++ int of_hwspin_lock_get_id(struct device_node *np, int index);
++ - retrieve the global lock id for an OF phandle-based specific lock.
++ This function provides a means for DT users of a hwspinlock module
++ to get the global lock id of a specific hwspinlock, so that it can
++ be requested using the normal hwspin_lock_request_specific() API.
++ The function returns a lock id number on success, -EPROBE_DEFER if
++ the hwspinlock device is not yet registered with the core, or other
++ error values.
++ Should be called from a process context (might sleep).
++
+ int hwspin_lock_free(struct hwspinlock *hwlock);
+ - free a previously-assigned hwspinlock; returns 0 on success, or an
+ appropriate error code on failure (e.g. -EINVAL if the hwspinlock
+--- a/drivers/hwspinlock/hwspinlock_core.c
++++ b/drivers/hwspinlock/hwspinlock_core.c
+@@ -27,6 +27,7 @@
+ #include <linux/hwspinlock.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/mutex.h>
++#include <linux/of.h>
+
+ #include "hwspinlock_internal.h"
+
+@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *
+ }
+ EXPORT_SYMBOL_GPL(__hwspin_unlock);
+
++/**
++ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
++ * @bank: the hwspinlock device bank
++ * @hwlock_spec: hwlock specifier as found in the device tree
++ *
++ * This is a simple translation function, suitable for hwspinlock platform
++ * drivers that only has a lock specifier length of 1.
++ *
++ * Returns a relative index of the lock within a specified bank on success,
++ * or -EINVAL on invalid specifier cell count.
++ */
++static inline int
++of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)
++{
++ if (WARN_ON(hwlock_spec->args_count != 1))
++ return -EINVAL;
++
++ return hwlock_spec->args[0];
++}
++
++/**
++ * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock
++ * @np: device node from which to request the specific hwlock
++ * @index: index of the hwlock in the list of values
++ *
++ * This function provides a means for DT users of the hwspinlock module to
++ * get the global lock id of a specific hwspinlock using the phandle of the
++ * hwspinlock device, so that it can be requested using the normal
++ * hwspin_lock_request_specific() API.
++ *
++ * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
++ * device is not yet registered, -EINVAL on invalid args specifier value or an
++ * appropriate error as returned from the OF parsing of the DT client node.
++ */
++int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++ struct of_phandle_args args;
++ struct hwspinlock *hwlock;
++ struct radix_tree_iter iter;
++ void **slot;
++ int id;
++ int ret;
++
++ ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,
++ &args);
++ if (ret)
++ return ret;
++
++ /* Find the hwspinlock device: we need its base_id */
++ ret = -EPROBE_DEFER;
++ rcu_read_lock();
++ radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {
++ hwlock = radix_tree_deref_slot(slot);
++ if (unlikely(!hwlock))
++ continue;
++
++ if (hwlock->bank->dev->of_node == args.np) {
++ ret = 0;
++ break;
++ }
++ }
++ rcu_read_unlock();
++ if (ret < 0)
++ goto out;
++
++ id = of_hwspin_lock_simple_xlate(&args);
++ if (id < 0 || id >= hwlock->bank->num_locks) {
++ ret = -EINVAL;
++ goto out;
++ }
++ id += hwlock->bank->base_id;
++
++out:
++ of_node_put(args.np);
++ return ret ? ret : id;
++}
++EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);
++
+ static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
+ {
+ struct hwspinlock *tmp;
+--- a/include/linux/hwspinlock.h
++++ b/include/linux/hwspinlock.h
+@@ -26,6 +26,7 @@
+ #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */
+
+ struct device;
++struct device_node;
+ struct hwspinlock;
+ struct hwspinlock_device;
+ struct hwspinlock_ops;
+@@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspin
+ struct hwspinlock *hwspin_lock_request(void);
+ struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
+ int hwspin_lock_free(struct hwspinlock *hwlock);
++int of_hwspin_lock_get_id(struct device_node *np, int index);
+ int hwspin_lock_get_id(struct hwspinlock *hwlock);
+ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
+ unsigned long *);
+@@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock *
+ {
+ }
+
++static inline int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++ return 0;
++}
++
+ static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)
+ {
+ return 0;
diff --git a/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch b/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch
new file mode 100644
index 0000000..581b199
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch
@@ -0,0 +1,234 @@
+From 19a0f61224d2d91860fa8291ab63cb104ee86bdd Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Tue, 24 Mar 2015 10:11:05 -0700
+Subject: [PATCH] hwspinlock: qcom: Add support for Qualcomm HW Mutex block
+
+Add driver for Qualcomm Hardware Mutex block found in many Qualcomm
+SoCs.
+
+Based on initial effort by Kumar Gala <galak@codeaurora.org>
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Jeffrey Hugo <jhugo@codeaurora.org>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/Kconfig | 12 +++
+ drivers/hwspinlock/Makefile | 1 +
+ drivers/hwspinlock/qcom_hwspinlock.c | 181 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 194 insertions(+)
+ create mode 100644 drivers/hwspinlock/qcom_hwspinlock.c
+
+--- a/drivers/hwspinlock/Kconfig
++++ b/drivers/hwspinlock/Kconfig
+@@ -18,6 +18,18 @@ config HWSPINLOCK_OMAP
+
+ If unsure, say N.
+
++config HWSPINLOCK_QCOM
++ tristate "Qualcomm Hardware Spinlock device"
++ depends on ARCH_QCOM
++ select HWSPINLOCK
++ select MFD_SYSCON
++ help
++ Say y here to support the Qualcomm Hardware Mutex functionality, which
++ provides a synchronisation mechanism for the various processors on
++ the SoC.
++
++ If unsure, say N.
++
+ config HSEM_U8500
+ tristate "STE Hardware Semaphore functionality"
+ depends on ARCH_U8500
+--- a/drivers/hwspinlock/Makefile
++++ b/drivers/hwspinlock/Makefile
+@@ -4,4 +4,5 @@
+
+ obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
+ obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
++obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
+ obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
+--- /dev/null
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -0,0 +1,181 @@
++/*
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2015, Sony Mobile Communications AB
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++
++#include "hwspinlock_internal.h"
++
++#define QCOM_MUTEX_APPS_PROC_ID 1
++#define QCOM_MUTEX_NUM_LOCKS 32
++
++static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
++{
++ struct regmap_field *field = lock->priv;
++ u32 lock_owner;
++ int ret;
++
++ ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
++ if (ret)
++ return ret;
++
++ ret = regmap_field_read(field, &lock_owner);
++ if (ret)
++ return ret;
++
++ return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
++}
++
++static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
++{
++ struct regmap_field *field = lock->priv;
++ u32 lock_owner;
++ int ret;
++
++ ret = regmap_field_read(field, &lock_owner);
++ if (ret) {
++ pr_err("%s: unable to query spinlock owner\n", __func__);
++ return;
++ }
++
++ if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
++ pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
++ __func__, lock_owner);
++ }
++
++ ret = regmap_field_write(field, 0);
++ if (ret)
++ pr_err("%s: failed to unlock spinlock\n", __func__);
++}
++
++static const struct hwspinlock_ops qcom_hwspinlock_ops = {
++ .trylock = qcom_hwspinlock_trylock,
++ .unlock = qcom_hwspinlock_unlock,
++};
++
++static const struct of_device_id qcom_hwspinlock_of_match[] = {
++ { .compatible = "qcom,sfpb-mutex" },
++ { .compatible = "qcom,tcsr-mutex" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
++
++static int qcom_hwspinlock_probe(struct platform_device *pdev)
++{
++ struct hwspinlock_device *bank;
++ struct device_node *syscon;
++ struct reg_field field;
++ struct regmap *regmap;
++ size_t array_size;
++ u32 stride;
++ u32 base;
++ int ret;
++ int i;
++
++ syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
++ if (!syscon) {
++ dev_err(&pdev->dev, "no syscon property\n");
++ return -ENODEV;
++ }
++
++ regmap = syscon_node_to_regmap(syscon);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no offset in syscon\n");
++ return -EINVAL;
++ }
++
++ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "no stride syscon\n");
++ return -EINVAL;
++ }
++
++ array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
++ bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
++ if (!bank)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, bank);
++
++ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
++ field.reg = base + i * stride;
++ field.lsb = 0;
++ field.msb = 32;
++
++ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
++ regmap, field);
++ }
++
++ pm_runtime_enable(&pdev->dev);
++
++ ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops,
++ 0, QCOM_MUTEX_NUM_LOCKS);
++ if (ret)
++ pm_runtime_disable(&pdev->dev);
++
++ return ret;
++}
++
++static int qcom_hwspinlock_remove(struct platform_device *pdev)
++{
++ struct hwspinlock_device *bank = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = hwspin_lock_unregister(bank);
++ if (ret) {
++ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
++ return ret;
++ }
++
++ pm_runtime_disable(&pdev->dev);
++
++ return 0;
++}
++
++static struct platform_driver qcom_hwspinlock_driver = {
++ .probe = qcom_hwspinlock_probe,
++ .remove = qcom_hwspinlock_remove,
++ .driver = {
++ .name = "qcom_hwspinlock",
++ .of_match_table = qcom_hwspinlock_of_match,
++ },
++};
++
++static int __init qcom_hwspinlock_init(void)
++{
++ return platform_driver_register(&qcom_hwspinlock_driver);
++}
++/* board init code might need to reserve hwspinlocks for predefined purposes */
++postcore_initcall(qcom_hwspinlock_init);
++
++static void __exit qcom_hwspinlock_exit(void)
++{
++ platform_driver_unregister(&qcom_hwspinlock_driver);
++}
++module_exit(qcom_hwspinlock_exit);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");
diff --git a/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch b/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch
new file mode 100644
index 0000000..70d5a58
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch
@@ -0,0 +1,26 @@
+From bd5717a4632cdecafe82d03de7dcb3b1876e2828 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Fri, 26 Jun 2015 14:47:21 -0700
+Subject: [PATCH] hwspinlock: qcom: Correct msb in regmap_field
+
+msb of the regmap_field was mistakenly given the value 32, to set all bits
+in the regmap update mask; although incorrect this worked until 921cc294,
+where the mask calculation was corrected.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/qcom_hwspinlock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwspinlock/qcom_hwspinlock.c
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -123,7 +123,7 @@ static int qcom_hwspinlock_probe(struct
+ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
+ field.reg = base + i * stride;
+ field.lsb = 0;
+- field.msb = 32;
++ field.msb = 31;
+
+ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
+ regmap, field);
diff --git a/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..a126d69
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
@@ -0,0 +1,33 @@
+From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:45:26 -0700
+Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts
+
+Add one new node to the ipq806x.dtsi file to declare & register the
+hardware spinlock devices. This mechanism is required to be used by
+other drivers such as SMEM.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -329,5 +329,16 @@
+ #reset-cells = <1>;
+ };
+
++ sfpb_mutex_block: syscon@1200600 {
++ compatible = "syscon";
++ reg = <0x01200600 0x100>;
++ };
+ };
++
++ sfpb_mutex: sfpb-mutex {
++ compatible = "qcom,sfpb-mutex";
++ syscon = <&sfpb_mutex_block 4 4>;
++
++ #hwlock-cells = <1>;
++ };
+ };
diff --git a/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch
new file mode 100644
index 0000000..d22db22
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202201
+Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com>
+To: Rob Herring <robh+dt@kernel.org>, Pawel Moll <pawel.moll@arm.com>,
+ Mark Rutland <mark.rutland@arm.com>,
+ Ian Campbell <ijc+devicetree@hellion.org.uk>,
+ Kumar Gala <galak@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>,
+ Andry Gross <agross@codeaurora.org>
+Cc: <devicetree@vger.kernel.org>,
+ linux-arm-msm <linux-arm-msm@vger.kernel.org>,
+ <linux-kernel@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:57 -0700
+
+Add device tree binding documentation for the Qualcom Shared Memory
+manager.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+
+---
+Changes since v1:
+- None
+
+ .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+@@ -0,0 +1,49 @@
++Qualcomm Shared Memory binding
++
++This binding describes the Qualcomm Shared Memory, used to share data between
++various subsystems and OSes in Qualcomm platforms.
++
++- compatible:
++ Usage: required
++ Value type: <stringlist>
++ Definition: must be:
++ "qcom,smem"
++
++- memory-region:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: handle to memory reservation for main smem memory region.
++
++- reg:
++ Usage: optional
++ Value type: <prop-encoded-array>
++ Definition: base address and size pair for any additional memory areas
++ of the shared memory.
++
++- hwspinlocks:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: reference to a hwspinlock used to protect allocations from
++ the shared memory
++
++= EXAMPLE
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ smem_region: smem@fa00000 {
++ reg = <0xfa00000 0x200000>;
++ no-map;
++ };
++ };
++
++ smem@fa00000 {
++ compatible = "qcom,smem";
++
++ memory-region = <&smem_region>;
++ reg = <0xfc428000 0x4000>;
++
++ hwlocks = <&tcsr_mutex 3>;
++ };
diff --git a/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch
new file mode 100644
index 0000000..5598da2
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch
@@ -0,0 +1,841 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202211
+Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com>
+To: Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+ David Brown <davidb@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>
+Cc: <linux-kernel@vger.kernel.org>, <linux-arm-msm@vger.kernel.org>,
+ <linux-soc@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:58 -0700
+
+The Shared Memory Manager driver implements an interface for allocating
+and accessing items in the memory area shared among all of the
+processors in a Qualcomm platform.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Tested-by: Andy Gross <agross@codeaurora.org>
+
+---
+Changes since v1:
+- ioremapping the regions nocache
+- improved documentation of the two regions of partitions
+- corrected free space check in private allocator
+
+ drivers/soc/qcom/Kconfig | 7 +
+ drivers/soc/qcom/Makefile | 1 +
+ drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/soc/qcom/smem.h | 14 +
+ 4 files changed, 790 insertions(+)
+ create mode 100644 drivers/soc/qcom/smem.c
+ create mode 100644 include/linux/soc/qcom/smem.h
+
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -10,3 +10,10 @@ config QCOM_GSBI
+ functions for connecting the underlying serial UART, SPI, and I2C
+ devices to the output pins.
+
++config QCOM_SMEM
++ tristate "Qualcomm Shared Memory Manager (SMEM)"
++ depends on ARCH_QCOM
++ help
++ Say y here to enable support for the Qualcomm Shared Memory Manager.
++ The driver provides an interface to items in a heap shared among all
++ processors in a Qualcomm platform.
+--- a/drivers/soc/qcom/Makefile
++++ b/drivers/soc/qcom/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
++obj-$(CONFIG_QCOM_SMEM) += smem.o
+--- /dev/null
++++ b/drivers/soc/qcom/smem.c
+@@ -0,0 +1,768 @@
++/*
++ * Copyright (c) 2015, Sony Mobile Communications AB.
++ * Copyright (c) 2012-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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/soc/qcom/smem.h>
++
++/*
++ * The Qualcomm shared memory system is a allocate only heap structure that
++ * consists of one of more memory areas that can be accessed by the processors
++ * in the SoC.
++ *
++ * All systems contains a global heap, accessible by all processors in the SoC,
++ * with a table of contents data structure (@smem_header) at the beginning of
++ * the main shared memory block.
++ *
++ * The global header contains metadata for allocations as well as a fixed list
++ * of 512 entries (@smem_global_entry) that can be initialized to reference
++ * parts of the shared memory space.
++ *
++ *
++ * In addition to this global heap a set of "private" heaps can be set up at
++ * boot time with access restrictions so that only certain processor pairs can
++ * access the data.
++ *
++ * These partitions are referenced from an optional partition table
++ * (@smem_ptable), that is found 4kB from the end of the main smem region. The
++ * partition table entries (@smem_ptable_entry) lists the involved processors
++ * (or hosts) and their location in the main shared memory region.
++ *
++ * Each partition starts with a header (@smem_partition_header) that identifies
++ * the partition and holds properties for the two internal memory regions. The
++ * two regions are cached and non-cached memory respectively. Each region
++ * contain a link list of allocation headers (@smem_private_entry) followed by
++ * their data.
++ *
++ * Items in the non-cached region are allocated from the start of the partition
++ * while items in the cached region are allocated from the end. The free area
++ * is hence the region between the cached and non-cached offsets.
++ *
++ *
++ * To synchronize allocations in the shared memory heaps a remote spinlock must
++ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
++ * platforms.
++ *
++ */
++
++/**
++ * struct smem_proc_comm - proc_comm communication struct (legacy)
++ * @command: current command to be executed
++ * @status: status of the currently requested command
++ * @params: parameters to the command
++ */
++struct smem_proc_comm {
++ u32 command;
++ u32 status;
++ u32 params[2];
++};
++
++/**
++ * struct smem_global_entry - entry to reference smem items on the heap
++ * @allocated: boolean to indicate if this entry is used
++ * @offset: offset to the allocated space
++ * @size: size of the allocated space, 8 byte aligned
++ * @aux_base: base address for the memory region used by this unit, or 0 for
++ * the default region. bits 0,1 are reserved
++ */
++struct smem_global_entry {
++ u32 allocated;
++ u32 offset;
++ u32 size;
++ u32 aux_base; /* bits 1:0 reserved */
++};
++#define AUX_BASE_MASK 0xfffffffc
++
++/**
++ * struct smem_header - header found in beginning of primary smem region
++ * @proc_comm: proc_comm communication interface (legacy)
++ * @version: array of versions for the various subsystems
++ * @initialized: boolean to indicate that smem is initialized
++ * @free_offset: index of the first unallocated byte in smem
++ * @available: number of bytes available for allocation
++ * @reserved: reserved field, must be 0
++ * toc: array of references to items
++ */
++struct smem_header {
++ struct smem_proc_comm proc_comm[4];
++ u32 version[32];
++ u32 initialized;
++ u32 free_offset;
++ u32 available;
++ u32 reserved;
++ struct smem_global_entry toc[];
++};
++
++/**
++ * struct smem_ptable_entry - one entry in the @smem_ptable list
++ * @offset: offset, within the main shared memory region, of the partition
++ * @size: size of the partition
++ * @flags: flags for the partition (currently unused)
++ * @host0: first processor/host with access to this partition
++ * @host1: second processor/host with access to this partition
++ * @reserved: reserved entries for later use
++ */
++struct smem_ptable_entry {
++ u32 offset;
++ u32 size;
++ u32 flags;
++ u16 host0;
++ u16 host1;
++ u32 reserved[8];
++};
++
++/**
++ * struct smem_ptable - partition table for the private partitions
++ * @magic: magic number, must be SMEM_PTABLE_MAGIC
++ * @version: version of the partition table
++ * @num_entries: number of partitions in the table
++ * @reserved: for now reserved entries
++ * @entry: list of @smem_ptable_entry for the @num_entries partitions
++ */
++struct smem_ptable {
++ u32 magic;
++ u32 version;
++ u32 num_entries;
++ u32 reserved[5];
++ struct smem_ptable_entry entry[];
++};
++#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$TOC" */
++
++/**
++ * struct smem_partition_header - header of the partitions
++ * @magic: magic number, must be SMEM_PART_MAGIC
++ * @host0: first processor/host with access to this partition
++ * @host1: second processor/host with access to this partition
++ * @size: size of the partition
++ * @offset_free_uncached: offset to the first free byte of uncached memory in
++ * this partition
++ * @offset_free_cached: offset to the first free byte of cached memory in this
++ * partition
++ * @reserved: for now reserved entries
++ */
++struct smem_partition_header {
++ u32 magic;
++ u16 host0;
++ u16 host1;
++ u32 size;
++ u32 offset_free_uncached;
++ u32 offset_free_cached;
++ u32 reserved[3];
++};
++#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */
++
++/**
++ * struct smem_private_entry - header of each item in the private partition
++ * @canary: magic number, must be SMEM_PRIVATE_CANARY
++ * @item: identifying number of the smem item
++ * @size: size of the data, including padding bytes
++ * @padding_data: number of bytes of padding of data
++ * @padding_hdr: number of bytes of padding between the header and the data
++ * @reserved: for now reserved entry
++ */
++struct smem_private_entry {
++ u16 canary;
++ u16 item;
++ u32 size; /* includes padding bytes */
++ u16 padding_data;
++ u16 padding_hdr;
++ u32 reserved;
++};
++#define SMEM_PRIVATE_CANARY 0xa5a5
++
++/*
++ * Item 3 of the global heap contains an array of versions for the various
++ * software components in the SoC. We verify that the boot loader version is
++ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
++ */
++#define SMEM_ITEM_VERSION 3
++#define SMEM_MASTER_SBL_VERSION_INDEX 7
++#define SMEM_EXPECTED_VERSION 11
++
++/*
++ * The first 8 items are only to be allocated by the boot loader while
++ * initializing the heap.
++ */
++#define SMEM_ITEM_LAST_FIXED 8
++
++/* Highest accepted item number, for both global and private heaps */
++#define SMEM_ITEM_LAST 512
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS 0
++
++/* Max number of processors/hosts in a system */
++#define SMEM_HOST_COUNT 7
++
++/**
++ * struct smem_region - representation of a chunk of memory used for smem
++ * @aux_base: identifier of aux_mem base
++ * @virt_base: virtual base address of memory with this aux_mem identifier
++ * @size: size of the memory region
++ */
++struct smem_region {
++ u32 aux_base;
++ void __iomem *virt_base;
++ size_t size;
++};
++
++/**
++ * struct qcom_smem - device data for the smem device
++ * @dev: device pointer
++ * @hwlock: reference to a hwspinlock
++ * @partitions: list of pointers to partitions affecting the current
++ * processor/host
++ * @num_regions: number of @regions
++ * @regions: list of the memory regions defining the shared memory
++ */
++struct qcom_smem {
++ struct device *dev;
++
++ struct hwspinlock *hwlock;
++
++ struct smem_partition_header *partitions[SMEM_HOST_COUNT];
++
++ unsigned num_regions;
++ struct smem_region regions[0];
++};
++
++/* Pointer to the one and only smem handle */
++static struct qcom_smem *__smem;
++
++/* Timeout (ms) for the trylock of remote spinlocks */
++#define HWSPINLOCK_TIMEOUT 1000
++
++static int qcom_smem_alloc_private(struct qcom_smem *smem,
++ unsigned host,
++ unsigned item,
++ size_t size)
++{
++ struct smem_partition_header *phdr;
++ struct smem_private_entry *hdr;
++ size_t alloc_size;
++ void *p;
++
++ /* We're not going to find it if there's no matching partition */
++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++ return -ENOENT;
++
++ phdr = smem->partitions[host];
++
++ p = (void *)phdr + sizeof(*phdr);
++ while (p < (void *)phdr + phdr->offset_free_uncached) {
++ hdr = p;
++
++ if (hdr->canary != SMEM_PRIVATE_CANARY) {
++ dev_err(smem->dev,
++ "Found invalid canary in host %d partition\n",
++ host);
++ return -EINVAL;
++ }
++
++ if (hdr->item == item)
++ return -EEXIST;
++
++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++ }
++
++ /* Check that we don't grow into the cached region */
++ alloc_size = sizeof(*hdr) + ALIGN(size, 8);
++ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) {
++ dev_err(smem->dev, "Out of memory\n");
++ return -ENOSPC;
++ }
++
++ hdr = p;
++ hdr->canary = SMEM_PRIVATE_CANARY;
++ hdr->item = item;
++ hdr->size = ALIGN(size, 8);
++ hdr->padding_data = hdr->size - size;
++ hdr->padding_hdr = 0;
++
++ /*
++ * Ensure the header is written before we advance the free offset, so
++ * that remote processors that does not take the remote spinlock still
++ * gets a consistent view of the linked list.
++ */
++ wmb();
++ phdr->offset_free_uncached += alloc_size;
++
++ return 0;
++}
++
++static int qcom_smem_alloc_global(struct qcom_smem *smem,
++ unsigned item,
++ size_t size)
++{
++ struct smem_header *header;
++ struct smem_global_entry *entry;
++
++ if (WARN_ON(item >= SMEM_ITEM_LAST))
++ return -EINVAL;
++
++ header = smem->regions[0].virt_base;
++ entry = &header->toc[item];
++ if (entry->allocated)
++ return -EEXIST;
++
++ size = ALIGN(size, 8);
++ if (WARN_ON(size > header->available))
++ return -ENOMEM;
++
++ entry->offset = header->free_offset;
++ entry->size = size;
++
++ /*
++ * Ensure the header is consistent before we mark the item allocated,
++ * so that remote processors will get a consistent view of the item
++ * even though they do not take the spinlock on read.
++ */
++ wmb();
++ entry->allocated = 1;
++
++ header->free_offset += size;
++ header->available -= size;
++
++ return 0;
++}
++
++/**
++ * qcom_smem_alloc - allocate space for a smem item
++ * @host: remote processor id, or -1
++ * @item: smem item handle
++ * @size: number of bytes to be allocated
++ *
++ * Allocate space for a given smem item of size @size, given that the item is
++ * not yet allocated.
++ */
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
++{
++ unsigned long flags;
++ int ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ if (item < SMEM_ITEM_LAST_FIXED) {
++ dev_err(__smem->dev,
++ "Rejecting allocation of static entry %d\n", item);
++ return -EINVAL;
++ }
++
++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++ HWSPINLOCK_TIMEOUT,
++ &flags);
++ if (ret)
++ return ret;
++
++ ret = qcom_smem_alloc_private(__smem, host, item, size);
++ if (ret == -ENOENT)
++ ret = qcom_smem_alloc_global(__smem, item, size);
++
++ hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++
++ return ret;
++}
++EXPORT_SYMBOL(qcom_smem_alloc);
++
++static int qcom_smem_get_global(struct qcom_smem *smem,
++ unsigned item,
++ void **ptr,
++ size_t *size)
++{
++ struct smem_header *header;
++ struct smem_region *area;
++ struct smem_global_entry *entry;
++ u32 aux_base;
++ unsigned i;
++
++ if (WARN_ON(item >= SMEM_ITEM_LAST))
++ return -EINVAL;
++
++ header = smem->regions[0].virt_base;
++ entry = &header->toc[item];
++ if (!entry->allocated)
++ return -ENXIO;
++
++ if (ptr != NULL) {
++ aux_base = entry->aux_base & AUX_BASE_MASK;
++
++ for (i = 0; i < smem->num_regions; i++) {
++ area = &smem->regions[i];
++
++ if (area->aux_base == aux_base || !aux_base) {
++ *ptr = area->virt_base + entry->offset;
++ break;
++ }
++ }
++ }
++ if (size != NULL)
++ *size = entry->size;
++
++ return 0;
++}
++
++static int qcom_smem_get_private(struct qcom_smem *smem,
++ unsigned host,
++ unsigned item,
++ void **ptr,
++ size_t *size)
++{
++ struct smem_partition_header *phdr;
++ struct smem_private_entry *hdr;
++ void *p;
++
++ /* We're not going to find it if there's no matching partition */
++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++ return -ENOENT;
++
++ phdr = smem->partitions[host];
++
++ p = (void *)phdr + sizeof(*phdr);
++ while (p < (void *)phdr + phdr->offset_free_uncached) {
++ hdr = p;
++
++ if (hdr->canary != SMEM_PRIVATE_CANARY) {
++ dev_err(smem->dev,
++ "Found invalid canary in host %d partition\n",
++ host);
++ return -EINVAL;
++ }
++
++ if (hdr->item == item) {
++ if (ptr != NULL)
++ *ptr = p + sizeof(*hdr) + hdr->padding_hdr;
++
++ if (size != NULL)
++ *size = hdr->size - hdr->padding_data;
++
++ return 0;
++ }
++
++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++ }
++
++ return -ENOENT;
++}
++
++/**
++ * qcom_smem_get - resolve ptr of size of a smem item
++ * @host: the remote processor, or -1
++ * @item: smem item handle
++ * @ptr: pointer to be filled out with address of the item
++ * @size: pointer to be filled out with size of the item
++ *
++ * Looks up pointer and size of a smem item.
++ */
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size)
++{
++ unsigned long flags;
++ int ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++ HWSPINLOCK_TIMEOUT,
++ &flags);
++ if (ret)
++ return ret;
++
++ ret = qcom_smem_get_private(__smem, host, item, ptr, size);
++ if (ret == -ENOENT)
++ ret = qcom_smem_get_global(__smem, item, ptr, size);
++
++ hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++ return ret;
++
++}
++EXPORT_SYMBOL(qcom_smem_get);
++
++/**
++ * qcom_smem_get_free_space - retrieve amont of free space in a partition
++ * @host: the remote processor identifing a partition, or -1
++ *
++ * To be used by smem clients as a quick way to determine if any new
++ * allocations has been made.
++ */
++int qcom_smem_get_free_space(unsigned host)
++{
++ struct smem_partition_header *phdr;
++ struct smem_header *header;
++ unsigned ret;
++
++ if (!__smem)
++ return -EPROBE_DEFER;
++
++ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
++ phdr = __smem->partitions[host];
++ ret = phdr->offset_free_uncached;
++ } else {
++ header = __smem->regions[0].virt_base;
++ ret = header->available;
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(qcom_smem_get_free_space);
++
++static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
++{
++ unsigned *versions;
++ size_t size;
++ int ret;
++
++ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION,
++ (void **)&versions, &size);
++ if (ret < 0) {
++ dev_err(smem->dev, "Unable to read the version item\n");
++ return -ENOENT;
++ }
++
++ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
++ dev_err(smem->dev, "Version item is too small\n");
++ return -EINVAL;
++ }
++
++ return versions[SMEM_MASTER_SBL_VERSION_INDEX];
++}
++
++static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
++ unsigned local_host)
++{
++ struct smem_partition_header *header;
++ struct smem_ptable_entry *entry;
++ struct smem_ptable *ptable;
++ unsigned remote_host;
++ int i;
++
++ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024;
++ if (ptable->magic != SMEM_PTABLE_MAGIC)
++ return 0;
++
++ if (ptable->version != 1) {
++ dev_err(smem->dev,
++ "Unsupported partition header version %d\n",
++ ptable->version);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < ptable->num_entries; i++) {
++ entry = &ptable->entry[i];
++
++ if (entry->host0 != local_host && entry->host1 != local_host)
++ continue;
++
++ if (!entry->offset)
++ continue;
++
++ if (!entry->size)
++ continue;
++
++ if (entry->host0 == local_host)
++ remote_host = entry->host1;
++ else
++ remote_host = entry->host0;
++
++ if (smem->partitions[remote_host]) {
++ dev_err(smem->dev,
++ "Already found a partition for host %d\n",
++ remote_host);
++ return -EINVAL;
++ }
++
++ header = smem->regions[0].virt_base + entry->offset;
++
++ if (header->magic != SMEM_PART_MAGIC) {
++ dev_err(smem->dev,
++ "Partition %d has invalid magic\n", i);
++ return -EINVAL;
++ }
++
++ if (header->host0 != local_host && header->host1 != local_host) {
++ dev_err(smem->dev,
++ "Partition %d hosts are invalid\n", i);
++ return -EINVAL;
++ }
++
++ if (header->host0 != remote_host && header->host1 != remote_host) {
++ dev_err(smem->dev,
++ "Partition %d hosts are invalid\n", i);
++ return -EINVAL;
++ }
++
++ if (header->size != entry->size) {
++ dev_err(smem->dev,
++ "Partition %d has invalid size\n", i);
++ return -EINVAL;
++ }
++
++ if (header->offset_free_uncached > header->size) {
++ dev_err(smem->dev,
++ "Partition %d has invalid free pointer\n", i);
++ return -EINVAL;
++ }
++
++ smem->partitions[remote_host] = header;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_count_mem_regions(struct platform_device *pdev)
++{
++ struct resource *res;
++ int num_regions = 0;
++ int i;
++
++ for (i = 0; i < pdev->num_resources; i++) {
++ res = &pdev->resource[i];
++
++ if (resource_type(res) == IORESOURCE_MEM)
++ num_regions++;
++ }
++
++ return num_regions;
++}
++
++static int qcom_smem_probe(struct platform_device *pdev)
++{
++ struct smem_header *header;
++ struct device_node *np;
++ struct qcom_smem *smem;
++ struct resource *res;
++ struct resource r;
++ size_t array_size;
++ int num_regions = 0;
++ int hwlock_id;
++ u32 version;
++ int ret;
++ int i;
++
++ num_regions = qcom_smem_count_mem_regions(pdev) + 1;
++
++ array_size = num_regions * sizeof(struct smem_region);
++ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL);
++ if (!smem)
++ return -ENOMEM;
++
++ smem->dev = &pdev->dev;
++ smem->num_regions = num_regions;
++
++ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
++ if (!np) {
++ dev_err(&pdev->dev, "No memory-region specified\n");
++ return -EINVAL;
++ }
++
++ ret = of_address_to_resource(np, 0, &r);
++ of_node_put(np);
++ if (ret)
++ return ret;
++
++ smem->regions[0].aux_base = (u32)r.start;
++ smem->regions[0].size = resource_size(&r);
++ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev,
++ r.start,
++ resource_size(&r));
++ if (!smem->regions[0].virt_base)
++ return -ENOMEM;
++
++ for (i = 1; i < num_regions; i++) {
++ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1);
++
++ smem->regions[i].aux_base = (u32)res->start;
++ smem->regions[i].size = resource_size(res);
++ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev,
++ res->start,
++ resource_size(res));
++ if (!smem->regions[i].virt_base)
++ return -ENOMEM;
++ }
++
++ header = smem->regions[0].virt_base;
++ if (header->initialized != 1 || header->reserved) {
++ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n");
++ return -EINVAL;
++ }
++
++ version = qcom_smem_get_sbl_version(smem);
++ if (version >> 16 != SMEM_EXPECTED_VERSION) {
++ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version);
++ return -EINVAL;
++ }
++
++ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
++ if (ret < 0)
++ return ret;
++
++ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
++ if (hwlock_id < 0) {
++ dev_err(&pdev->dev, "failed to retrieve hwlock\n");
++ return hwlock_id;
++ }
++
++ smem->hwlock = hwspin_lock_request_specific(hwlock_id);
++ if (!smem->hwlock)
++ return -ENXIO;
++
++ __smem = smem;
++
++ return 0;
++}
++
++static int qcom_smem_remove(struct platform_device *pdev)
++{
++ hwspin_lock_free(__smem->hwlock);
++ __smem = NULL;
++
++ return 0;
++}
++
++static const struct of_device_id qcom_smem_of_match[] = {
++ { .compatible = "qcom,smem" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
++
++static struct platform_driver qcom_smem_driver = {
++ .probe = qcom_smem_probe,
++ .remove = qcom_smem_remove,
++ .driver = {
++ .name = "qcom_smem",
++ .of_match_table = qcom_smem_of_match,
++ .suppress_bind_attrs = true,
++ },
++};
++
++static int __init qcom_smem_init(void)
++{
++ return platform_driver_register(&qcom_smem_driver);
++}
++arch_initcall(qcom_smem_init);
++
++static void __exit qcom_smem_exit(void)
++{
++ platform_driver_unregister(&qcom_smem_driver);
++}
++module_exit(qcom_smem_exit)
++
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
++MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
++MODULE_LICENSE("GPLv2");
+--- /dev/null
++++ b/include/linux/soc/qcom/smem.h
+@@ -0,0 +1,14 @@
++#ifndef __QCOM_SMEM_H__
++#define __QCOM_SMEM_H__
++
++struct device_node;
++struct qcom_smem;
++
++#define QCOM_SMEM_HOST_ANY -1
++
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size);
++
++int qcom_smem_get_free_space(unsigned host);
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..b55f174
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
@@ -0,0 +1,36 @@
+From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 14 Aug 2015 11:17:20 -0700
+Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts
+
+SMEM is used on IPQ806x to store various board related information such
+as boot device and flash partition layout. We'll declare it as a device
+so we can make use of it thanks to the new SMEM soc driver.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -55,7 +55,7 @@
+ no-map;
+ };
+
+- smem@41000000 {
++ smem: smem@41000000 {
+ reg = <0x41000000 0x200000>;
+ no-map;
+ };
+@@ -341,4 +341,10 @@
+
+ #hwlock-cells = <1>;
+ };
++
++ smem {
++ compatible = "qcom,smem";
++ memory-region = <&smem>;
++ hwlocks = <&sfpb_mutex 3>;
++ };
+ };
diff --git a/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
new file mode 100644
index 0000000..b2c8cd5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
@@ -0,0 +1,277 @@
+From 0501f76b138cf1dc11a313bb7a094da524b79337 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:53:14 -0700
+Subject: [PATCH 3/3] mtd: add SMEM parser for QCOM platforms
+
+On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
+used to store partition layout. This new parser can now be used to read
+SMEM and use it to register an MTD layout according to its content.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/mtd/Kconfig | 7 ++
+ drivers/mtd/Makefile | 1 +
+ drivers/mtd/qcom_smem_part.c | 231 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 239 insertions(+)
+ create mode 100644 drivers/mtd/qcom_smem_part.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -200,6 +200,13 @@ config MTD_MYLOADER_PARTS
+ You will still need the parsing functions to be called by the driver
+ for your particular device. It won't happen automatically.
+
++config MTD_QCOM_SMEM_PARTS
++ tristate "QCOM SMEM partitioning support"
++ depends on QCOM_SMEM
++ help
++ This provides partitions parser for QCOM devices using SMEM
++ such as IPQ806x.
++
+ comment "User Modules And Translation Layers"
+
+ #
+--- /dev/null
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -0,0 +1,231 @@
++/*
++ * Copyright (c) 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/device.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/spi/spi.h>
++#include <linux/module.h>
++
++#include <linux/soc/qcom/smem.h>
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS 0
++
++/* SMEM items index */
++#define SMEM_AARM_PARTITION_TABLE 9
++#define SMEM_BOOT_FLASH_TYPE 421
++#define SMEM_BOOT_FLASH_BLOCK_SIZE 424
++
++/* SMEM Flash types */
++#define SMEM_FLASH_NAND 2
++#define SMEM_FLASH_SPI 6
++
++#define SMEM_PART_NAME_SZ 16
++#define SMEM_PARTS_MAX 32
++
++struct smem_partition {
++ char name[SMEM_PART_NAME_SZ];
++ __le32 start;
++ __le32 size;
++ __le32 attr;
++};
++
++struct smem_partition_table {
++ u8 magic[8];
++ __le32 version;
++ __le32 len;
++ struct smem_partition parts[SMEM_PARTS_MAX];
++};
++
++/* SMEM Magic values in partition table */
++static const u8 SMEM_PTABLE_MAGIC[] = {
++ 0xaa, 0x73, 0xee, 0x55,
++ 0xdb, 0xbd, 0x5e, 0xe3,
++};
++
++static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
++ (void **) smem_blksz, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read flash blksz from SMEM\n");
++ return -ENOENT;
++ }
++
++ if (size != sizeof(**smem_blksz)) {
++ pr_err("Invalid flash blksz size in SMEM\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_get_flash_type(u64 **smem_flash_type)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
++ (void **) smem_flash_type, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read flash type from SMEM\n");
++ return -ENOENT;
++ }
++
++ if (size != sizeof(**smem_flash_type)) {
++ pr_err("Invalid flash type size in SMEM\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
++{
++ int ret;
++ size_t size;
++
++ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
++ (void **) pparts, &size);
++
++ if (ret < 0) {
++ pr_err("Unable to read partition table from SMEM\n");
++ return -ENOENT;
++ }
++
++ return 0;
++}
++
++static int of_dev_node_match(struct device *dev, void *data)
++{
++ return dev->of_node == data;
++}
++
++static bool is_spi_device(struct device_node *np)
++{
++ struct device *dev;
++
++ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
++ if (!dev)
++ return false;
++
++ put_device(dev);
++ return true;
++}
++
++static int parse_qcom_smem_partitions(struct mtd_info *master,
++ struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct smem_partition_table *smem_parts;
++ u64 *smem_flash_type, *smem_blksz;
++ struct mtd_partition *mtd_parts;
++ struct device_node *of_node = data->of_node;
++ int i, ret;
++
++ /*
++ * SMEM will only store the partition table of the boot device.
++ * If this is not the boot device, do not return any partition.
++ */
++ ret = qcom_smem_get_flash_type(&smem_flash_type);
++ if (ret < 0)
++ return ret;
++
++ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
++ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
++ return 0;
++
++ /*
++ * Just for sanity purpose, make sure the block size in SMEM matches the
++ * block size of the MTD device
++ */
++ ret = qcom_smem_get_flash_blksz(&smem_blksz);
++ if (ret < 0)
++ return ret;
++
++ if (*smem_blksz != master->erasesize) {
++ pr_err("SMEM block size differs from MTD block size\n");
++ return -EINVAL;
++ }
++
++ /* Get partition pointer from SMEM */
++ ret = qcom_smem_get_flash_partitions(&smem_parts);
++ if (ret < 0)
++ return ret;
++
++ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
++ sizeof(SMEM_PTABLE_MAGIC))) {
++ pr_err("SMEM partition magic invalid\n");
++ return -EINVAL;
++ }
++
++ /* Allocate and populate the mtd structures */
++ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
++ GFP_KERNEL);
++ if (!mtd_parts)
++ return -ENOMEM;
++
++ for (i = 0; i < smem_parts->len; i++) {
++ struct smem_partition *s_part = &smem_parts->parts[i];
++ struct mtd_partition *m_part = &mtd_parts[i];
++
++ m_part->name = s_part->name;
++ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
++ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
++
++ /*
++ * The last SMEM partition may have its size marked as
++ * something like 0xffffffff, which means "until the end of the
++ * flash device". In this case, truncate it.
++ */
++ if (m_part->offset + m_part->size > master->size)
++ m_part->size = master->size - m_part->offset;
++ }
++
++ *pparts = mtd_parts;
++
++ return smem_parts->len;
++}
++
++static struct mtd_part_parser qcom_smem_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_qcom_smem_partitions,
++ .name = "qcom-smem",
++};
++
++static int __init qcom_smem_parser_init(void)
++{
++ register_mtd_parser(&qcom_smem_parser);
++ return 0;
++}
++
++static void __exit qcom_smem_parser_exit(void)
++{
++ deregister_mtd_parser(&qcom_smem_parser);
++}
++
++module_init(qcom_smem_parser_init);
++module_exit(qcom_smem_parser_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+ obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
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 0000000..41f91fa
--- /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 0000000..dbf16a3
--- /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 0000000..77cf3c3
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,266 @@
+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
+@@ -35,6 +35,24 @@
+ bias-disable;
+ };
+
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
+ spi_pins: spi_pins {
+ mux {
+ pins = "gpio18", "gpio19", "gpio21";
+@@ -91,5 +109,19 @@
+ sata@29000000 {
+ status = "ok";
+ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_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,33 @@
+ bias-disable;
+ };
+
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie2_pins: pcie2_pinmux {
++ mux {
++ pins = "gpio63";
++ function = "pcie3_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
+ spi_pins: spi_pins {
+ mux {
+ pins = "gpio18", "gpio19", "gpio21";
+@@ -128,5 +155,26 @@
+ usb30@1 {
+ status = "ok";
+ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie2: pci@1b900000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 63 0>;
++ pinctrl-0 = <&pcie2_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 <dt-bindings/interrupt-controller/arm-gic.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -333,6 +335,129 @@
+ compatible = "syscon";
+ reg = <0x01200600 0x100>;
+ };
++
++ 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";
++ };
+ };
+
+ sfpb_mutex: sfpb-mutex {
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 0000000..e2d3135
--- /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/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.1/114-pcie-add-ctlr-init.patch
new file mode 100644
index 0000000..11c9810
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/114-pcie-add-ctlr-init.patch
@@ -0,0 +1,278 @@
+--- a/drivers/pci/host/pcie-qcom.c
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -29,8 +29,53 @@
+
+ #include "pcie-designware.h"
+
++/* DBI registers */
++#define PCIE20_CAP 0x70
++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
++
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c
++
++#define PCIE20_PLR_IATU_VIEWPORT 0x900
++#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31)
++#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0)
++
++#define PCIE20_PLR_IATU_CTRL1 0x904
++#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0)
++#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0)
++
++#define PCIE20_PLR_IATU_CTRL2 0x908
++#define PCIE20_PLR_IATU_ENABLE BIT(31)
++
++#define PCIE20_PLR_IATU_LBAR 0x90C
++#define PCIE20_PLR_IATU_UBAR 0x910
++#define PCIE20_PLR_IATU_LAR 0x914
++#define PCIE20_PLR_IATU_LTAR 0x918
++#define PCIE20_PLR_IATU_UTAR 0x91c
++
++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000
++
++/* PARF registers */
++#define PCIE20_PARF_PCS_DEEMPH 0x34
++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0)
++
++#define PCIE20_PARF_PCS_SWING 0x38
++#define PCS_SWING_TX_SWING_FULL(x) (x << 8)
++#define PCS_SWING_TX_SWING_LOW(x) (x << 0)
++
+ #define PCIE20_PARF_PHY_CTRL 0x40
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16)
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16)
++
+ #define PCIE20_PARF_PHY_REFCLK 0x4C
++#define REF_SSP_EN BIT(16)
++#define REF_USE_PAD BIT(12)
++
++#define PCIE20_PARF_CONFIG_BITS 0x50
++#define PHY_RX0_EQ(x) (x << 24)
++
+ #define PCIE20_PARF_DBI_BASE_ADDR 0x168
+ #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
+ #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
+@@ -39,9 +84,6 @@
+ #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
+
+@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 {
+ struct clk *iface_clk;
+ struct clk *core_clk;
+ struct clk *phy_clk;
++ struct clk *aux_clk;
++ struct clk *ref_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 reset_control *ext_reset;
+ struct regulator *vdda;
+ struct regulator *vdda_phy;
+ struct regulator *vdda_refclk;
++ uint8_t phy_tx0_term_offset;
+ };
+
+ struct qcom_pcie_resources_v1 {
+@@ -156,10 +202,13 @@ static void qcom_pcie_disable_resources_
+ reset_control_assert(res->axi_reset);
+ reset_control_assert(res->ahb_reset);
+ reset_control_assert(res->por_reset);
+- reset_control_assert(res->pci_reset);
++ reset_control_assert(res->phy_reset);
++ reset_control_assert(res->ext_reset);
+ clk_disable_unprepare(res->iface_clk);
+ clk_disable_unprepare(res->core_clk);
+ clk_disable_unprepare(res->phy_clk);
++ clk_disable_unprepare(res->aux_clk);
++ clk_disable_unprepare(res->ref_clk);
+ regulator_disable(res->vdda);
+ regulator_disable(res->vdda_phy);
+ regulator_disable(res->vdda_refclk);
+@@ -201,6 +250,12 @@ static int qcom_pcie_enable_resources_v0
+ goto err_vdda_phy;
+ }
+
++ ret = reset_control_deassert(res->ext_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert ext reset\n");
++ goto err_reset_ext;
++ }
++
+ ret = clk_prepare_enable(res->iface_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable iface clock\n");
+@@ -219,6 +274,18 @@ static int qcom_pcie_enable_resources_v0
+ goto err_clk_phy;
+ }
+
++ ret = clk_prepare_enable(res->aux_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable aux clock\n");
++ goto err_clk_aux;
++ }
++
++ ret = clk_prepare_enable(res->ref_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable ref clock\n");
++ goto err_clk_ref;
++ }
++
+ ret = reset_control_deassert(res->ahb_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert ahb reset\n");
+@@ -228,12 +295,18 @@ static int qcom_pcie_enable_resources_v0
+ return 0;
+
+ err_reset_ahb:
++ clk_disable_unprepare(res->ref_clk);
++err_clk_ref:
++ clk_disable_unprepare(res->aux_clk);
++err_clk_aux:
+ 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:
++ reset_control_assert(res->ext_reset);
++err_reset_ext:
+ regulator_disable(res->vdda_phy);
+ err_vdda_phy:
+ regulator_disable(res->vdda_refclk);
+@@ -329,6 +402,14 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_clk))
+ return PTR_ERR(res->phy_clk);
+
++ res->aux_clk = devm_clk_get(dev, "aux");
++ if (IS_ERR(res->aux_clk))
++ return PTR_ERR(res->aux_clk);
++
++ res->ref_clk = devm_clk_get(dev, "ref");
++ if (IS_ERR(res->ref_clk))
++ return PTR_ERR(res->ref_clk);
++
+ res->pci_reset = devm_reset_control_get(dev, "pci");
+ if (IS_ERR(res->pci_reset))
+ return PTR_ERR(res->pci_reset);
+@@ -349,6 +430,14 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_reset))
+ return PTR_ERR(res->phy_reset);
+
++ res->ext_reset = devm_reset_control_get(dev, "ext");
++ if (IS_ERR(res->ext_reset))
++ return PTR_ERR(res->ext_reset);
++
++ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
++ &res->phy_tx0_term_offset))
++ res->phy_tx0_term_offset = 0;
++
+ return 0;
+ }
+
+@@ -461,6 +550,57 @@ err_res:
+ qcom_pcie_disable_resources_v1(pcie);
+ }
+
++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev)
++{
++ struct pcie_port *pp = &pcie->pp;
++
++ /*
++ * program and enable address translation region 0 (device config
++ * address space); region type config;
++ * axi config address range to device config address range
++ */
++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++ PCIE20_PLR_IATU_REGION_INDEX(0),
++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++ writel(pp->cfg0_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++ writel((pp->cfg0_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++ writel((pp->cfg0_mod_base + pp->cfg0_size - 1),
++ pcie->dbi + PCIE20_PLR_IATU_LAR);
++ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR);
++}
++
++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie)
++{
++ struct pcie_port *pp = &pcie->pp;
++
++ /*
++ * program and enable address translation region 2 (device resource
++ * address space); region type memory;
++ * axi device bar address range to device bar address range
++ */
++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++ PCIE20_PLR_IATU_REGION_INDEX(2),
++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++ writel(pp->mem_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++ writel((pp->mem_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++ writel(pp->mem_mod_base + pp->mem_size - 1,
++ pcie->dbi + PCIE20_PLR_IATU_LAR);
++ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++ writel(upper_32_bits(pp->mem_bus_addr),
++ pcie->dbi + PCIE20_PLR_IATU_UTAR);
++
++ /* 1K PCIE buffer setting */
++ writel(0x3, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
++}
++
+ static void qcom_pcie_host_init_v0(struct pcie_port *pp)
+ {
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
+@@ -476,9 +616,26 @@ static void qcom_pcie_host_init_v0(struc
+
+ 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));
++ /* Set Tx termination offset */
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL,
++ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
++ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
++
++ /* PARF programming */
++ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
++ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
++ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
++ pcie->parf + PCIE20_PARF_PCS_DEEMPH);
++ writel(PCS_SWING_TX_SWING_FULL(0x78) |
++ PCS_SWING_TX_SWING_LOW(0x78),
++ pcie->parf + PCIE20_PARF_PCS_SWING);
++ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
++
++ /* Enable reference clock */
++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK,
++ REF_USE_PAD, REF_SSP_EN);
+
++ /* De-assert PHY, PCIe, POR and AXI resets */
+ ret = reset_control_deassert(res->phy_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert phy reset\n");
+@@ -517,6 +674,9 @@ static void qcom_pcie_host_init_v0(struc
+ if (ret)
+ goto err;
+
++ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR);
++ qcom_pcie_prog_viewport_mem2_outbound(pcie);
++
+ return;
+ err:
+ qcom_ep_reset_assert(pcie);
diff --git a/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch
new file mode 100644
index 0000000..d01e5a8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch
@@ -0,0 +1,80 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -364,15 +364,21 @@
+
+ clocks = <&gcc PCIE_A_CLK>,
+ <&gcc PCIE_H_CLK>,
+- <&gcc PCIE_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_PHY_CLK>,
++ <&gcc PCIE_AUX_CLK>,
++ <&gcc PCIE_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_PHY_RESET>,
++ <&gcc PCIE_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
+@@ -405,15 +411,21 @@
+
+ clocks = <&gcc PCIE_1_A_CLK>,
+ <&gcc PCIE_1_H_CLK>,
+- <&gcc PCIE_1_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_1_PHY_CLK>,
++ <&gcc PCIE_1_AUX_CLK>,
++ <&gcc PCIE_1_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_1_PHY_RESET>,
++ <&gcc PCIE_1_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
+@@ -446,15 +458,21 @@
+
+ clocks = <&gcc PCIE_2_A_CLK>,
+ <&gcc PCIE_2_H_CLK>,
+- <&gcc PCIE_2_PHY_CLK>;
+- clock-names = "core", "iface", "phy";
++ <&gcc PCIE_2_PHY_CLK>,
++ <&gcc PCIE_2_AUX_CLK>,
++ <&gcc PCIE_2_ALT_REF_CLK>;
++ clock-names = "core", "iface", "phy", "aux", "ref";
++
++ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>;
++ assigned-clock-rates = <100000000>;
+
+ 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";
++ <&gcc PCIE_2_PHY_RESET>,
++ <&gcc PCIE_2_EXT_RESET>;
++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+
+ status = "disabled";
+ };
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 0000000..be80f4c
--- /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 0000000..36a92c8
--- /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 0000000..3a475fb
--- /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 0000000..143e079
--- /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
+@@ -1744,21 +1744,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);
+@@ -1784,7 +1787,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);
+@@ -1797,12 +1800,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,
+@@ -1811,6 +1815,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;
+@@ -1836,8 +1841,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 0000000..b0d89a9
--- /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;
+@@ -1596,7 +1599,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;
+@@ -1606,6 +1610,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);
+@@ -1710,14 +1726,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;
+ }
+
+@@ -1763,7 +1808,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);
+
+@@ -1789,9 +1835,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.
+@@ -1851,6 +1894,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 0000000..6fad6e8
--- /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 0000000..4056784
--- /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 0000000..ffcd462
--- /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 0000000..5fcc787
--- /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 0000000..db43888
--- /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 0000000..59fc44f
--- /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 0000000..fd37c05
--- /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 0000000..9435de6
--- /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>;
+ };
+ };
+
+@@ -72,6 +90,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>;
+@@ -187,11 +245,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 0000000..84f856b
--- /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 = 0;
++ 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 = 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/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.1/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
new file mode 100644
index 0000000..4f5c0ef
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
@@ -0,0 +1,76 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 6027361
+Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org>
+To: Vinod Koul <vinod.koul@intel.com>
+Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org,
+ linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+ linux-arm-kernel@lists.infradead.org, Kumar Gala <galak@codeaurora.org>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ Andy Gross <agross@codeaurora.org>
+Date: Tue, 17 Mar 2015 00:46:11 -0500
+
+This patch removes the crci information from the dma channel property. At least
+one client device requires using more than one CRCI value for a channel. This
+does not match the current binding and the crci information needs to be removed.
+
+Instead, the client device will provide this information via other means.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
+ 1 file changed, 6 insertions(+), 10 deletions(-)
+
+--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt
+@@ -4,8 +4,7 @@ Required properties:
+ - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960
+ - reg: Address range for DMA registers
+ - interrupts: Should contain one interrupt shared by all channels
+-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell
+- denotes CRCI (client rate control interface) flow control assignment.
++- #dma-cells: must be <1>. First cell denotes the channel number.
+ - clocks: Should contain the core clock and interface clock.
+ - clock-names: Must contain "core" for the core clock and "iface" for the
+ interface clock.
+@@ -22,7 +21,7 @@ Example:
+ compatible = "qcom,adm";
+ reg = <0x18300000 0x100000>;
+ interrupts = <0 170 0>;
+- #dma-cells = <2>;
++ #dma-cells = <1>;
+
+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+ clock-names = "core", "iface";
+@@ -35,15 +34,12 @@ Example:
+ qcom,ee = <0>;
+ };
+
+-DMA clients must use the format descripted in the dma.txt file, using a three
++DMA clients must use the format descripted in the dma.txt file, using a two
+ cell specifier for each channel.
+
+-Each dmas request consists of 3 cells:
++Each dmas request consists of two cells:
+ 1. phandle pointing to the DMA controller
+ 2. channel number
+- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0.
+- The CRCI is used for flow control. It identifies the peripheral device that
+- is the source/destination for the transferred data.
+
+ Example:
+
+@@ -56,7 +52,7 @@ Example:
+
+ cs-gpios = <&qcom_pinmux 20 0>;
+
+- dmas = <&adm_dma 6 9>,
+- <&adm_dma 5 10>;
++ dmas = <&adm_dma 6>,
++ <&adm_dma 5>;
+ dma-names = "rx", "tx";
+ };
diff --git a/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch
new file mode 100644
index 0000000..29e8eaa
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch
@@ -0,0 +1,962 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v6,2/2] dmaengine: Add ADM driver
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 6027351
+Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org>
+To: Vinod Koul <vinod.koul@intel.com>
+Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org,
+ linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+ linux-arm-kernel@lists.infradead.org, Kumar Gala <galak@codeaurora.org>,
+ Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+ Andy Gross <agross@codeaurora.org>
+Date: Tue, 17 Mar 2015 00:46:12 -0500
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8x60 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions. The controller also provides flow
+control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from peripherals
+and also incorporates CRCI (client rate control interface) flow control.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: sricharan <sricharan@codeaurora.org>
+
+---
+drivers/dma/Kconfig | 10 +
+ drivers/dma/Makefile | 1 +
+ drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 911 insertions(+)
+ create mode 100644 drivers/dma/qcom_adm.c
+
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -492,4 +492,14 @@ config QCOM_BAM_DMA
+ Enable support for the QCOM BAM DMA controller. This controller
+ provides DMA capabilities for a variety of on-chip devices.
+
++config QCOM_ADM
++ tristate "Qualcomm ADM support"
++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ ---help---
++ Enable support for the Qualcomm ADM DMA controller. This controller
++ provides DMA capabilities for both general purpose and on-chip
++ peripheral devices.
++
+ endif
+--- /dev/null
++++ b/drivers/dma/qcom_adm.c
+@@ -0,0 +1,900 @@
++/*
++ * 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/io.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/scatterlist.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_dma.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++#include <linux/dmaengine.h>
++
++#include "dmaengine.h"
++#include "virt-dma.h"
++
++/* ADM registers - calculated from channel number and security domain */
++#define ADM_CHAN_MULTI 0x4
++#define ADM_CI_MULTI 0x4
++#define ADM_CRCI_MULTI 0x4
++#define ADM_EE_MULTI 0x800
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee)
++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci))
++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan))
++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
++#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI)
++#define ADM_GP_CTL 0x3d8
++#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \
++ ADM_EE_OFFS(ee))
++
++/* channel status */
++#define ADM_CH_STATUS_VALID BIT(1)
++
++/* channel result */
++#define ADM_CH_RSLT_VALID BIT(31)
++#define ADM_CH_RSLT_ERR BIT(3)
++#define ADM_CH_RSLT_FLUSH BIT(2)
++#define ADM_CH_RSLT_TPD BIT(1)
++
++/* channel conf */
++#define ADM_CH_CONF_SHADOW_EN BIT(12)
++#define ADM_CH_CONF_MPU_DISABLE BIT(11)
++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9)
++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7)
++#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
++
++/* channel result conf */
++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1)
++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0)
++
++/* CRCI CTL */
++#define ADM_CRCI_CTL_MUX_SEL BIT(18)
++#define ADM_CRCI_CTL_RST BIT(17)
++
++/* CI configuration */
++#define ADM_CI_RANGE_END(x) (x << 24)
++#define ADM_CI_RANGE_START(x) (x << 16)
++#define ADM_CI_BURST_4_WORDS BIT(2)
++#define ADM_CI_BURST_8_WORDS BIT(3)
++
++/* GP CTL */
++#define ADM_GP_CTL_LP_EN BIT(12)
++#define ADM_GP_CTL_LP_CNT(x) (x << 8)
++
++/* Command pointer list entry */
++#define ADM_CPLE_LP BIT(31)
++#define ADM_CPLE_CMD_PTR_LIST BIT(29)
++
++/* Command list entry */
++#define ADM_CMD_LC BIT(31)
++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7)
++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3)
++
++#define ADM_CMD_TYPE_SINGLE 0x0
++#define ADM_CMD_TYPE_BOX 0x3
++
++#define ADM_CRCI_MUX_SEL BIT(4)
++#define ADM_DESC_ALIGN 8
++#define ADM_MAX_XFER (SZ_64K-1)
++#define ADM_MAX_ROWS (SZ_64K-1)
++#define ADM_MAX_CHANNELS 16
++
++struct adm_desc_hw_box {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 row_len;
++ u32 num_rows;
++ u32 row_offset;
++};
++
++struct adm_desc_hw_single {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 len;
++};
++
++struct adm_async_desc {
++ struct virt_dma_desc vd;
++ struct adm_device *adev;
++
++ size_t length;
++ enum dma_transfer_direction dir;
++ dma_addr_t dma_addr;
++ size_t dma_len;
++
++ void *cpl;
++ dma_addr_t cp_addr;
++ u32 crci;
++ u32 mux;
++ u32 blk_size;
++};
++
++struct adm_chan {
++ struct virt_dma_chan vc;
++ struct adm_device *adev;
++
++ /* parsed from DT */
++ u32 id; /* channel id */
++
++ struct adm_async_desc *curr_txd;
++ struct dma_slave_config slave;
++ struct list_head node;
++
++ int error;
++ int initialized;
++};
++
++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
++{
++ return container_of(common, struct adm_chan, vc.chan);
++}
++
++struct adm_device {
++ void __iomem *regs;
++ struct device *dev;
++ struct dma_device common;
++ struct device_dma_parameters dma_parms;
++ struct adm_chan *channels;
++
++ u32 ee;
++
++ struct clk *core_clk;
++ struct clk *iface_clk;
++
++ struct reset_control *clk_reset;
++ struct reset_control *c0_reset;
++ struct reset_control *c1_reset;
++ struct reset_control *c2_reset;
++ int irq;
++};
++
++/**
++ * adm_free_chan - Frees dma resources associated with the specific channel
++ *
++ * Free all allocated descriptors associated with this channel
++ *
++ */
++static void adm_free_chan(struct dma_chan *chan)
++{
++ /* free all queued descriptors */
++ vchan_free_chan_resources(to_virt_chan(chan));
++}
++
++/**
++ * adm_get_blksize - Get block size from burst value
++ *
++ */
++static int adm_get_blksize(unsigned int burst)
++{
++ int ret;
++
++ switch (burst) {
++ case 16:
++ case 32:
++ case 64:
++ case 128:
++ ret = ffs(burst>>4) - 1;
++ break;
++ case 192:
++ ret = 4;
++ break;
++ case 256:
++ ret = 5;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++/**
++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @crci: CRCI value
++ * @burst: Burst size of transaction
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg, u32 crci, u32 burst,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_box *box_desc = NULL;
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 rows, row_offset, crci_cmd;
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ crci_cmd = ADM_CMD_SRC_CRCI(crci);
++ row_offset = burst;
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ crci_cmd = ADM_CMD_DST_CRCI(crci);
++ row_offset = burst << 16;
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ while (remainder >= burst) {
++ box_desc = desc;
++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
++ box_desc->row_offset = row_offset;
++ box_desc->src_addr = *src;
++ box_desc->dst_addr = *dst;
++
++ rows = remainder / burst;
++ rows = min_t(u32, rows, ADM_MAX_ROWS);
++ box_desc->num_rows = rows << 16 | rows;
++ box_desc->row_len = burst << 16 | burst;
++
++ *incr_addr += burst * rows;
++ remainder -= burst * rows;
++ desc += sizeof(*box_desc);
++ }
++
++ /* if leftover bytes, do one single descriptor */
++ if (remainder) {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
++ single_desc->len = remainder;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ desc += sizeof(*single_desc);
++
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++ } else {
++ if (box_desc && sg_is_last(sg))
++ box_desc->cmd |= ADM_CMD_LC;
++ }
++
++ return desc;
++}
++
++/**
++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
++ void *desc, struct scatterlist *sg,
++ enum dma_transfer_direction direction)
++{
++ struct adm_desc_hw_single *single_desc;
++ u32 remainder = sg_dma_len(sg);
++ u32 mem_addr = sg_dma_address(sg);
++ u32 *incr_addr = &mem_addr;
++ u32 *src, *dst;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ src = &achan->slave.src_addr;
++ dst = &mem_addr;
++ } else {
++ src = &mem_addr;
++ dst = &achan->slave.dst_addr;
++ }
++
++ do {
++ single_desc = desc;
++ single_desc->cmd = ADM_CMD_TYPE_SINGLE;
++ single_desc->src_addr = *src;
++ single_desc->dst_addr = *dst;
++ single_desc->len = (remainder > ADM_MAX_XFER) ?
++ ADM_MAX_XFER : remainder;
++
++ remainder -= single_desc->len;
++ *incr_addr += single_desc->len;
++ desc += sizeof(*single_desc);
++ } while (remainder);
++
++ /* set last command if this is the end of the whole transaction */
++ if (sg_is_last(sg))
++ single_desc->cmd |= ADM_CMD_LC;
++
++ return desc;
++}
++
++/**
++ * adm_prep_slave_sg - Prep slave sg transaction
++ *
++ * @chan: dma channel
++ * @sgl: scatter gather list
++ * @sg_len: length of sg
++ * @direction: DMA transfer direction
++ * @flags: DMA flags
++ * @context: transfer context (unused)
++ */
++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
++ struct scatterlist *sgl, unsigned int sg_len,
++ enum dma_transfer_direction direction, unsigned long flags,
++ void *context)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++ struct scatterlist *sg;
++ u32 i, burst;
++ u32 single_count = 0, box_count = 0, crci = 0;
++ void *desc;
++ u32 *cple;
++ int blk_size = 0;
++
++ if (!is_slave_direction(direction)) {
++ dev_err(adev->dev, "invalid dma direction\n");
++ return NULL;
++ }
++
++ /*
++ * get burst value from slave configuration
++ */
++ burst = (direction == DMA_MEM_TO_DEV) ?
++ achan->slave.dst_maxburst :
++ achan->slave.src_maxburst;
++
++ /* if using flow control, validate burst and crci values */
++ if (achan->slave.device_fc) {
++
++ blk_size = adm_get_blksize(burst);
++ if (blk_size < 0) {
++ dev_err(adev->dev, "invalid burst value: %d\n",
++ burst);
++ return ERR_PTR(-EINVAL);
++ }
++
++ crci = achan->slave.slave_id & 0xf;
++ if (!crci || achan->slave.slave_id > 0x1f) {
++ dev_err(adev->dev, "invalid crci value\n");
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
++ /* iterate through sgs and compute allocation size of structures */
++ for_each_sg(sgl, sg, sg_len, i) {
++ if (achan->slave.device_fc) {
++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
++ ADM_MAX_ROWS);
++ if (sg_dma_len(sg) % burst)
++ single_count++;
++ } else {
++ single_count += DIV_ROUND_UP(sg_dma_len(sg),
++ ADM_MAX_XFER);
++ }
++ }
++
++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++ if (!async_desc)
++ return ERR_PTR(-ENOMEM);
++
++ if (crci)
++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
++ ADM_CRCI_CTL_MUX_SEL : 0;
++ async_desc->crci = crci;
++ async_desc->blk_size = blk_size;
++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
++ box_count * sizeof(struct adm_desc_hw_box) +
++ sizeof(*cple) + 2 * ADM_DESC_ALIGN;
++
++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len,
++ &async_desc->dma_addr, GFP_NOWAIT);
++
++ if (!async_desc->cpl) {
++ kfree(async_desc);
++ return ERR_PTR(-ENOMEM);
++ }
++
++ async_desc->adev = adev;
++
++ /* both command list entry and descriptors must be 8 byte aligned */
++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
++
++ /* init cmd list */
++ *cple = ADM_CPLE_LP;
++ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3;
++
++ for_each_sg(sgl, sg, sg_len, i) {
++ async_desc->length += sg_dma_len(sg);
++
++ if (achan->slave.device_fc)
++ desc = adm_process_fc_descriptors(achan, desc, sg, crci,
++ burst, direction);
++ else
++ desc = adm_process_non_fc_descriptors(achan, desc, sg,
++ direction);
++ }
++
++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++}
++
++/**
++ * adm_terminate_all - terminate all transactions on a channel
++ * @achan: adm dma channel
++ *
++ * Dequeues and frees all transactions, aborts current transaction
++ * No callbacks are done
++ *
++ */
++static int adm_terminate_all(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct adm_device *adev = achan->adev;
++ unsigned long flags;
++ LIST_HEAD(head);
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ vchan_get_all_descriptors(&achan->vc, &head);
++
++ /* send flush command to terminate current transaction */
++ writel_relaxed(0x0,
++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ vchan_dma_desc_free_list(&achan->vc, &head);
++
++ return 0;
++}
++
++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flag;
++
++ spin_lock_irqsave(&achan->vc.lock, flag);
++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
++ spin_unlock_irqrestore(&achan->vc.lock, flag);
++
++ return 0;
++}
++
++/**
++ * adm_start_dma - start next transaction
++ * @achan - ADM dma channel
++ */
++static void adm_start_dma(struct adm_chan *achan)
++{
++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
++ struct adm_device *adev = achan->adev;
++ struct adm_async_desc *async_desc;
++
++ lockdep_assert_held(&achan->vc.lock);
++
++ if (!vd)
++ return;
++
++ list_del(&vd->node);
++
++ /* write next command list out to the CMD FIFO */
++ async_desc = container_of(vd, struct adm_async_desc, vd);
++ achan->curr_txd = async_desc;
++
++ /* reset channel error */
++ achan->error = 0;
++
++ if (!achan->initialized) {
++ /* enable interrupts */
++ writel(ADM_CH_CONF_SHADOW_EN |
++ ADM_CH_CONF_PERM_MPU_CONF |
++ ADM_CH_CONF_MPU_DISABLE |
++ ADM_CH_CONF_SEC_DOMAIN(adev->ee),
++ adev->regs + ADM_CH_CONF(achan->id));
++
++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ achan->initialized = 1;
++ }
++
++ /* set the crci block size if this transaction requires CRCI */
++ if (async_desc->crci) {
++ writel(async_desc->mux | async_desc->blk_size,
++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
++ }
++
++ /* make sure IRQ enable doesn't get reordered */
++ wmb();
++
++ /* write next command list out to the CMD FIFO */
++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
++}
++
++/**
++ * adm_dma_irq - irq handler for ADM controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t adm_dma_irq(int irq, void *data)
++{
++ struct adm_device *adev = data;
++ u32 srcs, i;
++ struct adm_async_desc *async_desc;
++ unsigned long flags;
++
++ srcs = readl_relaxed(adev->regs +
++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ struct adm_chan *achan = &adev->channels[i];
++ u32 status, result;
++
++ if (srcs & BIT(i)) {
++ status = readl_relaxed(adev->regs +
++ ADM_CH_STATUS_SD(i, adev->ee));
++
++ /* if no result present, skip */
++ if (!(status & ADM_CH_STATUS_VALID))
++ continue;
++
++ result = readl_relaxed(adev->regs +
++ ADM_CH_RSLT(i, adev->ee));
++
++ /* no valid results, skip */
++ if (!(result & ADM_CH_RSLT_VALID))
++ continue;
++
++ /* flag error if transaction was flushed or failed */
++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
++ achan->error = 1;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ async_desc = achan->curr_txd;
++
++ achan->curr_txd = NULL;
++
++ if (async_desc) {
++ vchan_cookie_complete(&async_desc->vd);
++
++ /* kick off next DMA */
++ adm_start_dma(achan);
++ }
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++ }
++ }
++
++ return IRQ_HANDLED;
++}
++
++/**
++ * adm_tx_status - returns status of transaction
++ * @chan: dma channel
++ * @cookie: transaction cookie
++ * @txstate: DMA transaction state
++ *
++ * Return status of dma transaction
++ */
++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++ struct dma_tx_state *txstate)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ struct virt_dma_desc *vd;
++ enum dma_status ret;
++ unsigned long flags;
++ size_t residue = 0;
++
++ ret = dma_cookie_status(chan, cookie, txstate);
++ if (ret == DMA_COMPLETE || !txstate)
++ return ret;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ vd = vchan_find_desc(&achan->vc, cookie);
++ if (vd)
++ residue = container_of(vd, struct adm_async_desc, vd)->length;
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ /*
++ * residue is either the full length if it is in the issued list, or 0
++ * if it is in progress. We have no reliable way of determining
++ * anything inbetween
++ */
++ dma_set_residue(txstate, residue);
++
++ if (achan->error)
++ return DMA_ERROR;
++
++ return ret;
++}
++
++/**
++ * adm_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Issues all pending transactions and starts DMA
++ */
++static void adm_issue_pending(struct dma_chan *chan)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flags;
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++
++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
++ adm_start_dma(achan);
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++}
++
++/**
++ * adm_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void adm_dma_free_desc(struct virt_dma_desc *vd)
++{
++ struct adm_async_desc *async_desc = container_of(vd,
++ struct adm_async_desc, vd);
++
++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len,
++ async_desc->cpl, async_desc->dma_addr);
++ kfree(async_desc);
++}
++
++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
++ u32 index)
++{
++ achan->id = index;
++ achan->adev = adev;
++
++ vchan_init(&achan->vc, &adev->common);
++ achan->vc.desc_free = adm_dma_free_desc;
++}
++
++static int adm_dma_probe(struct platform_device *pdev)
++{
++ struct adm_device *adev;
++ struct resource *iores;
++ int ret;
++ u32 i;
++
++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
++ if (!adev)
++ return -ENOMEM;
++
++ adev->dev = &pdev->dev;
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ adev->regs = devm_ioremap_resource(&pdev->dev, iores);
++ if (IS_ERR(adev->regs))
++ return PTR_ERR(adev->regs);
++
++ adev->irq = platform_get_irq(pdev, 0);
++ if (adev->irq < 0)
++ return adev->irq;
++
++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
++ if (ret) {
++ dev_err(adev->dev, "Execution environment unspecified\n");
++ return ret;
++ }
++
++ adev->core_clk = devm_clk_get(adev->dev, "core");
++ if (IS_ERR(adev->core_clk))
++ return PTR_ERR(adev->core_clk);
++
++ ret = clk_prepare_enable(adev->core_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable core clock\n");
++ return ret;
++ }
++
++ adev->iface_clk = devm_clk_get(adev->dev, "iface");
++ if (IS_ERR(adev->iface_clk)) {
++ ret = PTR_ERR(adev->iface_clk);
++ goto err_disable_core_clk;
++ }
++
++ ret = clk_prepare_enable(adev->iface_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++ goto err_disable_core_clk;
++ }
++
++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
++ if (IS_ERR(adev->clk_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 reset\n");
++ ret = PTR_ERR(adev->clk_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
++ if (IS_ERR(adev->c0_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
++ ret = PTR_ERR(adev->c0_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
++ if (IS_ERR(adev->c1_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
++ ret = PTR_ERR(adev->c1_reset);
++ goto err_disable_clks;
++ }
++
++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
++ if (IS_ERR(adev->c2_reset)) {
++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
++ ret = PTR_ERR(adev->c2_reset);
++ goto err_disable_clks;
++ }
++
++ reset_control_assert(adev->clk_reset);
++ reset_control_assert(adev->c0_reset);
++ reset_control_assert(adev->c1_reset);
++ reset_control_assert(adev->c2_reset);
++
++ reset_control_deassert(adev->clk_reset);
++ reset_control_deassert(adev->c0_reset);
++ reset_control_deassert(adev->c1_reset);
++ reset_control_deassert(adev->c2_reset);
++
++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
++ sizeof(*adev->channels), GFP_KERNEL);
++
++ if (!adev->channels) {
++ ret = -ENOMEM;
++ goto err_disable_clks;
++ }
++
++ /* allocate and initialize channels */
++ INIT_LIST_HEAD(&adev->common.channels);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++)
++ adm_channel_init(adev, &adev->channels[i], i);
++
++ /* reset CRCIs */
++ for (i = 0; i < 16; i++)
++ writel(ADM_CRCI_CTL_RST, adev->regs +
++ ADM_CRCI_CTL(i, adev->ee));
++
++ /* configure client interfaces */
++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
++ adev->regs + ADM_GP_CTL);
++
++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++ 0, "adm_dma", adev);
++ if (ret)
++ goto err_disable_clks;
++
++ platform_set_drvdata(pdev, adev);
++
++ adev->common.dev = adev->dev;
++ adev->common.dev->dma_parms = &adev->dma_parms;
++
++ /* set capabilities */
++ dma_cap_zero(adev->common.cap_mask);
++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++ /* initialize dmaengine apis */
++ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
++ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
++ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ adev->common.device_free_chan_resources = adm_free_chan;
++ adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++ adev->common.device_issue_pending = adm_issue_pending;
++ adev->common.device_tx_status = adm_tx_status;
++ adev->common.device_terminate_all = adm_terminate_all;
++ adev->common.device_config = adm_slave_config;
++
++ ret = dma_async_device_register(&adev->common);
++ if (ret) {
++ dev_err(adev->dev, "failed to register dma async device\n");
++ goto err_disable_clks;
++ }
++
++ ret = of_dma_controller_register(pdev->dev.of_node,
++ of_dma_xlate_by_chan_id,
++ &adev->common);
++ if (ret)
++ goto err_unregister_dma;
++
++ return 0;
++
++err_unregister_dma:
++ dma_async_device_unregister(&adev->common);
++err_disable_clks:
++ clk_disable_unprepare(adev->iface_clk);
++err_disable_core_clk:
++ clk_disable_unprepare(adev->core_clk);
++
++ return ret;
++}
++
++static int adm_dma_remove(struct platform_device *pdev)
++{
++ struct adm_device *adev = platform_get_drvdata(pdev);
++ struct adm_chan *achan;
++ u32 i;
++
++ of_dma_controller_free(pdev->dev.of_node);
++ dma_async_device_unregister(&adev->common);
++
++ for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++ achan = &adev->channels[i];
++
++ /* mask IRQs for this channel/EE pair */
++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++ adm_terminate_all(&adev->channels[i].vc.chan);
++ }
++
++ devm_free_irq(adev->dev, adev->irq, adev);
++
++ clk_disable_unprepare(adev->core_clk);
++ clk_disable_unprepare(adev->iface_clk);
++
++ return 0;
++}
++
++static const struct of_device_id adm_of_match[] = {
++ { .compatible = "qcom,adm", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++
++static struct platform_driver adm_dma_driver = {
++ .probe = adm_dma_probe,
++ .remove = adm_dma_remove,
++ .driver = {
++ .name = "adm-dma-engine",
++ .of_match_table = adm_of_match,
++ },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/dma/Makefile
++++ b/drivers/dma/Makefile
+@@ -54,3 +54,4 @@ obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
+ obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
+ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
+ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
diff --git a/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
new file mode 100644
index 0000000..159d912
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
@@ -0,0 +1,42 @@
+From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Wed, 20 May 2015 15:41:07 +0530
+Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node
+
+This patch adds support for the ADM DMA on the IPQ8064 SOC
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -600,6 +600,26 @@
+
+ status = "disabled";
+ };
++
++ adm_dma: dma@18300000 {
++ compatible = "qcom,adm";
++ reg = <0x18300000 0x100000>;
++ interrupts = <0 170 0>;
++ #dma-cells = <1>;
++
++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
++ clock-names = "core", "iface";
++
++ resets = <&gcc ADM0_RESET>,
++ <&gcc ADM0_PBUS_RESET>,
++ <&gcc ADM0_C0_RESET>,
++ <&gcc ADM0_C1_RESET>,
++ <&gcc ADM0_C2_RESET>;
++ reset-names = "clk", "pbus", "c0", "c1", "c2";
++ qcom,ee = <0>;
++
++ status = "disabled";
++ };
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
new file mode 100644
index 0000000..088b8cb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
@@ -0,0 +1,84 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,
+ 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw
+ mode
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927081
+Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>
+Date: Mon, 3 Aug 2015 10:38:14 +0530
+
+Some controllers can access the factory bad block marker from OOB only
+when they read it in raw mode. When ECC is enabled, these controllers
+discard reading/writing bad block markers, preventing access to them
+altogether.
+
+The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
+This results in the nand driver's ecc->read_oob() op to be called, which
+works with ECC enabled.
+
+Create a new BBT option flag that tells nand_bbt to force the mode to
+MTD_OPS_RAW. This would result in the correct op being called for the
+underlying nand controller driver.
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+drivers/mtd/nand/nand_base.c | 6 +++++-
+ drivers/mtd/nand/nand_bbt.c | 6 +++++-
+ include/linux/mtd/bbm.h | 7 +++++++
+ 3 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -395,7 +395,11 @@ static int nand_default_block_markbad(st
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
+- ops.mode = MTD_OPS_PLACE_OOB;
++
++ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
++ ops.mode = MTD_OPS_RAW;
++ else
++ ops.mode = MTD_OPS_PLACE_OOB;
+
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -423,7 +423,11 @@ static int scan_block_fast(struct mtd_in
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+- ops.mode = MTD_OPS_PLACE_OOB;
++
++ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
++ ops.mode = MTD_OPS_RAW;
++ else
++ ops.mode = MTD_OPS_PLACE_OOB;
+
+ for (j = 0; j < numpages; j++) {
+ /*
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -116,6 +116,13 @@ struct nand_bbt_descr {
+ #define NAND_BBT_NO_OOB_BBM 0x00080000
+
+ /*
++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
++ * when in RAW access mode
++ */
++#define NAND_BBT_ACCESS_BBM_RAW 0x00100000
++
++/*
+ * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+ * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+ * in nand_chip.bbt_options.
diff --git a/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
new file mode 100644
index 0000000..4be7ecb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
@@ -0,0 +1,2024 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927101
+Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>
+Date: Mon, 3 Aug 2015 10:38:15 +0530
+
+The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx,
+MDM9x15 series.
+
+It exists as a sub block inside the IPs EBI2 (External Bus Interface 2)
+and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a
+broader interface for external slow peripheral devices such as LCD and
+NAND/NOR flash memory or SRAM like interfaces.
+
+We add support for the NAND controller found within EBI2. For the SoCs
+of our interest, we only use the NAND controller within EBI2. Therefore,
+it's safe for us to assume that the NAND controller is a standalone block
+within the SoC.
+
+The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND
+flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and
+16 bit correction/step) and RS ECC(4 bit correction/step) that covers main
+and spare data. The controller contains an internal 512 byte page buffer
+to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA
+for register read/write and data transfers. The controller performs page
+reads and writes at a codeword/step level of 512 bytes. It can support up
+to 2 external chips of different configurations.
+
+The driver prepares register read and write configuration descriptors for
+each codeword, followed by data descriptors to read or write data from the
+controller's internal buffer. It uses a single ADM DMA channel that we get
+via dmaengine API. The controller requires 2 ADM CRCIs for command and
+data flow control. These are passed via DT.
+
+The ecc layout used by the controller is syndrome like, but we can't use
+the standard syndrome ecc ops because of several reasons. First, the amount
+of data bytes covered by ecc isn't same in each step. Second, writing to
+free oob space requires us writing to the entire step in which the oob
+lies. This forces us to create our own ecc ops.
+
+One more difference is how the controller accesses the bad block marker.
+The controller ignores reading the marker when ECC is enabled. ECC needs
+to be explicity disabled to read or write to the bad block marker. For
+this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to
+read the factory provided bad block markers.
+
+v3:
+- Refactor dma functions for maximum reuse
+- Use dma_slave_confing on stack
+- optimize and clean upempty_page_fixup using memchr_inv
+- ensure portability with dma register reads using le32_* funcs
+- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves
+- fix handling of return values of dmaengine funcs
+- constify wherever possible
+- Remove dependency on ADM DMA in Kconfig
+- Misc fixes and clean ups
+
+v2:
+- Use new BBT flag that allows us to read BBM in raw mode
+- reduce memcpy-s in the driver
+- some refactor and clean ups because of above changes
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1921 insertions(+)
+ create mode 100644 drivers/mtd/nand/qcom_nandc.c
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -530,4 +530,11 @@ config MTD_NAND_HISI504
+ help
+ Enables support for NAND controller on Hisilicon SoC Hip04.
+
++config MTD_NAND_QCOM
++ tristate "Support for NAND on QCOM SoCs"
++ depends on ARCH_QCOM
++ help
++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND
++ controller. This controller is found on IPQ806x SoC.
++
+ endif # MTD_NAND
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nandc.c
+@@ -0,0 +1,1918 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/slab.h>
++#include <linux/bitops.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmaengine.h>
++#include <linux/module.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_mtd.h>
++#include <linux/delay.h>
++
++/* NANDc reg offsets */
++#define NAND_FLASH_CMD 0x00
++#define NAND_ADDR0 0x04
++#define NAND_ADDR1 0x08
++#define NAND_FLASH_CHIP_SELECT 0x0c
++#define NAND_EXEC_CMD 0x10
++#define NAND_FLASH_STATUS 0x14
++#define NAND_BUFFER_STATUS 0x18
++#define NAND_DEV0_CFG0 0x20
++#define NAND_DEV0_CFG1 0x24
++#define NAND_DEV0_ECC_CFG 0x28
++#define NAND_DEV1_ECC_CFG 0x2c
++#define NAND_DEV1_CFG0 0x30
++#define NAND_DEV1_CFG1 0x34
++#define NAND_READ_ID 0x40
++#define NAND_READ_STATUS 0x44
++#define NAND_DEV_CMD0 0xa0
++#define NAND_DEV_CMD1 0xa4
++#define NAND_DEV_CMD2 0xa8
++#define NAND_DEV_CMD_VLD 0xac
++#define SFLASHC_BURST_CFG 0xe0
++#define NAND_ERASED_CW_DETECT_CFG 0xe8
++#define NAND_ERASED_CW_DETECT_STATUS 0xec
++#define NAND_EBI2_ECC_BUF_CFG 0xf0
++#define FLASH_BUF_ACC 0x100
++
++#define NAND_CTRL 0xf00
++#define NAND_VERSION 0xf08
++#define NAND_READ_LOCATION_0 0xf20
++#define NAND_READ_LOCATION_1 0xf24
++
++/* dummy register offsets, used by write_reg_dma */
++#define NAND_DEV_CMD1_RESTORE 0xdead
++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef
++
++/* NAND_FLASH_CMD bits */
++#define PAGE_ACC BIT(4)
++#define LAST_PAGE BIT(5)
++
++/* NAND_FLASH_CHIP_SELECT bits */
++#define NAND_DEV_SEL 0
++#define DM_EN BIT(2)
++
++/* NAND_FLASH_STATUS bits */
++#define FS_OP_ERR BIT(4)
++#define FS_READY_BSY_N BIT(5)
++#define FS_MPU_ERR BIT(8)
++#define FS_DEVICE_STS_ERR BIT(16)
++#define FS_DEVICE_WP BIT(23)
++
++/* NAND_BUFFER_STATUS bits */
++#define BS_UNCORRECTABLE_BIT BIT(8)
++#define BS_CORRECTABLE_ERR_MSK 0x1f
++
++/* NAND_DEVn_CFG0 bits */
++#define DISABLE_STATUS_AFTER_WRITE 4
++#define CW_PER_PAGE 6
++#define UD_SIZE_BYTES 9
++#define ECC_PARITY_SIZE_BYTES_RS 19
++#define SPARE_SIZE_BYTES 23
++#define NUM_ADDR_CYCLES 27
++#define STATUS_BFR_READ 30
++#define SET_RD_MODE_AFTER_STATUS 31
++
++/* NAND_DEVn_CFG0 bits */
++#define DEV0_CFG1_ECC_DISABLE 0
++#define WIDE_FLASH 1
++#define NAND_RECOVERY_CYCLES 2
++#define CS_ACTIVE_BSY 5
++#define BAD_BLOCK_BYTE_NUM 6
++#define BAD_BLOCK_IN_SPARE_AREA 16
++#define WR_RD_BSY_GAP 17
++#define ENABLE_BCH_ECC 27
++
++/* NAND_DEV0_ECC_CFG bits */
++#define ECC_CFG_ECC_DISABLE 0
++#define ECC_SW_RESET 1
++#define ECC_MODE 4
++#define ECC_PARITY_SIZE_BYTES_BCH 8
++#define ECC_NUM_DATA_BYTES 16
++#define ECC_FORCE_CLK_OPEN 30
++
++/* NAND_DEV_CMD1 bits */
++#define READ_ADDR 0
++
++/* NAND_DEV_CMD_VLD bits */
++#define READ_START_VLD 0
++
++/* NAND_EBI2_ECC_BUF_CFG bits */
++#define NUM_STEPS 0
++
++/* NAND_ERASED_CW_DETECT_CFG bits */
++#define ERASED_CW_ECC_MASK 1
++#define AUTO_DETECT_RES 0
++#define MASK_ECC (1 << ERASED_CW_ECC_MASK)
++#define RESET_ERASED_DET (1 << AUTO_DETECT_RES)
++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES)
++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC)
++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC)
++
++/* NAND_ERASED_CW_DETECT_STATUS bits */
++#define PAGE_ALL_ERASED BIT(7)
++#define CODEWORD_ALL_ERASED BIT(6)
++#define PAGE_ERASED BIT(5)
++#define CODEWORD_ERASED BIT(4)
++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
++
++/* Version Mask */
++#define NAND_VERSION_MAJOR_MASK 0xf0000000
++#define NAND_VERSION_MAJOR_SHIFT 28
++#define NAND_VERSION_MINOR_MASK 0x0fff0000
++#define NAND_VERSION_MINOR_SHIFT 16
++
++/* NAND OP_CMDs */
++#define PAGE_READ 0x2
++#define PAGE_READ_WITH_ECC 0x3
++#define PAGE_READ_WITH_ECC_SPARE 0x4
++#define PROGRAM_PAGE 0x6
++#define PAGE_PROGRAM_WITH_ECC 0x7
++#define PROGRAM_PAGE_SPARE 0x9
++#define BLOCK_ERASE 0xa
++#define FETCH_ID 0xb
++#define RESET_DEVICE 0xd
++
++/*
++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
++ * the driver calls the chunks 'step' or 'codeword' interchangeably
++ */
++#define NANDC_STEP_SIZE 512
++
++/*
++ * the largest page size we support is 8K, this will have 16 steps/codewords
++ * of 512 bytes each
++ */
++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE)
++
++/* we read at most 3 registers per codeword scan */
++#define MAX_REG_RD (3 * MAX_NUM_STEPS)
++
++/* ECC modes */
++#define ECC_NONE BIT(0)
++#define ECC_RS_4BIT BIT(1)
++#define ECC_BCH_4BIT BIT(2)
++#define ECC_BCH_8BIT BIT(3)
++
++struct desc_info {
++ struct list_head list;
++
++ enum dma_transfer_direction dir;
++ struct scatterlist sgl;
++ struct dma_async_tx_descriptor *dma_desc;
++};
++
++/*
++ * holds the current register values that we want to write. acts as a contiguous
++ * chunk of memory which we use to write the controller registers through DMA.
++ */
++struct nandc_regs {
++ u32 cmd;
++ u32 addr0;
++ u32 addr1;
++ u32 chip_sel;
++ u32 exec;
++
++ u32 cfg0;
++ u32 cfg1;
++ u32 ecc_bch_cfg;
++
++ u32 clrflashstatus;
++ u32 clrreadstatus;
++
++ u32 cmd1;
++ u32 vld;
++
++ u32 orig_cmd1;
++ u32 orig_vld;
++
++ u32 ecc_buf_cfg;
++};
++
++/*
++ * @cmd_crci: ADM DMA CRCI for command flow control
++ * @data_crci: ADM DMA CRCI for data flow control
++ * @list: DMA descriptor list (list of desc_infos)
++ * @dma_done: completion param to denote end of last
++ * descriptor in the list
++ * @data_buffer: our local DMA buffer for page read/writes,
++ * used when we can't use the buffer provided
++ * by upper layers directly
++ * @buf_size/count/start: markers for chip->read_buf/write_buf functions
++ * @reg_read_buf: buffer for reading register data via DMA
++ * @reg_read_pos: marker for data read in reg_read_buf
++ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
++ * ecc/non-ecc mode for the current nand flash
++ * device
++ * @regs: a contiguous chunk of memory for DMA register
++ * writes
++ * @ecc_strength: 4 bit or 8 bit ecc, received via DT
++ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT
++ * @ecc_modes: supported ECC modes by the current controller,
++ * initialized via DT match data
++ * @cw_size: the number of bytes in a single step/codeword
++ * of a page, consisting of all data, ecc, spare
++ * and reserved bytes
++ * @cw_data: the number of bytes within a codeword protected
++ * by ECC
++ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used
++ * @status: value to be returned if NAND_CMD_STATUS command
++ * is executed
++ */
++struct qcom_nandc_data {
++ struct platform_device *pdev;
++ struct device *dev;
++
++ void __iomem *base;
++ struct resource *res;
++
++ struct clk *core_clk;
++ struct clk *aon_clk;
++
++ /* DMA stuff */
++ struct dma_chan *chan;
++ struct dma_slave_config slave_conf;
++ unsigned int cmd_crci;
++ unsigned int data_crci;
++ struct list_head list;
++ struct completion dma_done;
++
++ /* MTD stuff */
++ struct nand_chip chip;
++ struct mtd_info mtd;
++
++ /* local data buffer and markers */
++ u8 *data_buffer;
++ int buf_size;
++ int buf_count;
++ int buf_start;
++
++ /* local buffer to read back registers */
++ u32 *reg_read_buf;
++ int reg_read_pos;
++
++ /* required configs */
++ u32 cfg0, cfg1;
++ u32 cfg0_raw, cfg1_raw;
++ u32 ecc_buf_cfg;
++ u32 ecc_bch_cfg;
++ u32 clrflashstatus;
++ u32 clrreadstatus;
++ u32 sflashc_burst_cfg;
++ u32 cmd1, vld;
++
++ /* register state */
++ struct nandc_regs *regs;
++
++ /* things we get from DT */
++ int ecc_strength;
++ int bus_width;
++
++ u32 ecc_modes;
++
++ /* misc params */
++ int cw_size;
++ int cw_data;
++ bool use_ecc;
++ bool bch_enabled;
++ u8 status;
++ int last_command;
++};
++
++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset)
++{
++ return ioread32(this->base + offset);
++}
++
++static inline void nandc_write(struct qcom_nandc_data *this, int offset,
++ u32 val)
++{
++ iowrite32(val, this->base + offset);
++}
++
++/* helper to configure address register values */
++static void set_address(struct qcom_nandc_data *this, u16 column, int page)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nandc_regs *regs = this->regs;
++
++ if (chip->options & NAND_BUSWIDTH_16)
++ column >>= 1;
++
++ regs->addr0 = page << 16 | column;
++ regs->addr1 = page >> 16 & 0xff;
++}
++
++/*
++ * update_rw_regs: set up read/write register values, these will be
++ * written to the NAND controller registers via DMA
++ *
++ * @num_cw: number of steps for the read/write operation
++ * @read: read or write operation
++ */
++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read)
++{
++ struct nandc_regs *regs = this->regs;
++
++ if (read) {
++ if (this->use_ecc)
++ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
++ else
++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++ } else {
++ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
++ }
++
++ if (this->use_ecc) {
++ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) |
++ (num_cw - 1) << CW_PER_PAGE;
++
++ regs->cfg1 = this->cfg1;
++ regs->ecc_bch_cfg = this->ecc_bch_cfg;
++ } else {
++ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++ (num_cw - 1) << CW_PER_PAGE;
++
++ regs->cfg1 = this->cfg1_raw;
++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++ }
++
++ regs->ecc_buf_cfg = this->ecc_buf_cfg;
++ regs->clrflashstatus = this->clrflashstatus;
++ regs->clrreadstatus = this->clrreadstatus;
++ regs->exec = 1;
++}
++
++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off,
++ const void *vaddr, int size, bool flow_control)
++{
++ struct desc_info *desc;
++ struct dma_async_tx_descriptor *dma_desc;
++ struct scatterlist *sgl;
++ struct dma_slave_config slave_conf;
++ int r;
++
++ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++ if (!desc)
++ return -ENOMEM;
++
++ list_add_tail(&desc->list, &this->list);
++
++ sgl = &desc->sgl;
++
++ sg_init_one(sgl, vaddr, size);
++
++ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
++
++ r = dma_map_sg(this->dev, sgl, 1, desc->dir);
++ if (r == 0) {
++ r = -ENOMEM;
++ goto err;
++ }
++
++ memset(&slave_conf, 0x00, sizeof(slave_conf));
++
++ slave_conf.device_fc = flow_control;
++ if (read) {
++ slave_conf.src_maxburst = 16;
++ slave_conf.src_addr = this->res->start + reg_off;
++ slave_conf.slave_id = this->data_crci;
++ } else {
++ slave_conf.dst_maxburst = 16;
++ slave_conf.dst_addr = this->res->start + reg_off;
++ slave_conf.slave_id = this->cmd_crci;
++ }
++
++ r = dmaengine_slave_config(this->chan, &slave_conf);
++ if (r) {
++ dev_err(this->dev, "failed to configure dma channel\n");
++ goto err;
++ }
++
++ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0);
++ if (!dma_desc) {
++ dev_err(this->dev, "failed to prepare desc\n");
++ r = -EINVAL;
++ goto err;
++ }
++
++ desc->dma_desc = dma_desc;
++
++ return 0;
++err:
++ kfree(desc);
++
++ return r;
++}
++
++/*
++ * read_reg_dma: prepares a descriptor to read a given number of
++ * contiguous registers to the reg_read_buf pointer
++ *
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to read
++ */
++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++ bool flow_control = false;
++ void *vaddr;
++ int size;
++
++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
++ flow_control = true;
++
++ size = num_regs * sizeof(u32);
++ vaddr = this->reg_read_buf + this->reg_read_pos;
++ this->reg_read_pos += num_regs;
++
++ return prep_dma_desc(this, true, first, vaddr, size, flow_control);
++}
++
++/*
++ * write_reg_dma: prepares a descriptor to write a given number of
++ * contiguous registers
++ *
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to write
++ */
++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++ bool flow_control = false;
++ struct nandc_regs *regs = this->regs;
++ void *vaddr;
++ int size;
++
++ switch (first) {
++ case NAND_FLASH_CMD:
++ vaddr = &regs->cmd;
++ flow_control = true;
++ break;
++ case NAND_EXEC_CMD:
++ vaddr = &regs->exec;
++ break;
++ case NAND_FLASH_STATUS:
++ vaddr = &regs->clrflashstatus;
++ break;
++ case NAND_DEV0_CFG0:
++ vaddr = &regs->cfg0;
++ break;
++ case NAND_READ_STATUS:
++ vaddr = &regs->clrreadstatus;
++ break;
++ case NAND_DEV_CMD1:
++ vaddr = &regs->cmd1;
++ break;
++ case NAND_DEV_CMD1_RESTORE:
++ first = NAND_DEV_CMD1;
++ vaddr = &regs->orig_cmd1;
++ break;
++ case NAND_DEV_CMD_VLD:
++ vaddr = &regs->vld;
++ break;
++ case NAND_DEV_CMD_VLD_RESTORE:
++ first = NAND_DEV_CMD_VLD;
++ vaddr = &regs->orig_vld;
++ break;
++ case NAND_EBI2_ECC_BUF_CFG:
++ vaddr = &regs->ecc_buf_cfg;
++ break;
++ default:
++ dev_err(this->dev, "invalid starting register\n");
++ return -EINVAL;
++ }
++
++ size = num_regs * sizeof(u32);
++
++ return prep_dma_desc(this, false, first, vaddr, size, flow_control);
++}
++
++/*
++ * read_data_dma: prepares a DMA descriptor to transfer data from the
++ * controller's internal buffer to the buffer 'vaddr'
++ *
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: DMA transaction size in bytes
++ */
++static int read_data_dma(struct qcom_nandc_data *this, int reg_off,
++ const u8 *vaddr, int size)
++{
++ return prep_dma_desc(this, true, reg_off, vaddr, size, false);
++}
++
++/*
++ * write_data_dma: prepares a DMA descriptor to transfer data from
++ * 'vaddr' to the controller's internal buffer
++ *
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to read from
++ * @size: DMA transaction size in bytes
++ */
++static int write_data_dma(struct qcom_nandc_data *this, int reg_off,
++ const u8 *vaddr, int size)
++{
++ return prep_dma_desc(this, false, reg_off, vaddr, size, false);
++}
++
++/*
++ * helper to prepare dma descriptors to configure registers needed for reading a
++ * codeword/step in a page
++ */
++static void config_cw_read(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 3);
++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 2);
++ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1);
++}
++
++/*
++ * helpers to prepare dma descriptors used to configure registers needed for
++ * writing a codeword/step in a page
++ */
++static void config_cw_write_pre(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 3);
++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++}
++
++static void config_cw_write_post(struct qcom_nandc_data *this)
++{
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ write_reg_dma(this, NAND_FLASH_STATUS, 1);
++ write_reg_dma(this, NAND_READ_STATUS, 1);
++}
++
++/*
++ * the following functions are used within chip->cmdfunc() to perform different
++ * NAND_CMD_* commands
++ */
++
++/* sets up descriptors for NAND_CMD_PARAM */
++static int nandc_param(struct qcom_nandc_data *this)
++{
++ struct nandc_regs *regs = this->regs;
++
++ /*
++ * NAND_CMD_PARAM is called before we know much about the FLASH chip
++ * in use. we configure the controller to perform a raw read of 512
++ * bytes to read onfi params
++ */
++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++ regs->addr0 = 0;
++ regs->addr1 = 0;
++ regs->cfg0 = 0 << CW_PER_PAGE
++ | 512 << UD_SIZE_BYTES
++ | 5 << NUM_ADDR_CYCLES
++ | 0 << SPARE_SIZE_BYTES;
++
++ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | 17 << BAD_BLOCK_BYTE_NUM
++ | 1 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | 0 << WIDE_FLASH
++ | 1 << DEV0_CFG1_ECC_DISABLE;
++
++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++
++ /* configure CMD1 and VLD for ONFI param probing */
++ regs->vld = (this->vld & ~(1 << READ_START_VLD))
++ | 0 << READ_START_VLD;
++
++ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR))
++ | NAND_CMD_PARAM << READ_ADDR;
++
++ regs->exec = 1;
++
++ regs->orig_cmd1 = this->cmd1;
++ regs->orig_vld = this->vld;
++
++ write_reg_dma(this, NAND_DEV_CMD_VLD, 1);
++ write_reg_dma(this, NAND_DEV_CMD1, 1);
++
++ this->buf_count = 512;
++ memset(this->data_buffer, 0xff, this->buf_count);
++
++ config_cw_read(this);
++
++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count);
++
++ /* restore CMD1 and VLD regs */
++ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1);
++ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_ERASE1 */
++static int erase_block(struct qcom_nandc_data *this, int page_addr)
++{
++ struct nandc_regs *regs = this->regs;
++
++ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE;
++ regs->addr0 = page_addr;
++ regs->addr1 = 0;
++ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE);
++ regs->cfg1 = this->cfg1_raw;
++ regs->exec = 1;
++ regs->clrflashstatus = this->clrflashstatus;
++ regs->clrreadstatus = this->clrreadstatus;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 3);
++ write_reg_dma(this, NAND_DEV0_CFG0, 2);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ write_reg_dma(this, NAND_FLASH_STATUS, 1);
++ write_reg_dma(this, NAND_READ_STATUS, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_READID */
++static int read_id(struct qcom_nandc_data *this, int column)
++{
++ struct nandc_regs *regs = this->regs;
++
++ if (column == -1)
++ return 0;
++
++ regs->cmd = FETCH_ID;
++ regs->addr0 = column;
++ regs->addr1 = 0;
++ regs->chip_sel = DM_EN;
++ regs->exec = 1;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 4);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_READ_ID, 1);
++
++ return 0;
++}
++
++/* sets up descriptors for NAND_CMD_RESET */
++static int reset(struct qcom_nandc_data *this)
++{
++ struct nandc_regs *regs = this->regs;
++
++ regs->cmd = RESET_DEVICE;
++ regs->exec = 1;
++
++ write_reg_dma(this, NAND_FLASH_CMD, 1);
++ write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++ read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++ return 0;
++}
++
++/* helpers to submit/free our list of dma descriptors */
++static void dma_callback(void *param)
++{
++ struct qcom_nandc_data *this = param;
++ struct completion *c = &this->dma_done;
++
++ complete(c);
++}
++
++static int submit_descs(struct qcom_nandc_data *this)
++{
++ struct completion *c = &this->dma_done;
++ struct desc_info *desc;
++ int r;
++
++ init_completion(c);
++
++ list_for_each_entry(desc, &this->list, list) {
++ /*
++ * we add a callback to the last descriptor in our list to
++ * notify completion of command
++ */
++ if (list_is_last(&desc->list, &this->list)) {
++ desc->dma_desc->callback = dma_callback;
++ desc->dma_desc->callback_param = this;
++ }
++
++ dmaengine_submit(desc->dma_desc);
++ }
++
++ dma_async_issue_pending(this->chan);
++
++ r = wait_for_completion_timeout(c, msecs_to_jiffies(500));
++ if (!r)
++ return -ETIMEDOUT;
++
++ return 0;
++}
++
++static void free_descs(struct qcom_nandc_data *this)
++{
++ struct desc_info *desc, *n;
++
++ list_for_each_entry_safe(desc, n, &this->list, list) {
++ list_del(&desc->list);
++ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir);
++ kfree(desc);
++ }
++}
++
++/* reset the register read buffer for next NAND operation */
++static void clear_read_regs(struct qcom_nandc_data *this)
++{
++ this->reg_read_pos = 0;
++ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf));
++}
++
++static void pre_command(struct qcom_nandc_data *this, int command)
++{
++ this->buf_count = 0;
++ this->buf_start = 0;
++ this->use_ecc = false;
++ this->last_command = command;
++
++ clear_read_regs(this);
++}
++
++/*
++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
++ * privately maintained status byte, this status byte can be read after
++ * NAND_CMD_STATUS is called
++ */
++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int num_cw;
++ int i;
++
++ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
++
++ for (i = 0; i < num_cw; i++) {
++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]);
++
++ if (flash_status & FS_MPU_ERR)
++ this->status &= ~NAND_STATUS_WP;
++
++ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
++ (flash_status & FS_DEVICE_STS_ERR)))
++ this->status |= NAND_STATUS_FAIL;
++ }
++}
++
++static void post_command(struct qcom_nandc_data *this, int command)
++{
++ switch (command) {
++ case NAND_CMD_READID:
++ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count);
++ break;
++ case NAND_CMD_PAGEPROG:
++ case NAND_CMD_ERASE1:
++ parse_erase_write_errors(this, command);
++ break;
++ default:
++ break;
++ }
++}
++
++/*
++ * Implements chip->cmdfunc. It's only used for a limited set of commands.
++ * The rest of the commands wouldn't be called by upper layers. For example,
++ * NAND_CMD_READOOB would never be called because we have our own versions
++ * of read_oob ops for nand_ecc_ctrl.
++ */
++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
++ int column, int page_addr)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ struct qcom_nandc_data *this = chip->priv;
++ bool wait = false;
++ int r = 0;
++
++ pre_command(this, command);
++
++ switch (command) {
++ case NAND_CMD_RESET:
++ r = reset(this);
++ wait = true;
++ break;
++
++ case NAND_CMD_READID:
++ this->buf_count = 4;
++ r = read_id(this, column);
++ wait = true;
++ break;
++
++ case NAND_CMD_PARAM:
++ r = nandc_param(this);
++ wait = true;
++ break;
++
++ case NAND_CMD_ERASE1:
++ r = erase_block(this, page_addr);
++ wait = true;
++ break;
++
++ case NAND_CMD_READ0:
++ /* we read the entire page for now */
++ WARN_ON(column != 0);
++
++ this->use_ecc = true;
++ set_address(this, 0, page_addr);
++ update_rw_regs(this, ecc->steps, true);
++ break;
++
++ case NAND_CMD_SEQIN:
++ WARN_ON(column != 0);
++ set_address(this, 0, page_addr);
++ break;
++
++ case NAND_CMD_PAGEPROG:
++ case NAND_CMD_STATUS:
++ case NAND_CMD_NONE:
++ default:
++ break;
++ }
++
++ if (r) {
++ dev_err(this->dev, "failure executing command %d\n",
++ command);
++ free_descs(this);
++ return;
++ }
++
++ if (wait) {
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev,
++ "failure submitting descs for command %d\n",
++ command);
++ }
++
++ free_descs(this);
++
++ post_command(this, command);
++}
++
++/*
++ * when using RS ECC, the NAND controller flags an error when reading an
++ * erased page. however, there are special characters at certain offsets when
++ * we read the erased page. we check here if the page is really empty. if so,
++ * we replace the magic characters with 0xffs
++ */
++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = ecc->steps;
++ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS];
++ int i, j;
++
++ /* if BCH is enabled, HW will take care of detecting erased pages */
++ if (this->bch_enabled || !this->use_ecc)
++ return false;
++
++ for (i = 0; i < cwperpage; i++) {
++ u8 *empty1, *empty2;
++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]);
++
++ /*
++ * an erased page flags an error in NAND_FLASH_STATUS, check if
++ * the page is erased by looking for 0x54s at offsets 3 and 175
++ * from the beginning of each codeword
++ */
++ if (!(flash_status & FS_OP_ERR))
++ break;
++
++ empty1 = &data_buf[3 + i * this->cw_data];
++ empty2 = &data_buf[175 + i * this->cw_data];
++
++ /*
++ * if the error wasn't because of an erased page, bail out and
++ * and let someone else do the error checking
++ */
++ if ((*empty1 == 0x54 && *empty2 == 0xff) ||
++ (*empty1 == 0xff && *empty2 == 0x54)) {
++ orig1[i] = *empty1;
++ orig2[i] = *empty2;
++
++ *empty1 = 0xff;
++ *empty2 = 0xff;
++ } else {
++ break;
++ }
++ }
++
++ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize))
++ goto not_empty;
++
++ /*
++ * tell the caller that the page was empty and is fixed up, so that
++ * parse_read_errors() doesn't think it's an error
++ */
++ return true;
++
++not_empty:
++ /* restore original values if not empty*/
++ for (j = 0; j < i; j++) {
++ data_buf[3 + j * this->cw_data] = orig1[j];
++ data_buf[175 + j * this->cw_data] = orig2[j];
++ }
++
++ return false;
++}
++
++struct read_stats {
++ __le32 flash;
++ __le32 buffer;
++ __le32 erased_cw;
++};
++
++/*
++ * reads back status registers set by the controller to notify page read
++ * errors. this is equivalent to what 'ecc->correct()' would do.
++ */
++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = ecc->steps;
++ unsigned int max_bitflips = 0;
++ int i;
++
++ for (i = 0; i < cwperpage; i++) {
++ int stat;
++ struct read_stats *buf;
++
++ buf = (struct read_stats *) (this->reg_read_buf + 3 * i);
++
++ buf->flash = le32_to_cpu(buf->flash);
++ buf->buffer = le32_to_cpu(buf->buffer);
++ buf->erased_cw = le32_to_cpu(buf->erased_cw);
++
++ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) {
++
++ /* ignore erased codeword errors */
++ if (this->bch_enabled) {
++ if ((buf->erased_cw & ERASED_CW) == ERASED_CW)
++ continue;
++ } else if (erased_page) {
++ continue;
++ }
++
++ if (buf->buffer & BS_UNCORRECTABLE_BIT) {
++ mtd->ecc_stats.failed++;
++ continue;
++ }
++ }
++
++ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK;
++ mtd->ecc_stats.corrected += stat;
++
++ max_bitflips = max_t(unsigned int, max_bitflips, stat);
++ }
++
++ return max_bitflips;
++}
++
++/*
++ * helper to perform the actual page read operation, used by ecc->read_page()
++ * and ecc->read_oob()
++ */
++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf,
++ u8 *oob_buf)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int i, r;
++
++ /* queue cmd descs for each codeword */
++ for (i = 0; i < ecc->steps; i++) {
++ int data_size, oob_size;
++
++ if (i == (ecc->steps - 1)) {
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++ } else {
++ data_size = this->cw_data;
++ oob_size = ecc->bytes;
++ }
++
++ config_cw_read(this);
++
++ if (data_buf)
++ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++ if (oob_buf)
++ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf,
++ oob_size);
++
++ if (data_buf)
++ data_buf += data_size;
++ if (oob_buf)
++ oob_buf += oob_size;
++ }
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failure to read page/oob\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/*
++ * a helper that copies the last step/codeword of a page (containing free oob)
++ * into our local buffer
++ */
++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page)
++{
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int size;
++ int r;
++
++ clear_read_regs(this);
++
++ size = use_ecc ? this->cw_data : this->cw_size;
++
++ /* prepare a clean read buffer */
++ memset(this->data_buffer, 0xff, size);
++
++ this->use_ecc = use_ecc;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, true);
++
++ config_cw_read(this);
++
++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size);
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failed to copy last codeword\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/* implements ecc->read_page() */
++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
++ uint8_t *buf, int oob_required, int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ u8 *data_buf, *oob_buf = NULL;
++ bool erased_page;
++ int r;
++
++ data_buf = buf;
++ oob_buf = oob_required ? chip->oob_poi : NULL;
++
++ r = read_page_low(this, data_buf, oob_buf);
++ if (r) {
++ dev_err(this->dev, "failure to read page\n");
++ return r;
++ }
++
++ erased_page = empty_page_fixup(this, data_buf);
++
++ return parse_read_errors(this, erased_page);
++}
++
++/* implements ecc->read_oob() */
++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int r;
++
++ clear_read_regs(this);
++
++ this->use_ecc = true;
++ set_address(this, 0, page);
++ update_rw_regs(this, ecc->steps, true);
++
++ r = read_page_low(this, NULL, chip->oob_poi);
++ if (r)
++ dev_err(this->dev, "failure to read oob\n");
++
++ return r;
++}
++
++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */
++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int start, length;
++ int r;
++
++ /*
++ * configure registers for a raw page read, the address is set to the
++ * beginning of the last codeword, we don't care about reading ecc
++ * portion of oob, just the free stuff
++ */
++ r = copy_last_cw(this, false, page);
++ if (r)
++ return r;
++
++ /*
++ * reading raw oob has 2 parts, first the bad block byte, then the
++ * actual free oob region. perform a memcpy in two steps
++ */
++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++ memcpy(oob, this->data_buffer + start, length);
++
++ oob += length;
++
++ start = this->cw_data - (ecc->steps << 2) + 1;
++ length = ecc->steps << 2;
++
++ memcpy(oob, this->data_buffer + start, length);
++
++ return 0;
++}
++
++/* implements ecc->write_page() */
++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++ const uint8_t *buf, int oob_required)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ u8 *data_buf, *oob_buf;
++ int i, r = 0;
++
++ clear_read_regs(this);
++
++ data_buf = (u8 *) buf;
++ oob_buf = chip->oob_poi;
++
++ this->use_ecc = true;
++ update_rw_regs(this, ecc->steps, false);
++
++ for (i = 0; i < ecc->steps; i++) {
++ int data_size, oob_size;
++
++ if (i == (ecc->steps - 1)) {
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++ } else {
++ data_size = this->cw_data;
++ oob_size = ecc->bytes;
++ }
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++ /*
++ * we don't really need to write anything to oob for the
++ * first n - 1 codewords since these oob regions just
++ * contain ecc that's written by the controller itself
++ */
++ if (i == (ecc->steps - 1))
++ write_data_dma(this, FLASH_BUF_ACC + data_size,
++ oob_buf, oob_size);
++ config_cw_write_post(this);
++
++ data_buf += data_size;
++ oob_buf += oob_size;
++ }
++
++ r = submit_descs(this);
++ if (r)
++ dev_err(this->dev, "failure to write page\n");
++
++ free_descs(this);
++
++ return r;
++}
++
++/*
++ * implements ecc->write_oob()
++ *
++ * the NAND controller cannot write only data or only oob within a codeword,
++ * since ecc is calculated for the combined codeword. we first copy the
++ * entire contents for the last codeword(data + oob), replace the old oob
++ * with the new one in chip->oob_poi, and then write the entire codeword.
++ * this read-copy-write operation results in a slight perormance loss.
++ */
++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++ int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int free_boff;
++ int data_size, oob_size;
++ int r, status = 0;
++
++ r = copy_last_cw(this, true, page);
++ if (r)
++ return r;
++
++ clear_read_regs(this);
++
++ /* calculate the data and oob size for the last codeword/step */
++ data_size = ecc->size - ((ecc->steps - 1) << 2);
++ oob_size = (ecc->steps << 2) + ecc->bytes;
++
++ /*
++ * the location of spare data in the oob buffer, we could also use
++ * ecc->layout.oobfree here
++ */
++ free_boff = ecc->bytes * (ecc->steps - 1);
++
++ /* override new oob content to last codeword */
++ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size);
++
++ this->use_ecc = true;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, false);
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer,
++ data_size + oob_size);
++ config_cw_write_post(this);
++
++ r = submit_descs(this);
++
++ free_descs(this);
++
++ if (r) {
++ dev_err(this->dev, "failure to write oob\n");
++ return -EIO;
++ }
++
++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ status = chip->waitfunc(mtd, chip);
++
++ return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/* implements ecc->write_oob_raw(), used to write bad block marker flag */
++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd,
++ struct nand_chip *chip, int page)
++{
++ struct qcom_nandc_data *this = chip->priv;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ uint8_t *oob = chip->oob_poi;
++ int start, length;
++ int r, status = 0;
++
++ r = copy_last_cw(this, false, page);
++ if (r)
++ return r;
++
++ clear_read_regs(this);
++
++ /*
++ * writing raw oob has 2 parts, first the bad block region, then the
++ * actual free region
++ */
++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++ memcpy(this->data_buffer + start, oob, length);
++
++ oob += length;
++
++ start = this->cw_data - (ecc->steps << 2) + 1;
++ length = ecc->steps << 2;
++
++ memcpy(this->data_buffer + start, oob, length);
++
++ /* prepare write */
++ this->use_ecc = false;
++ set_address(this, this->cw_size * (ecc->steps - 1), page);
++ update_rw_regs(this, 1, false);
++
++ config_cw_write_pre(this);
++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size);
++ config_cw_write_post(this);
++
++ r = submit_descs(this);
++
++ free_descs(this);
++
++ if (r) {
++ dev_err(this->dev, "failure to write updated oob\n");
++ return -EIO;
++ }
++
++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ status = chip->waitfunc(mtd, chip);
++
++ return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/*
++ * the three functions below implement chip->read_byte(), chip->read_buf()
++ * and chip->write_buf() respectively. these aren't used for
++ * reading/writing page data, they are used for smaller data like reading
++ * id, status etc
++ */
++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ uint8_t *buf = this->data_buffer;
++ uint8_t ret = 0x0;
++
++ if (this->last_command == NAND_CMD_STATUS) {
++ ret = this->status;
++
++ this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++
++ return ret;
++ }
++
++ if (this->buf_start < this->buf_count)
++ ret = buf[this->buf_start++];
++
++ return ret;
++}
++
++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++ memcpy(buf, this->data_buffer + this->buf_start, real_len);
++ this->buf_start += real_len;
++}
++
++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
++ int len)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++ memcpy(this->data_buffer + this->buf_start, buf, real_len);
++
++ this->buf_start += real_len;
++}
++
++/* we support only one external chip for now */
++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
++{
++ struct nand_chip *chip = mtd->priv;
++ struct qcom_nandc_data *this = chip->priv;
++
++ if (chipnr <= 0)
++ return;
++
++ dev_warn(this->dev, "invalid chip select\n");
++}
++
++/*
++ * NAND controller page layout info
++ *
++ * |-----------------------| |---------------------------------|
++ * | xx.......xx| | *********xx.......xx|
++ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx|
++ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx|
++ * | xx.......xx| | *********xx.......xx|
++ * |-----------------------| |---------------------------------|
++ * codeword 1,2..n-1 codeword n
++ * <---(528/532 Bytes)----> <-------(528/532 Bytes)---------->
++ *
++ * n = number of codewords in the page
++ * . = ECC bytes
++ * * = spare bytes
++ * x = unused/reserved bytes
++ *
++ * 2K page: n = 4, spare = 16 bytes
++ * 4K page: n = 8, spare = 32 bytes
++ * 8K page: n = 16, spare = 64 bytes
++ *
++ * the qcom nand controller operates at a sub page/codeword level. each
++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
++ * the number of ECC bytes vary based on the ECC strength and the bus width.
++ *
++ * the first n - 1 codewords contains 516 bytes of user data, the remaining
++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
++ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
++ *
++ * the layout described above is used by the controller when the ECC block is
++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are
++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout
++ * layouts defined below doesn't consider the positions occupied by the reserved
++ * bytes
++ *
++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width)
++ * in the last codeword is the position of bad block marker. the bad block
++ * marker cannot be accessed when ECC is enabled.
++ *
++ */
++
++/*
++ * Layouts for different page sizes and ecc modes. We skip the eccpos field
++ * since it isn't needed for this driver
++ */
++
++/* 2K page, 4 bit ECC */
++static struct nand_ecclayout layout_oob_64 = {
++ .eccbytes = 40,
++ .oobfree = {
++ { 30, 16 },
++ },
++};
++
++/* 4K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_128 = {
++ .eccbytes = 80,
++ .oobfree = {
++ { 70, 32 },
++ },
++};
++
++/* 4K page, 8 bit ECC, 8 bit bus width */
++static struct nand_ecclayout layout_oob_224_x8 = {
++ .eccbytes = 104,
++ .oobfree = {
++ { 91, 32 },
++ },
++};
++
++/* 4K page, 8 bit ECC, 16 bit bus width */
++static struct nand_ecclayout layout_oob_224_x16 = {
++ .eccbytes = 112,
++ .oobfree = {
++ { 98, 32 },
++ },
++};
++
++/* 8K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_256 = {
++ .eccbytes = 160,
++ .oobfree = {
++ { 151, 64 },
++ },
++};
++
++/*
++ * this is called before scan_ident, we do some minimal configurations so
++ * that reading ID and ONFI params work
++ */
++static void qcom_nandc_pre_init(struct qcom_nandc_data *this)
++{
++ /* kill onenand */
++ nandc_write(this, SFLASHC_BURST_CFG, 0);
++
++ /* enable ADM DMA */
++ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN);
++
++ /* save the original values of these registers */
++ this->cmd1 = nandc_read(this, NAND_DEV_CMD1);
++ this->vld = nandc_read(this, NAND_DEV_CMD_VLD);
++
++ /* initial status value */
++ this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++}
++
++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage;
++ bool wide_bus;
++
++ /* the nand controller fetches codewords/chunks of 512 bytes */
++ cwperpage = mtd->writesize >> 9;
++
++ ecc->strength = this->ecc_strength;
++
++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++ if (ecc->strength >= 8) {
++ /* 8 bit ECC defaults to BCH ECC on all platforms */
++ ecc->bytes = wide_bus ? 14 : 13;
++ } else {
++ /*
++ * if the controller supports BCH for 4 bit ECC, the controller
++ * uses lesser bytes for ECC. If RS is used, the ECC bytes is
++ * always 10 bytes
++ */
++ if (this->ecc_modes & ECC_BCH_4BIT)
++ ecc->bytes = wide_bus ? 8 : 7;
++ else
++ ecc->bytes = 10;
++ }
++
++ /* each step consists of 512 bytes of data */
++ ecc->size = NANDC_STEP_SIZE;
++
++ ecc->read_page = qcom_nandc_read_page;
++ ecc->read_oob = qcom_nandc_read_oob;
++ ecc->write_page = qcom_nandc_write_page;
++ ecc->write_oob = qcom_nandc_write_oob;
++
++ /*
++ * the bad block marker is readable only when we read the page with ECC
++ * disabled. all the ops above run with ECC enabled. We need raw read
++ * and write function for oob in order to access bad block marker.
++ */
++ ecc->read_oob_raw = qcom_nandc_read_oob_raw;
++ ecc->write_oob_raw = qcom_nandc_write_oob_raw;
++
++ switch (mtd->oobsize) {
++ case 64:
++ ecc->layout = &layout_oob_64;
++ break;
++ case 128:
++ ecc->layout = &layout_oob_128;
++ break;
++ case 224:
++ if (wide_bus)
++ ecc->layout = &layout_oob_224_x16;
++ else
++ ecc->layout = &layout_oob_224_x8;
++ break;
++ case 256:
++ ecc->layout = &layout_oob_256;
++ break;
++ default:
++ dev_err(this->dev, "unsupported NAND device, oobsize %d\n",
++ mtd->oobsize);
++ return -ENODEV;
++ }
++
++ ecc->mode = NAND_ECC_HW;
++
++ /* enable ecc by default */
++ this->use_ecc = true;
++
++ return 0;
++}
++
++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct nand_ecc_ctrl *ecc = &chip->ecc;
++ int cwperpage = mtd->writesize / ecc->size;
++ int spare_bytes, bad_block_byte;
++ bool wide_bus;
++ int ecc_mode = 0;
++
++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++ if (ecc->strength >= 8) {
++ this->cw_size = 532;
++
++ spare_bytes = wide_bus ? 0 : 2;
++
++ this->bch_enabled = true;
++ ecc_mode = 1;
++ } else {
++ this->cw_size = 528;
++
++ if (this->ecc_modes & ECC_BCH_4BIT) {
++ spare_bytes = wide_bus ? 2 : 4;
++
++ this->bch_enabled = true;
++ ecc_mode = 0;
++ } else {
++ spare_bytes = wide_bus ? 0 : 1;
++ }
++ }
++
++ /*
++ * DATA_UD_BYTES varies based on whether the read/write command protects
++ * spare data with ECC too. We protect spare data by default, so we set
++ * it to main + spare data, which are 512 and 4 bytes respectively.
++ */
++ this->cw_data = 516;
++
++ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1;
++
++ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE
++ | this->cw_data << UD_SIZE_BYTES
++ | 0 << DISABLE_STATUS_AFTER_WRITE
++ | 5 << NUM_ADDR_CYCLES
++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS
++ | 0 << STATUS_BFR_READ
++ | 1 << SET_RD_MODE_AFTER_STATUS
++ | spare_bytes << SPARE_SIZE_BYTES;
++
++ this->cfg1 = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | bad_block_byte << BAD_BLOCK_BYTE_NUM
++ | 0 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | wide_bus << WIDE_FLASH
++ | this->bch_enabled << ENABLE_BCH_ECC;
++
++ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
++ | this->cw_size << UD_SIZE_BYTES
++ | 5 << NUM_ADDR_CYCLES
++ | 0 << SPARE_SIZE_BYTES;
++
++ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
++ | 0 << CS_ACTIVE_BSY
++ | 17 << BAD_BLOCK_BYTE_NUM
++ | 1 << BAD_BLOCK_IN_SPARE_AREA
++ | 2 << WR_RD_BSY_GAP
++ | wide_bus << WIDE_FLASH
++ | 1 << DEV0_CFG1_ECC_DISABLE;
++
++ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE
++ | 0 << ECC_SW_RESET
++ | this->cw_data << ECC_NUM_DATA_BYTES
++ | 1 << ECC_FORCE_CLK_OPEN
++ | ecc_mode << ECC_MODE
++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH;
++
++ this->ecc_buf_cfg = 0x203 << NUM_STEPS;
++
++ this->clrflashstatus = FS_READY_BSY_N;
++ this->clrreadstatus = 0xc0;
++
++ dev_dbg(this->dev,
++ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
++ this->cfg0, this->cfg1, this->ecc_buf_cfg,
++ this->ecc_bch_cfg, this->cw_size, this->cw_data,
++ ecc->strength, ecc->bytes, cwperpage);
++}
++
++static int qcom_nandc_alloc(struct qcom_nandc_data *this)
++{
++ int r;
++
++ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32));
++ if (r) {
++ dev_err(this->dev, "failed to set DMA mask\n");
++ return r;
++ }
++
++ /*
++ * we use the internal buffer for reading ONFI params, reading small
++ * data like ID and status, and preforming read-copy-write operations
++ * when writing to a codeword partially. 532 is the maximum possible
++ * size of a codeword for our nand controller
++ */
++ this->buf_size = 532;
++
++ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL);
++ if (!this->data_buffer)
++ return -ENOMEM;
++
++ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL);
++ if (!this->regs)
++ return -ENOMEM;
++
++ this->reg_read_buf = devm_kzalloc(this->dev,
++ MAX_REG_RD * sizeof(*this->reg_read_buf),
++ GFP_KERNEL);
++ if (!this->reg_read_buf)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&this->list);
++
++ this->chan = dma_request_slave_channel(this->dev, "rxtx");
++ if (!this->chan) {
++ dev_err(this->dev, "failed to request slave channel\n");
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void qcom_nandc_unalloc(struct qcom_nandc_data *this)
++{
++ dma_release_channel(this->chan);
++}
++
++static int qcom_nandc_init(struct qcom_nandc_data *this)
++{
++ struct mtd_info *mtd = &this->mtd;
++ struct nand_chip *chip = &this->chip;
++ struct device_node *np = this->dev->of_node;
++ struct mtd_part_parser_data ppdata = { .of_node = np };
++ int r;
++
++ mtd->priv = chip;
++ mtd->name = "qcom-nandc";
++ mtd->owner = THIS_MODULE;
++
++ chip->priv = this;
++
++ chip->cmdfunc = qcom_nandc_command;
++ chip->select_chip = qcom_nandc_select_chip;
++ chip->read_byte = qcom_nandc_read_byte;
++ chip->read_buf = qcom_nandc_read_buf;
++ chip->write_buf = qcom_nandc_write_buf;
++
++ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
++ if (this->bus_width == 16)
++ chip->options |= NAND_BUSWIDTH_16;
++
++ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW;
++ if (of_get_nand_on_flash_bbt(np))
++ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++ qcom_nandc_pre_init(this);
++
++ r = nand_scan_ident(mtd, 1, NULL);
++ if (r)
++ return r;
++
++ r = qcom_nandc_ecc_init(this);
++ if (r)
++ return r;
++
++ qcom_nandc_hw_post_init(this);
++
++ r = nand_scan_tail(mtd);
++ if (r)
++ return r;
++
++ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
++}
++
++static int qcom_nandc_parse_dt(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++ struct device_node *np = this->dev->of_node;
++ int r;
++
++ this->ecc_strength = of_get_nand_ecc_strength(np);
++ if (this->ecc_strength < 0) {
++ dev_warn(this->dev,
++ "incorrect ecc strength, setting to 4 bits/step\n");
++ this->ecc_strength = 4;
++ }
++
++ this->bus_width = of_get_nand_bus_width(np);
++ if (this->bus_width < 0) {
++ dev_warn(this->dev, "incorrect bus width, setting to 8\n");
++ this->bus_width = 8;
++ }
++
++ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci);
++ if (r) {
++ dev_err(this->dev, "command CRCI unspecified\n");
++ return r;
++ }
++
++ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci);
++ if (r) {
++ dev_err(this->dev, "data CRCI unspecified\n");
++ return r;
++ }
++
++ return 0;
++}
++
++static int qcom_nandc_probe(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this;
++ const struct of_device_id *match;
++ int r;
++
++ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
++ if (!this)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, this);
++
++ this->pdev = pdev;
++ this->dev = &pdev->dev;
++
++ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
++ if (!match) {
++ dev_err(&pdev->dev, "failed to match device\n");
++ return -ENODEV;
++ }
++
++ if (!match->data) {
++ dev_err(&pdev->dev, "failed to get device data\n");
++ return -ENODEV;
++ }
++
++ this->ecc_modes = (u32) match->data;
++
++ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ this->base = devm_ioremap_resource(&pdev->dev, this->res);
++ if (IS_ERR(this->base))
++ return PTR_ERR(this->base);
++
++ this->core_clk = devm_clk_get(&pdev->dev, "core");
++ if (IS_ERR(this->core_clk))
++ return PTR_ERR(this->core_clk);
++
++ this->aon_clk = devm_clk_get(&pdev->dev, "aon");
++ if (IS_ERR(this->aon_clk))
++ return PTR_ERR(this->aon_clk);
++
++ r = qcom_nandc_parse_dt(pdev);
++ if (r)
++ return r;
++
++ r = qcom_nandc_alloc(this);
++ if (r)
++ return r;
++
++ r = clk_prepare_enable(this->core_clk);
++ if (r)
++ goto err_core_clk;
++
++ r = clk_prepare_enable(this->aon_clk);
++ if (r)
++ goto err_aon_clk;
++
++ r = qcom_nandc_init(this);
++ if (r)
++ goto err_init;
++
++ return 0;
++
++err_init:
++ clk_disable_unprepare(this->aon_clk);
++err_aon_clk:
++ clk_disable_unprepare(this->core_clk);
++err_core_clk:
++ qcom_nandc_unalloc(this);
++
++ return r;
++}
++
++static int qcom_nandc_remove(struct platform_device *pdev)
++{
++ struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++
++ qcom_nandc_unalloc(this);
++
++ clk_disable_unprepare(this->aon_clk);
++ clk_disable_unprepare(this->core_clk);
++
++ return 0;
++}
++
++#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
++
++/*
++ * data will hold a struct pointer containing more differences once we support
++ * more IPs
++ */
++static const struct of_device_id qcom_nandc_of_match[] = {
++ { .compatible = "qcom,ebi2-nandc",
++ .data = (void *) EBI2_NANDC_ECC_MODES,
++ },
++ {}
++};
++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
++
++static struct platform_driver qcom_nandc_driver = {
++ .driver = {
++ .name = "qcom-nandc",
++ .of_match_table = qcom_nandc_of_match,
++ },
++ .probe = qcom_nandc_probe,
++ .remove = qcom_nandc_remove,
++};
++module_platform_driver(qcom_nandc_driver);
++
++MODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>");
++MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nan
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
+
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
new file mode 100644
index 0000000..6530eb1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927141
+Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:16 +0530
+
+Add DT bindings document for the Qualcomm NAND controller driver.
+
+Cc: devicetree@vger.kernel.org
+
+v3:
+- Don't use '0x' when specifying nand controller address space
+- Add optional property for on-flash bbt usage
+
+Acked-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+@@ -0,0 +1,49 @@
++* Qualcomm NAND controller
++
++Required properties:
++- compatible: should be "qcom,ebi2-nand" for IPQ806x
++- reg: MMIO address range
++- clocks: must contain core clock and always on clock
++- clock-names: must contain "core" for the core clock and "aon" for the
++ always on clock
++- dmas: DMA specifier, consisting of a phandle to the ADM DMA
++ controller node and the channel number to be used for
++ NAND. Refer to dma.txt and qcom_adm.txt for more details
++- dma-names: must be "rxtx"
++- qcom,cmd-crci: must contain the ADM command type CRCI block instance
++ number specified for the NAND controller on the given
++ platform
++- qcom,data-crci: must contain the ADM data type CRCI block instance
++ number specified for the NAND controller on the given
++ platform
++
++Optional properties:
++- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen
++ as default
++
++- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8
++ bits. If not present, 4 is chosen as default
++- nand-on-flash-bbt: Create/use on-flash bad block table
++
++The device tree may optionally contain sub-nodes describing partitions of the
++address space. See partition.txt for more detail.
++
++Example:
++
++nand@1ac00000 {
++ compatible = "qcom,ebi2-nandc";
++ reg = <0x1ac00000 0x800>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&adm_dma 3>;
++ dma-names = "rxtx";
++ qcom,cmd-crci = <15>;
++ qcom,data-crci = <3>;
++
++ partition@0 {
++ ...
++ };
++};
diff --git a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..5049b60
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,51 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927121
+Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:17 +0530
+
+The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding
+compatible string.
+
+Cc: devicetree@vger.kernel.org
+
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -620,6 +620,22 @@
+
+ status = "disabled";
+ };
++
++ nand@1ac00000 {
++ compatible = "qcom,ebi2-nandc";
++ reg = <0x1ac00000 0x800>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&adm_dma 3>;
++ dma-names = "rxtx";
++ qcom,cmd-crci = <15>;
++ qcom,data-crci = <3>;
++
++ status = "disabled";
++ };
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
new file mode 100644
index 0000000..415bf30
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
@@ -0,0 +1,76 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform
+From: Archit Taneja <architt@codeaurora.org>
+X-Patchwork-Id: 6927091
+Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org>
+To: linux-mtd@lists.infradead.org, dehrenberg@google.com,
+ cernekee@gmail.com, computersforpeace@gmail.com
+Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org,
+ sboyd@codeaurora.org, linux-kernel@vger.kernel.org,
+ Archit Taneja <architt@codeaurora.org>, devicetree@vger.kernel.org
+Date: Mon, 3 Aug 2015 10:38:18 +0530
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Cc: devicetree@vger.kernel.org
+
+Signed-off-by: Archit Taneja <architt@codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -61,6 +61,28 @@
+ bias-none;
+ };
+ };
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -123,5 +145,19 @@
+ pinctrl-0 = <&pcie1_pins>;
+ pinctrl-names = "default";
+ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++ };
+ };
+ };
++
++&adm_dma {
++ status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
new file mode 100644
index 0000000..16df418
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -154,6 +154,8 @@
+
+ nand-ecc-strength = <4>;
+ nand-bus-width = <8>;
++
++ linux,part-probe = "qcom-smem";
+ };
+ };
+ };
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 0000000..51ebc19
--- /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/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch b/target/linux/ipq806x/patches-4.1/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch
new file mode 100644
index 0000000..12e06d2
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch
@@ -0,0 +1,387 @@
+From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo@openwrt.org>
+Date: Sun, 9 Aug 2015 13:02:38 +0200
+Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++
+ 2 files changed, 371 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -452,6 +452,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8084-mtp.dtb \
+ qcom-ipq8064-ap148.dtb \
+ qcom-ipq8064-db149.dtb \
++ qcom-ipq8064-r7500.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb \
+ qcom-msm8974-sony-xperia-honami.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+@@ -0,0 +1,362 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Netgear Nighthawk X4 R7500";
++ compatible = "netgear,r7500", "qcom,ipq8064";
++
++ memory@0 {
++ reg = <0x42000000 0xe000000>;
++ device_type = "memory";
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ aliases {
++ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
++ };
++
++ chosen {
++ bootargs = "rootfstype=squashfs noinitrd";
++ linux,stdout-path = "serial0:115200n8";
++ };
++
++ soc {
++ pinmux@800000 {
++ i2c4_pins: i2c4_pinmux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ bias-disable;
++ };
++
++ pcie0_pins: pcie0_pinmux {
++ mux {
++ pins = "gpio3";
++ function = "pcie1_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio48";
++ function = "pcie2_rst";
++ drive-strength = <12>;
++ bias-disable;
++ };
++ };
++
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ 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 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ /*
++ * 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.
++ */
++ };
++
++ 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";
++ };
++
++ pcie0: pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie0_pins>;
++ pinctrl-names = "default";
++ };
++
++ pcie1: pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++ };
++
++ nand@1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++
++ nand-ecc-strength = <4>;
++ nand-bus-width = <8>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ qcadata@0 {
++ label = "qcadata";
++ reg = <0x0000000 0x0c80000>;
++ read-only;
++ };
++
++ APPSBL@c80000 {
++ label = "APPSBL";
++ reg = <0x0c80000 0x0500000>;
++ read-only;
++ };
++
++ APPSBLENV@1180000 {
++ label = "APPSBLENV";
++ reg = <0x1180000 0x0080000>;
++ read-only;
++ };
++
++ art: art@1200000 {
++ label = "art";
++ reg = <0x1200000 0x0140000>;
++ read-only;
++ };
++
++ kernel@1340000 {
++ label = "kernel";
++ reg = <0x1340000 0x0200000>;
++ };
++
++ ubi@1540000 {
++ label = "ubi";
++ reg = <0x1540000 0x1800000>;
++ };
++
++ netgear@2d40000 {
++ label = "netgear";
++ reg = <0x2d40000 0x0c00000>;
++ read-only;
++ };
++
++ reserve@3940000 {
++ label = "reserve";
++ reg = <0x3940000 0x46c0000>;
++ read-only;
++ };
++
++ firmware@1340000 {
++ label = "firmware";
++ reg = <0x1340000 0x1a00000>;
++ };
++
++ };
++
++ 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>;
++ };
++ };
++
++ gmac1: ethernet@37200000 {
++ status = "ok";
++ phy-mode = "rgmii";
++ phy-handle = <&phy4>;
++ qcom,id = <1>;
++
++ pinctrl-0 = <&rgmii2_pins>;
++ pinctrl-names = "default";
++
++ mtd-mac-address = <&art 6>;
++ };
++
++ gmac2: ethernet@37400000 {
++ status = "ok";
++ phy-mode = "sgmii";
++ qcom,id = <2>;
++
++ mtd-mac-address = <&art 0>;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++
++ wifi {
++ label = "wifi";
++ gpios = <&qcom_pinmux 6 1>;
++ linux,code = <KEY_WLAN>;
++ };
++
++ reset {
++ label = "reset";
++ gpios = <&qcom_pinmux 54 1>;
++ linux,code = <KEY_RESTART>;
++ };
++
++ wps {
++ label = "wps";
++ gpios = <&qcom_pinmux 65 1>;
++ linux,code = <KEY_WPS_BUTTON>;
++ };
++ };
++
++ gpio-leds {
++ compatible = "gpio-leds";
++
++ usb1 {
++ label = "r7500:amber:usb1";
++ gpios = <&qcom_pinmux 7 0>;
++ };
++
++ usb3 {
++ label = "r7500:amber:usb3";
++ gpios = <&qcom_pinmux 8 0>;
++ };
++
++ status {
++ label = "r7500:amber:status";
++ gpios = <&qcom_pinmux 9 0>;
++ };
++
++ internet {
++ label = "r7500:white:internet";
++ gpios = <&qcom_pinmux 22 0>;
++ };
++
++ wan {
++ label = "r7500:white:wan";
++ gpios = <&qcom_pinmux 23 0>;
++ };
++
++ wps {
++ label = "r7500:white:wps";
++ gpios = <&qcom_pinmux 24 0>;
++ };
++
++ esata {
++ label = "r7500:white:esata";
++ gpios = <&qcom_pinmux 26 0>;
++ };
++
++ power {
++ label = "r7500:white:power";
++ gpios = <&qcom_pinmux 53 0>;
++ default-state = "on";
++ };
++
++ rfkill {
++ label = "r7500:white:rfkill";
++ gpios = <&qcom_pinmux 64 0>;
++ };
++
++ wifi5g {
++ label = "r7500:white:wifi5g";
++ gpios = <&qcom_pinmux 67 0>;
++ };
++ };
++};
++
++&adm_dma {
++ status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch
new file mode 100644
index 0000000..471a87b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch
@@ -0,0 +1,13 @@
+--- a/drivers/mtd/qcom_smem_part.c
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -192,6 +192,10 @@ static int parse_qcom_smem_partitions(st
+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
+
++ /* "rootfs" conflicts with OpenWrt auto mounting */
++ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
++ m_part->name = "ubi";
++
+ /*
+ * The last SMEM partition may have its size marked as
+ * something like 0xffffffff, which means "until the end of the
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 0000000..dfe7169
--- /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 0000000..3711e12
--- /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 0000000..37f1002
--- /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 0000000..a05875b
--- /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 0000000..7fd72be
--- /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 0000000..3144fa3
--- /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 0000000..0e2b305
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
@@ -0,0 +1,146 @@
+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
+@@ -19,8 +19,9 @@
+ };
+ };
+
+- alias {
++ aliases {
+ serial0 = &uart4;
++ mdio-gpio0 = &mdio0;
+ };
+
+ chosen {
+@@ -83,6 +84,15 @@
+ bias-bus-hold;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -157,6 +167,34 @@
+
+ linux,part-probe = "qcom-smem";
+ };
++
++ 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 {
+@@ -65,6 +66,15 @@
+ bias-none;
+ };
+ };
++
++ mdio0_pins: mdio0_pins {
++ mux {
++ pins = "gpio0", "gpio1";
++ function = "gpio";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -176,5 +186,44 @@
+ 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>;
++ };
++
++ 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 0000000..0918634
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,212 @@
+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
+@@ -93,6 +93,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 {
+@@ -195,6 +205,27 @@
+ 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
+@@ -75,6 +75,14 @@
+ bias-disable;
+ };
+ };
++
++ rgmii0_pins: rgmii0_pins {
++ mux {
++ pins = "gpio2", "gpio66";
++ drive-strength = <8>;
++ bias-disable;
++ };
++ };
+ };
+
+ gsbi2: gsbi@12480000 {
+@@ -225,5 +233,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
+@@ -636,6 +636,92 @@
+
+ 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";
++ };
+ };
+
+ sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch b/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch
new file mode 100644
index 0000000..84c15c7
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch
@@ -0,0 +1,35 @@
+From 5bf2dabde1fa3af0c9082b42b6847ef3fd198b13 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo@openwrt.org>
+Date: Sun, 9 Aug 2015 12:53:55 +0200
+Subject: [PATCH] stmac: platform: add support for retreiving mac from mtd
+
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -302,6 +302,7 @@ static int stmmac_pltfr_probe(struct pla
+ struct stmmac_priv *priv = NULL;
+ struct plat_stmmacenet_data *plat_dat = NULL;
+ const char *mac = NULL;
++ u8 mtd_mac[ETH_ALEN] = { };
+ int irq, wol_irq, lpi_irq;
+
+ /* Get IRQ information early to have an ability to ask for deferred
+@@ -362,6 +363,15 @@ static int stmmac_pltfr_probe(struct pla
+ pr_err("%s: main dt probe failed", __func__);
+ return ret;
+ }
++
++ if (!mac) {
++ ret = of_get_mac_address_mtd(dev->of_node, &mtd_mac);
++ if (ret == -EPROBE_DEFER)
++ return ret;
++
++ if (is_valid_ether_addr(&mtd_mac))
++ mac = mtd_mac;
++ }
+ }
+
+ /* Custom setup (if needed) */
diff --git a/target/linux/ipq806x/profiles/default.mk b/target/linux/ipq806x/profiles/default.mk
new file mode 100644
index 0000000..6469bcb
--- /dev/null
+++ b/target/linux/ipq806x/profiles/default.mk
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/Default
+ NAME:=Default Profile (minimum package set)
+ PACKAGES:= \
+ kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \
+ kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3
+endef
+
+define Profile/Default/Description
+ Default package set compatible with most boards.
+endef
+$(eval $(call Profile,Default))
diff --git a/target/linux/ipq806x/profiles/netgear.mk b/target/linux/ipq806x/profiles/netgear.mk
new file mode 100644
index 0000000..3402f45
--- /dev/null
+++ b/target/linux/ipq806x/profiles/netgear.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/R7500
+ NAME:=Netgear Nighthawk X4 R7500
+ PACKAGES:= \
+ kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \
+ kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3 \
+ kmod-ath10k wpad-mini
+endef
+
+define Profile/R7500/Description
+ Package set for the Netgear Nighthawk X4 R7500.
+endef
+$(eval $(call Profile,R7500))