diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4
new file mode 100644
index 0000000..3a9592f
--- /dev/null
+++ b/target/linux/ipq806x/config-4.4
@@ -0,0 +1,463 @@
+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_RNG2=y
+CONFIG_CRYPTO_WORKQUEUE=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_DWMAC_GENERIC is not set
+CONFIG_DWMAC_IPQ806X=y
+# CONFIG_DWMAC_LPC18XX is not set
+# CONFIG_DWMAC_MESON is not set
+# CONFIG_DWMAC_ROCKCHIP is not set
+# CONFIG_DWMAC_SOCFPGA is not set
+# CONFIG_DWMAC_STI is not set
+# CONFIG_DWMAC_SUNXI 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_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_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_MSM8660 is not set
+# 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_PINCTRL_QCOM_SSBI_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_PM is not set
+CONFIG_QCOM_SCM=y
+# CONFIG_QCOM_SMD is not set
+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_REGULATOR_QCOM_SPMI 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_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/patches-4.4/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch
new file mode 100644
index 0000000..9c33bad
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch
@@ -0,0 +1,61 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,14 +4,6 @@
+ 	model = "Qualcomm IPQ8064/AP148";
+ 	compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
+ 
+-	aliases {
+-		serial0 = &gsbi4_serial;
+-	};
+-
+-	chosen {
+-		stdout-path = "serial0:115200n8";
+-	};
+-
+ 	reserved-memory {
+ 		#address-cells = <1>;
+ 		#size-cells = <1>;
+@@ -22,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>;
+ 
+-			gsbi4_serial: 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.4/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch
new file mode 100644
index 0000000..bfdb30f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch
new file mode 100644
index 0000000..4d3e827
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -506,6 +506,7 @@
+ 	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.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
new file mode 100644
index 0000000..b8c527b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
@@ -0,0 +1,52 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -47,14 +47,12 @@
+ 				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
+@@ -294,7 +294,7 @@
+ 			.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,
+ 		},
+ 	},
+ };
+@@ -312,7 +312,7 @@
+ 			},
+ 			.num_parents = 1,
+ 			.ops = &clk_branch_ops,
+-			.flags = CLK_SET_RATE_PARENT,
++			.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ 		},
+ 	},
+ };
+@@ -890,7 +890,7 @@
+ 		.hw.init = &(struct clk_init_data){
+ 			.name = "gsbi1_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.4/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch
new file mode 100644
index 0000000..f026ed9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..e1d317d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
new file mode 100644
index 0000000..06b1405
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
new file mode 100644
index 0000000..d80eb86
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
@@ -0,0 +1,282 @@
+From 61e8e1b1af77f24339da3f0822a76fa65ed635c6 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:53:14 -0700
+Subject: [PATCH] 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>
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/mtd/Kconfig          |   7 ++
+ drivers/mtd/Makefile         |   1 +
+ drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 236 insertions(+)
+ create mode 100644 drivers/mtd/qcom_smem_part.c
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index a03ad29..debc887 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -190,6 +190,13 @@
+ 	  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"
+ 
+ #
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index 99bb9a1..2a44a64 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -16,6 +16,7 @@
+ 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/drivers/mtd/qcom_smem_part.c b/drivers/mtd/qcom_smem_part.c
+new file mode 100644
+index 0000000..f9c1bca
+--- /dev/null
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -0,0 +1,228 @@
++/*
++ * 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)
++{
++	size_t size;
++
++	*smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
++			    	    &size);
++
++	if (IS_ERR(*smem_blksz)) {
++		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)
++{
++	size_t size;
++
++	*smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
++			    		&size);
++
++	if (IS_ERR(*smem_flash_type)) {
++		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)
++{
++	size_t size;
++
++	*pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
++				&size);
++
++	if (IS_ERR(*pparts)) {
++		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");
diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
new file mode 100644
index 0000000..68f2b39
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
@@ -0,0 +1,510 @@
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -390,4 +390,15 @@
+	  Enable this to support the Broadcom Cygnus PCIe PHY.
+	  If unsure, say N.
+ 
++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
+@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210)	+=
+ obj-$(CONFIG_PHY_BRCMSTB_SATA)		+= phy-brcmstb-sata.o
+ obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
+ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
++obj-$(CONFIG_PHY_QCOM_DWC3)		+= phy-qcom-dwc3.o
+--- /dev/null
++++ b/drivers/phy/phy-qcom-dwc3.c
+@@ -0,0 +1,482 @@
++/* 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);
++
++	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-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
new file mode 100644
index 0000000..6e6f10d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
@@ -0,0 +1,125 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -92,5 +92,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
+@@ -333,6 +333,90 @@
+ 			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-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644
index 0000000..41f91fa
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644
index 0000000..ad1a1b9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
@@ -0,0 +1,752 @@
+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
+@@ -8253,6 +8253,13 @@
+ F:	Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
+ F:	drivers/pci/host/pcie-hisi.c
+ 
++PCIE DRIVER FOR QUALCOMM MSM
++M:     Stanimir Varbanov <svarbanov@mm-sol.com>
++L:     linux-pci@vger.kernel.org
++L:     linux-arm-msm@vger.kernel.org
++S:     Maintained
++F:     drivers/pci/host/*qcom*
++
+ PCMCIA SUBSYSTEM
+ P:	Linux PCMCIA Team
+ L:	linux-pcmcia@lists.infradead.org
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -173,4 +173,13 @@
+ 	help
+ 	  Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
+ 
++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,676 @@
++/*
++ * 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, 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", GPIOD_OUT_LOW);
++	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
+@@ -20,3 +20,4 @@
+ obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
+ obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
+ obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000..1246bfe
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,244 @@
+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
+@@ -116,5 +116,15 @@
+ 		usb30@1 {
+ 			status = "ok";
+ 		};
++
++		pcie0: pci@1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci@1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
+ 	};
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -128,5 +128,17 @@
+ 		usb30@1 {
+ 			status = "ok";
+ 		};
++
++		pcie0: pci@1b500000 {
++			status = "ok";
++		};
++
++		pcie1: pci@1b700000 {
++			status = "ok";
++		};
++
++		pcie2: pci@1b900000 {
++			status = "ok";
++		};
+ 	};
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -4,6 +4,9 @@
+ #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>
++#include <dt-bindings/gpio/gpio.h>
+ 
+ / {
+ 	model = "Qualcomm IPQ8064";
+@@ -99,6 +102,33 @@
+ 			interrupt-controller;
+ 			#interrupt-cells = <2>;
+ 			interrupts = <0 16 0x4>;
++
++			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;
++				};
++			};
+ 		};
+ 
+ 		intc: interrupt-controller@2000000 {
+@@ -417,6 +447,144 @@
+ 				dr_mode = "host";
+ 			};
+ 		};
++
++		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 0x0fe00000 0x0fe00000 0 0x00100000   /* downstream I/O */
++				  0x82000000 0 0x08000000 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";
++
++			pinctrl-0 = <&pcie0_pins>;
++			pinctrl-names = "default";
++
++			perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++
++			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 0x31e00000 0x31e00000 0 0x00100000   /* downstream I/O */
++				  0x82000000 0 0x2e000000 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";
++
++			pinctrl-0 = <&pcie1_pins>;
++			pinctrl-names = "default";
++
++			perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++
++			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 0x35e00000 0x35e00000 0 0x00100000   /* downstream I/O */
++				  0x82000000 0 0x32000000 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";
++
++			pinctrl-0 = <&pcie2_pins>;
++			pinctrl-names = "default";
++
++			perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>;
++
++			status = "disabled";
++		};
+ 	};
+ 
+ 	sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.4/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.4/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.4/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch
new file mode 100644
index 0000000..6328113
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch
@@ -0,0 +1,311 @@
+--- 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 {
+@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl
+ 
+ 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);
+-
++	gpiod_set_value(pcie->reset, assert);
+ 	usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
+ }
+ 
+@@ -156,10 +192,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 +240,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,21 +264,40 @@ 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");
+ 		goto err_reset_ahb;
+ 	}
++	udelay(1);
+ 
+ 	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 +393,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 +421,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 +541,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_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++	writel((pp->cfg0_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++	writel((pp->cfg0_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_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++	writel((pp->mem_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++	writel(pp->mem_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);
++
++	/* 256B PCIE buffer setting */
++	writel(0x1, 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);
+@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc
+ 
+ 	qcom_ep_reset_assert(pcie);
+ 
++	reset_control_assert(res->ahb_reset);
++
+ 	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));
++	/* 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 +667,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.4/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch
new file mode 100644
index 0000000..8ceace9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -476,15 +476,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";
+ 
+ 			pinctrl-0 = <&pcie0_pins>;
+ 			pinctrl-names = "default";
+@@ -522,15 +528,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";
+ 
+ 			pinctrl-0 = <&pcie1_pins>;
+ 			pinctrl-names = "default";
+@@ -568,15 +580,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";
+ 
+ 			pinctrl-0 = <&pcie2_pins>;
+ 			pinctrl-names = "default";
diff --git a/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch
new file mode 100644
index 0000000..7daa931
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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>
+@@ -93,6 +94,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>;
+@@ -164,6 +222,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.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch
new file mode 100644
index 0000000..01bd749
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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 @@
+ 	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-$(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.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch
new file mode 100644
index 0000000..acf5820
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch
@@ -0,0 +1,184 @@
+From 4c28a15ea536281c8d619e5c6716ade914c79a6e Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:21 -0700
+Subject: [PATCH 1/2] clk: mux: Split out register accessors for reuse
+
+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>
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/clk/clk-mux.c        | 74 +++++++++++++++++++++++++++-----------------
+ include/linux/clk-provider.h |  9 ++++--
+ 2 files changed, 53 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
+index 7129c86..b03a34d 100644
+--- a/drivers/clk/clk-mux.c
++++ b/drivers/clk/clk-mux.c
+@@ -28,35 +28,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_hw_get_num_parents(hw);
+-	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)
+@@ -64,24 +53,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
+ 
+ 	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;
+ 
+-	else {
+-		if (mux->flags & CLK_MUX_INDEX_BIT)
+-			index = 1 << index;
++	return clk_mux_get_parent(hw, val, mux->table, mux->flags);
++}
++
++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);
+ 	else
+@@ -105,7 +123,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+ }
+ 
+ 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,
+ };
+@@ -120,7 +138,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ 		const char * const *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;
+diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
+index c56988a..b6b17b5 100644
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -432,7 +432,7 @@ void clk_unregister_divider(struct clk *clk);
+ struct clk_mux {
+ 	struct clk_hw	hw;
+ 	void __iomem	*reg;
+-	u32		*table;
++	unsigned int    *table;
+ 	u32		mask;
+ 	u8		shift;
+ 	u8		flags;
+@@ -448,6 +448,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 * const *parent_names, u8 num_parents,
+ 		unsigned long flags,
+@@ -458,7 +463,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ 		const char * const *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);
+ 
+-- 
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
new file mode 100644
index 0000000..5df0a56
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
@@ -0,0 +1,127 @@
+From 39d42ce5031d2a4f92fa203b87acfbab340b15a2 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:22 -0700
+Subject: [PATCH 2/2] clk: Avoid sending high rates to downstream clocks during
+ set_rate
+
+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>
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
+ 1 file changed, 22 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
+index f13c3f4..8404c3c 100644
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -1427,21 +1427,24 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
+  * walk down a subtree and set the new rates notifying the rate
+  * change on the way
+  */
+-static void clk_change_rate(struct clk_core *core)
++static void
++clk_change_rate(struct clk_core *core, 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 = core->rate;
++	hlist_for_each_entry(child, &core->children, child_node) {
++		/* Skip children who will be reparented to another clock */
++		if (child->new_parent && child->new_parent != core)
++			continue;
++		if (child->new_rate > child->rate)
++			clk_change_rate(child, core->new_rate);
++	}
+ 
+-	if (core->new_parent)
+-		best_parent_rate = core->new_parent->rate;
+-	else if (core->parent)
+-		best_parent_rate = core->parent->rate;
++	old_rate = core->rate;
+ 
+ 	if (core->new_parent && core->new_parent != core->parent) {
+ 		old_parent = __clk_set_parent_before(core, core->new_parent);
+@@ -1467,7 +1470,7 @@ static void clk_change_rate(struct clk_core *core)
+ 
+ 	trace_clk_set_rate_complete(core, core->new_rate);
+ 
+-	core->rate = clk_recalc(core, best_parent_rate);
++	core->rate = core->new_rate;
+ 
+ 	if (core->notifier_count && old_rate != core->rate)
+ 		__clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
+@@ -1483,12 +1486,13 @@ static void clk_change_rate(struct clk_core *core)
+ 		/* Skip children who will be reparented to another clock */
+ 		if (child->new_parent && child->new_parent != core)
+ 			continue;
+-		clk_change_rate(child);
++		if (child->new_rate != child->rate)
++			clk_change_rate(child, core->new_rate);
+ 	}
+ 
+ 	/* handle the new child who might not be in core->children yet */
+-	if (core->new_child)
+-		clk_change_rate(core->new_child);
++	if (core->new_child && core->new_child->new_rate != core->new_child->rate)
++		clk_change_rate(core->new_child, core->new_rate);
+ }
+ 
+ static int clk_core_set_rate_nolock(struct clk_core *core,
+@@ -1497,6 +1501,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
+ 	struct clk_core *top, *fail_clk;
+ 	unsigned long rate = req_rate;
+ 	int ret = 0;
++	unsigned long parent_rate;
+ 
+ 	if (!core)
+ 		return 0;
+@@ -1522,8 +1527,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
+ 		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);
+ 
+ 	core->req_rate = req_rate;
+ 
+-- 
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch
new file mode 100644
index 0000000..e969f6b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch
@@ -0,0 +1,158 @@
+From f7a00ea959be31f9b742042294a359d508edce94 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 20 Mar 2015 23:45:23 -0700
+Subject: [PATCH] clk: Add safe switch hook
+
+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>
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/clk/clk.c            | 61 ++++++++++++++++++++++++++++++++++++++------
+ include/linux/clk-provider.h |  1 +
+ 2 files changed, 54 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
+index 8404c3c..a29319a 100644
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -51,9 +51,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;
+@@ -1271,7 +1274,8 @@ out:
+ static void clk_calc_subtree(struct clk_core *core, 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;
+ 
+ 	core->new_rate = new_rate;
+ 	core->new_parent = new_parent;
+@@ -1281,6 +1285,18 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
+ 	if (new_parent && new_parent != core->parent)
+ 		new_parent->new_child = core;
+ 
++	if (core->ops->get_safe_parent) {
++		parent_hw = core->ops->get_safe_parent(core->hw);
++		if (parent_hw) {
++			parent = parent_hw->core;
++			p_index = clk_fetch_parent_index(core, parent);
++			core->safe_parent_index = p_index;
++			core->safe_parent = parent;
++		}
++	} else {
++		core->safe_parent = NULL;
++	}
++
+ 	hlist_for_each_entry(child, &core->children, child_node) {
+ 		child->new_rate = clk_recalc(child, new_rate);
+ 		clk_calc_subtree(child, child->new_rate, NULL, 0);
+@@ -1393,14 +1409,43 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
+ 						  unsigned long event)
+ {
+ 	struct clk_core *child, *tmp_clk, *fail_clk = NULL;
++	struct clk_core *old_parent;
+ 	int ret = NOTIFY_DONE;
+ 
+-	if (core->rate == core->new_rate)
++	if (core->rate == core->new_rate && event != POST_RATE_CHANGE)
+ 		return NULL;
+ 
++	switch (event) {
++	case PRE_RATE_CHANGE:
++		if (core->safe_parent)
++			core->ops->set_parent(core->hw, core->safe_parent_index);
++		core->old_rate = core->rate;
++		break;
++	case POST_RATE_CHANGE:
++		if (core->safe_parent) {
++			old_parent = __clk_set_parent_before(core,
++							    core->new_parent);
++			if (core->ops->set_rate_and_parent) {
++				core->ops->set_rate_and_parent(core->hw,
++						core->new_rate,
++						core->new_parent ?
++						core->new_parent->rate : 0,
++						core->new_parent_index);
++			} else if (core->ops->set_parent) {
++				core->ops->set_parent(core->hw,
++						core->new_parent_index);
++			}
++			__clk_set_parent_after(core, core->new_parent,
++						old_parent);
++		}
++		break;
++	}
++
+ 	if (core->notifier_count) {
+-		ret = __clk_notify(core, event, core->rate, core->new_rate);
+-		if (ret & NOTIFY_STOP_MASK)
++		if (event != POST_RATE_CHANGE || core->old_rate != core->rate)
++			ret = __clk_notify(core, event, core->old_rate,
++					   core->new_rate);
++		if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
+ 			fail_clk = core;
+ 	}
+ 
+@@ -1446,7 +1491,8 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
+ 
+ 	old_rate = core->rate;
+ 
+-	if (core->new_parent && core->new_parent != core->parent) {
++	if (core->new_parent && core->new_parent != core->parent &&
++			!core->safe_parent) {
+ 		old_parent = __clk_set_parent_before(core, core->new_parent);
+ 		trace_clk_set_parent(core, core->new_parent);
+ 
+@@ -1472,9 +1518,6 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
+ 
+ 	core->rate = core->new_rate;
+ 
+-	if (core->notifier_count && old_rate != core->rate)
+-		__clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
+-
+ 	if (core->flags & CLK_RECALC_NEW_RATES)
+ 		(void)clk_calc_new_rates(core, core->new_rate);
+ 
+@@ -1537,6 +1580,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
+ 
+ 	core->req_rate = req_rate;
+ 
++	clk_propagate_rate_change(top, POST_RATE_CHANGE);
++
+ 	return ret;
+ }
+ 
+diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
+index b6b17b5..5d49262 100644
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -202,6 +202,7 @@ struct clk_ops {
+ 					  struct clk_rate_request *req);
+ 	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,
+-- 
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
new file mode 100644
index 0000000..a1b1f4b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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-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
+ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.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_hw_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.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch
new file mode 100644
index 0000000..5a452db
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -106,3 +106,11 @@
+ 	  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
+@@ -23,3 +23,4 @@
+ 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.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
new file mode 100644
index 0000000..5b191b5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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 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,
+@@ -2837,6 +2917,9 @@
+ 	[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
+ 	[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
+ 	[NSSTCM_CLK] = &nss_tcm_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.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
new file mode 100644
index 0000000..522482d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
@@ -0,0 +1,272 @@
+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
+@@ -114,3 +114,7 @@
+ 	  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-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
+ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.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_hw_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_hw_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_hw_round_rate(clk_hw_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.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
new file mode 100644
index 0000000..b2ddbc8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
@@ -0,0 +1,207 @@
+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 @@
+ 		    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
+@@ -115,6 +115,14 @@
+ 	  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
+@@ -9,6 +9,7 @@
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
+ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.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.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644
index 0000000..f0e9467
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -123,6 +123,14 @@
+ 	  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
+@@ -26,3 +26,4 @@
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
new file mode 100644
index 0000000..d4c43f4
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -95,6 +95,15 @@
+ 	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
+@@ -61,6 +61,7 @@
+ 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.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
new file mode 100644
index 0000000..aaf1401
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -26,6 +26,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 {
+@@ -36,11 +41,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>;
+ 		};
+ 	};
+ 
+@@ -73,6 +91,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>;
+@@ -215,11 +273,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.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
new file mode 100644
index 0000000..f33c9e0
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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 @@
+ 
+ 	  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
+@@ -13,6 +13,7 @@
+ 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_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.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
new file mode 100644
index 0000000..4f5c0ef
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch
new file mode 100644
index 0000000..805f28f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch
@@ -0,0 +1,964 @@
+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
+@@ -558,4 +558,14 @@
+ config DMA_ENGINE_RAID
+ 	bool
+ 
++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
+@@ -65,5 +65,6 @@
+ obj-$(CONFIG_TI_EDMA) += edma.o
+ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
+ obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+ 
+ obj-y += xilinx/
diff --git a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
new file mode 100644
index 0000000..b8abd0a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -727,6 +727,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.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
new file mode 100644
index 0000000..21fe405
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
@@ -0,0 +1,83 @@
+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
+@@ -394,7 +394,11 @@
+ 	} 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
+@@ -420,7 +420,11 @@
+ 	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,12 @@
+ #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.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
new file mode 100644
index 0000000..19e5f91
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -546,4 +546,11 @@
+ 	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
+@@ -55,5 +55,6 @@
+ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
++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.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
new file mode 100644
index 0000000..6530eb1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..4fc8a8c
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -747,6 +747,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.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
new file mode 100644
index 0000000..9de3d8f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -43,6 +43,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 {
+@@ -126,5 +148,19 @@
+ 			status = "ok";
+ 			phy-tx0-term-offset = <7>;
+ 		};
++
++		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.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
new file mode 100644
index 0000000..7060282
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -157,6 +157,8 @@
+ 
+ 			nand-ecc-strength = <4>;
+ 			nand-bus-width = <8>;
++
++			linux,part-probe = "qcom-smem";
+ 		};
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
new file mode 100644
index 0000000..c0281ff
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -323,7 +323,7 @@
+ 	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
+@@ -256,9 +256,11 @@
+ 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.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch
new file mode 100644
index 0000000..8890a91
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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
+@@ -189,6 +189,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.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
new file mode 100644
index 0000000..625ff0a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/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 {
+@@ -65,6 +66,15 @@
+ 					bias-bus-hold;
+ 				};
+ 			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
+ 		};
+ 
+ 		gsbi@16300000 {
+@@ -160,6 +170,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 {
+@@ -38,6 +39,15 @@
+ 					bias-none;
+ 				};
+ 			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
+ 		};
+ 
+ 		gsbi2: gsbi@12480000 {
+@@ -140,5 +150,44 @@
+ 		pcie2: pci@1b900000 {
+ 			status = "ok";
+ 		};
++
++		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.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
new file mode 100644
index 0000000..691ebb6
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -0,0 +1,216 @@
+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
+@@ -75,6 +75,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 {
+@@ -198,6 +208,31 @@
+ 				reg = <4>;
+ 			};
+ 		};
++
++		gmac1: ethernet@37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		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
+@@ -48,6 +48,14 @@
+ 					bias-disable;
+ 				};
+ 			};
++
++			rgmii0_pins: rgmii0_pins {
++				mux {
++					pins = "gpio2", "gpio66";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
+ 		};
+ 
+ 		gsbi2: gsbi@12480000 {
+@@ -189,5 +197,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
+@@ -763,6 +763,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.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch
new file mode 100644
index 0000000..8c4718e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch
@@ -0,0 +1,134 @@
+From 16d2871830ff3fe12a6bff582549a9264adff278 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangi@codeaurora.org>
+Date: Tue, 10 May 2016 20:19:31 +0530
+Subject: [PATCH] spi: qup: Fix fifo and dma support for IPQ806x
+
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 52 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 810a7fa..0808017 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -24,6 +24,7 @@
+ #include <linux/spi/spi.h>
+ #include <linux/dmaengine.h>
+ #include <linux/dma-mapping.h>
++#include <linux/gpio.h>
+ 
+ #define QUP_CONFIG			0x0000
+ #define QUP_STATE			0x0004
+@@ -152,6 +153,7 @@ struct spi_qup {
+ 	int			use_dma;
+ 	struct dma_slave_config	rx_conf;
+ 	struct dma_slave_config	tx_conf;
++	int mode;
+ };
+ 
+ 
+@@ -370,7 +372,8 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
+ 		return ret;
+ 	}
+ 
+-	spi_qup_fifo_write(qup, xfer);
++	if (qup->mode == QUP_IO_M_MODE_FIFO)
++		spi_qup_fifo_write(qup, xfer);
+ 
+ 	return 0;
+ }
+@@ -448,6 +451,7 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
+ {
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	u32 mode;
++	size_t dma_align = dma_get_cache_alignment();
+ 
+ 	qup->w_size = 4;
+ 
+@@ -458,6 +462,14 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
+ 
+ 	qup->n_words = xfer->len / qup->w_size;
+ 
++	if (!IS_ERR_OR_NULL(master->dma_rx) &&
++			IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
++			IS_ALIGNED((size_t)xfer->rx_buf, dma_align) &&
++			!is_vmalloc_addr(xfer->tx_buf) &&
++			!is_vmalloc_addr(xfer->rx_buf) &&
++			(xfer->len > 3*qup->in_blk_sz))
++		qup->use_dma = 1;
++
+ 	if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
+ 		mode = QUP_IO_M_MODE_FIFO;
+ 	else
+@@ -491,7 +503,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 		return -EIO;
+ 	}
+ 
+-	mode = spi_qup_get_mode(spi->master, xfer);
++	controller->mode = mode = spi_qup_get_mode(spi->master, xfer);
+ 	n_words = controller->n_words;
+ 
+ 	if (mode == QUP_IO_M_MODE_FIFO) {
+@@ -500,6 +512,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 		/* must be zero for FIFO */
+ 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++		controller->use_dma = 0;
+ 	} else if (!controller->use_dma) {
+ 		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+@@ -750,6 +763,38 @@ err_tx:
+ 	return ret;
+ }
+ 
++static void spi_qup_set_cs(struct spi_device *spi, bool val)
++{
++	struct spi_qup *controller;
++	u32 spi_ioc;
++	u32 spi_ioc_orig;
++
++	controller = spi_master_get_devdata(spi->master);
++	spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
++	spi_ioc_orig = spi_ioc;
++	if (!val)
++		spi_ioc |= SPI_IO_C_FORCE_CS;
++	else
++		spi_ioc &= ~SPI_IO_C_FORCE_CS;
++
++	if (spi_ioc != spi_ioc_orig)
++		writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
++}
++
++static int spi_qup_setup(struct spi_device *spi)
++{
++	if (spi->cs_gpio >= 0) {
++		if (spi->mode & SPI_CS_HIGH)
++			gpio_set_value(spi->cs_gpio, 0);
++		else
++			gpio_set_value(spi->cs_gpio, 1);
++
++		udelay(10);
++	}
++
++	return 0;
++}
++
+ static int spi_qup_probe(struct platform_device *pdev)
+ {
+ 	struct spi_master *master;
+@@ -846,6 +891,11 @@ static int spi_qup_probe(struct platform_device *pdev)
+ 	if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
+ 		controller->qup_v1 = 1;
+ 
++	if (!controller->qup_v1)
++		master->set_cs = spi_qup_set_cs;
++	else
++		master->setup = spi_qup_setup;
++
+ 	spin_lock_init(&controller->lock);
+ 	init_completion(&controller->done);
+ 
+-- 
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
new file mode 100644
index 0000000..7573c96
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
@@ -0,0 +1,43 @@
+From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangi@codeaurora.org>
+Date: Thu, 4 Feb 2016 12:41:56 +0530
+Subject: [PATCH 2/2] watchdog: qcom: set WDT_BARK_TIME register offset to one
+ second less of bite time
+
+Currently WDT_BARK_TIME register offset is not configured with bark
+timeout during wdt_start,and it is taking bark timeout's default value.
+For some versions of TZ (secure mode) will consider a BARK the same
+as BITE and reset the board.
+
+So instead let's just configure the BARK time to be less than a second
+of the bite timeout so the board does not reset in this scenario
+
+Change-Id: Ie09850ad7e0470ed721e6924911ca2a81fd9ff8a
+Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
+---
+ drivers/watchdog/qcom-wdt.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
+index 773dcfa..002274a 100644
+--- a/drivers/watchdog/qcom-wdt.c
++++ b/drivers/watchdog/qcom-wdt.c
+@@ -22,6 +22,7 @@
+ 
+ #define WDT_RST		0x38
+ #define WDT_EN		0x40
++#define WDT_BARK_TIME	0x4C
+ #define WDT_BITE_TIME	0x5C
+ 
+ struct qcom_wdt {
+@@ -44,6 +45,7 @@ static int qcom_wdt_start(struct watchdog_device *wdd)
+ 
+ 	writel(0, wdt->base + WDT_EN);
+ 	writel(1, wdt->base + WDT_RST);
++	writel((wdd->timeout - 1) * wdt->rate, wdt->base + WDT_BARK_TIME);
+ 	writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
+ 	writel(1, wdt->base + WDT_EN);
+ 	return 0;
+-- 
+2.7.2
+
