[source] ipq806x: add v4.9 support

LEDE Commits lede-commits at lists.infradead.org
Fri Feb 17 01:23:15 PST 2017


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/bb255f74290d889b65a563bac7a4be0427fdbec8

commit bb255f74290d889b65a563bac7a4be0427fdbec8
Author: John Crispin <john at phrozen.org>
AuthorDate: Thu Feb 16 12:25:25 2017 +0100

    ipq806x: add v4.9 support
    
    Signed-off-by: John Crispin <john at phrozen.org>
---
 target/linux/ipq806x/config-4.9                    |  457 ++
 .../arch/arm/boot/dts/qcom-ipq8064-c2600.dts       |    0
 .../arch/arm/boot/dts/qcom-ipq8064-d7800.dts       |    0
 .../arch/arm/boot/dts/qcom-ipq8064-ea8500.dts      |    0
 .../arch/arm/boot/dts/qcom-ipq8064-r7500.dts       |    0
 .../arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts     |    0
 .../arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts     |    0
 .../arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts     |    0
 .../arch/arm/boot/dts/qcom-ipq8065-r7800.dts       |    0
 .../arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi       |    0
 .../arch/arm/boot/dts/qcom-ipq8065.dtsi            |    0
 ...tbindings-qcom_adm-Fix-channel-specifiers.patch |   71 +
 .../0002-dmaengine-Add-ADM-driver.patch            |  952 ++++
 ...-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch |   52 +
 ...-dts-Add-NAND-controller-node-for-ipq806x.patch |   38 +
 ...s-Enable-NAND-node-on-IPQ8064-AP148-platf.patch |   74 +
 ...up-Make-sure-mode-is-only-determined-once.patch |  208 +
 ...07-spi-qup-Fix-transaction-done-signaling.patch |   29 +
 ...08-spi-qup-Fix-DMA-mode-to-work-correctly.patch |  219 +
 ...-spi-qup-Fix-block-mode-to-work-correctly.patch |  310 ++
 ...-spi-qup-properly-detect-extra-interrupts.patch |   61 +
 ...-t-re-read-opflags-to-see-if-transaction-.patch |   26 +
 ...factor-spi_qup_io_config-in-two-functions.patch |  202 +
 ...-call-io_config-in-mode-specific-function.patch |  391 ++
 ...ow-block-mode-to-generate-multiple-transa.patch |  268 ++
 ...actor-spi_qup_prep_sg-to-be-more-take-spe.patch |   73 +
 ...ow-mulitple-DMA-transactions-per-spi-xfer.patch |  166 +
 .../0017-spi-qup-Fix-sg-nents-calculation.patch    |   86 +
 ...-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch |   28 +
 ...ipq4019-report-accurate-fixed-clock-rates.patch |   33 +
 ...9-add-cpu-operating-points-for-cpufreq-su.patch |   77 +
 .../0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch    |   23 +
 ...m-ipq4019-use-correct-clock-for-i2c-bus-0.patch |   28 +
 .../0031-qcom-ipq4019-enable-DMA-for-spi.patch     |   23 +
 ...4019-use-v2-of-the-kpss-bringup-mechanism.patch |  108 +
 .../0033-dts-ipq4019-support-ARMv7-PMU.patch       |   28 +
 ...ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch |  488 ++
 ...9-add-USB-nodes-to-ipq4019-SoC-device-tre.patch |   88 +
 ...m-ipq4019-enable-USB-bus-for-DK01.1-board.patch |   45 +
 ...pq4019-Add-support-for-IPQ4019-DK04-board.patch |  258 +
 ...1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch |  746 +++
 ...012-2-clk-qcom-Add-support-for-RPM-Clocks.patch |  586 +++
 ...-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch |   94 +
 .../167-ARM-qcom_rpm_fix_support_for_smb208.patch  |   44 +
 ...c-Added-the-enable-regs-and-mask-for-PRNG.patch |   25 +
 .../patches-4.9/311-add-rpmcc-for-ipq806x.patch    |   81 +
 .../patches-4.9/801-override-compiler-flags.patch  |   11 +
 .../802-GPIO-add-named-gpio-exports.patch          |  166 +
 .../996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch       |  185 +
 target/linux/ipq806x/patches-4.9/999-dts.patch     | 5046 ++++++++++++++++++++
 50 files changed, 11894 insertions(+)

diff --git a/target/linux/ipq806x/config-4.9 b/target/linux/ipq806x/config-4.9
new file mode 100644
index 0000000..f475366
--- /dev/null
+++ b/target/linux/ipq806x/config-4.9
@@ -0,0 +1,457 @@
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_AMBA_PL08X is not set
+CONFIG_APQ_GCC_8084=y
+CONFIG_APQ_MMCC_8084=y
+CONFIG_AR8216_PHY=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=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_MDM9615 is not set
+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_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=y
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
+CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE is not set
+CONFIG_ARM_CPU_SUSPEND=y
+# CONFIG_ARM_CPU_TOPOLOGY is not set
+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_IDIV=y
+CONFIG_ARM_PATCH_PHYS_VIRT=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_AUTO_ZRELADDR=y
+# CONFIG_B53 is not set
+# CONFIG_BINFMT_FLAT is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_MQ_PCI=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_BOUNCE=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_PROBE=y
+CONFIG_CLKSRC_QCOM=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+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_ONDEMAND=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_GOV_ATTR_SET=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+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_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_THERMAL=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+# CONFIG_CRC32_SARWATE is not set
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+# CONFIG_DEBUG_UART_8250 is not set
+# CONFIG_DEBUG_USER is not set
+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_DYNAMIC_DEBUG=y
+CONFIG_EDAC_ATOMIC_SCRUB=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_ETHERNET_PACKET_MANGLE=y
+CONFIG_FIXED_PHY=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_EARLY_IOREMAP=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_MSI_IRQ_DOMAIN=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_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_ARM_SMCCC=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_CBPF_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_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_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=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 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_HELPER_AUTO=y
+CONFIG_I2C_QUP=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_HELPER=y
+# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set
+# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_IPQ_GCC_4019=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_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_MDM_GCC_9615 is not set
+# CONFIG_MDM_LCC_9615 is not set
+# CONFIG_MFD_MAX77620 is not set
+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_MMC=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_QCOM_DML=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_MSM=y
+# CONFIG_MMC_SDHCI_PCI is not set
+CONFIG_MMC_SDHCI_PLTFM=y
+# CONFIG_MMC_TIFM_SD is not set
+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_GCC_8996 is not set
+# CONFIG_MSM_IOMMU is not set
+# CONFIG_MSM_LCC_8960 is not set
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
+# CONFIG_MSM_MMCC_8996 is not set
+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_PHYSMAP_OF_VERSATILE is not set
+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_DSA=y
+CONFIG_NET_DSA_HWMON=y
+CONFIG_NET_DSA_QCA8K=y
+CONFIG_NET_DSA_TAG_QCA=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NET_SWITCHDEV=y
+CONFIG_NLS=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+CONFIG_NVMEM=y
+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_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_PADATA=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DEBUG=y
+CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_MSI_IRQ_DOMAIN=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_IPQ4019=y
+CONFIG_PINCTRL_IPQ8064=y
+# CONFIG_PINCTRL_MDM9615 is not set
+CONFIG_PINCTRL_MSM=y
+# CONFIG_PINCTRL_MSM8660 is not set
+# CONFIG_PINCTRL_MSM8916 is not set
+# CONFIG_PINCTRL_MSM8960 is not set
+# CONFIG_PINCTRL_MSM8996 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_OPP=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_BRCMKONA is not set
+CONFIG_POWER_RESET_MSM=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_QCOM_ADM=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_CLK_RPM=y
+# CONFIG_QCOM_EBI2 is not set
+CONFIG_QCOM_GDSC=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_PM=y
+# CONFIG_QCOM_Q6V5_PIL is not set
+CONFIG_QCOM_QFPROM=y
+CONFIG_QCOM_RPMCC=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SCM_32=y
+# CONFIG_QCOM_SMD is not set
+CONFIG_QCOM_SMEM=y
+# CONFIG_QCOM_SMP2P is not set
+# CONFIG_QCOM_SMSM is not set
+CONFIG_QCOM_TSENS=y
+# CONFIG_QCOM_WCNSS_PIL is not set
+CONFIG_QCOM_WDT=y
+# CONFIG_QRTR is not set
+CONFIG_RAS=y
+CONFIG_RATIONAL=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGMAP_SPI=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QCOM_RPM=y
+# CONFIG_REGULATOR_QCOM_SPMI is not set
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RFS_ACCEL=y
+# CONFIG_RPMSG_QCOM_SMD is not set
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_CMOS is not set
+CONFIG_RTC_I2C_AND_SPI=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_INFO is not set
+# CONFIG_SCSI_DMA is not set
+CONFIG_SERIAL_8250_FSL=y
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SG_POOL is not set
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+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_SWCONFIG=y
+CONFIG_SWCONFIG_LEDS=y
+CONFIG_SWIOTLB=y
+CONFIG_SWPHY=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TREE_RCU=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_USB=y
+CONFIG_USB_COMMON=y
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_IPQ4019_PHY is not set
+CONFIG_USB_SUPPORT=y
+# CONFIG_USB_UHCI_HCD is not set
+CONFIG_USE_OF=y
+CONFIG_VDSO=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_WATCHDOG_CORE=y
+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
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-d7800.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-d7800.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-r7800.dts
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-r7800.dts
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi b/target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065.dtsi
similarity index 100%
rename from target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi
rename to target/linux/ipq806x/files-4.4/arch/arm/boot/dts/qcom-ipq8065.dtsi
diff --git a/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch
new file mode 100644
index 0000000..fb629ae
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch
@@ -0,0 +1,71 @@
+From f275ae09b82aa423d2ea5a2be3ea315c8fcf6143 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Mon, 16 May 2016 17:58:50 -0700
+Subject: [PATCH 01/37] dtbindings: qcom_adm: Fix channel specifiers
+
+Original patch from Andy Gross.
+
+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 at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at 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.9/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch
new file mode 100644
index 0000000..4b56fed
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch
@@ -0,0 +1,952 @@
+From 1d32bf93c8e83db0aca04d2961badef7e86d663b Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Mon, 16 May 2016 17:58:51 -0700
+Subject: [PATCH 02/37] dmaengine: Add ADM driver
+
+Original patch by Andy Gross.
+
+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 at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+---
+ drivers/dma/qcom/Kconfig    |   10 +
+ drivers/dma/qcom/Makefile   |    1 +
+ drivers/dma/qcom/qcom_adm.c |  900 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 911 insertions(+)
+ create mode 100644 drivers/dma/qcom/qcom_adm.c
+
+--- a/drivers/dma/qcom/Kconfig
++++ b/drivers/dma/qcom/Kconfig
+@@ -27,3 +27,13 @@ config QCOM_HIDMA
+ 	  (user to kernel, kernel to kernel, etc.).  It only supports
+ 	  memcpy interface. The core is not intended for general
+ 	  purpose slave DMA.
++
++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.
+--- a/drivers/dma/qcom/Makefile
++++ b/drivers/dma/qcom/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg
+ hdma_mgmt-objs	 := hidma_mgmt.o hidma_mgmt_sys.o
+ obj-$(CONFIG_QCOM_HIDMA) +=  hdma.o
+ hdma-objs        := hidma_ll.o hidma.o hidma_dbg.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+--- /dev/null
++++ b/drivers/dma/qcom/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 at codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch
new file mode 100644
index 0000000..7bd0097
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0003-arm-qcom-dts-ipq8064-Add-ADM-device-node.patch
@@ -0,0 +1,52 @@
+From eb694e964310ba402d6ff99f08e0ef78345e7397 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Mon, 16 May 2016 17:58:52 -0700
+Subject: [PATCH 03/37] arm: qcom: dts: ipq8064: Add ADM device node
+
+Original patch by Andy Gross.
+
+Signed-off-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi |   21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -1,9 +1,11 @@
+ /dts-v1/;
+ 
+ #include "skeleton.dtsi"
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #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/interrupt-controller/arm-gic.h>
+ 
+ / {
+ 	model = "Qualcomm IPQ8064";
+@@ -342,5 +344,24 @@
+ 			#reset-cells = <1>;
+ 		};
+ 
++		adm_dma: dma at 18300000 {
++			compatible = "qcom,adm";
++			reg = <0x18300000 0x100000>;
++			interrupts = <GIC_SPI 170 IRQ_TYPE_NONE>;
++			#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";
++		};
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..4fd75c9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0004-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,38 @@
+From c6d45af259eb4fb2a598c0396c6dd580b1658558 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Mon, 16 May 2016 17:58:53 -0700
+Subject: [PATCH 04/37] arm: qcom: dts: Add NAND controller node for ipq806x
+
+Original patch by Archit Taneja.
+
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi |   17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -363,5 +363,22 @@
+ 
+ 			status = "disabled";
+ 		};
++
++		nand at 1ac00000 {
++			compatible = "qcom,ipq806x-nand";
++			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";
++		};
++
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch b/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch
new file mode 100644
index 0000000..2bfbf08
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0005-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platf.patch
@@ -0,0 +1,74 @@
+From f8d0939eca47f56449ec583810a6ff41a1caaa91 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Mon, 16 May 2016 17:58:54 -0700
+Subject: [PATCH 05/37] arm: qcom: dts: Enable NAND node on IPQ8064 AP148
+ platform
+
+Original patch by Archit Taneja.
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts |   42 ++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -38,6 +38,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 at 16300000 {
+@@ -98,5 +120,25 @@
+ 			ports-implemented = <0x1>;
+ 			status = "ok";
+ 		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nandcs at 0 {
++				compatible = "qcom,nandcs";
++				reg = <0>;
++
++				nand-ecc-strength = <4>;
++				nand-ecc-step-size = <512>;
++				nand-bus-width = <8>;
++			};
++		};
+ 	};
+ };
++
++&adm_dma {
++	status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch
new file mode 100644
index 0000000..9d99699
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch
@@ -0,0 +1,208 @@
+From 2852d6df4b60987f9248c3d36d5fe2462e6556b9 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Tue, 12 Apr 2016 09:11:47 -0500
+Subject: [PATCH 06/37] spi: qup: Make sure mode is only determined once
+
+This patch calculates the mode once.  All decisions on the current
+transaction
+is made using the mode instead of use_dma
+
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c |   87 +++++++++++++++++++++----------------------------
+ 1 file changed, 37 insertions(+), 50 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -149,12 +149,20 @@ struct spi_qup {
+ 	int			rx_bytes;
+ 	int			qup_v1;
+ 
+-	int			use_dma;
++	int			mode;
+ 	struct dma_slave_config	rx_conf;
+ 	struct dma_slave_config	tx_conf;
+ };
+ 
+ 
++static inline bool spi_qup_is_dma_xfer(int mode)
++{
++	if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
++		return true;
++
++	return false;
++}
++
+ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
+ {
+ 	u32 opstate = readl_relaxed(controller->base + QUP_STATE);
+@@ -424,7 +432,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 		error = -EIO;
+ 	}
+ 
+-	if (!controller->use_dma) {
++	if (!spi_qup_is_dma_xfer(controller->mode)) {
+ 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
+ 			spi_qup_fifo_read(controller, xfer);
+ 
+@@ -443,34 +451,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	return IRQ_HANDLED;
+ }
+ 
+-static u32
+-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
+-{
+-	struct spi_qup *qup = spi_master_get_devdata(master);
+-	u32 mode;
+-
+-	qup->w_size = 4;
+-
+-	if (xfer->bits_per_word <= 8)
+-		qup->w_size = 1;
+-	else if (xfer->bits_per_word <= 16)
+-		qup->w_size = 2;
+-
+-	qup->n_words = xfer->len / qup->w_size;
+-
+-	if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
+-		mode = QUP_IO_M_MODE_FIFO;
+-	else
+-		mode = QUP_IO_M_MODE_BLOCK;
+-
+-	return mode;
+-}
+-
+ /* set clock freq ... bits per word */
+ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ 	struct spi_qup *controller = spi_master_get_devdata(spi->master);
+-	u32 config, iomode, mode, control;
++	u32 config, iomode, control;
+ 	int ret, n_words;
+ 
+ 	if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+@@ -491,23 +476,22 @@ static int spi_qup_io_config(struct spi_
+ 		return -EIO;
+ 	}
+ 
+-	mode = spi_qup_get_mode(spi->master, xfer);
++	controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
++	controller->n_words = xfer->len / controller->w_size;
+ 	n_words = controller->n_words;
+ 
+-	if (mode == QUP_IO_M_MODE_FIFO) {
++	if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
++		controller->mode = QUP_IO_M_MODE_FIFO;
+ 		writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+ 		/* must be zero for FIFO */
+ 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-	} 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);
+-		/* must be zero for BLOCK and BAM */
+-		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-	} else {
+-		mode = QUP_IO_M_MODE_BAM;
++
++	} else if (spi->master->can_dma &&
++	    spi->master->can_dma(spi->master, spi, xfer) &&
++	    spi->master->cur_msg_mapped) {
++		controller->mode = QUP_IO_M_MODE_BAM;
+ 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ 
+@@ -528,19 +512,26 @@ static int spi_qup_io_config(struct spi_
+ 
+ 			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+ 		}
++	} else {
++		controller->mode = QUP_IO_M_MODE_BLOCK;
++		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++		/* must be zero for BLOCK and BAM */
++		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ 	}
+ 
+ 	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+ 	/* Set input and output transfer mode */
+ 	iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+ 
+-	if (!controller->use_dma)
++	if (!spi_qup_is_dma_xfer(controller->mode))
+ 		iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+ 	else
+ 		iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+ 
+-	iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+-	iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++	iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++	iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+ 
+ 	writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+ 
+@@ -581,7 +572,7 @@ static int spi_qup_io_config(struct spi_
+ 	config |= xfer->bits_per_word - 1;
+ 	config |= QUP_CONFIG_SPI_MODE;
+ 
+-	if (controller->use_dma) {
++	if (spi_qup_is_dma_xfer(controller->mode)) {
+ 		if (!xfer->tx_buf)
+ 			config |= QUP_CONFIG_NO_OUTPUT;
+ 		if (!xfer->rx_buf)
+@@ -599,7 +590,7 @@ static int spi_qup_io_config(struct spi_
+ 		 * status change in BAM mode
+ 		 */
+ 
+-		if (mode == QUP_IO_M_MODE_BAM)
++		if (spi_qup_is_dma_xfer(controller->mode))
+ 			mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
+ 
+ 		writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
+@@ -633,7 +624,7 @@ static int spi_qup_transfer_one(struct s
+ 	controller->tx_bytes = 0;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+ 
+-	if (controller->use_dma)
++	if (spi_qup_is_dma_xfer(controller->mode))
+ 		ret = spi_qup_do_dma(master, xfer);
+ 	else
+ 		ret = spi_qup_do_pio(master, xfer);
+@@ -657,7 +648,7 @@ exit:
+ 		ret = controller->error;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+ 
+-	if (ret && controller->use_dma)
++	if (ret && spi_qup_is_dma_xfer(controller->mode))
+ 		spi_qup_dma_terminate(master, xfer);
+ 
+ 	return ret;
+@@ -668,9 +659,7 @@ static bool spi_qup_can_dma(struct spi_m
+ {
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	size_t dma_align = dma_get_cache_alignment();
+-	u32 mode;
+-
+-	qup->use_dma = 0;
++	int n_words;
+ 
+ 	if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+ 	    IS_ERR_OR_NULL(master->dma_rx) ||
+@@ -682,12 +671,10 @@ static bool spi_qup_can_dma(struct spi_m
+ 	    !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+ 		return false;
+ 
+-	mode = spi_qup_get_mode(master, xfer);
+-	if (mode == QUP_IO_M_MODE_FIFO)
++	n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
++	if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+ 		return false;
+ 
+-	qup->use_dma = 1;
+-
+ 	return true;
+ }
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch
new file mode 100644
index 0000000..b42f8cd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch
@@ -0,0 +1,29 @@
+From 826290ee1fd1bcd26b6771f94f6680a4ff8951c4 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Fri, 29 Jan 2016 22:06:50 -0600
+Subject: [PATCH 07/37] spi: qup: Fix transaction done signaling
+
+Wait to signal done until we get all of the interrupts we are expecting
+to get for a transaction.  If we don't wait for the input done flag, we
+can be inbetween transactions when the done flag comes in and this can
+mess up the next transaction.
+
+CC: Grant Grundler <grundler at chromium.org>
+CC: Sarthak Kukreti <skukreti at codeaurora.org>
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -445,7 +445,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	controller->xfer = xfer;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+ 
+-	if (controller->rx_bytes == xfer->len || error)
++	if ((controller->rx_bytes == xfer->len &&
++		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+ 		complete(&controller->done);
+ 
+ 	return IRQ_HANDLED;
diff --git a/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch
new file mode 100644
index 0000000..d950446
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch
@@ -0,0 +1,219 @@
+From 715d008b67b21fb8bfefaeeefa5b8ddf23777872 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Tue, 2 Feb 2016 17:00:53 -0600
+Subject: [PATCH 08/37] spi: qup: Fix DMA mode to work correctly
+
+This patch fixes a few issues with the DMA mode.  The QUP needs to be
+placed in the run mode before the DMA transactions are executed.  The
+conditions for being able to DMA vary between revisions of the QUP.
+This is due to v1.1.1 using ADM DMA and later revisions using BAM.
+
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c |   94 ++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 62 insertions(+), 32 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -142,6 +142,7 @@ struct spi_qup {
+ 
+ 	struct spi_transfer	*xfer;
+ 	struct completion	done;
++	struct completion	dma_tx_done;
+ 	int			error;
+ 	int			w_size;	/* bytes per SPI word */
+ 	int			n_words;
+@@ -284,16 +285,16 @@ static void spi_qup_fifo_write(struct sp
+ 
+ static void spi_qup_dma_done(void *data)
+ {
+-	struct spi_qup *qup = data;
++	struct completion *done = data;
+ 
+-	complete(&qup->done);
++	complete(done);
+ }
+ 
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ 			   enum dma_transfer_direction dir,
+-			   dma_async_tx_callback callback)
++			   dma_async_tx_callback callback,
++			   void *data)
+ {
+-	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
+ 	struct dma_async_tx_descriptor *desc;
+ 	struct scatterlist *sgl;
+@@ -312,11 +313,11 @@ static int spi_qup_prep_sg(struct spi_ma
+ 	}
+ 
+ 	desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+-	if (!desc)
+-		return -EINVAL;
++	if (IS_ERR_OR_NULL(desc))
++		return desc ? PTR_ERR(desc) : -EINVAL;
+ 
+ 	desc->callback = callback;
+-	desc->callback_param = qup;
++	desc->callback_param = data;
+ 
+ 	cookie = dmaengine_submit(desc);
+ 
+@@ -332,18 +333,29 @@ static void spi_qup_dma_terminate(struct
+ 		dmaengine_terminate_all(master->dma_rx);
+ }
+ 
+-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
++unsigned long timeout)
+ {
++	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ 	int ret;
+ 
++	/* before issuing the descriptors, set the QUP to run */
++	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++	if (ret) {
++		dev_warn(qup->dev, "cannot set RUN state\n");
++		return ret;
++	}
++
+ 	if (xfer->rx_buf)
+ 		rx_done = spi_qup_dma_done;
+-	else if (xfer->tx_buf)
++
++	if (xfer->tx_buf)
+ 		tx_done = spi_qup_dma_done;
+ 
+ 	if (xfer->rx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
++		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
++				      &qup->done);
+ 		if (ret)
+ 			return ret;
+ 
+@@ -351,17 +363,25 @@ static int spi_qup_do_dma(struct spi_mas
+ 	}
+ 
+ 	if (xfer->tx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
++		ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
++				      &qup->dma_tx_done);
+ 		if (ret)
+ 			return ret;
+ 
+ 		dma_async_issue_pending(master->dma_tx);
+ 	}
+ 
+-	return 0;
++	if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
++		return -ETIMEDOUT;
++
++	if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
++		ret = -ETIMEDOUT;
++
++	return ret;
+ }
+ 
+-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
++			  unsigned long timeout)
+ {
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	int ret;
+@@ -380,6 +400,15 @@ static int spi_qup_do_pio(struct spi_mas
+ 
+ 	spi_qup_fifo_write(qup, xfer);
+ 
++	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++	if (ret) {
++		dev_warn(qup->dev, "cannot set RUN state\n");
++		return ret;
++	}
++
++	if (!wait_for_completion_timeout(&qup->done, timeout))
++		return -ETIMEDOUT;
++
+ 	return 0;
+ }
+ 
+@@ -428,7 +457,6 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 			dev_warn(controller->dev, "CLK_OVER_RUN\n");
+ 		if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
+ 			dev_warn(controller->dev, "CLK_UNDER_RUN\n");
+-
+ 		error = -EIO;
+ 	}
+ 
+@@ -617,6 +645,7 @@ static int spi_qup_transfer_one(struct s
+ 	timeout = 100 * msecs_to_jiffies(timeout);
+ 
+ 	reinit_completion(&controller->done);
++	reinit_completion(&controller->dma_tx_done);
+ 
+ 	spin_lock_irqsave(&controller->lock, flags);
+ 	controller->xfer     = xfer;
+@@ -626,21 +655,13 @@ static int spi_qup_transfer_one(struct s
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+ 
+ 	if (spi_qup_is_dma_xfer(controller->mode))
+-		ret = spi_qup_do_dma(master, xfer);
++		ret = spi_qup_do_dma(master, xfer, timeout);
+ 	else
+-		ret = spi_qup_do_pio(master, xfer);
++		ret = spi_qup_do_pio(master, xfer, timeout);
+ 
+ 	if (ret)
+ 		goto exit;
+ 
+-	if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+-		dev_warn(controller->dev, "cannot set EXECUTE state\n");
+-		goto exit;
+-	}
+-
+-	if (!wait_for_completion_timeout(&controller->done, timeout))
+-		ret = -ETIMEDOUT;
+-
+ exit:
+ 	spi_qup_set_state(controller, QUP_STATE_RESET);
+ 	spin_lock_irqsave(&controller->lock, flags);
+@@ -662,15 +683,23 @@ static bool spi_qup_can_dma(struct spi_m
+ 	size_t dma_align = dma_get_cache_alignment();
+ 	int n_words;
+ 
+-	if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+-	    IS_ERR_OR_NULL(master->dma_rx) ||
+-	    !IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
+-		return false;
++	if (xfer->rx_buf) {
++		if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
++		    IS_ERR_OR_NULL(master->dma_rx))
++			return false;
+ 
+-	if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
+-	    IS_ERR_OR_NULL(master->dma_tx) ||
+-	    !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+-		return false;
++		if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
++			return false;
++	}
++
++	if (xfer->tx_buf) {
++		if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
++		    IS_ERR_OR_NULL(master->dma_tx))
++			return false;
++
++		if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
++			return false;
++	}
+ 
+ 	n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
+ 	if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+@@ -836,6 +865,7 @@ static int spi_qup_probe(struct platform
+ 
+ 	spin_lock_init(&controller->lock);
+ 	init_completion(&controller->done);
++	init_completion(&controller->dma_tx_done);
+ 
+ 	iomode = readl_relaxed(base + QUP_IO_M_MODES);
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch
new file mode 100644
index 0000000..2f316b1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch
@@ -0,0 +1,310 @@
+From 4dc7631bbf7c7ac7548026ce45d889235e4f5892 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Sun, 31 Jan 2016 21:28:13 -0600
+Subject: [PATCH 09/37] spi: qup: Fix block mode to work correctly
+
+This patch corrects the behavior of the BLOCK transactions.  During block
+transactions, the controller must be read/written to in block size transactions.
+
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c |  182 ++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 142 insertions(+), 40 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -82,6 +82,8 @@
+ #define QUP_IO_M_MODE_BAM		3
+ 
+ /* QUP_OPERATIONAL fields */
++#define QUP_OP_IN_BLOCK_READ_REQ	BIT(13)
++#define QUP_OP_OUT_BLOCK_WRITE_REQ	BIT(12)
+ #define QUP_OP_MAX_INPUT_DONE_FLAG	BIT(11)
+ #define QUP_OP_MAX_OUTPUT_DONE_FLAG	BIT(10)
+ #define QUP_OP_IN_SERVICE_FLAG		BIT(9)
+@@ -155,6 +157,12 @@ struct spi_qup {
+ 	struct dma_slave_config	tx_conf;
+ };
+ 
++static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
++{
++	u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
++	return opflag & flag;
++}
+ 
+ static inline bool spi_qup_is_dma_xfer(int mode)
+ {
+@@ -216,29 +224,26 @@ static int spi_qup_set_state(struct spi_
+ 	return 0;
+ }
+ 
+-static void spi_qup_fifo_read(struct spi_qup *controller,
+-			    struct spi_transfer *xfer)
++static void spi_qup_read_from_fifo(struct spi_qup *controller,
++	struct spi_transfer *xfer, u32 num_words)
+ {
+ 	u8 *rx_buf = xfer->rx_buf;
+-	u32 word, state;
+-	int idx, shift, w_size;
+-
+-	w_size = controller->w_size;
++	int i, shift, num_bytes;
++	u32 word;
+ 
+-	while (controller->rx_bytes < xfer->len) {
+-
+-		state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+-		if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
+-			break;
++	for (; num_words; num_words--) {
+ 
+ 		word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+ 
++		num_bytes = min_t(int, xfer->len - controller->rx_bytes,
++					controller->w_size);
++
+ 		if (!rx_buf) {
+-			controller->rx_bytes += w_size;
++			controller->rx_bytes += num_bytes;
+ 			continue;
+ 		}
+ 
+-		for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++		for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
+ 			/*
+ 			 * The data format depends on bytes per SPI word:
+ 			 *  4 bytes: 0x12345678
+@@ -246,38 +251,80 @@ static void spi_qup_fifo_read(struct spi
+ 			 *  1 byte : 0x00000012
+ 			 */
+ 			shift = BITS_PER_BYTE;
+-			shift *= (w_size - idx - 1);
++			shift *= (controller->w_size - i - 1);
+ 			rx_buf[controller->rx_bytes] = word >> shift;
+ 		}
+ 	}
+ }
+ 
+-static void spi_qup_fifo_write(struct spi_qup *controller,
++static void spi_qup_read(struct spi_qup *controller,
+ 			    struct spi_transfer *xfer)
+ {
+-	const u8 *tx_buf = xfer->tx_buf;
+-	u32 word, state, data;
+-	int idx, w_size;
++	u32 remainder, words_per_block, num_words;
++	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++
++	remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
++				 controller->w_size);
++	words_per_block = controller->in_blk_sz >> 2;
++
++	do {
++		/* ACK by clearing service flag */
++		writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++		if (is_block_mode) {
++			num_words = (remainder > words_per_block) ?
++					words_per_block : remainder;
++		} else {
++			if (!spi_qup_is_flag_set(controller,
++						 QUP_OP_IN_FIFO_NOT_EMPTY))
++				break;
+ 
+-	w_size = controller->w_size;
++			num_words = 1;
++		}
++
++		/* read up to the maximum transfer size available */
++		spi_qup_read_from_fifo(controller, xfer, num_words);
+ 
+-	while (controller->tx_bytes < xfer->len) {
++		remainder -= num_words;
+ 
+-		state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+-		if (state & QUP_OP_OUT_FIFO_FULL)
++		/* if block mode, check to see if next block is available */
++		if (is_block_mode && !spi_qup_is_flag_set(controller,
++					QUP_OP_IN_BLOCK_READ_REQ))
+ 			break;
+ 
++	} while (remainder);
++
++	/*
++	 * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
++	 * mode reads, it has to be cleared again at the very end
++	 */
++	if (is_block_mode && spi_qup_is_flag_set(controller,
++				QUP_OP_MAX_INPUT_DONE_FLAG))
++		writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++}
++
++static void spi_qup_write_to_fifo(struct spi_qup *controller,
++	struct spi_transfer *xfer, u32 num_words)
++{
++	const u8 *tx_buf = xfer->tx_buf;
++	int i, num_bytes;
++	u32 word, data;
++
++	for (; num_words; num_words--) {
+ 		word = 0;
+-		for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
+ 
+-			if (!tx_buf) {
+-				controller->tx_bytes += w_size;
+-				break;
++		num_bytes = min_t(int, xfer->len - controller->tx_bytes,
++				    controller->w_size);
++		if (tx_buf)
++			for (i = 0; i < num_bytes; i++) {
++				data = tx_buf[controller->tx_bytes + i];
++				word |= data << (BITS_PER_BYTE * (3 - i));
+ 			}
+ 
+-			data = tx_buf[controller->tx_bytes];
+-			word |= data << (BITS_PER_BYTE * (3 - idx));
+-		}
++		controller->tx_bytes += num_bytes;
+ 
+ 		writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
+ 	}
+@@ -290,6 +337,44 @@ static void spi_qup_dma_done(void *data)
+ 	complete(done);
+ }
+ 
++static void spi_qup_write(struct spi_qup *controller,
++			    struct spi_transfer *xfer)
++{
++	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++	u32 remainder, words_per_block, num_words;
++
++	remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
++				 controller->w_size);
++	words_per_block = controller->out_blk_sz >> 2;
++
++	do {
++		/* ACK by clearing service flag */
++		writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++		if (is_block_mode) {
++			num_words = (remainder > words_per_block) ?
++				words_per_block : remainder;
++		} else {
++			if (spi_qup_is_flag_set(controller,
++						QUP_OP_OUT_FIFO_FULL))
++				break;
++
++			num_words = 1;
++		}
++
++		spi_qup_write_to_fifo(controller, xfer, num_words);
++
++		remainder -= num_words;
++
++		/* if block mode, check to see if next block is available */
++		if (is_block_mode && !spi_qup_is_flag_set(controller,
++					QUP_OP_OUT_BLOCK_WRITE_REQ))
++			break;
++
++	} while (remainder);
++}
++
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ 			   enum dma_transfer_direction dir,
+ 			   dma_async_tx_callback callback,
+@@ -347,11 +432,13 @@ unsigned long timeout)
+ 		return ret;
+ 	}
+ 
+-	if (xfer->rx_buf)
+-		rx_done = spi_qup_dma_done;
++	if (!qup->qup_v1) {
++		if (xfer->rx_buf)
++			rx_done = spi_qup_dma_done;
+ 
+-	if (xfer->tx_buf)
+-		tx_done = spi_qup_dma_done;
++		if (xfer->tx_buf)
++			tx_done = spi_qup_dma_done;
++	}
+ 
+ 	if (xfer->rx_buf) {
+ 		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
+@@ -398,7 +485,8 @@ static int spi_qup_do_pio(struct spi_mas
+ 		return ret;
+ 	}
+ 
+-	spi_qup_fifo_write(qup, xfer);
++	if (qup->mode == QUP_IO_M_MODE_FIFO)
++		spi_qup_write(qup, xfer);
+ 
+ 	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ 	if (ret) {
+@@ -431,10 +519,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 
+ 	writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
+ 	writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
+-	writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+ 
+ 	if (!xfer) {
+-		dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
++		writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++		dev_err_ratelimited(controller->dev,
++				    "unexpected irq %08x %08x %08x\n",
+ 				    qup_err, spi_err, opflags);
+ 		return IRQ_HANDLED;
+ 	}
+@@ -460,12 +549,20 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 		error = -EIO;
+ 	}
+ 
+-	if (!spi_qup_is_dma_xfer(controller->mode)) {
++	if (spi_qup_is_dma_xfer(controller->mode)) {
++		writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++		if (opflags & QUP_OP_IN_SERVICE_FLAG &&
++		    opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
++			complete(&controller->done);
++		if (opflags & QUP_OP_OUT_SERVICE_FLAG &&
++		    opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG)
++			complete(&controller->dma_tx_done);
++	} else {
+ 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
+-			spi_qup_fifo_read(controller, xfer);
++			spi_qup_read(controller, xfer);
+ 
+ 		if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+-			spi_qup_fifo_write(controller, xfer);
++			spi_qup_write(controller, xfer);
+ 	}
+ 
+ 	spin_lock_irqsave(&controller->lock, flags);
+@@ -473,6 +570,9 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	controller->xfer = xfer;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+ 
++	/* re-read opflags as flags may have changed due to actions above */
++	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+ 		complete(&controller->done);
+@@ -516,11 +616,13 @@ static int spi_qup_io_config(struct spi_
+ 		/* must be zero for FIFO */
+ 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-
+ 	} else if (spi->master->can_dma &&
+ 	    spi->master->can_dma(spi->master, spi, xfer) &&
+ 	    spi->master->cur_msg_mapped) {
+ 		controller->mode = QUP_IO_M_MODE_BAM;
++		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++		/* must be zero for BLOCK and BAM */
+ 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch
new file mode 100644
index 0000000..74ef25a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch
@@ -0,0 +1,61 @@
+From 543618f5388d487ba88e3d5304c161fc3ccf61d1 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 10 Mar 2016 16:44:55 -0600
+Subject: [PATCH 10/37] spi: qup: properly detect extra interrupts
+
+It's possible for a SPI transaction to complete and get another
+interrupt and have it processed on the same spi_transfer before the
+transfer_one can set it to NULL.
+
+This masks unexpected interrupts, so let's set the spi_transfer to
+NULL in the interrupt once the transaction is done. So we can
+properly detect these bad interrupts and print warning messages.
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |   15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -507,6 +507,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	u32 opflags, qup_err, spi_err;
+ 	unsigned long flags;
+ 	int error = 0;
++	bool done = 0;
+ 
+ 	spin_lock_irqsave(&controller->lock, flags);
+ 	xfer = controller->xfer;
+@@ -565,16 +566,19 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 			spi_qup_write(controller, xfer);
+ 	}
+ 
+-	spin_lock_irqsave(&controller->lock, flags);
+-	controller->error = error;
+-	controller->xfer = xfer;
+-	spin_unlock_irqrestore(&controller->lock, flags);
+-
+ 	/* re-read opflags as flags may have changed due to actions above */
+ 	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+ 
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
++		done = true;
++
++	spin_lock_irqsave(&controller->lock, flags);
++	controller->error = error;
++	controller->xfer = done ? NULL : xfer;
++	spin_unlock_irqrestore(&controller->lock, flags);
++
++	if (done)
+ 		complete(&controller->done);
+ 
+ 	return IRQ_HANDLED;
+@@ -767,7 +771,6 @@ static int spi_qup_transfer_one(struct s
+ exit:
+ 	spi_qup_set_state(controller, QUP_STATE_RESET);
+ 	spin_lock_irqsave(&controller->lock, flags);
+-	controller->xfer = NULL;
+ 	if (!ret)
+ 		ret = controller->error;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
diff --git a/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
new file mode 100644
index 0000000..da1569f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
@@ -0,0 +1,26 @@
+From ba96e9449a63acd658d8ad0a5b3755b559410999 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 10 Mar 2016 16:48:27 -0600
+Subject: [PATCH 11/37] spi: qup: don't re-read opflags to see if transaction
+ is done for reads
+
+For reads, we will get another interrupt so we need to handle things
+then. For writes, we can finish up now.
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -567,7 +567,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	}
+ 
+ 	/* re-read opflags as flags may have changed due to actions above */
+-	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++	if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++		opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+ 
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
diff --git a/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch
new file mode 100644
index 0000000..7beb5d9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch
@@ -0,0 +1,202 @@
+From ef00ad56d728618203358d9eba7ca8e7eb48e701 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Tue, 26 Apr 2016 12:57:46 -0500
+Subject: [PATCH 12/37] spi: qup: refactor spi_qup_io_config in two functions
+
+This is preparation for handling transactions larger than 64K-1 bytes in
+block mode which is currently unsupported quietly fails.
+
+We need to break these into two functions 1) prep is called once per
+spi_message and 2) io_config is calle once per spi-qup bus transaction
+
+This is just refactoring, there should be no functional change
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |  141 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 86 insertions(+), 55 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -585,12 +585,11 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 	return IRQ_HANDLED;
+ }
+ 
+-/* set clock freq ... bits per word */
+-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++/* set clock freq ... bits per word, determine mode */
++static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ 	struct spi_qup *controller = spi_master_get_devdata(spi->master);
+-	u32 config, iomode, control;
+-	int ret, n_words;
++	int ret;
+ 
+ 	if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+ 		dev_err(controller->dev, "too big size for loopback %d > %d\n",
+@@ -605,56 +604,94 @@ static int spi_qup_io_config(struct spi_
+ 		return -EIO;
+ 	}
+ 
+-	if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+-		dev_err(controller->dev, "cannot set RESET state\n");
+-		return -EIO;
+-	}
+-
+ 	controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
+ 	controller->n_words = xfer->len / controller->w_size;
+-	n_words = controller->n_words;
+ 
+-	if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
++	if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
+ 		controller->mode = QUP_IO_M_MODE_FIFO;
+-		writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+-		writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+-		/* must be zero for FIFO */
+-		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+-		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-	} else if (spi->master->can_dma &&
+-	    spi->master->can_dma(spi->master, spi, xfer) &&
+-	    spi->master->cur_msg_mapped) {
++	else if (spi->master->can_dma &&
++		 spi->master->can_dma(spi->master, spi, xfer) &&
++		 spi->master->cur_msg_mapped)
+ 		controller->mode = QUP_IO_M_MODE_BAM;
+-		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+-		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+-		/* must be zero for BLOCK and BAM */
+-		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-
+-		if (!controller->qup_v1) {
+-			void __iomem *input_cnt;
+-
+-			input_cnt = controller->base + QUP_MX_INPUT_CNT;
+-			/*
+-			 * for DMA transfers, both QUP_MX_INPUT_CNT and
+-			 * QUP_MX_OUTPUT_CNT must be zero to all cases but one.
+-			 * That case is a non-balanced transfer when there is
+-			 * only a rx_buf.
+-			 */
+-			if (xfer->tx_buf)
+-				writel_relaxed(0, input_cnt);
+-			else
+-				writel_relaxed(n_words, input_cnt);
++	else
++		controller->mode = QUP_IO_M_MODE_BLOCK;
++
++	return 0;
++}
+ 
++/* prep qup for another spi transaction of specific type */
++static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++{
++	struct spi_qup *controller = spi_master_get_devdata(spi->master);
++	u32 config, iomode, control;
++	unsigned long flags;
++
++	reinit_completion(&controller->done);
++	reinit_completion(&controller->dma_tx_done);
++
++	spin_lock_irqsave(&controller->lock, flags);
++	controller->xfer     = xfer;
++	controller->error    = 0;
++	controller->rx_bytes = 0;
++	controller->tx_bytes = 0;
++	spin_unlock_irqrestore(&controller->lock, flags);
++
++
++	if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++		dev_err(controller->dev, "cannot set RESET state\n");
++		return -EIO;
++	}
++
++	switch (controller->mode) {
++		case QUP_IO_M_MODE_FIFO:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_WRITE_CNT);
++			/* must be zero for FIFO */
++			writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-		}
+-	} else {
+-		controller->mode = QUP_IO_M_MODE_BLOCK;
+-		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+-		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+-		/* must be zero for BLOCK and BAM */
+-		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++			break;
++		case QUP_IO_M_MODE_BAM:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_INPUT_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_OUTPUT_CNT);
++			/* must be zero for BLOCK and BAM */
++			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++			if (!controller->qup_v1) {
++				void __iomem *input_cnt;
++
++				input_cnt = controller->base + QUP_MX_INPUT_CNT;
++				/*
++				 * for DMA transfers, both QUP_MX_INPUT_CNT and
++				 * QUP_MX_OUTPUT_CNT must be zero to all cases
++				 * but one. That case is a non-balanced
++				 * transfer when there is only a rx_buf.
++				 */
++				if (xfer->tx_buf)
++					writel_relaxed(0, input_cnt);
++				else
++					writel_relaxed(controller->n_words,
++						       input_cnt);
++
++				writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++			}
++			break;
++		case QUP_IO_M_MODE_BLOCK:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_INPUT_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_OUTPUT_CNT);
++			/* must be zero for BLOCK and BAM */
++			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++			break;
++		default:
++			dev_err(controller->dev, "unknown mode = %d\n",
++					controller->mode);
++			return -EIO;
+ 	}
+ 
+ 	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+@@ -743,6 +780,10 @@ static int spi_qup_transfer_one(struct s
+ 	unsigned long timeout, flags;
+ 	int ret = -EIO;
+ 
++	ret = spi_qup_io_prep(spi, xfer);
++	if (ret)
++		return ret;
++
+ 	ret = spi_qup_io_config(spi, xfer);
+ 	if (ret)
+ 		return ret;
+@@ -751,16 +792,6 @@ static int spi_qup_transfer_one(struct s
+ 	timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
+ 	timeout = 100 * msecs_to_jiffies(timeout);
+ 
+-	reinit_completion(&controller->done);
+-	reinit_completion(&controller->dma_tx_done);
+-
+-	spin_lock_irqsave(&controller->lock, flags);
+-	controller->xfer     = xfer;
+-	controller->error    = 0;
+-	controller->rx_bytes = 0;
+-	controller->tx_bytes = 0;
+-	spin_unlock_irqrestore(&controller->lock, flags);
+-
+ 	if (spi_qup_is_dma_xfer(controller->mode))
+ 		ret = spi_qup_do_dma(master, xfer, timeout);
+ 	else
diff --git a/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch b/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch
new file mode 100644
index 0000000..3aebaac
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch
@@ -0,0 +1,391 @@
+From 9263d98e255e1d51b41c752d53e39877728a9419 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Tue, 26 Apr 2016 13:14:45 -0500
+Subject: [PATCH 13/37] spi: qup: call io_config in mode specific function
+
+DMA transactions should only only need to call io_config only once, but
+block mode might call it several times to setup several transactions so
+it can handle reads/writes larger than the max size per transaction, so
+we move the call to the do_ functions.
+
+This is just refactoring, there should be no functional change
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |  327 +++++++++++++++++++++++++------------------------
+ 1 file changed, 166 insertions(+), 161 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -418,13 +418,170 @@ static void spi_qup_dma_terminate(struct
+ 		dmaengine_terminate_all(master->dma_rx);
+ }
+ 
+-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
++/* prep qup for another spi transaction of specific type */
++static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
++{
++	struct spi_qup *controller = spi_master_get_devdata(spi->master);
++	u32 config, iomode, control;
++	unsigned long flags;
++
++	reinit_completion(&controller->done);
++	reinit_completion(&controller->dma_tx_done);
++
++	spin_lock_irqsave(&controller->lock, flags);
++	controller->xfer     = xfer;
++	controller->error    = 0;
++	controller->rx_bytes = 0;
++	controller->tx_bytes = 0;
++	spin_unlock_irqrestore(&controller->lock, flags);
++
++	if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++		dev_err(controller->dev, "cannot set RESET state\n");
++		return -EIO;
++	}
++
++	switch (controller->mode) {
++		case QUP_IO_M_MODE_FIFO:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_WRITE_CNT);
++			/* must be zero for FIFO */
++			writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
++			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++			break;
++		case QUP_IO_M_MODE_BAM:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_INPUT_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_OUTPUT_CNT);
++			/* must be zero for BLOCK and BAM */
++			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++			if (!controller->qup_v1) {
++				void __iomem *input_cnt;
++
++				input_cnt = controller->base + QUP_MX_INPUT_CNT;
++				/*
++				 * for DMA transfers, both QUP_MX_INPUT_CNT and
++				 * QUP_MX_OUTPUT_CNT must be zero to all cases
++				 * but one. That case is a non-balanced
++				 * transfer when there is only a rx_buf.
++				 */
++				if (xfer->tx_buf)
++					writel_relaxed(0, input_cnt);
++				else
++					writel_relaxed(controller->n_words,
++						       input_cnt);
++
++				writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++			}
++			break;
++		case QUP_IO_M_MODE_BLOCK:
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_INPUT_CNT);
++			writel_relaxed(controller->n_words,
++				       controller->base + QUP_MX_OUTPUT_CNT);
++			/* must be zero for BLOCK and BAM */
++			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++			break;
++		default:
++			dev_err(controller->dev, "unknown mode = %d\n",
++					controller->mode);
++			return -EIO;
++	}
++
++	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
++	/* Set input and output transfer mode */
++	iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
++
++	if (!spi_qup_is_dma_xfer(controller->mode))
++		iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++	else
++		iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
++
++	iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++	iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++
++	writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
++
++	control = readl_relaxed(controller->base + SPI_IO_CONTROL);
++
++	if (spi->mode & SPI_CPOL)
++		control |= SPI_IO_C_CLK_IDLE_HIGH;
++	else
++		control &= ~SPI_IO_C_CLK_IDLE_HIGH;
++
++	writel_relaxed(control, controller->base + SPI_IO_CONTROL);
++
++	config = readl_relaxed(controller->base + SPI_CONFIG);
++
++	if (spi->mode & SPI_LOOP)
++		config |= SPI_CONFIG_LOOPBACK;
++	else
++		config &= ~SPI_CONFIG_LOOPBACK;
++
++	if (spi->mode & SPI_CPHA)
++		config &= ~SPI_CONFIG_INPUT_FIRST;
++	else
++		config |= SPI_CONFIG_INPUT_FIRST;
++
++	/*
++	 * HS_MODE improves signal stability for spi-clk high rates,
++	 * but is invalid in loop back mode.
++	 */
++	if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
++		config |= SPI_CONFIG_HS_MODE;
++	else
++		config &= ~SPI_CONFIG_HS_MODE;
++
++	writel_relaxed(config, controller->base + SPI_CONFIG);
++
++	config = readl_relaxed(controller->base + QUP_CONFIG);
++	config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
++	config |= xfer->bits_per_word - 1;
++	config |= QUP_CONFIG_SPI_MODE;
++
++	if (spi_qup_is_dma_xfer(controller->mode)) {
++		if (!xfer->tx_buf)
++			config |= QUP_CONFIG_NO_OUTPUT;
++		if (!xfer->rx_buf)
++			config |= QUP_CONFIG_NO_INPUT;
++	}
++
++	writel_relaxed(config, controller->base + QUP_CONFIG);
++
++	/* only write to OPERATIONAL_MASK when register is present */
++	if (!controller->qup_v1) {
++		u32 mask = 0;
++
++		/*
++		 * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
++		 * status change in BAM mode
++		 */
++
++		if (spi_qup_is_dma_xfer(controller->mode))
++			mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
++
++		writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
++	}
++
++	return 0;
++}
++
++static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
++	struct spi_master *master = spi->master;
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ 	int ret;
+ 
++	ret = spi_qup_io_config(spi, xfer);
++	if (ret)
++		return ret;
++
+ 	/* before issuing the descriptors, set the QUP to run */
+ 	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ 	if (ret) {
+@@ -467,12 +624,17 @@ unsigned long timeout)
+ 	return ret;
+ }
+ 
+-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
++static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
+ 			  unsigned long timeout)
+ {
++	struct spi_master *master = spi->master;
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	int ret;
+ 
++	ret = spi_qup_io_config(spi, xfer);
++	if (ret)
++		return ret;
++
+ 	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ 	if (ret) {
+ 		dev_warn(qup->dev, "cannot set RUN state\n");
+@@ -619,159 +781,6 @@ static int spi_qup_io_prep(struct spi_de
+ 	return 0;
+ }
+ 
+-/* prep qup for another spi transaction of specific type */
+-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+-{
+-	struct spi_qup *controller = spi_master_get_devdata(spi->master);
+-	u32 config, iomode, control;
+-	unsigned long flags;
+-
+-	reinit_completion(&controller->done);
+-	reinit_completion(&controller->dma_tx_done);
+-
+-	spin_lock_irqsave(&controller->lock, flags);
+-	controller->xfer     = xfer;
+-	controller->error    = 0;
+-	controller->rx_bytes = 0;
+-	controller->tx_bytes = 0;
+-	spin_unlock_irqrestore(&controller->lock, flags);
+-
+-
+-	if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+-		dev_err(controller->dev, "cannot set RESET state\n");
+-		return -EIO;
+-	}
+-
+-	switch (controller->mode) {
+-		case QUP_IO_M_MODE_FIFO:
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_READ_CNT);
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_WRITE_CNT);
+-			/* must be zero for FIFO */
+-			writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+-			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-			break;
+-		case QUP_IO_M_MODE_BAM:
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_INPUT_CNT);
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_OUTPUT_CNT);
+-			/* must be zero for BLOCK and BAM */
+-			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-			if (!controller->qup_v1) {
+-				void __iomem *input_cnt;
+-
+-				input_cnt = controller->base + QUP_MX_INPUT_CNT;
+-				/*
+-				 * for DMA transfers, both QUP_MX_INPUT_CNT and
+-				 * QUP_MX_OUTPUT_CNT must be zero to all cases
+-				 * but one. That case is a non-balanced
+-				 * transfer when there is only a rx_buf.
+-				 */
+-				if (xfer->tx_buf)
+-					writel_relaxed(0, input_cnt);
+-				else
+-					writel_relaxed(controller->n_words,
+-						       input_cnt);
+-
+-				writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-			}
+-			break;
+-		case QUP_IO_M_MODE_BLOCK:
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_INPUT_CNT);
+-			writel_relaxed(controller->n_words,
+-				       controller->base + QUP_MX_OUTPUT_CNT);
+-			/* must be zero for BLOCK and BAM */
+-			writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-			writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-			break;
+-		default:
+-			dev_err(controller->dev, "unknown mode = %d\n",
+-					controller->mode);
+-			return -EIO;
+-	}
+-
+-	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+-	/* Set input and output transfer mode */
+-	iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+-
+-	if (!spi_qup_is_dma_xfer(controller->mode))
+-		iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+-	else
+-		iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+-
+-	iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+-	iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+-
+-	writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+-
+-	control = readl_relaxed(controller->base + SPI_IO_CONTROL);
+-
+-	if (spi->mode & SPI_CPOL)
+-		control |= SPI_IO_C_CLK_IDLE_HIGH;
+-	else
+-		control &= ~SPI_IO_C_CLK_IDLE_HIGH;
+-
+-	writel_relaxed(control, controller->base + SPI_IO_CONTROL);
+-
+-	config = readl_relaxed(controller->base + SPI_CONFIG);
+-
+-	if (spi->mode & SPI_LOOP)
+-		config |= SPI_CONFIG_LOOPBACK;
+-	else
+-		config &= ~SPI_CONFIG_LOOPBACK;
+-
+-	if (spi->mode & SPI_CPHA)
+-		config &= ~SPI_CONFIG_INPUT_FIRST;
+-	else
+-		config |= SPI_CONFIG_INPUT_FIRST;
+-
+-	/*
+-	 * HS_MODE improves signal stability for spi-clk high rates,
+-	 * but is invalid in loop back mode.
+-	 */
+-	if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
+-		config |= SPI_CONFIG_HS_MODE;
+-	else
+-		config &= ~SPI_CONFIG_HS_MODE;
+-
+-	writel_relaxed(config, controller->base + SPI_CONFIG);
+-
+-	config = readl_relaxed(controller->base + QUP_CONFIG);
+-	config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
+-	config |= xfer->bits_per_word - 1;
+-	config |= QUP_CONFIG_SPI_MODE;
+-
+-	if (spi_qup_is_dma_xfer(controller->mode)) {
+-		if (!xfer->tx_buf)
+-			config |= QUP_CONFIG_NO_OUTPUT;
+-		if (!xfer->rx_buf)
+-			config |= QUP_CONFIG_NO_INPUT;
+-	}
+-
+-	writel_relaxed(config, controller->base + QUP_CONFIG);
+-
+-	/* only write to OPERATIONAL_MASK when register is present */
+-	if (!controller->qup_v1) {
+-		u32 mask = 0;
+-
+-		/*
+-		 * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
+-		 * status change in BAM mode
+-		 */
+-
+-		if (spi_qup_is_dma_xfer(controller->mode))
+-			mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
+-
+-		writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
+-	}
+-
+-	return 0;
+-}
+-
+ static int spi_qup_transfer_one(struct spi_master *master,
+ 			      struct spi_device *spi,
+ 			      struct spi_transfer *xfer)
+@@ -784,18 +793,14 @@ static int spi_qup_transfer_one(struct s
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = spi_qup_io_config(spi, xfer);
+-	if (ret)
+-		return ret;
+-
+ 	timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
+ 	timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
+ 	timeout = 100 * msecs_to_jiffies(timeout);
+ 
+ 	if (spi_qup_is_dma_xfer(controller->mode))
+-		ret = spi_qup_do_dma(master, xfer, timeout);
++		ret = spi_qup_do_dma(spi, xfer, timeout);
+ 	else
+-		ret = spi_qup_do_pio(master, xfer, timeout);
++		ret = spi_qup_do_pio(spi, xfer, timeout);
+ 
+ 	if (ret)
+ 		goto exit;
diff --git a/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch b/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch
new file mode 100644
index 0000000..6c275f6
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch
@@ -0,0 +1,268 @@
+From 05a08cc5620df0fcf8e260feee04b9671705723e Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Tue, 26 Apr 2016 15:46:24 -0500
+Subject: [PATCH 14/37] spi: qup: allow block mode to generate multiple
+ transactions
+
+This let's you write more to the SPI bus than 64K-1 which is important
+if the block size of a SPI device is >= 64K or some other device wants
+to something larger.
+
+This has the benefit of completly removing spi_message from the spi-qup
+transactions
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |  120 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 75 insertions(+), 45 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -120,7 +120,7 @@
+ 
+ #define SPI_NUM_CHIPSELECTS		4
+ 
+-#define SPI_MAX_DMA_XFER		(SZ_64K - 64)
++#define SPI_MAX_XFER			(SZ_64K - 64)
+ 
+ /* high speed mode is when bus rate is greater then 26MHz */
+ #define SPI_HS_MIN_RATE			26000000
+@@ -150,6 +150,8 @@ struct spi_qup {
+ 	int			n_words;
+ 	int			tx_bytes;
+ 	int			rx_bytes;
++	const u8		*tx_buf;
++	u8			*rx_buf;
+ 	int			qup_v1;
+ 
+ 	int			mode;
+@@ -172,6 +174,12 @@ static inline bool spi_qup_is_dma_xfer(i
+ 	return false;
+ }
+ 
++/* get's the transaction size length */
++static inline unsigned spi_qup_len(struct spi_qup *controller)
++{
++       return controller->n_words * controller->w_size;
++}
++
+ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
+ {
+ 	u32 opstate = readl_relaxed(controller->base + QUP_STATE);
+@@ -224,10 +232,9 @@ static int spi_qup_set_state(struct spi_
+ 	return 0;
+ }
+ 
+-static void spi_qup_read_from_fifo(struct spi_qup *controller,
+-	struct spi_transfer *xfer, u32 num_words)
++static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
+ {
+-	u8 *rx_buf = xfer->rx_buf;
++	u8 *rx_buf = controller->rx_buf;
+ 	int i, shift, num_bytes;
+ 	u32 word;
+ 
+@@ -235,7 +242,7 @@ static void spi_qup_read_from_fifo(struc
+ 
+ 		word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+ 
+-		num_bytes = min_t(int, xfer->len - controller->rx_bytes,
++		num_bytes = min_t(int, spi_qup_len(controller) - controller->rx_bytes,
+ 					controller->w_size);
+ 
+ 		if (!rx_buf) {
+@@ -257,13 +264,12 @@ static void spi_qup_read_from_fifo(struc
+ 	}
+ }
+ 
+-static void spi_qup_read(struct spi_qup *controller,
+-			    struct spi_transfer *xfer)
++static void spi_qup_read(struct spi_qup *controller)
+ {
+ 	u32 remainder, words_per_block, num_words;
+ 	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
+ 
+-	remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
++	remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
+ 				 controller->w_size);
+ 	words_per_block = controller->in_blk_sz >> 2;
+ 
+@@ -284,7 +290,7 @@ static void spi_qup_read(struct spi_qup
+ 		}
+ 
+ 		/* read up to the maximum transfer size available */
+-		spi_qup_read_from_fifo(controller, xfer, num_words);
++		spi_qup_read_from_fifo(controller, num_words);
+ 
+ 		remainder -= num_words;
+ 
+@@ -306,17 +312,16 @@ static void spi_qup_read(struct spi_qup
+ 
+ }
+ 
+-static void spi_qup_write_to_fifo(struct spi_qup *controller,
+-	struct spi_transfer *xfer, u32 num_words)
++static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
+ {
+-	const u8 *tx_buf = xfer->tx_buf;
++	const u8 *tx_buf = controller->tx_buf;
+ 	int i, num_bytes;
+ 	u32 word, data;
+ 
+ 	for (; num_words; num_words--) {
+ 		word = 0;
+ 
+-		num_bytes = min_t(int, xfer->len - controller->tx_bytes,
++		num_bytes = min_t(int, spi_qup_len(controller) - controller->tx_bytes,
+ 				    controller->w_size);
+ 		if (tx_buf)
+ 			for (i = 0; i < num_bytes; i++) {
+@@ -337,13 +342,12 @@ static void spi_qup_dma_done(void *data)
+ 	complete(done);
+ }
+ 
+-static void spi_qup_write(struct spi_qup *controller,
+-			    struct spi_transfer *xfer)
++static void spi_qup_write(struct spi_qup *controller)
+ {
+ 	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
+ 	u32 remainder, words_per_block, num_words;
+ 
+-	remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
++	remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
+ 				 controller->w_size);
+ 	words_per_block = controller->out_blk_sz >> 2;
+ 
+@@ -363,7 +367,7 @@ static void spi_qup_write(struct spi_qup
+ 			num_words = 1;
+ 		}
+ 
+-		spi_qup_write_to_fifo(controller, xfer, num_words);
++		spi_qup_write_to_fifo(controller, num_words);
+ 
+ 		remainder -= num_words;
+ 
+@@ -629,35 +633,61 @@ static int spi_qup_do_pio(struct spi_dev
+ {
+ 	struct spi_master *master = spi->master;
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+-	int ret;
++	int ret, n_words, iterations, offset = 0;
+ 
+-	ret = spi_qup_io_config(spi, xfer);
+-	if (ret)
+-		return ret;
++	n_words = qup->n_words;
++	iterations = n_words / SPI_MAX_XFER; /* round down */
+ 
+-	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+-	if (ret) {
+-		dev_warn(qup->dev, "cannot set RUN state\n");
+-		return ret;
+-	}
++	qup->rx_buf = xfer->rx_buf;
++	qup->tx_buf = xfer->tx_buf;
+ 
+-	ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
+-	if (ret) {
+-		dev_warn(qup->dev, "cannot set PAUSE state\n");
+-		return ret;
+-	}
++	do {
++		if (iterations)
++			qup->n_words = SPI_MAX_XFER;
++		else
++			qup->n_words = n_words % SPI_MAX_XFER;
++
++		if (qup->tx_buf && offset)
++			qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;
++
++		if (qup->rx_buf && offset)
++			qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;
++
++		/* if the transaction is small enough, we need
++		 * to fallback to FIFO mode */
++		if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
++			qup->mode = QUP_IO_M_MODE_FIFO;
+ 
+-	if (qup->mode == QUP_IO_M_MODE_FIFO)
+-		spi_qup_write(qup, xfer);
++		ret = spi_qup_io_config(spi, xfer);
++		if (ret)
++			return ret;
+ 
+-	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+-	if (ret) {
+-		dev_warn(qup->dev, "cannot set RUN state\n");
+-		return ret;
+-	}
++		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++		if (ret) {
++			dev_warn(qup->dev, "cannot set RUN state\n");
++			return ret;
++		}
+ 
+-	if (!wait_for_completion_timeout(&qup->done, timeout))
+-		return -ETIMEDOUT;
++		ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
++		if (ret) {
++			dev_warn(qup->dev, "cannot set PAUSE state\n");
++			return ret;
++		}
++
++		if (qup->mode == QUP_IO_M_MODE_FIFO)
++			spi_qup_write(qup);
++
++		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++		if (ret) {
++			dev_warn(qup->dev, "cannot set RUN state\n");
++			return ret;
++		}
++
++		if (!wait_for_completion_timeout(&qup->done, timeout))
++			return -ETIMEDOUT;
++
++		offset++;
++	} while (iterations--);
+ 
+ 	return 0;
+ }
+@@ -722,17 +752,17 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 			complete(&controller->dma_tx_done);
+ 	} else {
+ 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
+-			spi_qup_read(controller, xfer);
++			spi_qup_read(controller);
+ 
+ 		if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+-			spi_qup_write(controller, xfer);
++			spi_qup_write(controller);
+ 	}
+ 
+ 	/* re-read opflags as flags may have changed due to actions above */
+ 	if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+ 		opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+ 
+-	if ((controller->rx_bytes == xfer->len &&
++	if ((controller->rx_bytes == spi_qup_len(controller) &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+ 		done = true;
+ 
+@@ -794,7 +824,7 @@ static int spi_qup_transfer_one(struct s
+ 		return ret;
+ 
+ 	timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
+-	timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
++	timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER, xfer->len) * 8, timeout);
+ 	timeout = 100 * msecs_to_jiffies(timeout);
+ 
+ 	if (spi_qup_is_dma_xfer(controller->mode))
+@@ -983,7 +1013,7 @@ static int spi_qup_probe(struct platform
+ 	master->dev.of_node = pdev->dev.of_node;
+ 	master->auto_runtime_pm = true;
+ 	master->dma_alignment = dma_get_cache_alignment();
+-	master->max_dma_len = SPI_MAX_DMA_XFER;
++	master->max_dma_len = SPI_MAX_XFER;
+ 
+ 	platform_set_drvdata(pdev, master);
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch b/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch
new file mode 100644
index 0000000..90ffe86
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch
@@ -0,0 +1,73 @@
+From a24914d34a4c6df4323c6d98950166600da79bc6 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Wed, 4 May 2016 16:33:42 -0500
+Subject: [PATCH 15/37] spi: qup: refactor spi_qup_prep_sg to be more take
+ specific sgl and nent
+
+This is in preparation for splitting DMA into multiple transacations,
+this contains no code changes just refactoring.
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |   28 +++++++++++-----------------
+ 1 file changed, 11 insertions(+), 17 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -379,27 +379,19 @@ static void spi_qup_write(struct spi_qup
+ 	} while (remainder);
+ }
+ 
+-static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+-			   enum dma_transfer_direction dir,
+-			   dma_async_tx_callback callback,
+-			   void *data)
++static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl,
++			   unsigned int nents, enum dma_transfer_direction dir,
++			   dma_async_tx_callback callback, void *data)
+ {
+ 	unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
+ 	struct dma_async_tx_descriptor *desc;
+-	struct scatterlist *sgl;
+ 	struct dma_chan *chan;
+ 	dma_cookie_t cookie;
+-	unsigned int nents;
+ 
+-	if (dir == DMA_MEM_TO_DEV) {
++	if (dir == DMA_MEM_TO_DEV)
+ 		chan = master->dma_tx;
+-		nents = xfer->tx_sg.nents;
+-		sgl = xfer->tx_sg.sgl;
+-	} else {
++	else
+ 		chan = master->dma_rx;
+-		nents = xfer->rx_sg.nents;
+-		sgl = xfer->rx_sg.sgl;
+-	}
+ 
+ 	desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+ 	if (IS_ERR_OR_NULL(desc))
+@@ -602,8 +594,9 @@ unsigned long timeout)
+ 	}
+ 
+ 	if (xfer->rx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
+-				      &qup->done);
++		ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
++				      xfer->rx_sg.nents, DMA_DEV_TO_MEM,
++				      rx_done, &qup->done);
+ 		if (ret)
+ 			return ret;
+ 
+@@ -611,8 +604,9 @@ unsigned long timeout)
+ 	}
+ 
+ 	if (xfer->tx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
+-				      &qup->dma_tx_done);
++		ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
++				      xfer->tx_sg.nents, DMA_MEM_TO_DEV,
++				      tx_done, &qup->dma_tx_done);
+ 		if (ret)
+ 			return ret;
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch b/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch
new file mode 100644
index 0000000..de324ff
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch
@@ -0,0 +1,166 @@
+From 6b2bb8803f19116bad41a271f9035d4c853f4553 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 5 May 2016 10:07:11 -0500
+Subject: [PATCH 16/37] spi: qup: allow mulitple DMA transactions per spi xfer
+
+Much like the block mode changes, we are breaking up DMA transactions
+into 64K chunks so we can reset the QUP engine.
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |  120 +++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 86 insertions(+), 34 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -566,6 +566,21 @@ static int spi_qup_io_config(struct spi_
+ 	return 0;
+ }
+ 
++static unsigned int spi_qup_sgl_get_size(struct scatterlist *sgl, unsigned int nents)
++{
++	struct scatterlist *sg;
++	int i;
++	unsigned int length = 0;
++
++	if (!nents)
++		return 0;
++
++	for_each_sg(sgl, sg, nents, i)
++		length += sg_dma_len(sg);
++
++	return length;
++}
++
+ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
+@@ -573,53 +588,90 @@ unsigned long timeout)
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ 	int ret;
++	struct scatterlist *tx_sgl, *rx_sgl;
+ 
+-	ret = spi_qup_io_config(spi, xfer);
+-	if (ret)
+-		return ret;
+-
+-	/* before issuing the descriptors, set the QUP to run */
+-	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+-	if (ret) {
+-		dev_warn(qup->dev, "cannot set RUN state\n");
+-		return ret;
+-	}
+-
+-	if (!qup->qup_v1) {
+-		if (xfer->rx_buf)
+-			rx_done = spi_qup_dma_done;
+-
+-		if (xfer->tx_buf)
+-			tx_done = spi_qup_dma_done;
+-	}
+-
+-	if (xfer->rx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
+-				      xfer->rx_sg.nents, DMA_DEV_TO_MEM,
+-				      rx_done, &qup->done);
+-		if (ret)
+-			return ret;
++	rx_sgl = xfer->rx_sg.sgl;
++	tx_sgl = xfer->tx_sg.sgl;
+ 
+-		dma_async_issue_pending(master->dma_rx);
+-	}
++	do {
++		int rx_nents = 0, tx_nents = 0;
+ 
+-	if (xfer->tx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
+-				      xfer->tx_sg.nents, DMA_MEM_TO_DEV,
+-				      tx_done, &qup->dma_tx_done);
++		if (rx_sgl) {
++			rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
++			if (rx_nents < 0)
++				rx_nents = sg_nents(rx_sgl);
++
++			qup->n_words = spi_qup_sgl_get_size(rx_sgl, rx_nents) /
++						qup->w_size;
++		}
++
++		if (tx_sgl) {
++			tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
++			if (tx_nents < 0)
++				tx_nents = sg_nents(tx_sgl);
++
++			qup->n_words = spi_qup_sgl_get_size(tx_sgl, tx_nents) /
++						qup->w_size;
++		}
++
++
++		ret = spi_qup_io_config(spi, xfer);
+ 		if (ret)
+ 			return ret;
+ 
+-		dma_async_issue_pending(master->dma_tx);
+-	}
++		/* before issuing the descriptors, set the QUP to run */
++		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++		if (ret) {
++			dev_warn(qup->dev, "cannot set RUN state\n");
++			return ret;
++		}
++
++		if (!qup->qup_v1) {
++			if (rx_sgl) {
++				rx_done = spi_qup_dma_done;
++			}
++
++			if (tx_sgl) {
++				tx_done = spi_qup_dma_done;
++			}
++		}
++
++		if (rx_sgl) {
++			ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
++					      DMA_DEV_TO_MEM, rx_done,
++					      &qup->done);
++			if (ret)
++				return ret;
++
++			dma_async_issue_pending(master->dma_rx);
++		}
++
++		if (tx_sgl) {
++			ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
++					      DMA_MEM_TO_DEV, tx_done,
++					      &qup->dma_tx_done);
++			if (ret)
++				return ret;
++
++			dma_async_issue_pending(master->dma_tx);
++		}
++
++		if (rx_sgl && !wait_for_completion_timeout(&qup->done, timeout)) {
++			pr_emerg(" rx timed out");
++			return -ETIMEDOUT;
++		}
++
++		if (tx_sgl && !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) {
++			pr_emerg(" tx timed out\n");
++			return -ETIMEDOUT;
++		}
+ 
+-	if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
+-		return -ETIMEDOUT;
++		for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl));
++		for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl));
+ 
+-	if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
+-		ret = -ETIMEDOUT;
++	} while (rx_sgl || tx_sgl);
+ 
+-	return ret;
++	return 0;
+ }
+ 
+ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
diff --git a/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch
new file mode 100644
index 0000000..d6d92b8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch
@@ -0,0 +1,86 @@
+From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001
+From: Varadarajan Narayanan <varada at codeaurora.org>
+Date: Wed, 25 May 2016 13:40:03 +0530
+Subject: [PATCH 17/37] spi: qup: Fix sg nents calculation
+
+lib/scatterlist.c:sg_nents_for_len() returns the number of SG
+entries that total up to greater than or equal to the given
+length. However, the spi-qup driver assumed that the returned
+nents is for a total less than or equal to the given length. The
+spi-qup driver requests nents for SPI_MAX_XFER, however the API
+returns nents for SPI_MAX_XFER+delta (actually SZ_64K).
+
+Based on this, spi_qup_do_dma() calculates n_words and programs
+that into  QUP_MX_{IN|OUT}PUT_CNT register. The calculated
+n_words value is more than the maximum value that can fit in the
+the 16-bit COUNT field of the QUP_MX_{IN|OUT}PUT_CNT register.
+And, the field gets programmed to zero. Since the COUNT field is
+zero, the i/o doesn't start eventually resulting in the i/o
+timing out.
+
+Signed-off-by: Varadarajan Narayanan <varada at codeaurora.org>
+---
+ drivers/spi/spi-qup.c |   38 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 36 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -581,6 +581,38 @@ static unsigned int spi_qup_sgl_get_size
+ 	return length;
+ }
+ 
++/**
++ * spi_qup_sg_nents_for_len - return total count of entries in scatterlist
++ *			      needed to satisfy the supplied length
++ * @sg:		The scatterlist
++ * @len:	The total required length
++ *
++ * Description:
++ * Determines the number of entries in sg that sum upto a maximum of
++ * the supplied length, taking into acount chaining as well
++ *
++ * Returns:
++ *   the number of sg entries needed, negative error on failure
++ *
++ **/
++int spi_qup_sg_nents_for_len(struct scatterlist *sg, u64 len)
++{
++	int nents;
++	u64 total;
++
++	if (!len)
++		return 0;
++
++	for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
++		nents++;
++		total += sg_dma_len(sg);
++		if (total > len)
++			return (nents - 1);
++	}
++
++	return -EINVAL;
++}
++
+ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ unsigned long timeout)
+ {
+@@ -597,7 +629,8 @@ unsigned long timeout)
+ 		int rx_nents = 0, tx_nents = 0;
+ 
+ 		if (rx_sgl) {
+-			rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
++			rx_nents = spi_qup_sg_nents_for_len(rx_sgl,
++								SPI_MAX_XFER);
+ 			if (rx_nents < 0)
+ 				rx_nents = sg_nents(rx_sgl);
+ 
+@@ -606,7 +639,8 @@ unsigned long timeout)
+ 		}
+ 
+ 		if (tx_sgl) {
+-			tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
++			tx_nents = spi_qup_sg_nents_for_len(tx_sgl,
++								SPI_MAX_XFER);
+ 			if (tx_nents < 0)
+ 				tx_nents = sg_nents(tx_sgl);
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch b/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch
new file mode 100644
index 0000000..3d6a827
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch
@@ -0,0 +1,28 @@
+From 94aa51061597d9db86f3ad4d54eb4a560fa66f2f Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Wed, 13 Apr 2016 14:03:14 -0500
+Subject: [PATCH 26/37] cpufreq: dt: qcom: ipq4019: Add compat for qcom
+ ipq4019
+
+Instantiate  cpufreq-dt-platdev driver for ipq4019 to support changing
+CPU frequencies.
+
+This depends on Viresh Kumar's patches in this series:
+http://comments.gmane.org/gmane.linux.power-management.general/73887
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/cpufreq/cpufreq-dt-platdev.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
+@@ -35,6 +35,8 @@ static const struct of_device_id machine
+ 
+ 	{ .compatible = "marvell,berlin", },
+ 
++	{ .compatible = "qcom,ipq4019", },
++
+ 	{ .compatible = "samsung,exynos3250", },
+ 	{ .compatible = "samsung,exynos4210", },
+ 	{ .compatible = "samsung,exynos4212", },
diff --git a/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch b/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch
new file mode 100644
index 0000000..87b25d5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch
@@ -0,0 +1,33 @@
+From 4f0328557a670d481f2609474b56890c28ab4694 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at qca.qualcomm.com>
+Date: Thu, 28 Apr 2016 12:55:08 -0500
+Subject: [PATCH 27/37] clk: ipq4019: report accurate fixed clock rates
+
+This looks like a copy-and-paste gone wrong, but update all
+the fixed clock rates to report the correct values.
+
+Signed-off-by: Matthew McClintock <mmcclint at qca.qualcomm.com>
+---
+ drivers/clk/qcom/gcc-ipq4019.c |   10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/clk/qcom/gcc-ipq4019.c
++++ b/drivers/clk/qcom/gcc-ipq4019.c
+@@ -1317,12 +1317,12 @@ static int gcc_ipq4019_probe(struct plat
+ {
+ 	struct device *dev = &pdev->dev;
+ 
+-	clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000);
+-	clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000);
+-	clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000);
+-	clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000);
++	clk_register_fixed_rate(dev, "fepll125", "xo", 0, 125000000);
++	clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 125000000);
++	clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 250000000);
++	clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 250000000);
+ 	clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000);
+-	clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000);
++	clk_register_fixed_rate(dev, "fepll500", "xo", 0, 500000000);
+ 	clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000);
+ 
+ 	return qcom_cc_probe(pdev, &gcc_ipq4019_desc);
diff --git a/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch b/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch
new file mode 100644
index 0000000..c2b0b44
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch
@@ -0,0 +1,77 @@
+From ce6cb947b9535b2d81717925cb6a3bc9f8500f44 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 17 Mar 2016 15:01:09 -0500
+Subject: [PATCH 28/37] qcom: ipq4019: add cpu operating points for cpufreq
+ support
+
+This adds some operating points for cpu frequeny scaling
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |   34 ++++++++++++++++++++++++++--------
+ 1 file changed, 26 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -40,14 +40,7 @@
+ 			reg = <0x0>;
+ 			clocks = <&gcc GCC_APPS_CLK_SRC>;
+ 			clock-frequency = <0>;
+-			operating-points = <
+-				/* kHz	uV (fixed) */
+-				48000	1100000
+-				200000	1100000
+-				500000	1100000
+-				666000	1100000
+-			>;
+-			clock-latency = <256000>;
++			operating-points-v2 = <&cpu0_opp_table>;
+ 		};
+ 
+ 		cpu at 1 {
+@@ -59,6 +52,7 @@
+ 			reg = <0x1>;
+ 			clocks = <&gcc GCC_APPS_CLK_SRC>;
+ 			clock-frequency = <0>;
++			operating-points-v2 = <&cpu0_opp_table>;
+ 		};
+ 
+ 		cpu at 2 {
+@@ -70,6 +64,7 @@
+ 			reg = <0x2>;
+ 			clocks = <&gcc GCC_APPS_CLK_SRC>;
+ 			clock-frequency = <0>;
++			operating-points-v2 = <&cpu0_opp_table>;
+ 		};
+ 
+ 		cpu at 3 {
+@@ -81,6 +76,29 @@
+ 			reg = <0x3>;
+ 			clocks = <&gcc GCC_APPS_CLK_SRC>;
+ 			clock-frequency = <0>;
++			operating-points-v2 = <&cpu0_opp_table>;
++		};
++	};
++
++	cpu0_opp_table: opp_table0 {
++		compatible = "operating-points-v2";
++		opp-shared;
++
++		opp at 48000000 {
++			opp-hz = /bits/ 64 <48000000>;
++			clock-latency-ns = <256000>;
++		};
++		opp at 200000000 {
++			opp-hz = /bits/ 64 <200000000>;
++			clock-latency-ns = <256000>;
++		};
++		opp at 500000000 {
++			opp-hz = /bits/ 64 <500000000>;
++			clock-latency-ns = <256000>;
++		};
++		opp at 666000000 {
++			opp-hz = /bits/ 64 <666000000>;
++			clock-latency-ns = <256000>;
+ 		};
+ 	};
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch b/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch
new file mode 100644
index 0000000..98dffc9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch
@@ -0,0 +1,23 @@
+From f033e344b8a02e98beaf16c1eb188bc175219756 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Mon, 28 Mar 2016 11:16:51 -0500
+Subject: [PATCH 29/37] qcom: ipq4019: turn on DMA for i2c
+
+These are the required nodes for i2c-qup to use DMA
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -179,6 +179,8 @@
+ 			clock-names = "iface", "core";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
++			dmas = <&blsp_dma 9>, <&blsp_dma 8>;
++			dma-names = "rx", "tx";
+ 			status = "disabled";
+ 		};
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch b/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch
new file mode 100644
index 0000000..7f79dba
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch
@@ -0,0 +1,28 @@
+From 79f698655871f2eeceb1f9051e60d97bc434f52f Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at qca.qualcomm.com>
+Date: Fri, 29 Apr 2016 12:48:02 -0500
+Subject: [PATCH 30/37] qcom: ipq4019: use correct clock for i2c bus 0
+
+For the record the mapping is as follows:
+
+QUP0 = SPI QUP1
+QUP1 = SPI QUP2
+QUP2 = I2C QUP1
+QUP3 = I2C QUP2
+
+Signed-off-by: Matthew McClintock <mmcclint at qca.qualcomm.com>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -175,7 +175,7 @@
+ 			reg = <0x78b7000 0x6000>;
+ 			interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ 			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+-				 <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
++				 <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+ 			clock-names = "iface", "core";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
diff --git a/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch b/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch
new file mode 100644
index 0000000..e715618
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch
@@ -0,0 +1,23 @@
+From 20249a0d746265a6663208fa768614784553edac Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Mon, 11 Apr 2016 14:49:12 -0500
+Subject: [PATCH 31/37] qcom: ipq4019: enable DMA for spi
+
+These are the required nodes for spi-qup to use DMA
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -167,6 +167,8 @@
+ 			clock-names = "core", "iface";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
++			dmas = <&blsp_dma 5>, <&blsp_dma 4>;
++			dma-names = "rx", "tx";
+ 			status = "disabled";
+ 		};
+ 
diff --git a/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch b/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch
new file mode 100644
index 0000000..9263ffd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch
@@ -0,0 +1,108 @@
+From 4d7fe4171b01cbfc01e4a00f44b3e7f7a8013eb3 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Fri, 8 Apr 2016 15:26:10 -0500
+Subject: [PATCH 32/37] qcom: ipq4019: use v2 of the kpss bringup mechanism
+
+v1 was the incorrect choice here and sometimes the board would not come
+up properly
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |   32 ++++++++++++++++++++++++--------
+ 1 file changed, 24 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -34,7 +34,8 @@
+ 		cpu at 0 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+-			enable-method = "qcom,kpss-acc-v1";
++			enable-method = "qcom,kpss-acc-v2";
++			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc0>;
+ 			qcom,saw = <&saw0>;
+ 			reg = <0x0>;
+@@ -46,7 +47,8 @@
+ 		cpu at 1 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+-			enable-method = "qcom,kpss-acc-v1";
++			enable-method = "qcom,kpss-acc-v2";
++			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc1>;
+ 			qcom,saw = <&saw1>;
+ 			reg = <0x1>;
+@@ -58,7 +60,8 @@
+ 		cpu at 2 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+-			enable-method = "qcom,kpss-acc-v1";
++			enable-method = "qcom,kpss-acc-v2";
++			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc2>;
+ 			qcom,saw = <&saw2>;
+ 			reg = <0x2>;
+@@ -70,7 +73,8 @@
+ 		cpu at 3 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+-			enable-method = "qcom,kpss-acc-v1";
++			enable-method = "qcom,kpss-acc-v2";
++			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc3>;
+ 			qcom,saw = <&saw3>;
+ 			reg = <0x3>;
+@@ -100,6 +104,12 @@
+ 			opp-hz = /bits/ 64 <666000000>;
+ 			clock-latency-ns = <256000>;
+ 		};
++
++		L2: l2-cache {
++			compatible = "qcom,arch-cache";
++			cache-level = <2>;
++			qcom,saw = <&saw_l2>;
++		};
+ 	};
+ 
+ 	pmu {
+@@ -212,22 +222,22 @@
+ 		};
+ 
+                 acc0: clock-controller at b088000 {
+-                        compatible = "qcom,kpss-acc-v1";
++                        compatible = "qcom,kpss-acc-v2";
+                         reg = <0x0b088000 0x1000>, <0xb008000 0x1000>;
+                 };
+ 
+                 acc1: clock-controller at b098000 {
+-                        compatible = "qcom,kpss-acc-v1";
++                        compatible = "qcom,kpss-acc-v2";
+                         reg = <0x0b098000 0x1000>, <0xb008000 0x1000>;
+                 };
+ 
+                 acc2: clock-controller at b0a8000 {
+-                        compatible = "qcom,kpss-acc-v1";
++                        compatible = "qcom,kpss-acc-v2";
+                         reg = <0x0b0a8000 0x1000>, <0xb008000 0x1000>;
+                 };
+ 
+                 acc3: clock-controller at b0b8000 {
+-                        compatible = "qcom,kpss-acc-v1";
++                        compatible = "qcom,kpss-acc-v2";
+                         reg = <0x0b0b8000 0x1000>, <0xb008000 0x1000>;
+                 };
+ 
+@@ -255,6 +265,12 @@
+                         regulator;
+                 };
+ 
++		saw_l2: regulator at b012000 {
++			compatible = "qcom,saw2";
++			reg = <0xb012000 0x1000>;
++			regulator;
++		};
++
+ 		serial at 78af000 {
+ 			compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ 			reg = <0x78af000 0x200>;
diff --git a/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch b/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch
new file mode 100644
index 0000000..0ea66d9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch
@@ -0,0 +1,28 @@
+From 25ee3761c072037b48246b992c3aec671c77a406 Mon Sep 17 00:00:00 2001
+From: Thomas Pedersen <twp at codeaurora.org>
+Date: Wed, 4 May 2016 12:25:41 -0700
+Subject: [PATCH 33/37] dts: ipq4019: support ARMv7 PMU
+
+Add support for cortex-a7-pmu present on ipq4019 SoCs.
+
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+Signed-off-by: Matthew McClintock <mmcclint at qca.qualcomm.com>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -118,6 +118,12 @@
+ 					 IRQ_TYPE_LEVEL_HIGH)>;
+ 	};
+ 
++	pmu {
++		compatible = "arm,cortex-a7-pmu";
++		interrupts = <GIC_PPI 7 (GIC_CPU_MASK_SIMPLE(4) |
++					 IRQ_TYPE_LEVEL_HIGH)>;
++	};
++
+ 	clocks {
+ 		sleep_clk: sleep_clk {
+ 			compatible = "fixed-clock";
diff --git a/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch
new file mode 100644
index 0000000..86a8a87
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch
@@ -0,0 +1,488 @@
+From e8d6fd46f5f3c5860fa7fa98004de9bd97c0d869 Mon Sep 17 00:00:00 2001
+From: Senthilkumar N L <snlakshm at codeaurora.org>
+Date: Tue, 6 Jan 2015 12:52:23 +0530
+Subject: [PATCH 34/37] qcom: ipq4019: Add IPQ4019 USB HS/SS PHY drivers
+
+These drivers handles control and configuration of the HS
+and SS USB PHY transceivers.
+
+Signed-off-by: Senthilkumar N L <snlakshm at codeaurora.org>
+---
+ drivers/usb/phy/Kconfig          |   11 ++
+ drivers/usb/phy/Makefile         |    2 +
+ drivers/usb/phy/phy-qca-baldur.c |  262 ++++++++++++++++++++++++++++++++++++++
+ drivers/usb/phy/phy-qca-uniphy.c |  171 +++++++++++++++++++++++++
+ 4 files changed, 446 insertions(+)
+ create mode 100644 drivers/usb/phy/phy-qca-baldur.c
+ create mode 100644 drivers/usb/phy/phy-qca-uniphy.c
+
+--- a/drivers/usb/phy/Kconfig
++++ b/drivers/usb/phy/Kconfig
+@@ -194,6 +194,17 @@ config USB_MXS_PHY
+ 
+ 	  MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+ 
++config USB_IPQ4019_PHY
++	tristate "IPQ4019 PHY wrappers support"
++	depends on (USB || USB_GADGET) && ARCH_QCOM
++	select USB_PHY
++	help
++	  Enable this to support the USB PHY transceivers on QCA961x chips.
++	  It handles PHY initialization, clock management required after
++	  resetting the hardware and power management.
++	  This driver is required even for peripheral only or host only
++	  mode configurations.
++
+ config USB_ULPI
+ 	bool "Generic ULPI Transceiver Driver"
+ 	depends on ARM || ARM64
+--- a/drivers/usb/phy/Makefile
++++ b/drivers/usb/phy/Makefile
+@@ -21,6 +21,8 @@ obj-$(CONFIG_USB_GPIO_VBUS)		+= phy-gpio
+ obj-$(CONFIG_USB_ISP1301)		+= phy-isp1301.o
+ obj-$(CONFIG_USB_MSM_OTG)		+= phy-msm-usb.o
+ obj-$(CONFIG_USB_QCOM_8X16_PHY)	+= phy-qcom-8x16-usb.o
++obj-$(CONFIG_USB_IPQ4019_PHY)           += phy-qca-baldur.o
++obj-$(CONFIG_USB_IPQ4019_PHY)           += phy-qca-uniphy.o
+ obj-$(CONFIG_USB_MV_OTG)		+= phy-mv-usb.o
+ obj-$(CONFIG_USB_MXS_PHY)		+= phy-mxs-usb.o
+ obj-$(CONFIG_USB_ULPI)			+= phy-ulpi.o
+--- /dev/null
++++ b/drivers/usb/phy/phy-qca-baldur.c
+@@ -0,0 +1,262 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++#include <linux/reset.h>
++#include <linux/of_device.h>
++
++/**
++ *  USB Hardware registers
++ */
++#define PHY_CTRL0_ADDR	0x000
++#define PHY_CTRL1_ADDR	0x004
++#define PHY_CTRL2_ADDR	0x008
++#define PHY_CTRL3_ADDR	0x00C
++#define PHY_CTRL4_ADDR	0x010
++#define PHY_MISC_ADDR	0x024
++#define PHY_IPG_ADDR	0x030
++
++#define PHY_CTRL0_EMU_ADDR	0x180
++#define PHY_CTRL1_EMU_ADDR	0x184
++#define PHY_CTRL2_EMU_ADDR	0x188
++#define PHY_CTRL3_EMU_ADDR	0x18C
++#define PHY_CTRL4_EMU_ADDR	0x190
++#define PHY_MISC_EMU_ADDR	0x1A4
++#define PHY_IPG_EMU_ADDR	0x1B0
++
++#define PHY_CTRL0_VAL	0xA4600015
++#define PHY_CTRL1_VAL	0x09500000
++#define PHY_CTRL2_VAL	0x00058180
++#define PHY_CTRL3_VAL	0x6DB6DCD6
++#define PHY_CTRL4_VAL	0x836DB6DB
++#define PHY_MISC_VAL	0x3803FB0C
++#define PHY_IPG_VAL		0x47323232
++
++#define PHY_CTRL0_EMU_VAL	0xb4000015
++#define PHY_CTRL1_EMU_VAL	0x09500000
++#define PHY_CTRL2_EMU_VAL	0x00058180
++#define PHY_CTRL3_EMU_VAL	0x6DB6DCD6
++#define PHY_CTRL4_EMU_VAL	0x836DB6DB
++#define PHY_MISC_EMU_VAL	0x3803FB0C
++#define PHY_IPG_EMU_VAL		0x47323232
++
++#define USB30_HS_PHY_HOST_MODE	(0x01 << 21)
++#define USB20_HS_PHY_HOST_MODE	(0x01 << 5)
++
++/* used to differentiate between USB3 HS and USB2 HS PHY */
++struct qca_baldur_hs_data {
++	unsigned int usb3_hs_phy;
++	unsigned int phy_config_offset;
++};
++
++struct qca_baldur_hs_phy {
++	struct device *dev;
++	struct usb_phy phy;
++
++	void __iomem *base;
++	void __iomem *qscratch_base;
++
++	struct reset_control *por_rst;
++	struct reset_control *srif_rst;
++
++	unsigned int host;
++	unsigned int emulation;
++	const struct qca_baldur_hs_data *data;
++};
++
++#define	phy_to_dw_phy(x)	container_of((x), struct qca_baldur_hs_phy, phy)
++
++static int qca_baldur_phy_read(struct usb_phy *x, u32 reg)
++{
++	struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++	return readl(phy->base + reg);
++}
++
++static int qca_baldur_phy_write(struct usb_phy *x, u32 val, u32 reg)
++{
++	struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
++
++	writel(val, phy->base + reg);
++	return 0;
++}
++
++static int qca_baldur_hs_phy_init(struct usb_phy *x)
++{
++	struct qca_baldur_hs_phy	*phy = phy_to_dw_phy(x);
++
++	/* assert HS PHY POR reset */
++	reset_control_assert(phy->por_rst);
++	msleep(10);
++
++	/* assert HS PHY SRIF reset */
++	reset_control_assert(phy->srif_rst);
++	msleep(10);
++
++	/* deassert HS PHY SRIF reset and program HS PHY registers */
++	reset_control_deassert(phy->srif_rst);
++	msleep(10);
++
++	if (!phy->emulation) {
++		/* perform PHY register writes */
++		writel(PHY_CTRL0_VAL, phy->base + PHY_CTRL0_ADDR);
++		writel(PHY_CTRL1_VAL, phy->base + PHY_CTRL1_ADDR);
++		writel(PHY_CTRL2_VAL, phy->base + PHY_CTRL2_ADDR);
++		writel(PHY_CTRL3_VAL, phy->base + PHY_CTRL3_ADDR);
++		writel(PHY_CTRL4_VAL, phy->base + PHY_CTRL4_ADDR);
++		writel(PHY_MISC_VAL, phy->base + PHY_MISC_ADDR);
++		writel(PHY_IPG_VAL, phy->base + PHY_IPG_ADDR);
++	} else {
++		/* perform PHY register writes */
++		writel(PHY_CTRL0_EMU_VAL, phy->base + PHY_CTRL0_EMU_ADDR);
++		writel(PHY_CTRL1_EMU_VAL, phy->base + PHY_CTRL1_EMU_ADDR);
++		writel(PHY_CTRL2_EMU_VAL, phy->base + PHY_CTRL2_EMU_ADDR);
++		writel(PHY_CTRL3_EMU_VAL, phy->base + PHY_CTRL3_EMU_ADDR);
++		writel(PHY_CTRL4_EMU_VAL, phy->base + PHY_CTRL4_EMU_ADDR);
++		writel(PHY_MISC_EMU_VAL, phy->base + PHY_MISC_EMU_ADDR);
++		writel(PHY_IPG_EMU_VAL, phy->base + PHY_IPG_EMU_ADDR);
++	}
++
++	msleep(10);
++
++	/* de-assert USB3 HS PHY POR reset */
++	reset_control_deassert(phy->por_rst);
++
++	return 0;
++}
++
++static int qca_baldur_hs_get_resources(struct qca_baldur_hs_phy *phy)
++{
++	struct platform_device *pdev = to_platform_device(phy->dev);
++	struct resource *res;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	phy->base = devm_ioremap_resource(phy->dev, res);
++	if (IS_ERR(phy->base))
++		return PTR_ERR(phy->base);
++
++	phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
++	if (IS_ERR(phy->por_rst))
++		return PTR_ERR(phy->por_rst);
++
++	phy->srif_rst = devm_reset_control_get(phy->dev, "srif_rst");
++	if (IS_ERR(phy->srif_rst))
++		return PTR_ERR(phy->srif_rst);
++
++	return 0;
++}
++
++static void qca_baldur_hs_put_resources(struct qca_baldur_hs_phy *phy)
++{
++	reset_control_assert(phy->srif_rst);
++	reset_control_assert(phy->por_rst);
++}
++
++static int qca_baldur_hs_remove(struct platform_device *pdev)
++{
++	struct qca_baldur_hs_phy *phy = platform_get_drvdata(pdev);
++
++	usb_remove_phy(&phy->phy);
++	return 0;
++}
++
++static void qca_baldur_hs_phy_shutdown(struct usb_phy *x)
++{
++	struct qca_baldur_hs_phy   *phy = phy_to_dw_phy(x);
++
++	qca_baldur_hs_put_resources(phy);
++}
++
++static struct usb_phy_io_ops qca_baldur_io_ops = {
++	.read = qca_baldur_phy_read,
++	.write = qca_baldur_phy_write,
++};
++
++static const struct qca_baldur_hs_data usb3_hs_data = {
++	.usb3_hs_phy = 1,
++	.phy_config_offset = USB30_HS_PHY_HOST_MODE,
++};
++
++static const struct qca_baldur_hs_data usb2_hs_data = {
++	.usb3_hs_phy = 0,
++	.phy_config_offset = USB20_HS_PHY_HOST_MODE,
++};
++
++static const struct of_device_id qca_baldur_hs_id_table[] = {
++	{ .compatible = "qca,baldur-usb3-hsphy", .data = &usb3_hs_data },
++	{ .compatible = "qca,baldur-usb2-hsphy", .data = &usb2_hs_data },
++	{ /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qca_baldur_hs_id_table);
++
++static int qca_baldur_hs_probe(struct platform_device *pdev)
++{
++	const struct of_device_id *match;
++	struct qca_baldur_hs_phy *phy;
++	int err;
++
++	match = of_match_device(qca_baldur_hs_id_table, &pdev->dev);
++	if (!match)
++		return -ENODEV;
++
++	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++	if (!phy)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, phy);
++	phy->dev = &pdev->dev;
++
++	phy->data = match->data;
++
++	err = qca_baldur_hs_get_resources(phy);
++	if (err < 0) {
++		dev_err(&pdev->dev, "failed to request resources: %d\n", err);
++		return err;
++	}
++
++	phy->phy.dev		= phy->dev;
++	phy->phy.label		= "qca-baldur-hsphy";
++	phy->phy.init		= qca_baldur_hs_phy_init;
++	phy->phy.shutdown	= qca_baldur_hs_phy_shutdown;
++	phy->phy.type		= USB_PHY_TYPE_USB2;
++	phy->phy.io_ops		= &qca_baldur_io_ops;
++
++	err = usb_add_phy_dev(&phy->phy);
++	return err;
++}
++
++static struct platform_driver qca_baldur_hs_driver = {
++	.probe		= qca_baldur_hs_probe,
++	.remove		= qca_baldur_hs_remove,
++	.driver		= {
++		.name	= "qca-baldur-hsphy",
++		.owner	= THIS_MODULE,
++		.of_match_table = qca_baldur_hs_id_table,
++	},
++};
++
++module_platform_driver(qca_baldur_hs_driver);
++
++MODULE_ALIAS("platform:qca-baldur-hsphy");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("USB3 QCA BALDUR HSPHY driver");
+--- /dev/null
++++ b/drivers/usb/phy/phy-qca-uniphy.c
+@@ -0,0 +1,171 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++#include <linux/reset.h>
++#include <linux/of_device.h>
++
++struct qca_uni_ss_phy {
++	struct usb_phy phy;
++	struct device *dev;
++
++	void __iomem *base;
++
++	struct reset_control *por_rst;
++
++	unsigned int host;
++};
++
++#define	phy_to_dw_phy(x)	container_of((x), struct qca_uni_ss_phy, phy)
++
++/**
++ * Write register
++ *
++ * @base - PHY base virtual address.
++ * @offset - register offset.
++ */
++static u32 qca_uni_ss_read(void __iomem *base, u32 offset)
++{
++	u32 value;
++	value = readl_relaxed(base + offset);
++	return value;
++}
++
++/**
++ * Write register
++ *
++ * @base - PHY base virtual address.
++ * @offset - register offset.
++ * @val - value to write.
++ */
++static void qca_uni_ss_write(void __iomem *base, u32 offset, u32 val)
++{
++	writel(val, base + offset);
++	udelay(100);
++}
++
++static void qca_uni_ss_phy_shutdown(struct usb_phy *x)
++{
++	struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
++
++	/* assert SS PHY POR reset */
++	reset_control_assert(phy->por_rst);
++}
++
++static int qca_uni_ss_phy_init(struct usb_phy *x)
++{
++	struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
++
++	/* assert SS PHY POR reset */
++	reset_control_assert(phy->por_rst);
++
++	msleep(10);
++
++	msleep(10);
++
++	/* deassert SS PHY POR reset */
++	reset_control_deassert(phy->por_rst);
++
++	return 0;
++}
++
++static int qca_uni_ss_get_resources(struct platform_device *pdev,
++		struct qca_uni_ss_phy *phy)
++{
++	struct resource *res;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	phy->base = devm_ioremap_resource(phy->dev, res);
++	if (IS_ERR(phy->base))
++		return PTR_ERR(phy->base);
++
++	phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
++	if (IS_ERR(phy->por_rst))
++		return PTR_ERR(phy->por_rst);
++
++	return 0;
++}
++
++static int qca_uni_ss_remove(struct platform_device *pdev)
++{
++	struct qca_uni_ss_phy *phy = platform_get_drvdata(pdev);
++
++	usb_remove_phy(&phy->phy);
++	return 0;
++}
++
++static const struct of_device_id qca_uni_ss_id_table[] = {
++	{ .compatible = "qca,uni-ssphy" },
++	{ /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qca_uni_ss_id_table);
++
++static int qca_uni_ss_probe(struct platform_device *pdev)
++{
++	const struct of_device_id *match;
++	struct device_node *np = pdev->dev.of_node;
++	struct qca_uni_ss_phy  *phy;
++	int ret;
++
++	match = of_match_device(qca_uni_ss_id_table, &pdev->dev);
++	if (!match)
++		return -ENODEV;
++
++	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++	if (!phy)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, phy);
++	phy->dev = &pdev->dev;
++
++	ret = qca_uni_ss_get_resources(pdev, phy);
++	if (ret < 0) {
++		dev_err(&pdev->dev, "failed to request resources: %d\n", ret);
++		return ret;
++	}
++
++	phy->phy.dev        = phy->dev;
++	phy->phy.label      = "qca-uni-ssphy";
++	phy->phy.init       = qca_uni_ss_phy_init;
++	phy->phy.shutdown   = qca_uni_ss_phy_shutdown;
++	phy->phy.type       = USB_PHY_TYPE_USB3;
++
++	ret = usb_add_phy_dev(&phy->phy);
++	return ret;
++}
++
++static struct platform_driver qca_uni_ss_driver = {
++	.probe		= qca_uni_ss_probe,
++	.remove		= qca_uni_ss_remove,
++	.driver		= {
++		.name	= "qca-uni-ssphy",
++		.owner	= THIS_MODULE,
++		.of_match_table = qca_uni_ss_id_table,
++	},
++};
++
++module_platform_driver(qca_uni_ss_driver);
++
++MODULE_ALIAS("platform:qca-uni-ssphy");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("USB3 QCA UNI SSPHY driver");
diff --git a/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch b/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch
new file mode 100644
index 0000000..61768bc
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch
@@ -0,0 +1,88 @@
+From d2ed553484fecdf02fa53bf431599412348afa95 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 17 Mar 2016 16:22:28 -0500
+Subject: [PATCH 35/37] qcom: ipq4019: add USB nodes to ipq4019 SoC device
+ tree
+
+This adds the SoC nodes to the ipq4019 device tree
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi |   67 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -313,5 +313,72 @@
+ 			compatible = "qcom,pshold";
+ 			reg = <0x4ab000 0x4>;
+ 		};
++
++                usb3_ss_phy: ssphy at 9a000 {
++                        compatible = "qca,uni-ssphy";
++                        reg = <0x9a000 0x800>;
++                        reg-names = "phy_base";
++                        resets = <&gcc USB3_UNIPHY_PHY_ARES>;
++                        reset-names = "por_rst";
++                        status = "disabled";
++                };
++
++                usb3_hs_phy: hsphy at a6000 {
++                        compatible = "qca,baldur-usb3-hsphy";
++                        reg = <0xa6000 0x40>;
++                        reg-names = "phy_base";
++                        resets = <&gcc USB3_HSPHY_POR_ARES>, <&gcc USB3_HSPHY_S_ARES>;
++                        reset-names = "por_rst", "srif_rst";
++                        status = "disabled";
++                };
++
++		usb3 at 0 {
++                        compatible = "qcom,dwc3";
++                        #address-cells = <1>;
++                        #size-cells = <1>;
++                        clocks = <&gcc GCC_USB3_MASTER_CLK>;
++			clock-names = "core";
++                        ranges;
++                        status = "disabled";
++
++                        dwc3 at 8a00000 {
++                                compatible = "snps,dwc3";
++                                reg = <0x8a00000 0xf8000>;
++                                interrupts = <0 132 0>;
++                                usb-phy = <&usb3_hs_phy>, <&usb3_ss_phy>;
++                                phy-names = "usb2-phy", "usb3-phy";
++                                tx-fifo-resize;
++                                dr_mode = "host";
++                        };
++                };
++
++                usb2_hs_phy: hsphy at a8000 {
++                        compatible = "qca,baldur-usb2-hsphy";
++                        reg = <0xa8000 0x40>;
++                        reg-names = "phy_base";
++                        resets = <&gcc USB2_HSPHY_POR_ARES>, <&gcc USB2_HSPHY_S_ARES>;
++                        reset-names = "por_rst", "srif_rst";
++                        status = "disabled";
++                };
++
++                usb2 at 0 {
++                        compatible = "qcom,dwc3";
++                        #address-cells = <1>;
++                        #size-cells = <1>;
++                        clocks = <&gcc GCC_USB2_MASTER_CLK>;
++			clock-names = "core";
++                        ranges;
++                        status = "disabled";
++
++                        dwc3 at 6000000 {
++                                compatible = "snps,dwc3";
++                                reg = <0x6000000 0xf8000>;
++                                interrupts = <0 136 0>;
++                                usb-phy = <&usb2_hs_phy>;
++                                phy-names = "usb2-phy";
++                                tx-fifo-resize;
++                                dr_mode = "host";
++                        };
++                };
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch b/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch
new file mode 100644
index 0000000..77be518
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch
@@ -0,0 +1,45 @@
+From c15caf58ff5c3b4093fa388b9c6228bb3c9c5413 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Mon, 21 Mar 2016 16:12:05 -0500
+Subject: [PATCH 36/37] qcom: ipq4019: enable USB bus for DK01.1 board
+
+This enables the USB block
+
+Change-Id: I384dd1874bba341713f384cf6199abd446e3f3c0
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi |   24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -108,5 +108,29 @@
+ 		watchdog at b017000 {
+ 			status = "ok";
+ 		};
++
++                usb3_ss_phy: ssphy at 0 {
++                        status = "ok";
++                };
++
++                dummy_ss_phy: ssphy at 1 {
++                        status = "ok";
++                };
++
++                usb3_hs_phy: hsphy at a6000 {
++                        status = "ok";
++                };
++
++                usb2_hs_phy: hsphy at a8000 {
++                        status = "ok";
++                };
++
++                usb3: usb3 at 8a00000 {
++                        status = "ok";
++                };
++
++                usb2: usb2 at 6000000 {
++                        status = "ok";
++                };
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch b/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch
new file mode 100644
index 0000000..21d7e39
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch
@@ -0,0 +1,258 @@
+From ff1ecc5bfc11377e82894d05aa45a92657ef8a06 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Mon, 21 Mar 2016 15:55:21 -0500
+Subject: [PATCH 37/37] dts: ipq4019: Add support for IPQ4019 DK04 board
+
+This is pretty similiar to a DK01 but has a bit more IO. Some notable
+differences are listed below however they are not in the device tree yet
+as we continue adding more support
+
+- second serial port
+- PCIe
+- NAND
+- SD/EMMC
+
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile                      |    1 +
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi   |   12 +-
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts |   21 +++
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi   |  163 +++++++++++++++++++++++
+ 4 files changed, 189 insertions(+), 8 deletions(-)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
+ create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -617,6 +617,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ 	qcom-apq8084-ifc6540.dtb \
+ 	qcom-apq8084-mtp.dtb \
+ 	qcom-ipq4019-ap.dk01.1-c1.dtb \
++	qcom-ipq4019-ap.dk04.1-c1.dtb \
+ 	qcom-ipq8064-ap148.dtb \
+ 	qcom-msm8660-surf.dtb \
+ 	qcom-msm8960-cdp.dtb \
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -109,11 +109,7 @@
+ 			status = "ok";
+ 		};
+ 
+-                usb3_ss_phy: ssphy at 0 {
+-                        status = "ok";
+-                };
+-
+-                dummy_ss_phy: ssphy at 1 {
++                usb3_ss_phy: ssphy at 9a000 {
+                         status = "ok";
+                 };
+ 
+@@ -121,15 +117,15 @@
+                         status = "ok";
+                 };
+ 
+-                usb2_hs_phy: hsphy at a8000 {
++                usb3 at 0 {
+                         status = "ok";
+                 };
+ 
+-                usb3: usb3 at 8a00000 {
++                usb2_hs_phy: hsphy at a8000 {
+                         status = "ok";
+                 };
+ 
+-                usb2: usb2 at 6000000 {
++                usb2 at 0{
+                         status = "ok";
+                 };
+ 	};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
+@@ -0,0 +1,21 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include "qcom-ipq4019-ap.dk04.1.dtsi"
++
++/ {
++	model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK04.1-C1";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+@@ -0,0 +1,163 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include "qcom-ipq4019.dtsi"
++
++/ {
++	model = "Qualcomm Technologies, Inc. IPQ4019/AP-DK04.1";
++	compatible = "qcom,ipq4019";
++
++	clocks {
++                xo: xo {
++                        compatible = "fixed-clock";
++                        clock-frequency = <48000000>;
++                        #clock-cells = <0>;
++                };
++	};
++
++	soc {
++		timer {
++			compatible = "arm,armv7-timer";
++			interrupts = <1 2 0xf08>,
++				     <1 3 0xf08>,
++				     <1 4 0xf08>,
++				     <1 1 0xf08>;
++			clock-frequency = <48000000>;
++		};
++
++		pinctrl at 0x01000000 {
++			serial_0_pins: serial_pinmux {
++				mux {
++					pins = "gpio16", "gpio17";
++					function = "blsp_uart0";
++					bias-disable;
++				};
++			};
++
++                        serial_1_pins: serial1_pinmux {
++                                mux {
++                                        pins = "gpio8", "gpio9";
++                                        function = "blsp_uart1";
++                                        bias-disable;
++                                };
++                        };
++
++			spi_0_pins: spi_0_pinmux {
++				pinmux {
++					function = "blsp_spi0";
++					pins = "gpio13", "gpio14", "gpio15";
++				};
++				pinmux_cs {
++					function = "gpio";
++					pins = "gpio12";
++				};
++				pinconf {
++					pins = "gpio13", "gpio14", "gpio15";
++					drive-strength = <12>;
++					bias-disable;
++				};
++				pinconf_cs {
++					pins = "gpio12";
++					drive-strength = <2>;
++					bias-disable;
++					output-high;
++				};
++			};
++
++			i2c_0_pins: i2c_0_pinmux {
++				pinmux {
++					function = "blsp_i2c0";
++					pins = "gpio10", "gpio11";
++				};
++				pinconf {
++					pins = "gpio10", "gpio11";
++					drive-strength = <16>;
++					bias-disable;
++				};
++			};
++		};
++
++		blsp_dma: dma at 7884000 {
++			status = "ok";
++		};
++
++		spi_0: spi at 78b5000 {
++			pinctrl-0 = <&spi_0_pins>;
++			pinctrl-names = "default";
++			status = "ok";
++			cs-gpios = <&tlmm 12 0>;
++
++			mx25l25635e at 0 {
++				#address-cells = <1>;
++				#size-cells = <1>;
++				reg = <0>;
++				compatible = "mx25l25635e";
++				spi-max-frequency = <24000000>;
++			};
++		};
++
++                i2c_0: i2c at 78b7000 { /* BLSP1 QUP2 */
++                        pinctrl-0 = <&i2c_0_pins>;
++                        pinctrl-names = "default";
++
++			status = "ok";
++                };
++
++		serial at 78af000 {
++			pinctrl-0 = <&serial_0_pins>;
++			pinctrl-names = "default";
++			status = "ok";
++		};
++
++                serial at 78b0000 {
++                        pinctrl-0 = <&serial_1_pins>;
++                        pinctrl-names = "default";
++                        status = "ok";
++                };
++
++                usb3_ss_phy: ssphy at 9a000 {
++                        status = "ok";
++                };
++
++                usb3_hs_phy: hsphy at a6000 {
++                        status = "ok";
++                };
++
++                usb3: usb3 at 0 {
++                        status = "ok";
++                };
++
++                usb2_hs_phy: hsphy at a8000 {
++                        status = "ok";
++                };
++
++                usb2: usb2 at 6000000 {
++                        status = "ok";
++                };
++
++		cryptobam: dma at 8e04000 {
++			status = "ok";
++		};
++
++		crypto at 8e3a000 {
++			status = "ok";
++		};
++
++		watchdog at b017000 {
++			status = "ok";
++		};
++	};
++};
diff --git a/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch
new file mode 100644
index 0000000..bd79a89
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch
@@ -0,0 +1,746 @@
+From patchwork Wed Nov  2 15:56:56 2016
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks
+From: Georgi Djakov <georgi.djakov at linaro.org>
+X-Patchwork-Id: 9409419
+Message-Id: <20161102155658.32203-2-georgi.djakov at linaro.org>
+To: sboyd at codeaurora.org, mturquette at baylibre.com
+Cc: linux-clk at vger.kernel.org, devicetree at vger.kernel.org,
+ robh+dt at kernel.org, mark.rutland at arm.com,
+ linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
+ georgi.djakov at linaro.org
+Date: Wed,  2 Nov 2016 17:56:56 +0200
+
+This adds initial support for clocks controlled by the Resource
+Power Manager (RPM) processor on some Qualcomm SoCs, which use
+the qcom_smd_rpm driver to communicate with RPM.
+Such platforms are msm8916, apq8084 and msm8974.
+
+The RPM is a dedicated hardware engine for managing the shared
+SoC resources in order to keep the lowest power profile. It
+communicates with other hardware subsystems via shared memory
+and accepts clock requests, aggregates the requests and turns
+the clocks on/off or scales them on demand.
+
+This driver is based on the codeaurora.org driver:
+https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c
+
+Signed-off-by: Georgi Djakov <georgi.djakov at linaro.org>
+---
+ .../devicetree/bindings/clock/qcom,rpmcc.txt       |  36 ++
+ drivers/clk/qcom/Kconfig                           |  16 +
+ drivers/clk/qcom/Makefile                          |   1 +
+ drivers/clk/qcom/clk-smd-rpm.c                     | 571 +++++++++++++++++++++
+ include/dt-bindings/clock/qcom,rpmcc.h             |  45 ++
+ 5 files changed, 669 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+ create mode 100644 drivers/clk/qcom/clk-smd-rpm.c
+ create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h
+
+--
+To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
+the body of a message to majordomo at vger.kernel.org
+More majordomo info at  http://vger.kernel.org/majordomo-info.html
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -0,0 +1,36 @@
++Qualcomm RPM Clock Controller Binding
++------------------------------------------------
++The RPM is a dedicated hardware engine for managing the shared
++SoC resources in order to keep the lowest power profile. It
++communicates with other hardware subsystems via shared memory
++and accepts clock requests, aggregates the requests and turns
++the clocks on/off or scales them on demand.
++
++Required properties :
++- compatible : shall contain only one of the following. The generic
++               compatible "qcom,rpmcc" should be also included.
++
++			"qcom,rpmcc-msm8916", "qcom,rpmcc"
++
++- #clock-cells : shall contain 1
++
++Example:
++	smd {
++		compatible = "qcom,smd";
++
++		rpm {
++			interrupts = <0 168 1>;
++			qcom,ipc = <&apcs 8 0>;
++			qcom,smd-edge = <15>;
++
++			rpm_requests {
++				compatible = "qcom,rpm-msm8916";
++				qcom,smd-channels = "rpm_requests";
++
++				rpmcc: clock-controller {
++					compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
++					#clock-cells = <1>;
++				};
++			};
++		};
++	};
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -2,6 +2,9 @@
+ 	bool
+ 	select PM_GENERIC_DOMAINS if PM
+ 
++config QCOM_RPMCC
++	bool
++
+ config COMMON_CLK_QCOM
+ 	tristate "Support for Qualcomm's clock controllers"
+ 	depends on OF
+@@ -9,6 +12,19 @@
+ 	select REGMAP_MMIO
+ 	select RESET_CONTROLLER
+ 
++config QCOM_CLK_SMD_RPM
++	tristate "RPM over SMD based Clock Controller"
++	depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
++	select QCOM_RPMCC
++	help
++	  The RPM (Resource Power Manager) is a dedicated hardware engine for
++	  managing the shared SoC resources in order to keep the lowest power
++	  profile. It communicates with other hardware subsystems via shared
++	  memory and accepts clock requests, aggregates the requests and turns
++	  the clocks on/off or scales them on demand.
++	  Say Y if you want to support the clocks exposed by the RPM on
++	  platforms such as apq8016, apq8084, msm8974 etc.
++
+ config APQ_GCC_8084
+ 	tristate "APQ8084 Global Clock Controller"
+ 	select QCOM_GDSC
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -29,3 +29,4 @@
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
++obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-smd-rpm.c
+@@ -0,0 +1,571 @@
++/*
++ * Copyright (c) 2016, Linaro Limited
++ * Copyright (c) 2014, 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-provider.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/soc/qcom/smd-rpm.h>
++
++#include <dt-bindings/clock/qcom,rpmcc.h>
++#include <dt-bindings/mfd/qcom-rpm.h>
++
++#define QCOM_RPM_KEY_SOFTWARE_ENABLE			0x6e657773
++#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY	0x62636370
++#define QCOM_RPM_SMD_KEY_RATE				0x007a484b
++#define QCOM_RPM_SMD_KEY_ENABLE				0x62616e45
++#define QCOM_RPM_SMD_KEY_STATE				0x54415453
++#define QCOM_RPM_SCALING_ENABLE_ID			0x2
++
++#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id,  \
++			     key)					      \
++	static struct clk_smd_rpm _platform##_##_active;		      \
++	static struct clk_smd_rpm _platform##_##_name = {		      \
++		.rpm_res_type = (type),					      \
++		.rpm_clk_id = (r_id),					      \
++		.rpm_status_id = (stat_id),				      \
++		.rpm_key = (key),					      \
++		.peer = &_platform##_##_active,				      \
++		.rate = INT_MAX,					      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_smd_rpm_ops,			      \
++			.name = #_name,					      \
++			.parent_names = (const char *[]){ "xo_board" },       \
++			.num_parents = 1,				      \
++		},							      \
++	};								      \
++	static struct clk_smd_rpm _platform##_##_active = {		      \
++		.rpm_res_type = (type),					      \
++		.rpm_clk_id = (r_id),					      \
++		.rpm_status_id = (stat_id),				      \
++		.active_only = true,					      \
++		.rpm_key = (key),					      \
++		.peer = &_platform##_##_name,				      \
++		.rate = INT_MAX,					      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_smd_rpm_ops,			      \
++			.name = #_active,				      \
++			.parent_names = (const char *[]){ "xo_board" },	      \
++			.num_parents = 1,				      \
++		},							      \
++	}
++
++#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id,    \
++				    stat_id, r, key)			      \
++	static struct clk_smd_rpm _platform##_##_active;		      \
++	static struct clk_smd_rpm _platform##_##_name = {		      \
++		.rpm_res_type = (type),					      \
++		.rpm_clk_id = (r_id),					      \
++		.rpm_status_id = (stat_id),				      \
++		.rpm_key = (key),					      \
++		.branch = true,						      \
++		.peer = &_platform##_##_active,				      \
++		.rate = (r),						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_smd_rpm_branch_ops,			      \
++			.name = #_name,					      \
++			.parent_names = (const char *[]){ "xo_board" },	      \
++			.num_parents = 1,				      \
++		},							      \
++	};								      \
++	static struct clk_smd_rpm _platform##_##_active = {		      \
++		.rpm_res_type = (type),					      \
++		.rpm_clk_id = (r_id),					      \
++		.rpm_status_id = (stat_id),				      \
++		.active_only = true,					      \
++		.rpm_key = (key),					      \
++		.branch = true,						      \
++		.peer = &_platform##_##_name,				      \
++		.rate = (r),						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_smd_rpm_branch_ops,			      \
++			.name = #_active,				      \
++			.parent_names = (const char *[]){ "xo_board" },	      \
++			.num_parents = 1,				      \
++		},							      \
++	}
++
++#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id)	      \
++		__DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id,   \
++		0, QCOM_RPM_SMD_KEY_RATE)
++
++#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r)   \
++		__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type,  \
++		r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
++
++#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id)	      \
++		__DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id,   \
++		0, QCOM_RPM_SMD_KEY_STATE)
++
++#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id)	      \
++		__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active,	      \
++		QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000,			      \
++		QCOM_RPM_KEY_SOFTWARE_ENABLE)
++
++#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
++		__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active,	      \
++		QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000,			      \
++		QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
++
++#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw)
++
++struct clk_smd_rpm {
++	const int rpm_res_type;
++	const int rpm_key;
++	const int rpm_clk_id;
++	const int rpm_status_id;
++	const bool active_only;
++	bool enabled;
++	bool branch;
++	struct clk_smd_rpm *peer;
++	struct clk_hw hw;
++	unsigned long rate;
++	struct qcom_smd_rpm *rpm;
++};
++
++struct clk_smd_rpm_req {
++	__le32 key;
++	__le32 nbytes;
++	__le32 value;
++};
++
++struct rpm_cc {
++	struct qcom_rpm *rpm;
++	struct clk_hw_onecell_data data;
++	struct clk_hw *hws[];
++};
++
++struct rpm_smd_clk_desc {
++	struct clk_smd_rpm **clks;
++	size_t num_clks;
++};
++
++static DEFINE_MUTEX(rpm_smd_clk_lock);
++
++static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
++{
++	int ret;
++	struct clk_smd_rpm_req req = {
++		.key = cpu_to_le32(r->rpm_key),
++		.nbytes = cpu_to_le32(sizeof(u32)),
++		.value = cpu_to_le32(INT_MAX),
++	};
++
++	ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++				 r->rpm_res_type, r->rpm_clk_id, &req,
++				 sizeof(req));
++	if (ret)
++		return ret;
++	ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
++				 r->rpm_res_type, r->rpm_clk_id, &req,
++				 sizeof(req));
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
++				       unsigned long rate)
++{
++	struct clk_smd_rpm_req req = {
++		.key = cpu_to_le32(r->rpm_key),
++		.nbytes = cpu_to_le32(sizeof(u32)),
++		.value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
++	};
++
++	return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++				  r->rpm_res_type, r->rpm_clk_id, &req,
++				  sizeof(req));
++}
++
++static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r,
++				      unsigned long rate)
++{
++	struct clk_smd_rpm_req req = {
++		.key = cpu_to_le32(r->rpm_key),
++		.nbytes = cpu_to_le32(sizeof(u32)),
++		.value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
++	};
++
++	return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
++				  r->rpm_res_type, r->rpm_clk_id, &req,
++				  sizeof(req));
++}
++
++static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate,
++			    unsigned long *active, unsigned long *sleep)
++{
++	*active = rate;
++
++	/*
++	 * Active-only clocks don't care what the rate is during sleep. So,
++	 * they vote for zero.
++	 */
++	if (r->active_only)
++		*sleep = 0;
++	else
++		*sleep = *active;
++}
++
++static int clk_smd_rpm_prepare(struct clk_hw *hw)
++{
++	struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++	struct clk_smd_rpm *peer = r->peer;
++	unsigned long this_rate = 0, this_sleep_rate = 0;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	unsigned long active_rate, sleep_rate;
++	int ret = 0;
++
++	mutex_lock(&rpm_smd_clk_lock);
++
++	/* Don't send requests to the RPM if the rate has not been set. */
++	if (!r->rate)
++		goto out;
++
++	to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate,
++				&peer_rate, &peer_sleep_rate);
++
++	active_rate = max(this_rate, peer_rate);
++
++	if (r->branch)
++		active_rate = !!active_rate;
++
++	ret = clk_smd_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++	if (r->branch)
++		sleep_rate = !!sleep_rate;
++
++	ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		/* Undo the active set vote and restore it */
++		ret = clk_smd_rpm_set_rate_active(r, peer_rate);
++
++out:
++	if (!ret)
++		r->enabled = true;
++
++	mutex_unlock(&rpm_smd_clk_lock);
++
++	return ret;
++}
++
++static void clk_smd_rpm_unprepare(struct clk_hw *hw)
++{
++	struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++	struct clk_smd_rpm *peer = r->peer;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	unsigned long active_rate, sleep_rate;
++	int ret;
++
++	mutex_lock(&rpm_smd_clk_lock);
++
++	if (!r->rate)
++		goto out;
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate, &peer_rate,
++				&peer_sleep_rate);
++
++	active_rate = r->branch ? !!peer_rate : peer_rate;
++	ret = clk_smd_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
++	ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		goto out;
++
++	r->enabled = false;
++
++out:
++	mutex_unlock(&rpm_smd_clk_lock);
++}
++
++static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
++				unsigned long parent_rate)
++{
++	struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++	struct clk_smd_rpm *peer = r->peer;
++	unsigned long active_rate, sleep_rate;
++	unsigned long this_rate = 0, this_sleep_rate = 0;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	int ret = 0;
++
++	mutex_lock(&rpm_smd_clk_lock);
++
++	if (!r->enabled)
++		goto out;
++
++	to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate,
++				&peer_rate, &peer_sleep_rate);
++
++	active_rate = max(this_rate, peer_rate);
++	ret = clk_smd_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++	ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		goto out;
++
++	r->rate = rate;
++
++out:
++	mutex_unlock(&rpm_smd_clk_lock);
++
++	return ret;
++}
++
++static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
++				   unsigned long *parent_rate)
++{
++	/*
++	 * RPM handles rate rounding and we don't have a way to
++	 * know what the rate will be, so just return whatever
++	 * rate is requested.
++	 */
++	return rate;
++}
++
++static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
++					     unsigned long parent_rate)
++{
++	struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
++
++	/*
++	 * RPM handles rate rounding and we don't have a way to
++	 * know what the rate will be, so just return whatever
++	 * rate was set.
++	 */
++	return r->rate;
++}
++
++static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm)
++{
++	int ret;
++	struct clk_smd_rpm_req req = {
++		.key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE),
++		.nbytes = cpu_to_le32(sizeof(u32)),
++		.value = cpu_to_le32(1),
++	};
++
++	ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE,
++				 QCOM_SMD_RPM_MISC_CLK,
++				 QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
++	if (ret) {
++		pr_err("RPM clock scaling (sleep set) not enabled!\n");
++		return ret;
++	}
++
++	ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE,
++				 QCOM_SMD_RPM_MISC_CLK,
++				 QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
++	if (ret) {
++		pr_err("RPM clock scaling (active set) not enabled!\n");
++		return ret;
++	}
++
++	pr_debug("%s: RPM clock scaling is enabled\n", __func__);
++	return 0;
++}
++
++static const struct clk_ops clk_smd_rpm_ops = {
++	.prepare	= clk_smd_rpm_prepare,
++	.unprepare	= clk_smd_rpm_unprepare,
++	.set_rate	= clk_smd_rpm_set_rate,
++	.round_rate	= clk_smd_rpm_round_rate,
++	.recalc_rate	= clk_smd_rpm_recalc_rate,
++};
++
++static const struct clk_ops clk_smd_rpm_branch_ops = {
++	.prepare	= clk_smd_rpm_prepare,
++	.unprepare	= clk_smd_rpm_unprepare,
++	.round_rate	= clk_smd_rpm_round_rate,
++	.recalc_rate	= clk_smd_rpm_recalc_rate,
++};
++
++/* msm8916 */
++DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
++DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
++DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
++DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4);
++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4);
++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
++
++static struct clk_smd_rpm *msm8916_clks[] = {
++	[RPM_SMD_PCNOC_CLK]		= &msm8916_pcnoc_clk,
++	[RPM_SMD_PCNOC_A_CLK]		= &msm8916_pcnoc_a_clk,
++	[RPM_SMD_SNOC_CLK]		= &msm8916_snoc_clk,
++	[RPM_SMD_SNOC_A_CLK]		= &msm8916_snoc_a_clk,
++	[RPM_SMD_BIMC_CLK]		= &msm8916_bimc_clk,
++	[RPM_SMD_BIMC_A_CLK]		= &msm8916_bimc_a_clk,
++	[RPM_SMD_QDSS_CLK]		= &msm8916_qdss_clk,
++	[RPM_SMD_QDSS_A_CLK]		= &msm8916_qdss_a_clk,
++	[RPM_SMD_BB_CLK1]		= &msm8916_bb_clk1,
++	[RPM_SMD_BB_CLK1_A]		= &msm8916_bb_clk1_a,
++	[RPM_SMD_BB_CLK2]		= &msm8916_bb_clk2,
++	[RPM_SMD_BB_CLK2_A]		= &msm8916_bb_clk2_a,
++	[RPM_SMD_RF_CLK1]		= &msm8916_rf_clk1,
++	[RPM_SMD_RF_CLK1_A]		= &msm8916_rf_clk1_a,
++	[RPM_SMD_RF_CLK2]		= &msm8916_rf_clk2,
++	[RPM_SMD_RF_CLK2_A]		= &msm8916_rf_clk2_a,
++	[RPM_SMD_BB_CLK1_PIN]		= &msm8916_bb_clk1_pin,
++	[RPM_SMD_BB_CLK1_A_PIN]		= &msm8916_bb_clk1_a_pin,
++	[RPM_SMD_BB_CLK2_PIN]		= &msm8916_bb_clk2_pin,
++	[RPM_SMD_BB_CLK2_A_PIN]		= &msm8916_bb_clk2_a_pin,
++	[RPM_SMD_RF_CLK1_PIN]		= &msm8916_rf_clk1_pin,
++	[RPM_SMD_RF_CLK1_A_PIN]		= &msm8916_rf_clk1_a_pin,
++	[RPM_SMD_RF_CLK2_PIN]		= &msm8916_rf_clk2_pin,
++	[RPM_SMD_RF_CLK2_A_PIN]		= &msm8916_rf_clk2_a_pin,
++};
++
++static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
++	.clks = msm8916_clks,
++	.num_clks = ARRAY_SIZE(msm8916_clks),
++};
++
++static const struct of_device_id rpm_smd_clk_match_table[] = {
++	{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
++
++static int rpm_smd_clk_probe(struct platform_device *pdev)
++{
++	struct clk_hw **hws;
++	struct rpm_cc *rcc;
++	struct clk_hw_onecell_data *data;
++	int ret;
++	size_t num_clks, i;
++	struct qcom_smd_rpm *rpm;
++	struct clk_smd_rpm **rpm_smd_clks;
++	const struct rpm_smd_clk_desc *desc;
++
++	rpm = dev_get_drvdata(pdev->dev.parent);
++	if (!rpm) {
++		dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
++		return -ENODEV;
++	}
++
++	desc = of_device_get_match_data(&pdev->dev);
++	if (!desc)
++		return -EINVAL;
++
++	rpm_smd_clks = desc->clks;
++	num_clks = desc->num_clks;
++
++	rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
++			   GFP_KERNEL);
++	if (!rcc)
++		return -ENOMEM;
++
++	hws = rcc->hws;
++	data = &rcc->data;
++	data->num = num_clks;
++
++	for (i = 0; i < num_clks; i++) {
++		if (!rpm_smd_clks[i]) {
++			continue;
++		}
++
++		rpm_smd_clks[i]->rpm = rpm;
++
++		ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
++		if (ret)
++			goto err;
++	}
++
++	ret = clk_smd_rpm_enable_scaling(rpm);
++	if (ret)
++		goto err;
++
++	for (i = 0; i < num_clks; i++) {
++		if (!rpm_smd_clks[i]) {
++			data->hws[i] = ERR_PTR(-ENOENT);
++			continue;
++		}
++
++		ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw);
++		if (ret)
++			goto err;
++	}
++
++	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
++				     data);
++	if (ret)
++		goto err;
++
++	return 0;
++err:
++	dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
++	return ret;
++}
++
++static int rpm_smd_clk_remove(struct platform_device *pdev)
++{
++	of_clk_del_provider(pdev->dev.of_node);
++	return 0;
++}
++
++static struct platform_driver rpm_smd_clk_driver = {
++	.driver = {
++		.name = "qcom-clk-smd-rpm",
++		.of_match_table = rpm_smd_clk_match_table,
++	},
++	.probe = rpm_smd_clk_probe,
++	.remove = rpm_smd_clk_remove,
++};
++
++static int __init rpm_smd_clk_init(void)
++{
++	return platform_driver_register(&rpm_smd_clk_driver);
++}
++core_initcall(rpm_smd_clk_init);
++
++static void __exit rpm_smd_clk_exit(void)
++{
++	platform_driver_unregister(&rpm_smd_clk_driver);
++}
++module_exit(rpm_smd_clk_exit);
++
++MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-clk-smd-rpm");
+--- /dev/null
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -0,0 +1,45 @@
++/*
++ * Copyright 2015 Linaro Limited
++ *
++ * 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.
++ */
++
++#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
++#define _DT_BINDINGS_CLK_MSM_RPMCC_H
++
++/* msm8916 */
++#define RPM_SMD_XO_CLK_SRC				0
++#define RPM_SMD_XO_A_CLK_SRC			1
++#define RPM_SMD_PCNOC_CLK				2
++#define RPM_SMD_PCNOC_A_CLK				3
++#define RPM_SMD_SNOC_CLK				4
++#define RPM_SMD_SNOC_A_CLK				5
++#define RPM_SMD_BIMC_CLK				6
++#define RPM_SMD_BIMC_A_CLK				7
++#define RPM_SMD_QDSS_CLK				8
++#define RPM_SMD_QDSS_A_CLK				9
++#define RPM_SMD_BB_CLK1				10
++#define RPM_SMD_BB_CLK1_A				11
++#define RPM_SMD_BB_CLK2				12
++#define RPM_SMD_BB_CLK2_A				13
++#define RPM_SMD_RF_CLK1				14
++#define RPM_SMD_RF_CLK1_A				15
++#define RPM_SMD_RF_CLK2				16
++#define RPM_SMD_RF_CLK2_A				17
++#define RPM_SMD_BB_CLK1_PIN				18
++#define RPM_SMD_BB_CLK1_A_PIN			19
++#define RPM_SMD_BB_CLK2_PIN				20
++#define RPM_SMD_BB_CLK2_A_PIN			21
++#define RPM_SMD_RF_CLK1_PIN				22
++#define RPM_SMD_RF_CLK1_A_PIN			23
++#define RPM_SMD_RF_CLK2_PIN				24
++#define RPM_SMD_RF_CLK2_A_PIN			25
++
++#endif
diff --git a/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch
new file mode 100644
index 0000000..8a38ead
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch
@@ -0,0 +1,586 @@
+From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov at linaro.org>
+Date: Wed, 2 Nov 2016 17:56:57 +0200
+Subject: clk: qcom: Add support for RPM Clocks
+
+This adds initial support for clocks controlled by the Resource
+Power Manager (RPM) processor on some Qualcomm SoCs, which use
+the qcom_rpm driver to communicate with RPM.
+Such platforms are apq8064 and msm8960.
+
+Signed-off-by: Georgi Djakov <georgi.djakov at linaro.org>
+Acked-by: Rob Herring <robh at kernel.org>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+---
+ .../devicetree/bindings/clock/qcom,rpmcc.txt       |   1 +
+ drivers/clk/qcom/Kconfig                           |  13 +
+ drivers/clk/qcom/Makefile                          |   1 +
+ drivers/clk/qcom/clk-rpm.c                         | 489 +++++++++++++++++++++
+ include/dt-bindings/clock/qcom,rpmcc.h             |  24 +
+ 5 files changed, 528 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-rpm.c
+
+--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -11,6 +11,7 @@ Required properties :
+                compatible "qcom,rpmcc" should be also included.
+ 
+ 			"qcom,rpmcc-msm8916", "qcom,rpmcc"
++			"qcom,rpmcc-apq8064", "qcom,rpmcc"
+ 
+ - #clock-cells : shall contain 1
+ 
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM
+ 	select REGMAP_MMIO
+ 	select RESET_CONTROLLER
+ 
++config QCOM_CLK_RPM
++	tristate "RPM based Clock Controller"
++	depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
++	select QCOM_RPMCC
++	help
++	  The RPM (Resource Power Manager) is a dedicated hardware engine for
++	  managing the shared SoC resources in order to keep the lowest power
++	  profile. It communicates with other hardware subsystems via shared
++	  memory and accepts clock requests, aggregates the requests and turns
++	  the clocks on/off or scales them on demand.
++	  Say Y if you want to support the clocks exposed by the RPM on
++	  platforms such as ipq806x, msm8660, msm8960 etc.
++
+ config QCOM_CLK_SMD_RPM
+ 	tristate "RPM over SMD based Clock Controller"
+ 	depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
++obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -0,0 +1,489 @@
++/*
++ * Copyright (c) 2016, Linaro Limited
++ * Copyright (c) 2014, 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-provider.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/mfd/qcom_rpm.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++
++#include <dt-bindings/mfd/qcom-rpm.h>
++#include <dt-bindings/clock/qcom,rpmcc.h>
++
++#define QCOM_RPM_MISC_CLK_TYPE				0x306b6c63
++#define QCOM_RPM_SCALING_ENABLE_ID			0x2
++
++#define DEFINE_CLK_RPM(_platform, _name, _active, r_id)			      \
++	static struct clk_rpm _platform##_##_active;			      \
++	static struct clk_rpm _platform##_##_name = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.peer = &_platform##_##_active,				      \
++		.rate = INT_MAX,					      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_ops,				      \
++			.name = #_name,					      \
++			.parent_names = (const char *[]){ "pxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	};								      \
++	static struct clk_rpm _platform##_##_active = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.peer = &_platform##_##_name,				      \
++		.active_only = true,					      \
++		.rate = INT_MAX,					      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_ops,				      \
++			.name = #_active,				      \
++			.parent_names = (const char *[]){ "pxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	}
++
++#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r)	      \
++	static struct clk_rpm _platform##_##_active;			      \
++	static struct clk_rpm _platform##_##_name = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.active_only = true,					      \
++		.peer = &_platform##_##_active,				      \
++		.rate = (r),						      \
++		.branch = true,						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_branch_ops,			      \
++			.name = #_name,					      \
++			.parent_names = (const char *[]){ "pxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	};								      \
++	static struct clk_rpm _platform##_##_active = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.peer = &_platform##_##_name,				      \
++		.rate = (r),						      \
++		.branch = true,						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_branch_ops,			      \
++			.name = #_active,				      \
++			.parent_names = (const char *[]){ "pxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	}
++
++#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r)	      \
++	static struct clk_rpm _platform##_##_active;			      \
++	static struct clk_rpm _platform##_##_name = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.peer = &_platform##_##_active,				      \
++		.rate = (r),						      \
++		.branch = true,						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_branch_ops,			      \
++			.name = #_name,					      \
++			.parent_names = (const char *[]){ "cxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	};								      \
++	static struct clk_rpm _platform##_##_active = {			      \
++		.rpm_clk_id = (r_id),					      \
++		.active_only = true,					      \
++		.peer = &_platform##_##_name,				      \
++		.rate = (r),						      \
++		.branch = true,						      \
++		.hw.init = &(struct clk_init_data){			      \
++			.ops = &clk_rpm_branch_ops,			      \
++			.name = #_active,				      \
++			.parent_names = (const char *[]){ "cxo_board" },      \
++			.num_parents = 1,				      \
++		},							      \
++	}
++
++#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
++
++struct clk_rpm {
++	const int rpm_clk_id;
++	const bool active_only;
++	unsigned long rate;
++	bool enabled;
++	bool branch;
++	struct clk_rpm *peer;
++	struct clk_hw hw;
++	struct qcom_rpm *rpm;
++};
++
++struct rpm_cc {
++	struct qcom_rpm *rpm;
++	struct clk_hw_onecell_data data;
++	struct clk_hw *hws[];
++};
++
++struct rpm_clk_desc {
++	struct clk_rpm **clks;
++	size_t num_clks;
++};
++
++static DEFINE_MUTEX(rpm_clk_lock);
++
++static int clk_rpm_handoff(struct clk_rpm *r)
++{
++	int ret;
++	u32 value = INT_MAX;
++
++	ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
++			     r->rpm_clk_id, &value, 1);
++	if (ret)
++		return ret;
++	ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
++			     r->rpm_clk_id, &value, 1);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate)
++{
++	u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
++
++	return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
++			      r->rpm_clk_id, &value, 1);
++}
++
++static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate)
++{
++	u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
++
++	return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
++			      r->rpm_clk_id, &value, 1);
++}
++
++static void to_active_sleep(struct clk_rpm *r, unsigned long rate,
++			    unsigned long *active, unsigned long *sleep)
++{
++	*active = rate;
++
++	/*
++	 * Active-only clocks don't care what the rate is during sleep. So,
++	 * they vote for zero.
++	 */
++	if (r->active_only)
++		*sleep = 0;
++	else
++		*sleep = *active;
++}
++
++static int clk_rpm_prepare(struct clk_hw *hw)
++{
++	struct clk_rpm *r = to_clk_rpm(hw);
++	struct clk_rpm *peer = r->peer;
++	unsigned long this_rate = 0, this_sleep_rate = 0;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	unsigned long active_rate, sleep_rate;
++	int ret = 0;
++
++	mutex_lock(&rpm_clk_lock);
++
++	/* Don't send requests to the RPM if the rate has not been set. */
++	if (!r->rate)
++		goto out;
++
++	to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate,
++				&peer_rate, &peer_sleep_rate);
++
++	active_rate = max(this_rate, peer_rate);
++
++	if (r->branch)
++		active_rate = !!active_rate;
++
++	ret = clk_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++	if (r->branch)
++		sleep_rate = !!sleep_rate;
++
++	ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		/* Undo the active set vote and restore it */
++		ret = clk_rpm_set_rate_active(r, peer_rate);
++
++out:
++	if (!ret)
++		r->enabled = true;
++
++	mutex_unlock(&rpm_clk_lock);
++
++	return ret;
++}
++
++static void clk_rpm_unprepare(struct clk_hw *hw)
++{
++	struct clk_rpm *r = to_clk_rpm(hw);
++	struct clk_rpm *peer = r->peer;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	unsigned long active_rate, sleep_rate;
++	int ret;
++
++	mutex_lock(&rpm_clk_lock);
++
++	if (!r->rate)
++		goto out;
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate, &peer_rate,
++				&peer_sleep_rate);
++
++	active_rate = r->branch ? !!peer_rate : peer_rate;
++	ret = clk_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
++	ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		goto out;
++
++	r->enabled = false;
++
++out:
++	mutex_unlock(&rpm_clk_lock);
++}
++
++static int clk_rpm_set_rate(struct clk_hw *hw,
++			    unsigned long rate, unsigned long parent_rate)
++{
++	struct clk_rpm *r = to_clk_rpm(hw);
++	struct clk_rpm *peer = r->peer;
++	unsigned long active_rate, sleep_rate;
++	unsigned long this_rate = 0, this_sleep_rate = 0;
++	unsigned long peer_rate = 0, peer_sleep_rate = 0;
++	int ret = 0;
++
++	mutex_lock(&rpm_clk_lock);
++
++	if (!r->enabled)
++		goto out;
++
++	to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
++
++	/* Take peer clock's rate into account only if it's enabled. */
++	if (peer->enabled)
++		to_active_sleep(peer, peer->rate,
++				&peer_rate, &peer_sleep_rate);
++
++	active_rate = max(this_rate, peer_rate);
++	ret = clk_rpm_set_rate_active(r, active_rate);
++	if (ret)
++		goto out;
++
++	sleep_rate = max(this_sleep_rate, peer_sleep_rate);
++	ret = clk_rpm_set_rate_sleep(r, sleep_rate);
++	if (ret)
++		goto out;
++
++	r->rate = rate;
++
++out:
++	mutex_unlock(&rpm_clk_lock);
++
++	return ret;
++}
++
++static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
++			       unsigned long *parent_rate)
++{
++	/*
++	 * RPM handles rate rounding and we don't have a way to
++	 * know what the rate will be, so just return whatever
++	 * rate is requested.
++	 */
++	return rate;
++}
++
++static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
++					 unsigned long parent_rate)
++{
++	struct clk_rpm *r = to_clk_rpm(hw);
++
++	/*
++	 * RPM handles rate rounding and we don't have a way to
++	 * know what the rate will be, so just return whatever
++	 * rate was set.
++	 */
++	return r->rate;
++}
++
++static const struct clk_ops clk_rpm_ops = {
++	.prepare	= clk_rpm_prepare,
++	.unprepare	= clk_rpm_unprepare,
++	.set_rate	= clk_rpm_set_rate,
++	.round_rate	= clk_rpm_round_rate,
++	.recalc_rate	= clk_rpm_recalc_rate,
++};
++
++static const struct clk_ops clk_rpm_branch_ops = {
++	.prepare	= clk_rpm_prepare,
++	.unprepare	= clk_rpm_unprepare,
++	.round_rate	= clk_rpm_round_rate,
++	.recalc_rate	= clk_rpm_recalc_rate,
++};
++
++/* apq8064 */
++DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
++DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
++DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
++DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
++DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
++DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
++
++static struct clk_rpm *apq8064_clks[] = {
++	[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
++	[RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
++	[RPM_CFPB_CLK] = &apq8064_cfpb_clk,
++	[RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk,
++	[RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
++	[RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk,
++	[RPM_EBI1_CLK] = &apq8064_ebi1_clk,
++	[RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk,
++	[RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
++	[RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk,
++	[RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
++	[RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk,
++	[RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
++	[RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk,
++	[RPM_SFPB_CLK] = &apq8064_sfpb_clk,
++	[RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
++	[RPM_QDSS_CLK] = &apq8064_qdss_clk,
++	[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
++};
++
++static const struct rpm_clk_desc rpm_clk_apq8064 = {
++	.clks = apq8064_clks,
++	.num_clks = ARRAY_SIZE(apq8064_clks),
++};
++
++static const struct of_device_id rpm_clk_match_table[] = {
++	{ .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
++
++static int rpm_clk_probe(struct platform_device *pdev)
++{
++	struct clk_hw **hws;
++	struct rpm_cc *rcc;
++	struct clk_hw_onecell_data *data;
++	int ret;
++	size_t num_clks, i;
++	struct qcom_rpm *rpm;
++	struct clk_rpm **rpm_clks;
++	const struct rpm_clk_desc *desc;
++
++	rpm = dev_get_drvdata(pdev->dev.parent);
++	if (!rpm) {
++		dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
++		return -ENODEV;
++	}
++
++	desc = of_device_get_match_data(&pdev->dev);
++	if (!desc)
++		return -EINVAL;
++
++	rpm_clks = desc->clks;
++	num_clks = desc->num_clks;
++
++	rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
++			   GFP_KERNEL);
++	if (!rcc)
++		return -ENOMEM;
++
++	hws = rcc->hws;
++	data = &rcc->data;
++	data->num = num_clks;
++
++	for (i = 0; i < num_clks; i++) {
++		if (!rpm_clks[i])
++			continue;
++
++		rpm_clks[i]->rpm = rpm;
++
++		ret = clk_rpm_handoff(rpm_clks[i]);
++		if (ret)
++			goto err;
++	}
++
++	for (i = 0; i < num_clks; i++) {
++		if (!rpm_clks[i]) {
++			data->hws[i] = ERR_PTR(-ENOENT);
++			continue;
++		}
++
++		ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
++		if (ret)
++			goto err;
++	}
++
++	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
++				     data);
++	if (ret)
++		goto err;
++
++	return 0;
++err:
++	dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret);
++	return ret;
++}
++
++static int rpm_clk_remove(struct platform_device *pdev)
++{
++	of_clk_del_provider(pdev->dev.of_node);
++	return 0;
++}
++
++static struct platform_driver rpm_clk_driver = {
++	.driver = {
++		.name = "qcom-clk-rpm",
++		.of_match_table = rpm_clk_match_table,
++	},
++	.probe = rpm_clk_probe,
++	.remove = rpm_clk_remove,
++};
++
++static int __init rpm_clk_init(void)
++{
++	return platform_driver_register(&rpm_clk_driver);
++}
++core_initcall(rpm_clk_init);
++
++static void __exit rpm_clk_exit(void)
++{
++	platform_driver_unregister(&rpm_clk_driver);
++}
++module_exit(rpm_clk_exit);
++
++MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-clk-rpm");
+--- a/include/dt-bindings/clock/qcom,rpmcc.h
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -14,6 +14,30 @@
+ #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
+ #define _DT_BINDINGS_CLK_MSM_RPMCC_H
+ 
++/* apq8064 */
++#define RPM_PXO_CLK				0
++#define RPM_PXO_A_CLK				1
++#define RPM_CXO_CLK				2
++#define RPM_CXO_A_CLK				3
++#define RPM_APPS_FABRIC_CLK			4
++#define RPM_APPS_FABRIC_A_CLK			5
++#define RPM_CFPB_CLK				6
++#define RPM_CFPB_A_CLK				7
++#define RPM_QDSS_CLK				8
++#define RPM_QDSS_A_CLK				9
++#define RPM_DAYTONA_FABRIC_CLK			10
++#define RPM_DAYTONA_FABRIC_A_CLK		11
++#define RPM_EBI1_CLK				12
++#define RPM_EBI1_A_CLK				13
++#define RPM_MM_FABRIC_CLK			14
++#define RPM_MM_FABRIC_A_CLK			15
++#define RPM_MMFPB_CLK				16
++#define RPM_MMFPB_A_CLK				17
++#define RPM_SYS_FABRIC_CLK			18
++#define RPM_SYS_FABRIC_A_CLK			19
++#define RPM_SFPB_CLK				20
++#define RPM_SFPB_A_CLK				21
++
+ /* msm8916 */
+ #define RPM_SMD_XO_CLK_SRC				0
+ #define RPM_SMD_XO_A_CLK_SRC			1
diff --git a/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch
new file mode 100644
index 0000000..e5c1870
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch
@@ -0,0 +1,94 @@
+From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov at linaro.org>
+Date: Wed, 23 Nov 2016 16:52:49 +0200
+Subject: clk: qcom: clk-rpm: Fix clk_hw references
+
+Fix the clk_hw references to the actual clocks and add a xlate function
+to return the hw pointers from the already existing static array.
+
+Reported-by: Michael Scott <michael.scott at linaro.org>
+Signed-off-by: Georgi Djakov <georgi.djakov at linaro.org>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+---
+ drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++--------------
+ 1 file changed, 22 insertions(+), 14 deletions(-)
+
+--- a/drivers/clk/qcom/clk-rpm.c
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -127,8 +127,8 @@ struct clk_rpm {
+ 
+ struct rpm_cc {
+ 	struct qcom_rpm *rpm;
+-	struct clk_hw_onecell_data data;
+-	struct clk_hw *hws[];
++	struct clk_rpm **clks;
++	size_t num_clks;
+ };
+ 
+ struct rpm_clk_desc {
+@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk
+ };
+ MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
+ 
++static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec,
++					  void *data)
++{
++	struct rpm_cc *rcc = data;
++	unsigned int idx = clkspec->args[0];
++
++	if (idx >= rcc->num_clks) {
++		pr_err("%s: invalid index %u\n", __func__, idx);
++		return ERR_PTR(-EINVAL);
++	}
++
++	return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT);
++}
++
+ static int rpm_clk_probe(struct platform_device *pdev)
+ {
+-	struct clk_hw **hws;
+ 	struct rpm_cc *rcc;
+-	struct clk_hw_onecell_data *data;
+ 	int ret;
+ 	size_t num_clks, i;
+ 	struct qcom_rpm *rpm;
+@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform
+ 	rpm_clks = desc->clks;
+ 	num_clks = desc->num_clks;
+ 
+-	rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
+-			   GFP_KERNEL);
++	rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL);
+ 	if (!rcc)
+ 		return -ENOMEM;
+ 
+-	hws = rcc->hws;
+-	data = &rcc->data;
+-	data->num = num_clks;
++	rcc->clks = rpm_clks;
++	rcc->num_clks = num_clks;
+ 
+ 	for (i = 0; i < num_clks; i++) {
+ 		if (!rpm_clks[i])
+@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform
+ 	}
+ 
+ 	for (i = 0; i < num_clks; i++) {
+-		if (!rpm_clks[i]) {
+-			data->hws[i] = ERR_PTR(-ENOENT);
++		if (!rpm_clks[i])
+ 			continue;
+-		}
+ 
+ 		ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
+ 		if (ret)
+ 			goto err;
+ 	}
+ 
+-	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+-				     data);
++	ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get,
++				     rcc);
+ 	if (ret)
+ 		goto err;
+ 
diff --git a/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch b/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch
new file mode 100644
index 0000000..d46e97b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch
@@ -0,0 +1,44 @@
+
+In commit "regulator: qcom: Rework to single platform device" the smb208 regulator
+used in IPQ8064 was left out.
+
+Add it to that new framework and update Docs accordingly.
+
+Signed-off-by: Adrian Panella <ianchi74 at outlook.com>
+
+--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -171,6 +171,9 @@ pm8018:
+ 	s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
+ 	l12, l14, lvs1
+ 
++smb208:
++	s1a, s1b, s2a, s2b
++
+ The content of each sub-node is defined by the standard binding for regulators -
+ see regulator.txt - with additional custom properties described below:
+ 
+--- a/drivers/regulator/qcom_rpm-regulator.c
++++ b/drivers/regulator/qcom_rpm-regulator.c
+@@ -933,12 +933,21 @@ static const struct rpm_regulator_data r
+ 	{ }
+ };
+ 
++static const struct rpm_regulator_data rpm_smb208_regulators[] = {
++	{ "s1a",  QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" },
++	{ "s1b",  QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" },
++	{ "s2a",  QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" },
++	{ "s2b",  QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" },
++	{ }
++};
++
+ static const struct of_device_id rpm_of_match[] = {
+ 	{ .compatible = "qcom,rpm-pm8018-regulators",
+ 		.data = &rpm_pm8018_regulators },
+ 	{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
+ 	{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
+ 	{ .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
++	{ .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators },
+ 	{ }
+ };
+ MODULE_DEVICE_TABLE(of, rpm_of_match);
diff --git a/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch
new file mode 100644
index 0000000..7a7cff3
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch
@@ -0,0 +1,25 @@
+From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001
+From: Abhishek Sahu <absahu at codeaurora.org>
+Date: Wed, 18 Nov 2015 12:38:56 +0530
+Subject: ipq806x: gcc: Added the enable regs and mask for PRNG
+
+kernel got hanged while reading from /dev/hwrng at the
+time of PRNG clock enable
+
+Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172
+Signed-off-by: Abhishek Sahu <absahu at codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -1153,6 +1153,8 @@ static struct clk_rcg prng_src = {
+ 		.parent_map = gcc_pxo_pll8_map,
+ 	},
+ 	.clkr = {
++		.enable_reg = 0x2e80,
++		.enable_mask = BIT(11),
+ 		.hw.init = &(struct clk_init_data){
+ 			.name = "prng_src",
+ 			.parent_names = gcc_pxo_pll8,
diff --git a/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch
new file mode 100644
index 0000000..bb9dd87
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch
@@ -0,0 +1,81 @@
+--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+@@ -12,6 +12,7 @@ Required properties :
+ 
+ 			"qcom,rpmcc-msm8916", "qcom,rpmcc"
+ 			"qcom,rpmcc-apq8064", "qcom,rpmcc"
++			"qcom,rpmcc-ipq806x", "qcom,rpmcc"
+ 
+ - #clock-cells : shall contain 1
+ 
+--- a/drivers/clk/qcom/clk-rpm.c
++++ b/drivers/clk/qcom/clk-rpm.c
+@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a
+ DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
+ DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
+ 
++/* ipq806x */
++DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
++DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
++DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
++DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
++DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK);
++DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK);
++
+ static struct clk_rpm *apq8064_clks[] = {
+ 	[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
+ 	[RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
+@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] =
+ 	[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
+ };
+ 
++static struct clk_rpm *ipq806x_clks[] = {
++	[RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk,
++	[RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk,
++	[RPM_CFPB_CLK] = &ipq806x_cfpb_clk,
++	[RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk,
++	[RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk,
++	[RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk,
++	[RPM_EBI1_CLK] = &ipq806x_ebi1_clk,
++	[RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk,
++	[RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk,
++	[RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk,
++	[RPM_SFPB_CLK] = &ipq806x_sfpb_clk,
++	[RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk,
++	[RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk,
++	[RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk,
++	[RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk,
++	[RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk,
++};
++
+ static const struct rpm_clk_desc rpm_clk_apq8064 = {
+ 	.clks = apq8064_clks,
+ 	.num_clks = ARRAY_SIZE(apq8064_clks),
+ };
+ 
++static const struct rpm_clk_desc rpm_clk_ipq806x = {
++	.clks = ipq806x_clks,
++	.num_clks = ARRAY_SIZE(ipq806x_clks),
++};
++
+ static const struct of_device_id rpm_clk_match_table[] = {
+ 	{ .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
++	{ .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x },
+ 	{ }
+ };
+ MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
+--- a/include/dt-bindings/clock/qcom,rpmcc.h
++++ b/include/dt-bindings/clock/qcom,rpmcc.h
+@@ -37,6 +37,10 @@
+ #define RPM_SYS_FABRIC_A_CLK			19
+ #define RPM_SFPB_CLK				20
+ #define RPM_SFPB_A_CLK				21
++#define RPM_NSS_FABRIC_0_CLK				22
++#define RPM_NSS_FABRIC_0_A_CLK				23
++#define RPM_NSS_FABRIC_1_CLK				24
++#define RPM_NSS_FABRIC_1_A_CLK				25
+ 
+ /* msm8916 */
+ #define RPM_SMD_XO_CLK_SRC				0
diff --git a/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch
new file mode 100644
index 0000000..6591dbd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -65,7 +65,7 @@ KBUILD_CFLAGS	+= $(call cc-option,-fno-i
+ # macro, but instead defines a whole series of macros which makes
+ # testing for a specific architecture or later rather impossible.
+ arch-$(CONFIG_CPU_32v7M)	=-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m
+-arch-$(CONFIG_CPU_32v7)		=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a)
++arch-$(CONFIG_CPU_32v7)		=-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15
+ arch-$(CONFIG_CPU_32v6)		=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6)
+ # Only override the compiler option if ARMv6. The ARMv6K extensions are
+ # always available in ARMv7
diff --git a/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch
new file mode 100644
index 0000000..3434864
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch
@@ -0,0 +1,166 @@
+From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 24/53] GPIO: add named gpio exports
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/gpio/gpiolib-of.c     |   68 +++++++++++++++++++++++++++++++++++++++++
+ drivers/gpio/gpiolib-sysfs.c  |   10 +++++-
+ include/asm-generic/gpio.h    |    6 ++++
+ include/linux/gpio/consumer.h |    8 +++++
+ 4 files changed, 91 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpio/gpiolib-of.c
++++ b/drivers/gpio/gpiolib-of.c
+@@ -23,6 +23,8 @@
+ #include <linux/pinctrl/pinctrl.h>
+ #include <linux/slab.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+ 
+ #include "gpiolib.h"
+ 
+@@ -538,3 +540,69 @@ void of_gpiochip_remove(struct gpio_chip
+ 	gpiochip_remove_pin_ranges(chip);
+ 	of_node_put(chip->of_node);
+ }
++
++static struct of_device_id gpio_export_ids[] = {
++	{ .compatible = "gpio-export" },
++	{ /* sentinel */ }
++};
++
++static int __init of_gpio_export_probe(struct platform_device *pdev)
++{
++	struct device_node *np = pdev->dev.of_node;
++	struct device_node *cnp;
++	u32 val;
++	int nb = 0;
++
++	for_each_child_of_node(np, cnp) {
++		const char *name = NULL;
++		int gpio;
++		bool dmc;
++		int max_gpio = 1;
++		int i;
++
++		of_property_read_string(cnp, "gpio-export,name", &name);
++
++		if (!name)
++			max_gpio = of_gpio_count(cnp);
++
++		for (i = 0; i < max_gpio; i++) {
++			unsigned flags = 0;
++			enum of_gpio_flags of_flags;
++
++			gpio = of_get_gpio_flags(cnp, i, &of_flags);
++
++			if (of_flags == OF_GPIO_ACTIVE_LOW)
++				flags |= GPIOF_ACTIVE_LOW;
++
++			if (!of_property_read_u32(cnp, "gpio-export,output", &val))
++				flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++			else
++				flags |= GPIOF_IN;
++
++			if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
++				continue;
++
++			dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
++			gpio_export_with_name(gpio, dmc, name);
++			nb++;
++		}
++	}
++
++	dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
++
++	return 0;
++}
++
++static struct platform_driver gpio_export_driver = {
++	.driver		= {
++		.name		= "gpio-export",
++		.owner	= THIS_MODULE,
++		.of_match_table	= of_match_ptr(gpio_export_ids),
++	},
++};
++
++static int __init of_gpio_export_init(void)
++{
++	return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe);
++}
++device_initcall(of_gpio_export_init);
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -544,7 +544,7 @@ static struct class gpio_class = {
+  *
+  * Returns zero on success, else an error.
+  */
+-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
+ {
+ 	struct gpio_chip	*chip;
+ 	struct gpio_device	*gdev;
+@@ -606,6 +606,8 @@ int gpiod_export(struct gpio_desc *desc,
+ 	offset = gpio_chip_hwgpio(desc);
+ 	if (chip->names && chip->names[offset])
+ 		ioname = chip->names[offset];
++	if (name)
++		ioname = name;
+ 
+ 	dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ 					MKDEV(0, 0), data, gpio_groups,
+@@ -627,6 +629,12 @@ err_unlock:
+ 	gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ 	return status;
+ }
++EXPORT_SYMBOL_GPL(__gpiod_export);
++
++int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++{
++	return __gpiod_export(desc, direction_may_change, NULL);
++}
+ EXPORT_SYMBOL_GPL(gpiod_export);
+ 
+ static int match_export(struct device *dev, const void *desc)
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -126,6 +126,12 @@ static inline int gpio_export(unsigned g
+ 	return gpiod_export(gpio_to_desc(gpio), direction_may_change);
+ }
+ 
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
++{
++	return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
++}
++
+ static inline int gpio_export_link(struct device *dev, const char *name,
+ 				   unsigned gpio)
+ {
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get
+ 
+ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
+ 
++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
+ int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+ int gpiod_export_link(struct device *dev, const char *name,
+ 		      struct gpio_desc *desc);
+@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de
+ 
+ #else  /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
+ 
++static inline int _gpiod_export(struct gpio_desc *desc,
++			       bool direction_may_change,
++			       const char *name)
++{
++	return -ENOSYS;
++}
++
+ static inline int gpiod_export(struct gpio_desc *desc,
+ 			       bool direction_may_change)
+ {
diff --git a/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch b/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch
new file mode 100644
index 0000000..36490a2
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch
@@ -0,0 +1,185 @@
+Author: Adrian Panella <ianchi74 at outlook.com>
+Date:   Fri Jun 10 19:10:15 2016 -0500
+
+generic: Mangle bootloader's kernel arguments
+
+The command-line arguments provided by the boot loader will be
+appended to a new device tree property: bootloader-args.
+If there is a property "append-rootblock" in DT under /chosen
+and a root= option in bootloaders command line it will be parsed
+and added to DT bootargs with the form: <append-rootblock>XX.
+Only command line ATAG will be processed, the rest of the ATAGs
+sent by bootloader will be ignored.
+This is usefull in dual boot systems, to get the current root partition
+without afecting the rest of the system.
+
+
+Signed-off-by: Adrian Panella <ianchi74 at outlook.com>
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1949,6 +1949,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN
+ 	  The command-line arguments provided by the boot loader will be
+ 	  appended to the the device tree bootargs property.
+ 
++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++	bool "Append rootblock parsing bootloader's kernel arguments"
++	help
++	  The command-line arguments provided by the boot loader will be
++	  appended to a new device tree property: bootloader-args.
++	  If there is a property "append-rootblock" in DT under /chosen 
++	  and a root= option in bootloaders command line it will be parsed 
++	  and added to DT bootargs with the form: <append-rootblock>XX.
++	  Only command line ATAG will be processed, the rest of the ATAGs
++	  sent by bootloader will be ignored.
++
+ endchoice
+ 
+ config CMDLINE
+--- a/arch/arm/boot/compressed/atags_to_fdt.c
++++ b/arch/arm/boot/compressed/atags_to_fdt.c
+@@ -3,6 +3,8 @@
+ 
+ #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
+ #define do_extend_cmdline 1
++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#define do_extend_cmdline 1
+ #else
+ #define do_extend_cmdline 0
+ #endif
+@@ -66,6 +68,59 @@ static uint32_t get_cell_size(const void
+ 	return cell_size;
+ }
+ 
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++
++static char *append_rootblock(char *dest, const char *str, int len, void *fdt)
++{
++	char *ptr, *end;
++	char *root="root=";
++	int i, l;
++	const char *rootblock;
++
++	//ARM doesn't have __HAVE_ARCH_STRSTR, so search manually
++	ptr = str - 1;
++
++	do {
++		//first find an 'r' at the begining or after a space
++		do {
++			ptr++;
++			ptr = strchr(ptr, 'r');
++			if(!ptr) return dest;
++
++		} while (ptr != str && *(ptr-1) != ' ');
++
++		//then check for the rest
++		for(i = 1; i <= 4; i++)
++			if(*(ptr+i) != *(root+i)) break;
++
++	} while (i != 5);
++
++	end = strchr(ptr, ' ');
++	end = end ? (end - 1) : (strchr(ptr, 0) - 1);
++
++	//find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX )
++	for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++);
++	ptr = end + 1;
++
++	/* if append-rootblock property is set use it to append to command line */
++	rootblock = getprop(fdt, "/chosen", "append-rootblock", &l);
++	if(rootblock != NULL) {
++		if(*dest != ' ') {
++			*dest = ' ';
++			dest++;
++			len++;
++		}
++		if (len + l + i <= COMMAND_LINE_SIZE) {
++			memcpy(dest, rootblock, l);
++			dest += l - 1;
++			memcpy(dest, ptr, i);
++			dest += i;
++		}
++	}
++	return dest;
++}
++#endif
++
+ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
+ {
+ 	char cmdline[COMMAND_LINE_SIZE];
+@@ -85,12 +140,21 @@ static void merge_fdt_bootargs(void *fdt
+ 
+ 	/* and append the ATAG_CMDLINE */
+ 	if (fdt_cmdline) {
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++		//save original bootloader args
++		//and append ubi.mtd with root partition number to current cmdline
++		setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline);
++		ptr = append_rootblock(ptr, fdt_cmdline, len, fdt);
++
++#else
+ 		len = strlen(fdt_cmdline);
+ 		if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
+ 			*ptr++ = ' ';
+ 			memcpy(ptr, fdt_cmdline, len);
+ 			ptr += len;
+ 		}
++#endif
+ 	}
+ 	*ptr = '\0';
+ 
+@@ -147,7 +211,9 @@ int atags_to_fdt(void *atag_list, void *
+ 			else
+ 				setprop_string(fdt, "/chosen", "bootargs",
+ 					       atag->u.cmdline.cmdline);
+-		} else if (atag->hdr.tag == ATAG_MEM) {
++		}
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++		else if (atag->hdr.tag == ATAG_MEM) {
+ 			if (memcount >= sizeof(mem_reg_property)/4)
+ 				continue;
+ 			if (!atag->u.mem.size)
+@@ -186,6 +252,10 @@ int atags_to_fdt(void *atag_list, void *
+ 		setprop(fdt, "/memory", "reg", mem_reg_property,
+ 			4 * memcount * memsize);
+ 	}
++#else
++
++	}
++#endif
+ 
+ 	return fdt_pack(fdt);
+ }
+--- a/init/main.c
++++ b/init/main.c
+@@ -88,6 +88,10 @@
+ #include <asm/sections.h>
+ #include <asm/cacheflush.h>
+ 
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#include <linux/of.h>
++#endif
++
+ static int kernel_init(void *);
+ 
+ extern void init_IRQ(void);
+@@ -538,6 +542,18 @@ asmlinkage __visible void __init start_k
+ 	page_alloc_init();
+ 
+ 	pr_notice("Kernel command line: %s\n", boot_command_line);
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++	//Show bootloader's original command line for reference
++	if(of_chosen) {
++		const char *prop = of_get_property(of_chosen, "bootloader-args", NULL);
++		if(prop)
++			pr_notice("Bootloader command line (ignored): %s\n", prop);
++		else
++			pr_notice("Bootloader command line not present\n");
++	}
++#endif
++
+ 	parse_early_param();
+ 	after_dashes = parse_args("Booting kernel",
+ 				  static_command_line, __start___param,
diff --git a/target/linux/ipq806x/patches-4.9/999-dts.patch b/target/linux/ipq806x/patches-4.9/999-dts.patch
new file mode 100644
index 0000000..3977d0b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/999-dts.patch
@@ -0,0 +1,5046 @@
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -573,92 +573,61 @@
+ 	omap4-var-stk-om44.dtb
+ dtb-$(CONFIG_SOC_AM43XX) += \
+ 	am43x-epos-evm.dtb \
+-	am437x-cm-t43.dtb \
+-	am437x-gp-evm.dtb \
++	am437x-sk-evm.dtb \
+ 	am437x-idk-evm.dtb \
+-	am437x-sbc-t43.dtb \
+-	am437x-sk-evm.dtb
++	am437x-gp-evm.dtb
+ dtb-$(CONFIG_SOC_OMAP5) += \
+ 	omap5-cm-t54.dtb \
+ 	omap5-igep0050.dtb \
+ 	omap5-sbc-t54.dtb \
+ 	omap5-uevm.dtb
+ dtb-$(CONFIG_SOC_DRA7XX) += \
+-	am57xx-beagle-x15.dtb \
+-	am57xx-beagle-x15-revb1.dtb \
+-	am57xx-cl-som-am57x.dtb \
+-	am57xx-sbc-am57x.dtb \
+-	am572x-idk.dtb \
+ 	dra7-evm.dtb \
+-	dra72-evm.dtb \
+-	dra72-evm-revc.dtb
++	am57xx-beagle-x15.dtb \
++	dra72-evm.dtb
+ dtb-$(CONFIG_ARCH_ORION5X) += \
+-	orion5x-kuroboxpro.dtb \
+ 	orion5x-lacie-d2-network.dtb \
+ 	orion5x-lacie-ethernet-disk-mini-v2.dtb \
+-	orion5x-linkstation-lsgl.dtb \
+ 	orion5x-linkstation-lswtgl.dtb \
+ 	orion5x-lswsgl.dtb \
+ 	orion5x-maxtor-shared-storage-2.dtb \
+-	orion5x-netgear-wnr854t.dtb \
+ 	orion5x-rd88f5182-nas.dtb
+ dtb-$(CONFIG_ARCH_PRIMA2) += \
+ 	prima2-evb.dtb
+-dtb-$(CONFIG_ARCH_OXNAS) += \
+-	wd-mbwe.dtb
+ dtb-$(CONFIG_ARCH_QCOM) += \
+-	qcom-apq8060-dragonboard.dtb \
+-	qcom-apq8064-arrow-sd-600eval.dtb \
+ 	qcom-apq8064-cm-qs600.dtb \
+ 	qcom-apq8064-ifc6410.dtb \
+-	qcom-apq8064-sony-xperia-yuga.dtb \
+-	qcom-apq8064-asus-nexus7-flo.dtb \
+ 	qcom-apq8074-dragonboard.dtb \
+ 	qcom-apq8084-ifc6540.dtb \
+ 	qcom-apq8084-mtp.dtb \
+-	qcom-ipq4019-ap.dk01.1-c1.dtb \
+-	qcom-ipq4019-ap.dk04.1-c1.dtb \
+ 	qcom-ipq8064-ap148.dtb \
++	qcom-ipq8064-c2600.dtb \
++	qcom-ipq8064-d7800.dtb \
++	qcom-ipq8064-db149.dtb \
++	qcom-ipq8064-ea8500.dtb \
++	qcom-ipq8064-r7500.dtb \
++	qcom-ipq8064-r7500v2.dtb \
++	qcom-ipq8065-nbg6817.dtb \
++	qcom-ipq8065-r7800.dtb \
+ 	qcom-msm8660-surf.dtb \
+ 	qcom-msm8960-cdp.dtb \
+-	qcom-msm8974-lge-nexus5-hammerhead.dtb \
+ 	qcom-msm8974-sony-xperia-honami.dtb
+ dtb-$(CONFIG_ARCH_REALVIEW) += \
+-	arm-realview-pb1176.dtb \
+-	arm-realview-pb11mp.dtb \
+-	arm-realview-eb.dtb \
+-	arm-realview-eb-bbrevd.dtb \
+-	arm-realview-eb-11mp.dtb \
+-	arm-realview-eb-11mp-bbrevd.dtb \
+-	arm-realview-eb-11mp-ctrevb.dtb \
+-	arm-realview-eb-11mp-bbrevd-ctrevb.dtb \
+-	arm-realview-eb-a9mp.dtb \
+-	arm-realview-eb-a9mp-bbrevd.dtb \
+-	arm-realview-pba8.dtb \
+-	arm-realview-pbx-a9.dtb
++	arm-realview-pb1176.dtb
+ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
+-	rk3036-evb.dtb \
+-	rk3036-kylin.dtb \
+ 	rk3066a-bqcurie2.dtb \
+ 	rk3066a-marsboard.dtb \
+ 	rk3066a-rayeager.dtb \
+ 	rk3188-radxarock.dtb \
+-	rk3228-evb.dtb \
+-	rk3229-evb.dtb \
+ 	rk3288-evb-act8846.dtb \
+ 	rk3288-evb-rk808.dtb \
+-	rk3288-fennec.dtb \
+ 	rk3288-firefly-beta.dtb \
+ 	rk3288-firefly.dtb \
+-	rk3288-firefly-reload.dtb \
+-	rk3288-miqi.dtb \
+ 	rk3288-popmetal.dtb \
+ 	rk3288-r89.dtb \
+ 	rk3288-rock2-square.dtb \
+-	rk3288-veyron-brain.dtb \
+ 	rk3288-veyron-jaq.dtb \
+ 	rk3288-veyron-jerry.dtb \
+-	rk3288-veyron-mickey.dtb \
+ 	rk3288-veyron-minnie.dtb \
+ 	rk3288-veyron-pinky.dtb \
+ 	rk3288-veyron-speedy.dtb
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,12 +4,9 @@
+ 	model = "Qualcomm IPQ8064/AP148";
+ 	compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
+ 
+-	aliases {
+-		serial0 = &gsbi4_serial;
+-	};
+-
+-	chosen {
+-		stdout-path = "serial0:115200n8";
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
+ 	};
+ 
+ 	reserved-memory {
+@@ -22,6 +19,15 @@
+ 		};
+ 	};
+ 
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++	};
++
++	chosen {
++		linux,stdout-path = "serial0:115200n8";
++	};
++
+ 	soc {
+ 		pinmux at 800000 {
+ 			i2c4_pins: i2c4_pinmux {
+@@ -60,6 +66,25 @@
+ 					bias-bus-hold;
+ 				};
+ 			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			rgmii2_pins: rgmii2_pins {
++				mux {
++					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++					function = "rgmii2";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
+ 		};
+ 
+ 		gsbi at 16300000 {
+@@ -69,14 +94,12 @@
+ 				status = "ok";
+ 			};
+ 
+-			i2c4: i2c at 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 at 1a200000 {
+@@ -99,15 +122,7 @@
+ 					spi-max-frequency = <50000000>;
+ 					reg = <0>;
+ 
+-					partition at 0 {
+-						label = "rootfs";
+-						reg = <0x0 0x1000000>;
+-					};
+-
+-					partition at 1 {
+-						label = "scratch";
+-						reg = <0x1000000 0x1000000>;
+-					};
++					linux,part-probe = "qcom-smem";
+ 				};
+ 			};
+ 		};
+@@ -117,23 +132,105 @@
+ 		};
+ 
+ 		sata at 29000000 {
+-			ports-implemented = <0x1>;
+ 			status = "ok";
+ 		};
+ 
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
+ 		nand at 1ac00000 {
+ 			status = "ok";
+ 
+ 			pinctrl-0 = <&nand_pins>;
+ 			pinctrl-names = "default";
+ 
+-			nandcs at 0 {
+-				compatible = "qcom,nandcs";
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++
++			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 at 0 {
++				device_type = "ethernet-phy";
+ 				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
+ 
+-				nand-ecc-strength = <4>;
+-				nand-ecc-step-size = <512>;
+-				nand-bus-width = <8>;
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
+ 			};
+ 		};
+ 	};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
+@@ -0,0 +1,501 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "TP-Link Archer C2600";
++	compatible = "tplink,c2600", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power;
++		led-failsafe = &general;
++		led-running = &power;
++		led-upgrade = &general;
++	};
++
++	chosen {
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio16", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio6", "gpio7", "gpio8", "gpio9", "gpio26", "gpio33",
++					       "gpio53", "gpio66";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			spi_pins: spi_pins {
++				mux {
++					pins = "gpio18", "gpio19", "gpio21";
++					function = "gsbi5";
++					bias-pull-down;
++				};
++
++				data {
++					pins = "gpio18", "gpio19";
++					drive-strength = <10>;
++				};
++
++				cs {
++					pins = "gpio20";
++					function = "gpio";
++					drive-strength = <10>;
++					bias-pull-up;
++				};
++
++				clk {
++					pins = "gpio21";
++					drive-strength = <12>;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			rgmii2_pins: rgmii2_pins {
++				mux {
++					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++					function = "rgmii2";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			usb0_pwr_en_pin: usb0_pwr_en_pin {
++				mux {
++					pins = "gpio25";
++					function = "gpio";
++					drive-strength = <10>;
++					bias-pull-up;
++					output-high;
++				};
++			};
++
++			usb1_pwr_en_pin: usb1_pwr_en_pin {
++				mux {
++					pins = "gpio23";
++					function = "gpio";
++					drive-strength = <10>;
++					bias-pull-up;
++					output-high;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		gsbi5: gsbi at 1a200000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++
++			spi5: spi at 1a280000 {
++				status = "ok";
++
++				pinctrl-0 = <&spi_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++				flash: m25p80 at 0 {
++					compatible = "jedec,spi-nor";
++					#address-cells = <1>;
++					#size-cells = <1>;
++					spi-max-frequency = <50000000>;
++					reg = <0>;
++
++					SBL1 at 0 {
++						label = "SBL1";
++						reg = <0x0 0x20000>;
++						read-only;
++					};
++
++					MIBIB at 20000 {
++						label = "MIBIB";
++						reg = <0x20000 0x20000>;
++						read-only;
++					};
++
++					SBL2 at 40000 {
++						label = "SBL2";
++						reg = <0x40000 0x20000>;
++						read-only;
++					};
++
++					SBL3 at 60000 {
++						label = "SBL3";
++						reg = <0x60000 0x30000>;
++						read-only;
++					};
++
++					DDRCONFIG at 90000 {
++						label = "DDRCONFIG";
++						reg = <0x90000 0x10000>;
++						read-only;
++					};
++
++					SSD at a0000 {
++						label = "SSD";
++						reg = <0xa0000 0x10000>;
++						read-only;
++					};
++
++					TZ at b0000 {
++						label = "TZ";
++						reg = <0xb0000 0x30000>;
++						read-only;
++					};
++
++					RPM at e0000 {
++						label = "RPM";
++						reg = <0xe0000 0x20000>;
++						read-only;
++					};
++
++					fs-uboot at 100000 {
++						label = "fs-uboot";
++						reg = <0x100000 0x70000>;
++						read-only;
++					};
++
++					uboot-env at 170000 {
++						label = "uboot-env";
++						reg = <0x170000 0x40000>;
++						read-only;
++					};
++
++					radio at 1b0000 {
++						label = "radio";
++						reg = <0x1b0000 0x40000>;
++						read-only;
++					};
++
++					os-image at 1f0000 {
++						label = "os-image";
++						reg = <0x1f0000 0x200000>;
++					};
++
++					rootfs at 3f0000 {
++						label = "rootfs";
++						reg = <0x3f0000 0x1b00000>;
++					};
++
++					defaultmac: default-mac at 1ef0000 {
++						label = "default-mac";
++						reg = <0x1ef0000 0x00200>;
++						read-only;
++					};
++
++					pin at 1ef0200 {
++						label = "pin";
++						reg = <0x1ef0200 0x00200>;
++						read-only;
++					};
++
++					product-info at 1ef0400 {
++						label = "product-info";
++						reg = <0x1ef0400 0x0fc00>;
++						read-only;
++					};
++
++					partition-table at 1f00000 {
++						label = "partition-table";
++						reg = <0x1f00000 0x10000>;
++						read-only;
++					};
++
++					soft-version at 1f10000 {
++						label = "soft-version";
++						reg = <0x1f10000 0x10000>;
++						read-only;
++					};
++
++					support-list at 1f20000 {
++						label = "support-list";
++						reg = <0x1f20000 0x10000>;
++						read-only;
++					};
++
++					profile at 1f30000 {
++						label = "profile";
++						reg = <0x1f30000 0x10000>;
++						read-only;
++					};
++
++					default-config at 1f40000 {
++						label = "default-config";
++						reg = <0x1f40000 0x10000>;
++						read-only;
++					};
++
++					user-config at 1f50000 {
++						label = "user-config";
++						reg = <0x1f50000 0x40000>;
++						read-only;
++					};
++
++					qos-db at 1f90000 {
++						label = "qos-db";
++						reg = <0x1f90000 0x40000>;
++						read-only;
++					};
++
++					usb-config at 1fd0000 {
++						label = "usb-config";
++						reg = <0x1fd0000 0x10000>;
++						read-only;
++					};
++
++					log at 1fe0000 {
++						label = "log";
++						reg = <0x1fe0000 0x20000>;
++						read-only;
++					};
++				};
++			};
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++
++			pinctrl-0 = <&usb0_pwr_en_pin>;
++			pinctrl-names = "default";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++
++			pinctrl-0 = <&usb1_pwr_en_pin>;
++			pinctrl-names = "default";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&defaultmac 0x8>;
++			mtd-mac-address-increment = <1>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++
++			mtd-mac-address = <&defaultmac 0x8>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 49 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++
++		ledswitch {
++			label = "ledswitch";
++			gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_LIGHTS_TOGGLE>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		lan {
++			label = "c2600:white:lan";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb4 {
++			label = "c2600:white:usb_4";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb2 {
++			label = "c2600:white:usb_2";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		wps {
++			label = "c2600:white:wps";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_amber {
++			label = "c2600:amber:wan";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_LOW>;
++		};
++
++		wan_white {
++			label = "c2600:white:wan";
++			gpios = <&qcom_pinmux 33 GPIO_ACTIVE_LOW>;
++		};
++
++		power: power {
++			label = "c2600:white:power";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		general: general {
++			label = "c2600:white:general";
++			gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-d7800.dts
+@@ -0,0 +1,406 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "Netgear Nighthawk X4 D7800";
++	compatible = "netgear,d7800", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0xe000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power_white;
++		led-failsafe = &power_amber;
++		led-running = &power_white;
++		led-upgrade = &power_amber;
++	};
++
++	chosen {
++		bootargs = "rootfstype=squashfs noinitrd";
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio6", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++						"gpio24","gpio26", "gpio53", "gpio64";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
++
++			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;
++				};
++			};
++
++			usb0_pwr_en_pins: usb0_pwr_en_pins {
++				mux {
++					pins = "gpio15";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++
++			usb1_pwr_en_pins: usb1_pwr_en_pins {
++				mux {
++					pins = "gpio16", "gpio68";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++
++			pinctrl-0 = <&usb0_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++
++			pinctrl-0 = <&usb1_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&pcie0_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&pcie1_pins>;
++			pinctrl-names = "default";
++		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++
++			#address-cells = <1>;
++			#size-cells = <1>;
++
++			qcadata at 0 {
++				label = "qcadata";
++				reg = <0x0000000 0x0c80000>;
++				read-only;
++			};
++
++			APPSBL at c80000 {
++				label = "APPSBL";
++				reg = <0x0c80000 0x0500000>;
++				read-only;
++			};
++
++			APPSBLENV at 1180000 {
++				label = "APPSBLENV";
++				reg = <0x1180000 0x0080000>;
++				read-only;
++			};
++
++			art: art at 1200000 {
++				label = "art";
++				reg = <0x1200000 0x0140000>;
++				read-only;
++			};
++
++			artbak: art at 1340000 {
++				label = "artbak";
++				reg = <0x1340000 0x0140000>;
++				read-only;
++			};
++
++			kernel at 1480000 {
++				label = "kernel";
++				reg = <0x1480000 0x0200000>;
++			};
++
++			ubi at 1680000 {
++				label = "ubi";
++				reg = <0x1680000 0x1E00000>;
++			};
++
++			netgear at 3480000 {
++				label = "netgear";
++				reg = <0x3480000 0x4480000>;
++				read-only;
++			};
++
++			reserve at 7900000 {
++				label = "reserve";
++				reg = <0x7900000 0x0700000>;
++				read-only;
++			};
++
++			firmware at 1480000 {
++				label = "firmware";
++				reg = <0x1480000 0x2000000>;
++			};
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			phy-handle = <&phy4>;
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&art 6>;
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++
++			mtd-mac-address = <&art 0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		usb1 {
++			label = "d7800:white:usb1";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb2 {
++			label = "d7800:white:usb2";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		power_amber: power_amber {
++			label = "d7800:amber:power";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_white {
++			label = "d7800:white:wan";
++			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_amber {
++			label = "d7800:amber:wan";
++			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++		};
++
++		wps {
++			label = "d7800:white:wps";
++			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++		};
++
++		esata {
++			label = "d7800:white:esata";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++		};
++
++		power_white: power_white {
++			label = "d7800:white:power";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		wifi {
++			label = "d7800:white:wifi";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -0,0 +1,236 @@
++#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 at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	alias {
++		serial0 = &uart2;
++		mdio-gpio0 = &mdio0;
++	};
++
++	chosen {
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 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;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			rgmii0_pins: rgmii0_pins {
++				mux {
++					pins = "gpio2", "gpio66";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++		};
++
++		gsbi2: gsbi at 12480000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			uart2: serial at 12490000 {
++				status = "ok";
++			};
++		};
++
++		gsbi5: gsbi at 1a200000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++
++			spi4: spi at 1a280000 {
++				status = "ok";
++				spi-max-frequency = <50000000>;
++
++				pinctrl-0 = <&spi_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 20 0>;
++
++				flash: m25p80 at 0 {
++					compatible = "s25fl256s1";
++					#address-cells = <1>;
++					#size-cells = <1>;
++					spi-max-frequency = <50000000>;
++					reg = <0>;
++					m25p,fast-read;
++
++					partition at 0 {
++						label = "lowlevel_init";
++						reg = <0x0 0x1b0000>;
++					};
++
++					partition at 1 {
++						label = "u-boot";
++						reg = <0x1b0000 0x80000>;
++					};
++
++					partition at 2 {
++						label = "u-boot-env";
++						reg = <0x230000 0x40000>;
++					};
++
++					partition at 3 {
++						label = "caldata";
++						reg = <0x270000 0x40000>;
++					};
++
++					partition at 4 {
++						label = "firmware";
++						reg = <0x2b0000 0x1d50000>;
++					};
++				};
++			};
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++		};
++
++		pcie2: pci at 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 at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++				>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++
++			phy6: ethernet-phy at 6 {
++				device_type = "ethernet-phy";
++				reg = <6>;
++			};
++
++			phy7: ethernet-phy at 7 {
++				device_type = "ethernet-phy";
++				reg = <7>;
++			};
++		};
++
++		gmac0: ethernet at 37000000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <0>;
++			phy-handle = <&phy4>;
++
++			pinctrl-0 = <&rgmii0_pins>;
++			pinctrl-names = "default";
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <1>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++			phy-handle = <&phy6>;
++		};
++
++		gmac3: ethernet at 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
+@@ -1,11 +1,13 @@
+ /dts-v1/;
+ 
+ #include "skeleton.dtsi"
+-#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #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>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/gpio/gpio.h>
+ 
+ / {
+ 	model = "Qualcomm IPQ8064";
+@@ -16,7 +18,7 @@
+ 		#address-cells = <1>;
+ 		#size-cells = <0>;
+ 
+-		cpu at 0 {
++		cpu0: cpu at 0 {
+ 			compatible = "qcom,krait";
+ 			enable-method = "qcom,kpss-acc-v1";
+ 			device_type = "cpu";
+@@ -24,9 +26,18 @@
+ 			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc0>;
+ 			qcom,saw = <&saw0>;
++			clocks = <&kraitcc 0>, <&kraitcc 4>;
++			clock-names = "cpu", "l2";
++			clock-latency = <100000>;
++			cpu-supply = <&smb208_s2a>;
++			voltage-tolerance = <5>;
++			cooling-min-state = <0>;
++			cooling-max-state = <10>;
++			#cooling-cells = <2>;
++			cpu-idle-states = <&CPU_SPC>;
+ 		};
+ 
+-		cpu at 1 {
++		cpu1: cpu at 1 {
+ 			compatible = "qcom,krait";
+ 			enable-method = "qcom,kpss-acc-v1";
+ 			device_type = "cpu";
+@@ -34,11 +45,120 @@
+ 			next-level-cache = <&L2>;
+ 			qcom,acc = <&acc1>;
+ 			qcom,saw = <&saw1>;
++			clocks = <&kraitcc 1>, <&kraitcc 4>;
++			clock-names = "cpu", "l2";
++			clock-latency = <100000>;
++			cpu-supply = <&smb208_s2b>;
++			cooling-min-state = <0>;
++			cooling-max-state = <10>;
++			#cooling-cells = <2>;
++			cpu-idle-states = <&CPU_SPC>;
+ 		};
+ 
+ 		L2: l2-cache {
+ 			compatible = "cache";
+ 			cache-level = <2>;
++			qcom,saw = <&saw_l2>;
++		};
++
++		qcom,l2 {
++			qcom,l2-rates = <384000000 1000000000 1200000000>;
++		};
++
++		idle-states {
++			CPU_SPC: spc {
++				compatible = "qcom,idle-state-spc",
++						"arm,idle-state";
++				entry-latency-us = <400>;
++				exit-latency-us = <900>;
++				min-residency-us = <3000>;
++			};
++		};
++	};
++
++	thermal-zones {
++		cpu-thermal0 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 5>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert0: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit0: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal1 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 6>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert1: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit1: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal2 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 7>;
++			coefficients = <1199 0>;
++
++			trips {
++				cpu_alert2: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit2: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal3 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 8>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert3: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit3: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
+ 		};
+ 	};
+ 
+@@ -57,7 +177,7 @@
+ 			no-map;
+ 		};
+ 
+-		smem at 41000000 {
++		smem: smem at 41000000 {
+ 			reg = <0x41000000 0x200000>;
+ 			no-map;
+ 		};
+@@ -67,13 +187,13 @@
+ 		cxo_board {
+ 			compatible = "fixed-clock";
+ 			#clock-cells = <0>;
+-			clock-frequency = <19200000>;
++			clock-frequency = <25000000>;
+ 		};
+ 
+ 		pxo_board {
+ 			compatible = "fixed-clock";
+ 			#clock-cells = <0>;
+-			clock-frequency = <27000000>;
++			clock-frequency = <25000000>;
+ 		};
+ 
+ 		sleep_clk: sleep_clk {
+@@ -83,6 +203,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>;
+@@ -104,6 +264,85 @@
+ 			reg-names = "lpass-lpaif";
+ 		};
+ 
++		qfprom: qfprom at 700000 {
++			compatible = "qcom,qfprom", "syscon";
++			reg = <0x00700000 0x1000>;
++			#address-cells = <1>;
++			#size-cells = <1>;
++			ranges;
++
++			tsens_calib: calib {
++				reg = <0x400 0x10>;
++			};
++			tsens_backup: backup_calib {
++				reg = <0x410 0x10>;
++			};
++		};
++
++		rpm at 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";
++
++			clocks = <&gcc RPM_MSG_RAM_H_CLK>;
++			clock-names = "ram";
++
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			rpmcc: clock-controller {
++				compatible	= "qcom,rpmcc-ipq806x", "qcom,rpmcc";
++				#clock-cells = <1>;
++			};
++
++			regulators {
++				compatible = "qcom,rpm-smb208-regulators";
++
++				smb208_s1a: s1a {
++					regulator-min-microvolt = <1050000>;
++					regulator-max-microvolt = <1150000>;
++
++					qcom,switch-mode-frequency = <1200000>;
++
++				};
++
++				smb208_s1b: s1b {
++					regulator-min-microvolt = <1050000>;
++					regulator-max-microvolt = <1150000>;
++
++					qcom,switch-mode-frequency = <1200000>;
++				};
++
++				smb208_s2a: s2a {
++					regulator-min-microvolt = < 800000>;
++					regulator-max-microvolt = <1250000>;
++
++					qcom,switch-mode-frequency = <1200000>;
++				};
++
++				smb208_s2b: s2b {
++					regulator-min-microvolt = < 800000>;
++					regulator-max-microvolt = <1250000>;
++
++					qcom,switch-mode-frequency = <1200000>;
++				};
++			};
++		};
++
++		rng at 1a500000 {
++			compatible = "qcom,prng";
++			reg = <0x1a500000 0x200>;
++			clocks = <&gcc PRNG_CLK>;
++			clock-names = "core";
++		};
++
+ 		qcom_pinmux: pinmux at 800000 {
+ 			compatible = "qcom,ipq8064-pinctrl";
+ 			reg = <0x800000 0x4000>;
+@@ -113,6 +352,34 @@
+ 			interrupt-controller;
+ 			#interrupt-cells = <2>;
+ 			interrupts = <0 16 0x4>;
++
++			pcie0_pins: pcie0_pinmux {
++				mux {
++					pins = "gpio3";
++					function = "pcie1_rst";
++					drive-strength = <2>;
++					bias-disable;
++				};
++			};
++
++			pcie1_pins: pcie1_pinmux {
++				mux {
++					pins = "gpio48";
++					function = "pcie2_rst";
++					drive-strength = <2>;
++					bias-disable;
++				};
++			};
++
++			pcie2_pins: pcie2_pinmux {
++				mux {
++					pins = "gpio63";
++					function = "pcie3_rst";
++					drive-strength = <2>;
++					bias-disable;
++					output-low;
++				};
++			};
+ 		};
+ 
+ 		intc: interrupt-controller at 2000000 {
+@@ -124,8 +391,7 @@
+ 		};
+ 
+ 		timer at 200a000 {
+-			compatible = "qcom,kpss-timer",
+-				     "qcom,kpss-wdt-ipq8064", "qcom,msm-timer";
++			compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ 			interrupts = <1 1 0x301>,
+ 				     <1 2 0x301>,
+ 				     <1 3 0x301>,
+@@ -142,25 +408,44 @@
+ 		acc0: clock-controller at 2088000 {
+ 			compatible = "qcom,kpss-acc-v1";
+ 			reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++			clock-output-names = "acpu0_aux";
+ 		};
+ 
+ 		acc1: clock-controller at 2098000 {
+ 			compatible = "qcom,kpss-acc-v1";
+ 			reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++			clock-output-names = "acpu1_aux";
+ 		};
+ 
++		l2cc: clock-controller at 2011000 {
++			compatible = "qcom,kpss-gcc", "syscon";
++			reg = <0x2011000 0x1000>;
++			clock-output-names = "acpu_l2_aux";
++ 		};
++
+ 		saw0: regulator at 2089000 {
+-			compatible = "qcom,saw2";
++			compatible = "qcom,saw2", "syscon";
+ 			reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
+ 			regulator;
+ 		};
+ 
+ 		saw1: regulator at 2099000 {
+-			compatible = "qcom,saw2";
++			compatible = "qcom,saw2", "syscon";
+ 			reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
+ 			regulator;
+ 		};
+ 
++		saw_l2: regulator at 02012000 {
++			compatible = "qcom,saw2", "syscon";
++			reg = <0x02012000 0x1000>;
++			regulator;
++		};
++ 
++		sic_non_secure: sic-non-secure at 12100000 {
++			compatible = "syscon";
++			reg = <0x12100000 0x10000>;
++		};
++
+ 		gsbi2: gsbi at 12480000 {
+ 			compatible = "qcom,gsbi-v1.0.0";
+ 			cell-index = <2>;
+@@ -174,7 +459,7 @@
+ 
+ 			syscon-tcsr = <&tcsr>;
+ 
+-			serial at 12490000 {
++			uart2: serial at 12490000 {
+ 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ 				reg = <0x12490000 0x1000>,
+ 				      <0x12480000 0x1000>;
+@@ -212,7 +497,7 @@
+ 
+ 			syscon-tcsr = <&tcsr>;
+ 
+-			gsbi4_serial: serial at 16340000 {
++			uart4: serial at 16340000 {
+ 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ 				reg = <0x16340000 0x1000>,
+ 				      <0x16300000 0x1000>;
+@@ -249,7 +534,7 @@
+ 
+ 			syscon-tcsr = <&tcsr>;
+ 
+-			serial at 1a240000 {
++			uart5: serial at 1a240000 {
+ 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ 				reg = <0x1a240000 0x1000>,
+ 				      <0x1a200000 0x1000>;
+@@ -328,8 +613,12 @@
+ 		gcc: clock-controller at 900000 {
+ 			compatible = "qcom,gcc-ipq8064";
+ 			reg = <0x00900000 0x4000>;
++			nvmem-cells = <&tsens_calib>, <&tsens_backup>;
++			nvmem-cell-names = "calib", "calib_backup";
+ 			#clock-cells = <1>;
+ 			#reset-cells = <1>;
++			#power-domain-cells = <1>;
++			#thermal-sensor-cells = <1>;
+ 		};
+ 
+ 		tcsr: syscon at 1a400000 {
+@@ -344,10 +633,259 @@
+ 			#reset-cells = <1>;
+ 		};
+ 
++		sfpb_mutex_block: syscon at 1200600 {
++			compatible = "syscon";
++			reg = <0x01200600 0x100>;
++		};
++
++		hs_phy_1: phy at 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 at 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 at 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 at 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 at 0 {
++			compatible = "qcom,dwc3";
++			#address-cells = <1>;
++			#size-cells = <1>;
++			clocks = <&gcc USB30_0_MASTER_CLK>;
++			clock-names = "core";
++
++			syscon-tcsr = <&tcsr 0xb0 1>;
++
++			ranges;
++
++			status = "disabled";
++
++			dwc3 at 11000000 {
++				compatible = "snps,dwc3";
++				reg = <0x11000000 0xcd00>;
++				interrupts = <0 110 0x4>;
++				phys = <&hs_phy_0>, <&ss_phy_0>;
++				phy-names = "usb2-phy", "usb3-phy";
++				dr_mode = "host";
++				snps,dis_u3_susphy_quirk;
++			};
++		};
++
++		usb3_1: usb30 at 1 {
++			compatible = "qcom,dwc3";
++			#address-cells = <1>;
++			#size-cells = <1>;
++			clocks = <&gcc USB30_1_MASTER_CLK>;
++			clock-names = "core";
++
++			syscon-tcsr = <&tcsr 0xb0 0>;
++
++			ranges;
++
++			status = "disabled";
++
++			dwc3 at 10000000 {
++				compatible = "snps,dwc3";
++				reg = <0x10000000 0xcd00>;
++				interrupts = <0 205 0x4>;
++				phys = <&hs_phy_1>, <&ss_phy_1>;
++				phy-names = "usb2-phy", "usb3-phy";
++				dr_mode = "host";
++				snps,dis_u3_susphy_quirk;
++			};
++		};
++
++		pcie0: pci at 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>,
++				 <&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>,
++				 <&gcc PCIE_EXT_RESET>;
++			reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++			pinctrl-0 = <&pcie0_pins>;
++			pinctrl-names = "default";
++
++			perst-gpios = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++
++			status = "disabled";
++		};
++
++		pcie1: pci at 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>,
++				 <&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>,
++				 <&gcc PCIE_1_EXT_RESET>;
++			reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++			pinctrl-0 = <&pcie1_pins>;
++			pinctrl-names = "default";
++
++			perst-gpios = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++
++			status = "disabled";
++		};
++
++		pcie2: pci at 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>,
++				 <&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>,
++				 <&gcc PCIE_2_EXT_RESET>;
++			reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
++
++			pinctrl-0 = <&pcie2_pins>;
++			pinctrl-names = "default";
++
++			perst-gpios = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>;
++
++			status = "disabled";
++		};
++
+ 		adm_dma: dma at 18300000 {
+ 			compatible = "qcom,adm";
+ 			reg = <0x18300000 0x100000>;
+-			interrupts = <GIC_SPI 170 IRQ_TYPE_NONE>;
++			interrupts = <0 170 0>;
+ 			#dma-cells = <1>;
+ 
+ 			clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+@@ -365,7 +903,7 @@
+ 		};
+ 
+ 		nand at 1ac00000 {
+-			compatible = "qcom,ipq806x-nand";
++			compatible = "qcom,ebi2-nandc";
+ 			reg = <0x1ac00000 0x800>;
+ 
+ 			clocks = <&gcc EBI2_CLK>,
+@@ -380,5 +918,103 @@
+ 			status = "disabled";
+ 		};
+ 
++		nss_common: syscon at 03000000 {
++			compatible = "syscon";
++			reg = <0x03000000 0x0000FFFF>;
++		};
++
++		qsgmii_csr: syscon at 1bb00000 {
++			compatible = "syscon";
++			reg = <0x1bb00000 0x000001FF>;
++		};
++
++		gmac0: ethernet at 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 at 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 at 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 at 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 {
++		compatible = "qcom,sfpb-mutex";
++		syscon = <&sfpb_mutex_block 4 4>;
++
++		#hwlock-cells = <1>;
++	};
++
++	smem {
++		compatible = "qcom,smem";
++		memory-region = <&smem>;
++		hwlocks = <&sfpb_mutex 3>;
+ 	};
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts
+@@ -0,0 +1,399 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "Linksys EA8500 WiFi Router";
++	compatible = "linksys,ea8500", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power;
++		led-failsafe = &power;
++		led-running = &power;
++		led-upgrade = &power;
++	};
++
++	chosen {
++		bootargs = "console=ttyMSM0,115200n8"; 
++		linux,stdout-path = "serial0:115200n8";
++		append-rootblock = "ubi.mtd=";  /* append to bootargs adding the root deviceblock nbr from bootloader */
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio65", "gpio67", "gpio68";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio6", "gpio53", "gpio54";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
++
++			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 at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++		
++		pcie2: pci at 1b900000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++		
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++
++			#address-cells = <1>;
++			#size-cells = <1>;
++
++			SBL1 at 0 {
++				label = "SBL1";
++				reg = <0x0000000 0x0040000>;
++				read-only;
++			};
++
++			MIBIB at 40000 {
++				label = "MIBIB";
++				reg = <0x0040000 0x0140000>;
++				read-only;
++			};
++
++			SBL2 at 180000 {
++				label = "SBL2";
++				reg = <0x0180000 0x0140000>;
++				read-only;
++			};
++
++			SBL3 at 2c0000 {
++				label = "SBL3";
++				reg = <0x02c0000 0x0280000>;
++				read-only;
++			};
++
++			DDRCONFIG at 540000 {
++				label = "DDRCONFIG";
++				reg = <0x0540000 0x0120000>;
++				read-only;
++			};
++
++			SSD at 660000 {
++				label = "SSD";
++				reg = <0x0660000 0x0120000>;
++				read-only;
++			};
++
++			TZ at 780000 {
++				label = "TZ";
++				reg = <0x0780000 0x0280000>;
++				read-only;
++			};
++
++			RPM at a00000 {
++				label = "RPM";
++				reg = <0x0a00000 0x0280000>;
++				read-only;
++			};
++
++			art: art at c80000 {
++				label = "art";
++				reg = <0x0c80000 0x0140000>;
++				read-only;
++			};
++
++			APPSBL at dc0000 {
++				label = "APPSBL";
++				reg = <0x0dc0000 0x0100000>;
++				read-only;
++			};
++
++			u_env at ec0000 {
++				label = "u_env";
++				reg = <0x0ec0000 0x0040000>;
++			};
++
++			s_env at f00000 {
++				label = "s_env";
++				reg = <0x0f00000 0x0040000>;
++			};
++
++			devinfo at f40000 {
++				label = "devinfo";
++				reg = <0x0f40000 0x0040000>;
++			};
++
++			linux at f80000 { 
++				label = "kernel1";
++				reg = <0x0f80000 0x2800000>;  /* 3 MB spill to rootfs*/
++			};
++
++			rootfs at 1280000 {
++				label = "rootfs1";
++				reg = <0x1280000 0x2500000>;
++			};
++
++			linux2 at 3780000 {
++				label = "kernel2";
++				reg = <0x3780000 0x2800000>;
++			};
++
++			rootfs2 at 3a80000 {
++				label = "rootfs2";
++				reg = <0x3a80000 0x2500000>;
++			};
++
++			syscfg at 5f80000 {
++				label = "syscfg";
++				reg = <0x5f80000 0x2080000>;
++			};
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++			qcom,phy_mdio_addr = <4>;
++			qcom,poll_required = <1>;
++			qcom,rgmii_delay = <0>;
++			qcom,emulation = <0>;
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++		//lan
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++			qcom,phy_mdio_addr = <0>;	/* none */
++			qcom,poll_required = <0>;	/* no polling */
++			qcom,rgmii_delay = <0>;
++			qcom,emulation = <0>;
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++
++		adm_dma: dma at 18300000 {
++			status = "ok";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART >;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		wps {
++			label = "ea8500:green:wps";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++		};
++
++		power: power {
++			label = "ea8500:white:power";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			default-state = "keep";
++		};
++
++		wifi {
++			label = "ea8500:green:wifi";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts
+@@ -0,0 +1,373 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "Netgear Nighthawk X4 R7500";
++	compatible = "netgear,r7500", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0xe000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power_white;
++		led-failsafe = &power_amber;
++		led-running = &power_white;
++		led-upgrade = &power_amber;
++	};
++
++	chosen {
++		bootargs = "rootfstype=squashfs noinitrd";
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio6", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++						"gpio24","gpio26", "gpio53", "gpio64";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
++
++			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 at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++
++			#address-cells = <1>;
++			#size-cells = <1>;
++
++			qcadata at 0 {
++				label = "qcadata";
++				reg = <0x0000000 0x0c80000>;
++				read-only;
++			};
++
++			APPSBL at c80000 {
++				label = "APPSBL";
++				reg = <0x0c80000 0x0500000>;
++				read-only;
++			};
++
++			APPSBLENV at 1180000 {
++				label = "APPSBLENV";
++				reg = <0x1180000 0x0080000>;
++				read-only;
++			};
++
++			art: art at 1200000 {
++				label = "art";
++				reg = <0x1200000 0x0140000>;
++				read-only;
++			};
++
++			kernel at 1340000 {
++				label = "kernel";
++				reg = <0x1340000 0x0200000>;
++			};
++
++			ubi at 1540000 {
++				label = "ubi";
++				reg = <0x1540000 0x1800000>;
++			};
++
++			netgear at 2d40000 {
++				label = "netgear";
++				reg = <0x2d40000 0x0c00000>;
++				read-only;
++			};
++
++			reserve at 3940000 {
++				label = "reserve";
++				reg = <0x3940000 0x46c0000>;
++				read-only;
++			};
++
++			firmware at 1340000 {
++				label = "firmware";
++				reg = <0x1340000 0x1a00000>;
++			};
++
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&art 6>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++
++			mtd-mac-address = <&art 0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		usb1 {
++			label = "r7500:white:usb1";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb2 {
++			label = "r7500:white:usb2";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		power_amber: power_amber {
++			label = "r7500:amber:power";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_white {
++			label = "r7500:white:wan";
++			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_amber {
++			label = "r7500:amber:wan";
++			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++		};
++
++		wps {
++			label = "r7500:white:wps";
++			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++		};
++
++		esata {
++			label = "r7500:white:esata";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++		};
++
++		power_white: power_white {
++			label = "r7500:white:power";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		wifi {
++			label = "r7500:white:wifi";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts
+@@ -0,0 +1,416 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "Netgear Nighthawk X4 R7500v2";
++	compatible = "netgear,r7500v2", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++
++		rsvd at 5fe00000 {
++			reg = <0x5fe00000 0x200000>;
++			reusable;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power;
++		led-failsafe = &power;
++		led-running = &power;
++		led-upgrade = &power;
++	};
++
++	chosen {
++		bootargs = "rootfstype=squashfs noinitrd";
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio6", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++						"gpio24","gpio26", "gpio53", "gpio64";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
++
++			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;
++				};
++			};
++
++			usb0_pwr_en_pins: usb0_pwr_en_pins {
++				mux {
++					pins = "gpio15";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++
++			usb1_pwr_en_pins: usb1_pwr_en_pins {
++				mux {
++					pins = "gpio16", "gpio68";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++
++			pinctrl-0 = <&usb0_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++
++			pinctrl-0 = <&usb1_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++			pinctrl-0 = <&pcie0_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++			pinctrl-0 = <&pcie1_pins>;
++			pinctrl-names = "default";
++		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++
++			#address-cells = <1>;
++			#size-cells = <1>;
++
++			qcadata at 0 {
++				label = "qcadata";
++				reg = <0x0000000 0x0c80000>;
++				read-only;
++			};
++
++			APPSBL at c80000 {
++				label = "APPSBL";
++				reg = <0x0c80000 0x0500000>;
++				read-only;
++			};
++
++			APPSBLENV at 1180000 {
++				label = "APPSBLENV";
++				reg = <0x1180000 0x0080000>;
++				read-only;
++			};
++
++			art: art at 1200000 {
++				label = "art";
++				reg = <0x1200000 0x0140000>;
++				read-only;
++			};
++
++			artbak: art at 1340000 {
++				label = "artbak";
++				reg = <0x1340000 0x0140000>;
++				read-only;
++			};
++
++			kernel at 1480000 {
++				label = "kernel";
++				reg = <0x1480000 0x0200000>;
++			};
++
++			ubi at 1680000 {
++				label = "ubi";
++				reg = <0x1680000 0x1E00000>;
++			};
++
++			netgear at 3480000 {
++				label = "netgear";
++				reg = <0x3480000 0x4480000>;
++				read-only;
++			};
++
++			reserve at 7900000 {
++				label = "reserve";
++				reg = <0x7900000 0x0700000>;
++				read-only;
++			};
++
++			firmware at 1480000 {
++				label = "firmware";
++				reg = <0x1480000 0x2000000>;
++			};
++
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 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 at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&art 6>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++
++			mtd-mac-address = <&art 0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		usb1 {
++			label = "r7500v2:amber:usb1";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb3 {
++			label = "r7500v2:amber:usb3";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		status {
++			label = "r7500v2:amber:status";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		internet {
++			label = "r7500v2:white:internet";
++			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan {
++			label = "r7500v2:white:wan";
++			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++		};
++
++		wps {
++			label = "r7500v2:white:wps";
++			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++		};
++
++		esata {
++			label = "r7500v2:white:esata";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++		};
++
++		power: power {
++			label = "r7500v2:white:power";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		wifi {
++			label = "r7500v2:white:wifi";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts
+@@ -0,0 +1,425 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "TP-Link Archer VR2600v";
++	compatible = "tplink,vr2600v", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power;
++		led-failsafe = &general;
++		led-running = &power;
++		led-upgrade = &general;
++	};
++
++	chosen {
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			led_pins: led_pins {
++				mux {
++					pins = "gpio7", "gpio8", "gpio9", "gpio16", "gpio17",
++						"gpio26", "gpio53", "gpio56", "gpio66";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			button_pins: button_pins {
++				mux {
++					pins = "gpio54", "gpio64", "gpio65", "gpio67", "gpio68";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			spi_pins: spi_pins {
++				mux {
++					pins = "gpio18", "gpio19", "gpio21";
++					function = "gsbi5";
++					bias-pull-down;
++				};
++
++				data {
++					pins = "gpio18", "gpio19";
++					drive-strength = <10>;
++				};
++
++				cs {
++					pins = "gpio20";
++					drive-strength = <10>;
++					bias-pull-up;
++				};
++
++				clk {
++					pins = "gpio21";
++					drive-strength = <12>;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++
++			rgmii2_pins: rgmii2_pins {
++				mux {
++					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
++					function = "rgmii2";
++					drive-strength = <8>;
++					bias-disable;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		gsbi5: gsbi at 1a200000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++
++			spi4: spi at 1a280000 {
++				status = "ok";
++
++				pinctrl-0 = <&spi_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++				flash: W25Q128 at 0 {
++					compatible = "jedec,spi-nor";
++					#address-cells = <1>;
++					#size-cells = <1>;
++					spi-max-frequency = <50000000>;
++					reg = <0>;
++
++					SBL1 at 0 {
++						label = "SBL1";
++						reg = <0x0 0x20000>;
++						read-only;
++					};
++
++					MIBIB at 20000 {
++						label = "MIBIB";
++						reg = <0x20000 0x20000>;
++						read-only;
++					};
++
++					SBL2 at 40000 {
++						label = "SBL2";
++						reg = <0x40000 0x40000>;
++						read-only;
++					};
++
++					SBL3 at 80000 {
++						label = "SBL3";
++						reg = <0x80000 0x80000>;
++						read-only;
++					};
++
++					DDRCONFIG at 100000 {
++						label = "DDRCONFIG";
++						reg = <0x100000 0x10000>;
++						read-only;
++					};
++
++					SSD at 110000 {
++						label = "SSD";
++						reg = <0x110000 0x10000>;
++						read-only;
++					};
++
++					TZ at 120000 {
++						label = "TZ";
++						reg = <0x120000 0x80000>;
++						read-only;
++					};
++
++					RPM at 1a0000 {
++						label = "RPM";
++						reg = <0x1a0000 0x80000>;
++						read-only;
++					};
++
++					APPSBL at 220000 {
++						label = "APPSBL";
++						reg = <0x220000 0x80000>;
++						read-only;
++					};
++
++					APPSBLENV at 2a0000 {
++						label = "APPSBLENV";
++						reg = <0x2a0000 0x40000>;
++						read-only;
++					};
++
++					OLDART at 2e0000 {
++						label = "OLDART";
++						reg = <0x2e0000 0x40000>;
++						read-only;
++					};
++
++					kernel at 320000 {
++						label = "kernel";
++						reg = <0x320000 0x200000>;
++					};
++
++					rootfs at 520000 {
++						label = "rootfs";
++						reg = <0x520000 0xa60000>;
++					};
++
++					defaultmac: default-mac at 0xfaf100 {
++						label = "default-mac";
++						reg = <0xfaf100 0x00200>;
++						read-only;
++					};
++
++					ART at fc0000 {
++						label = "ART";
++						reg = <0xfc0000 0x40000>;
++						read-only;
++					};
++				};
++			};
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 0 {
++				device_type = "ethernet-phy";
++				reg = <0>;
++				qca,ar8327-initvals = <
++					0x00004 0x7600000   /* PAD0_MODE */
++					0x00008 0x1000000   /* PAD5_MODE */
++					0x0000c 0x80        /* PAD6_MODE */
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x000e0 0xc74164de  /* SGMII_CTRL */
++					0x0007c 0x4e        /* PORT0_STATUS */
++					0x00094 0x4e        /* PORT6_STATUS */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&defaultmac 0>;
++			mtd-mac-address-increment = <1>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++
++			mtd-mac-address = <&defaultmac 0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++
++		dect {
++			label = "dect";
++			gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_PHONE>;
++		};
++
++		ledswitch {
++			label = "ledswitch";
++			gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_LIGHTS_TOGGLE>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		dsl {
++			label = "vr2600v:white:dsl";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb {
++			label = "vr2600v:white:usb";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		lan {
++			label = "vr2600v:white:lan";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		wlan2g {
++			label = "vr2600v:white:wlan2g";
++			gpios = <&qcom_pinmux 16 GPIO_ACTIVE_HIGH>;
++		};
++
++		wlan5g {
++			label = "vr2600v:white:wlan5g";
++			gpios = <&qcom_pinmux 17 GPIO_ACTIVE_HIGH>;
++		};
++
++		power: power {
++			label = "vr2600v:white:power";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		phone {
++			label = "vr2600v:white:phone";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan {
++			label = "vr2600v:white:wan";
++			gpios = <&qcom_pinmux 56 GPIO_ACTIVE_HIGH>;
++		};
++
++		general: general {
++			label = "vr2600v:white:general";
++			gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065.dtsi
+@@ -0,0 +1,153 @@
++#include "qcom-ipq8064.dtsi"
++
++/ {
++	model = "Qualcomm IPQ8065";
++	compatible = "qcom,ipq8065", "qcom,ipq8064";
++
++	qcom,pvs {
++		qcom,pvs-format-a;
++		qcom,speed0-pvs0-bin-v0 =
++			< 1725000000 1262500 >,
++			< 1400000000 1175000 >,
++			< 1000000000 1100000 >,
++			 < 800000000 1050000 >,
++			 < 600000000 1000000 >,
++			 < 384000000 975000 >;
++		qcom,speed0-pvs1-bin-v0 =
++			< 1725000000 1225000 >,
++			< 1400000000 1150000 >,
++			< 1000000000 1075000 >,
++			 < 800000000 1025000 >,
++			 < 600000000 975000 >,
++			 < 384000000 950000 >;
++		qcom,speed0-pvs2-bin-v0 =
++			< 1725000000 1200000 >,
++			< 1400000000 1125000 >,
++			< 1000000000 1050000 >,
++			 < 800000000 1000000 >,
++			 < 600000000 950000 >,
++			 < 384000000 925000 >;
++		qcom,speed0-pvs3-bin-v0 =
++			< 1725000000 1175000 >,
++			< 1400000000 1100000 >,
++			< 1000000000 1025000 >,
++			 < 800000000 975000 >,
++			 < 600000000 925000 >,
++			 < 384000000 900000 >;
++		qcom,speed0-pvs4-bin-v0 =
++			< 1725000000 1150000 >,
++			< 1400000000 1075000 >,
++			< 1000000000 1000000 >,
++			 < 800000000 950000 >,
++			 < 600000000 900000 >,
++			 < 384000000 875000 >;
++		qcom,speed0-pvs5-bin-v0 =
++			< 1725000000 1100000 >,
++			< 1400000000 1025000 >,
++			< 1000000000 950000 >,
++			 < 800000000 900000 >,
++			 < 600000000 850000 >,
++			 < 384000000 825000 >;
++		qcom,speed0-pvs6-bin-v0 =
++			< 1725000000 1050000 >,
++			< 1400000000 975000 >,
++			< 1000000000 900000 >,
++			 < 800000000 850000 >,
++			 < 600000000 800000 >,
++			 < 384000000 775000 >;
++	};
++
++	soc: soc {
++
++		rpm at 108000 {
++
++			regulators {
++
++				smb208_s2a: s2a {
++					regulator-min-microvolt = <775000>;
++					regulator-max-microvolt = <1275000>;
++				};
++
++				smb208_s2b: s2b {
++					regulator-min-microvolt = <775000>;
++					regulator-max-microvolt = <1275000>;
++				};
++			};
++		};
++
++		/* Temporary fixed regulator */
++		vsdcc_fixed: vsdcc-regulator {
++			compatible = "regulator-fixed";
++			regulator-name = "SDCC Power";
++			regulator-min-microvolt = <3300000>;
++			regulator-max-microvolt = <3300000>;
++			regulator-always-on;
++		};
++
++		sdcc1bam:dma at 12402000 {
++			compatible = "qcom,bam-v1.3.0";
++			reg = <0x12402000 0x8000>;
++			interrupts = <0 98 0>;
++			clocks = <&gcc SDC1_H_CLK>;
++			clock-names = "bam_clk";
++			#dma-cells = <1>;
++			qcom,ee = <0>;
++                };
++
++		sdcc3bam:dma at 12182000 {
++			compatible = "qcom,bam-v1.3.0";
++			reg = <0x12182000 0x8000>;
++			interrupts = <0 96 0>;
++			clocks = <&gcc SDC3_H_CLK>;
++			clock-names = "bam_clk";
++			#dma-cells = <1>;
++			qcom,ee = <0>;
++		};
++
++		amba {
++			compatible = "arm,amba-bus";
++			#address-cells = <1>;
++			#size-cells = <1>;
++			ranges;
++			sdcc1: sdcc at 12400000 {
++				status          = "disabled";
++				compatible      = "arm,pl18x", "arm,primecell";
++				arm,primecell-periphid = <0x00051180>;
++				reg             = <0x12400000 0x2000>;
++				interrupts      = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
++				interrupt-names = "cmd_irq";
++				clocks          = <&gcc SDC1_CLK>, <&gcc SDC1_H_CLK>;
++				clock-names     = "mclk", "apb_pclk";
++				bus-width       = <8>;
++				max-frequency   = <96000000>;
++				non-removable;
++				cap-sd-highspeed;
++				cap-mmc-highspeed;
++				vmmc-supply = <&vsdcc_fixed>;
++				dmas = <&sdcc1bam 2>, <&sdcc1bam 1>;
++				dma-names = "tx", "rx";
++			};
++
++			sdcc3: sdcc at 12180000 {
++				compatible      = "arm,pl18x", "arm,primecell";
++				arm,primecell-periphid = <0x00051180>;
++				status          = "disabled";
++				reg             = <0x12180000 0x2000>;
++				interrupts      = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
++				interrupt-names = "cmd_irq";
++				clocks          = <&gcc SDC3_CLK>, <&gcc SDC3_H_CLK>;
++				clock-names     = "mclk", "apb_pclk";
++				bus-width       = <8>;
++				cap-sd-highspeed;
++				cap-mmc-highspeed;
++				max-frequency   = <192000000>;
++				#mmc-ddr-1_8v;
++				sd-uhs-sdr104;
++				sd-uhs-ddr50;
++				vqmmc-supply = <&vsdcc_fixed>;
++				dmas = <&sdcc3bam 2>, <&sdcc3bam 1>;
++				dma-names = "tx", "rx";
++			};
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
+@@ -0,0 +1,388 @@
++#include "qcom-ipq8065-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "ZyXEL NBG6817";
++	compatible = "zyxel,nbg6817", "qcom,ipq8065";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++		sdcc1 = &sdcc1;
++
++		led-boot = &power;
++		led-failsafe = &power;
++		led-running = &power;
++		led-upgrade = &power;
++	};
++
++	chosen {
++		bootargs = "root=/dev/mmcblk0p5 rootfstype=squashfs,ext4 rootwait noinitrd";
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio6", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				mux {
++					pins = "gpio9", "gpio26", "gpio33", "gpio64";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-down;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++
++				clk {
++					pins = "gpio1";
++					input-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;
++				};
++
++				tx {
++					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ;
++					input-disable;
++				};
++			};
++
++			spi_pins: spi_pins {
++				mux {
++					pins = "gpio18", "gpio19", "gpio21";
++					function = "gsbi5";
++					drive-strength = <10>;
++					bias-none;
++				};
++
++				cs {
++					pins = "gpio20";
++					drive-strength = <12>;
++				};
++			};
++
++			usb0_pwr_en_pins: usb0_pwr_en_pins {
++				mux {
++					pins = "gpio16", "gpio17";
++					function = "gpio";
++					drive-strength = <12>;
++				};
++
++				pwr {
++					pins = "gpio17";
++					bias-pull-down;
++					output-high;
++				};
++
++				ovc {
++					pins = "gpio16";
++					bias-pull-up;
++				};
++			};
++
++			usb1_pwr_en_pins: usb1_pwr_en_pins {
++				mux {
++					pins = "gpio14", "gpio15";
++					function = "gpio";
++					drive-strength = <12>;
++				};
++
++				pwr {
++					pins = "gpio14";
++					bias-pull-down;
++					output-high;
++				};
++
++				ovc {
++					pins = "gpio15";
++					bias-pull-up;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		gsbi5: gsbi at 1a200000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++
++			spi4: spi at 1a280000 {
++				status = "ok";
++
++				pinctrl-0 = <&spi_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++				flash: m25p80 at 0 {
++					compatible = "jedec,spi-nor";
++					#address-cells = <1>;
++					#size-cells = <1>;
++					spi-max-frequency = <51200000>;
++					reg = <0>;
++
++					linux,part-probe = "qcom-smem";
++				};
++			};
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++
++			pinctrl-0 = <&usb0_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++
++			pinctrl-0 = <&usb1_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++			pinctrl-0 = <&pcie0_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++			pinctrl-0 = <&pcie1_pins>;
++			pinctrl-names = "default";
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++			phy0: ethernet-phy at 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 */
++					0x00970 0x1e864443  /* QM_PORT0_CTRL0 */
++					0x00974 0x000001c6  /* QM_PORT0_CTRL1 */
++					0x00978 0x19008643  /* QM_PORT1_CTRL0 */
++					0x0097c 0x000001c6  /* QM_PORT1_CTRL1 */
++					0x00980 0x19008643  /* QM_PORT2_CTRL0 */
++					0x00984 0x000001c6  /* QM_PORT2_CTRL1 */
++					0x00988 0x19008643  /* QM_PORT3_CTRL0 */
++					0x0098c 0x000001c6  /* QM_PORT3_CTRL1 */
++					0x00990 0x19008643  /* QM_PORT4_CTRL0 */
++					0x00994 0x000001c6  /* QM_PORT4_CTRL1 */
++					0x00998 0x1e864443  /* QM_PORT5_CTRL0 */
++					0x0099c 0x000001c6  /* QM_PORT5_CTRL1 */
++					0x009a0 0x1e864443  /* QM_PORT6_CTRL0 */
++					0x009a4 0x000001c6  /* QM_PORT6_CTRL1 */
++					>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++				qca,ar8327-initvals = <
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x0000c 0x80        /* PAD6_MODE */
++					>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++			qcom,phy_mdio_addr = <4>;
++			qcom,poll_required = <0>;
++			qcom,rgmii_delay = <1>;
++			qcom,phy_mii_type = <0>;
++			qcom,emulation = <0>;
++			qcom,irq = <255>;
++			mdiobus = <&mdio0>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++			qcom,phy_mdio_addr = <0>;	/* none */
++			qcom,poll_required = <0>;	/* no polling */
++			qcom,rgmii_delay = <0>;
++			qcom,phy_mii_type = <1>;
++			qcom,emulation = <0>;
++			qcom,irq = <258>;
++			mdiobus = <&mdio0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++
++		amba {
++			sdcc1: sdcc at 12400000 {
++				status = "okay";
++			};
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		internet {
++			label = "nbg6817:white:internet";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++		};
++
++		power: power {
++			label = "nbg6817:white:power";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		wifi2g {
++			label = "nbg6817:amber:wifi2g";
++			gpios = <&qcom_pinmux 33 GPIO_ACTIVE_HIGH>;
++		};
++
++		/* wifi2g amber from the manual is missing */
++
++		wifi5g {
++			label = "nbg6817:amber:wifi5g";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++		};
++
++		/* wifi5g amber from the manual is missing */
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-r7800.dts
+@@ -0,0 +1,566 @@
++#include "qcom-ipq8065-v1.0.dtsi"
++
++#include <dt-bindings/input/input.h>
++
++/ {
++	model = "Netgear Nighthawk X4S R7800";
++	compatible = "netgear,r7800", "qcom,ipq8065", "qcom,ipq8064";
++
++	memory at 0 {
++		reg = <0x42000000 0x1e000000>;
++		device_type = "memory";
++	};
++
++	reserved-memory {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++		rsvd at 41200000 {
++			reg = <0x41200000 0x300000>;
++			no-map;
++		};
++
++		rsvd at 5fe00000 {
++			reg = <0x5fe00000 0x200000>;
++			reusable;
++		};
++	};
++
++	aliases {
++		serial0 = &uart4;
++		mdio-gpio0 = &mdio0;
++
++		led-boot = &power_white;
++		led-failsafe = &power_amber;
++		led-running = &power_white;
++		led-upgrade = &power_amber;
++	};
++
++	chosen {
++		linux,stdout-path = "serial0:115200n8";
++	};
++
++	soc {
++		pinmux at 800000 {
++			button_pins: button_pins {
++				mux {
++					pins = "gpio6", "gpio54", "gpio65";
++					function = "gpio";
++					drive-strength = <2>;
++					bias-pull-up;
++				};
++			};
++
++			i2c4_pins: i2c4_pinmux {
++				mux {
++					pins = "gpio12", "gpio13";
++					function = "gsbi4";
++					drive-strength = <12>;
++					bias-disable;
++				};
++			};
++
++			led_pins: led_pins {
++				pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23",
++					"gpio24","gpio26", "gpio53", "gpio64";
++				function = "gpio";
++				drive-strength = <2>;
++				bias-pull-down;
++			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
++
++			mdio0_pins: mdio0_pins {
++				mux {
++					pins = "gpio0", "gpio1";
++					function = "gpio";
++					drive-strength = <8>;
++					bias-disable;
++				};
++
++				clk {
++					pins = "gpio1";
++					input-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;
++				};
++
++				tx {
++					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ;
++					input-disable;
++				};
++			};
++
++			spi_pins: spi_pins {
++				mux {
++					pins = "gpio18", "gpio19", "gpio21";
++					function = "gsbi5";
++					bias-pull-down;
++				};
++
++				data {
++					pins = "gpio18", "gpio19";
++					drive-strength = <10>;
++				};
++
++				cs {
++					pins = "gpio20";
++					drive-strength = <10>;
++					bias-pull-up;
++				};
++
++				clk {
++					pins = "gpio21";
++					drive-strength = <12>;
++				};
++			};
++
++			spi6_pins: spi6_pins {
++				mux {
++					pins = "gpio55", "gpio56", "gpio58";
++					function = "gsbi6";
++					bias-pull-down;
++				};
++
++				mosi {
++					pins = "gpio55";
++					drive-strength = <12>;
++				};
++
++				miso {
++					pins = "gpio56";
++					drive-strength = <14>;
++				};
++
++				cs {
++					pins = "gpio57";
++					drive-strength = <12>;
++					bias-pull-up;
++				};
++
++				clk {
++					pins = "gpio58";
++					drive-strength = <12>;
++				};
++
++				reset {
++					pins = "gpio33";
++					drive-strength = <10>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++
++			usb0_pwr_en_pins: usb0_pwr_en_pins {
++				mux {
++					pins = "gpio15";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++
++			usb1_pwr_en_pins: usb1_pwr_en_pins {
++				mux {
++					pins = "gpio16", "gpio68";
++					function = "gpio";
++					drive-strength = <12>;
++					bias-pull-down;
++					output-high;
++				};
++			};
++		};
++
++		gsbi at 16300000 {
++			qcom,mode = <GSBI_PROT_I2C_UART>;
++			status = "ok";
++			serial at 16340000 {
++				status = "ok";
++			};
++			/*
++			 * The i2c device on gsbi4 should not be enabled.
++			 * On ipq806x designs gsbi4 i2c is meant for exclusive
++			 * RPM usage. Turning this on in kernel manifests as
++			 * i2c failure for the RPM.
++			 */
++		};
++
++		gsbi5: gsbi at 1a200000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++
++			spi5: spi at 1a280000 {
++				status = "ok";
++
++				pinctrl-0 = <&spi_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>;
++
++				flash: m25p80 at 0 {
++					compatible = "jedec,spi-nor";
++					#address-cells = <1>;
++					#size-cells = <1>;
++					spi-max-frequency = <50000000>;
++					reg = <0>;
++
++					linux,part-probe = "qcom-smem";
++				};
++			};
++		};
++
++		gsbi6: gsbi at 16500000 {
++			qcom,mode = <GSBI_PROT_SPI>;
++			status = "ok";
++			spi6: spi at 16580000 {
++				status = "ok";
++
++				pinctrl-0 = <&spi6_pins>;
++				pinctrl-names = "default";
++
++				cs-gpios = <&qcom_pinmux 57 GPIO_ACTIVE_HIGH>;
++
++				spi-nor at 0 {
++					compatible = "jedec,spi-nor";
++					reg = <0>;
++					spi-max-frequency = <6000000>;
++				};
++			};
++		};
++
++		sata-phy at 1b400000 {
++			status = "ok";
++		};
++
++		sata at 29000000 {
++			ports-implemented = <0x1>;
++			status = "ok";
++		};
++
++		phy at 100f8800 {		/* USB3 port 1 HS phy */
++			status = "ok";
++		};
++
++		phy at 100f8830 {		/* USB3 port 1 SS phy */
++			status = "ok";
++		};
++
++		phy at 110f8800 {		/* USB3 port 0 HS phy */
++			status = "ok";
++		};
++
++		phy at 110f8830 {		/* USB3 port 0 SS phy */
++			status = "ok";
++		};
++
++		usb30 at 0 {
++			status = "ok";
++
++			pinctrl-0 = <&usb0_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		usb30 at 1 {
++			status = "ok";
++
++			pinctrl-0 = <&usb1_pwr_en_pins>;
++			pinctrl-names = "default";
++		};
++
++		pcie0: pci at 1b500000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		pcie1: pci at 1b700000 {
++			status = "ok";
++			phy-tx0-term-offset = <7>;
++		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-ecc-step-size = <512>;
++			nand-bus-width = <8>;
++
++			#address-cells = <1>;
++			#size-cells = <1>;
++
++			qcadata at 0 {
++				label = "qcadata";
++				reg = <0x0000000 0x0c80000>;
++				read-only;
++			};
++
++			APPSBL at c80000 {
++				label = "APPSBL";
++				reg = <0x0c80000 0x0500000>;
++				read-only;
++			};
++
++			APPSBLENV at 1180000 {
++				label = "APPSBLENV";
++				reg = <0x1180000 0x0080000>;
++				read-only;
++			};
++
++			art: art at 1200000 {
++				label = "art";
++				reg = <0x1200000 0x0140000>;
++				read-only;
++			};
++
++			artbak: art at 1340000 {
++				label = "artbak";
++				reg = <0x1340000 0x0140000>;
++				read-only;
++			};
++
++			kernel at 1480000 {
++				label = "kernel";
++				reg = <0x1480000 0x0200000>;
++			};
++
++			ubi at 1680000 {
++				label = "ubi";
++				reg = <0x1680000 0x1E00000>;
++			};
++
++			netgear at 3480000 {
++				label = "netgear";
++				reg = <0x3480000 0x4480000>;
++				read-only;
++			};
++
++			reserve at 7900000 {
++				label = "reserve";
++				reg = <0x7900000 0x0700000>;
++				read-only;
++			};
++
++			firmware at 1480000 {
++				label = "firmware";
++				reg = <0x1480000 0x2000000>;
++			};
++		};
++
++		mdio0: mdio {
++			compatible = "virtual,mdio-gpio";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
++			pinctrl-0 = <&mdio0_pins>;
++			pinctrl-names = "default";
++
++
++			phy0: ethernet-phy at 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 */
++					0x00970 0x1e864443  /* QM_PORT0_CTRL0 */
++					0x00974 0x000001c6  /* QM_PORT0_CTRL1 */
++					0x00978 0x19008643  /* QM_PORT1_CTRL0 */
++					0x0097c 0x000001c6  /* QM_PORT1_CTRL1 */
++					0x00980 0x19008643  /* QM_PORT2_CTRL0 */
++					0x00984 0x000001c6  /* QM_PORT2_CTRL1 */
++					0x00988 0x19008643  /* QM_PORT3_CTRL0 */
++					0x0098c 0x000001c6  /* QM_PORT3_CTRL1 */
++					0x00990 0x19008643  /* QM_PORT4_CTRL0 */
++					0x00994 0x000001c6  /* QM_PORT4_CTRL1 */
++					0x00998 0x1e864443  /* QM_PORT5_CTRL0 */
++					0x0099c 0x000001c6  /* QM_PORT5_CTRL1 */
++					0x009a0 0x1e864443  /* QM_PORT6_CTRL0 */
++					0x009a4 0x000001c6  /* QM_PORT6_CTRL1 */
++					>;
++				qca,ar8327-vlans = <
++					0x1	0x5e	    /* VLAN1 Ports 1/2/3/4/6 */
++					0x2	0x21	    /* VLAN2 Ports 0/5 */
++				>;
++			};
++
++			phy4: ethernet-phy at 4 {
++				device_type = "ethernet-phy";
++				reg = <4>;
++				qca,ar8327-initvals = <
++					0x000e4 0x6a545     /* MAC_POWER_SEL */
++					0x0000c 0x80        /* PAD6_MODE */
++					>;
++			};
++		};
++
++		gmac1: ethernet at 37200000 {
++			status = "ok";
++			phy-mode = "rgmii";
++			qcom,id = <1>;
++			qcom,phy_mdio_addr = <4>;
++			qcom,poll_required = <0>;
++			qcom,rgmii_delay = <1>;
++			qcom,phy_mii_type = <0>;
++			qcom,emulation = <0>;
++			qcom,irq = <255>;
++			mdiobus = <&mdio0>;
++
++			pinctrl-0 = <&rgmii2_pins>;
++			pinctrl-names = "default";
++
++			mtd-mac-address = <&art 6>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		gmac2: ethernet at 37400000 {
++			status = "ok";
++			phy-mode = "sgmii";
++			qcom,id = <2>;
++			qcom,phy_mdio_addr = <0>;	/* none */
++			qcom,poll_required = <0>;	/* no polling */
++			qcom,rgmii_delay = <0>;
++			qcom,phy_mii_type = <1>;
++			qcom,emulation = <0>;
++			qcom,irq = <258>;
++			mdiobus = <&mdio0>;
++
++			mtd-mac-address = <&art 0>;
++
++			fixed-link {
++				speed = <1000>;
++				full-duplex;
++			};
++		};
++
++		rpm at 108000 {
++			pinctrl-0 = <&i2c4_pins>;
++			pinctrl-names = "default";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-0 = <&button_pins>;
++		pinctrl-names = "default";
++
++		wifi {
++			label = "wifi";
++			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RFKILL>;
++			debounce-interval = <60>;
++			wakeup-source;
++		};
++
++		reset {
++			label = "reset";
++			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_RESTART>;
++			debounce-interval = <60>;
++			wakeup-source;
++		};
++
++		wps {
++			label = "wps";
++			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>;
++			linux,code = <KEY_WPS_BUTTON>;
++			debounce-interval = <60>;
++			wakeup-source;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-0 = <&led_pins>;
++		pinctrl-names = "default";
++
++		power_white: power_white {
++			label = "r7800:white:power";
++			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>;
++			default-state = "keep";
++		};
++
++		power_amber: power_amber {
++			label = "r7800:amber:power";
++			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_white {
++			label = "r7800:white:wan";
++			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>;
++		};
++
++		wan_amber {
++			label = "r7800:amber:wan";
++			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb1 {
++			label = "r7800:white:usb1";
++			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>;
++		};
++
++		usb2 {
++			label = "r7800:white:usb2";
++			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>;
++		};
++
++		esata {
++			label = "r7800:white:esata";
++			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>;
++		};
++
++		wifi {
++			label = "r7800:white:wifi";
++			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>;
++		};
++
++		wps {
++			label = "r7800:white:wps";
++			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>;
++		};
++	};
++};
++
++&adm_dma {
++	status = "ok";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi
+@@ -0,0 +1 @@
++#include "qcom-ipq8065.dtsi"



More information about the lede-commits mailing list