[OpenWrt-Devel] [PATCH v2] ipq806x: Add support for linux-4.4

Matthew McClintock msm-oss at mcclintock.net
Wed Aug 10 20:31:14 EDT 2016


I think there are a few more SPI fixes you could add right?

-M

On Tue, Aug 9, 2016 at 9:29 AM, Ram Chandra Jangir
<rjangi at codeaurora.org> wrote:
> 1)Changes
>
> - Rebased the patches for linux-4.4.7
> - Added patch to fix spi nor fifo and dma support
> - Added patch to configure watchdog barktime
>
> 2)Testing
>
> Tested on IPQ AP148 Board:
>  a. NOR boot and NAND boot
>  b. ethernet network and ath10k wifi
>  c. ubi sysupgrade
>  d. usb support
>
> 3)Known Issues:
>  Once we flash ubi image on AP148, and if we reset the board, uboot on
>  first boot creates PEB and LEB for dynamic sized partitions, which is incorrect
>  and not what linux expects which causes errors when trying to mount rootfs.
>  In order to test this, we can use the below steps:
>   a. Flash the ubi image on board and don't reset the board
>   b. load the kernel fit image in RAM and boot from there.
>
> Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> ---
>
> changes in v2:
>  Fixed spi unexpected irq bug
>  Fixed RPM-controller SMB208 support
>  Added usb-dwc3-of-simple for supporting DWC3 USB
>
> ---
>  target/linux/ipq806x/config-4.4                    |  463 +++++
>  target/linux/ipq806x/modules.mk                    |   17 +-
>  .../patches-4.4/020-add-ap148-bootargs.patch       |   61 +
>  .../patches-4.4/021-add-ap148-partitions.patch     |   19 +
>  .../ipq806x/patches-4.4/022-add-db149-dts.patch    |  160 ++
>  ...M-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch |   52 +
>  .../patches-4.4/024-ap148-add-memory-node.patch    |   14 +
>  ...33-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch |   33 +
>  ...-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch |   36 +
>  ...37-mtd-add-SMEM-parser-for-QCOM-platforms.patch |  282 +++
>  .../097-usb-dwc3-add-generic-OF-glue-layer.patch   |  244 +++
>  ...sb-dwc3-of-simple-fix-build-warning-on-PM.patch |   41 +
>  ...move-impossible-check-for-of_clk_get_pare.patch |   52 +
>  ...b-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch |  512 +++++
>  ...1-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch |  125 ++
>  ...CI-qcom-Document-PCIe-devicetree-bindings.patch |  263 +++
>  ...-qcom-Add-Qualcomm-PCIe-controller-driver.patch |  752 ++++++++
>  ...-qcom-add-pcie-nodes-to-ipq806x-platforms.patch |  244 +++
>  ...tomatically-select-PCI_DOMAINS-if-PCI-is-.patch |   29 +
>  .../patches-4.4/114-pcie-add-ctlr-init.patch       |  311 +++
>  .../patches-4.4/115-add-pcie-aux-clk-dts.patch     |   80 +
>  ...com-Update-support-for-RPM-controller-SMB.patch |  147 ++
>  ...-Add-Krait-L2-register-accessor-functions.patch |  144 ++
>  ...ux-Split-out-register-accessors-for-reuse.patch |  184 ++
>  ...ending-high-rates-to-downstream-clocks-du.patch |  127 ++
>  .../patches-4.4/136-clk-Add-safe-switch-hook.patch |  158 ++
>  ...dd-support-for-High-Frequency-PLLs-HFPLLs.patch |  351 ++++
>  .../138-clk-qcom-Add-HFPLL-driver.patch            |  206 ++
>  .../139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch        |  127 ++
>  ...140-clk-qcom-Add-support-for-Krait-clocks.patch |  272 +++
>  .../141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch     |  207 ++
>  ...lk-qcom-Add-Krait-clock-controller-driver.patch |  435 +++++
>  ...-module-to-register-cpufreq-on-Krait-CPUs.patch |  304 +++
>  ...m-Add-necessary-DT-data-for-Krait-cpufreq.patch |  100 +
>  ...ufreq-Add-a-cpufreq-krait-based-on-cpufre.patch |  461 +++++
>  ...-bindings-qcom_adm-Fix-channel-specifiers.patch |   76 +
>  .../patches-4.4/156-dmaengine-Add-ADM-driver.patch |  964 ++++++++++
>  .../157-ARM-DT-ipq8064-Add-ADM-device-node.patch   |   42 +
>  ...g-to-access-bad-block-markers-in-raw-mode.patch |   83 +
>  ...-mtd-nand-Qualcomm-NAND-controller-driver.patch | 2024 ++++++++++++++++++++
>  ...63-dt-bindings-qcom_nandc-Add-DT-bindings.patch |   82 +
>  ...-dts-Add-NAND-controller-node-for-ipq806x.patch |   51 +
>  ...nable-NAND-node-on-IPQ8064-AP148-platform.patch |   76 +
>  ...h-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch |   11 +
>  .../300-arch-arm-force-ZRELADDR-on-arch-qcom.patch |   62 +
>  .../302-mtd-qcom-smem-rename-rootfs-ubi.patch      |   13 +
>  ...RM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch |  146 ++
>  ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch |  216 +++
>  ...-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch |  134 ++
>  ...up-Make-sure-mode-is-only-determined-once.patch |  225 +++
>  ...om-set-WDT_BARK_TIME-register-offset-to-o.patch |   43 +
>  ...11-spi-qup-Fix-transaction-done-signaling.patch |   35 +
>  ...12-spi-qup-Fix-DMA-mode-to-work-correctly.patch |  226 +++
>  ...-spi-qup-Fix-block-mode-to-work-correctly.patch |  317 +++
>  ...-spi-qup-properly-detect-extra-interrupts.patch |   67 +
>  ...-t-re-read-opflags-to-see-if-transaction-.patch |   32 +
>  56 files changed, 11937 insertions(+), 1 deletion(-)
>  create mode 100644 target/linux/ipq806x/config-4.4
>  create mode 100644 target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/126-regulator-qcom-Update-support-for-RPM-controller-SMB.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch
>  create mode 100644 target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
>
> diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4
> new file mode 100644
> index 0000000..3a9592f
> --- /dev/null
> +++ b/target/linux/ipq806x/config-4.4
> @@ -0,0 +1,463 @@
> +CONFIG_ALIGNMENT_TRAP=y
> +# CONFIG_AMBA_PL08X is not set
> +# CONFIG_APM_EMULATION is not set
> +CONFIG_APQ_GCC_8084=y
> +CONFIG_APQ_MMCC_8084=y
> +CONFIG_AR8216_PHY=y
> +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
> +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
> +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
> +CONFIG_ARCH_HAS_SG_CHAIN=y
> +CONFIG_ARCH_HAS_TICK_BROADCAST=y
> +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
> +CONFIG_ARCH_HIBERNATION_POSSIBLE=y
> +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
> +CONFIG_ARCH_MSM8960=y
> +CONFIG_ARCH_MSM8974=y
> +CONFIG_ARCH_MSM8X60=y
> +CONFIG_ARCH_MULTIPLATFORM=y
> +# CONFIG_ARCH_MULTI_CPU_AUTO is not set
> +CONFIG_ARCH_MULTI_V6_V7=y
> +CONFIG_ARCH_MULTI_V7=y
> +CONFIG_ARCH_NR_GPIO=0
> +CONFIG_ARCH_QCOM=y
> +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
> +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
> +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
> +CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
> +CONFIG_ARCH_SUPPORTS_UPROBES=y
> +CONFIG_ARCH_SUSPEND_POSSIBLE=y
> +CONFIG_ARCH_USE_BUILTIN_BSWAP=y
> +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
> +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
> +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
> +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
> +CONFIG_ARM=y
> +CONFIG_ARM_AMBA=y
> +CONFIG_ARM_APPENDED_DTB=y
> +CONFIG_ARM_ARCH_TIMER=y
> +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
> +# CONFIG_ARM_ATAG_DTB_COMPAT is not set
> +CONFIG_ARM_CCI=y
> +CONFIG_ARM_CCI400_COMMON=y
> +CONFIG_ARM_CCI400_PMU=y
> +# CONFIG_ARM_CPUIDLE is not set
> +CONFIG_ARM_CPU_SUSPEND=y
> +CONFIG_ARM_GIC=y
> +CONFIG_ARM_HAS_SG_CHAIN=y
> +CONFIG_ARM_L1_CACHE_SHIFT=6
> +CONFIG_ARM_L1_CACHE_SHIFT_6=y
> +# CONFIG_ARM_LPAE is not set
> +CONFIG_ARM_PATCH_PHYS_VIRT=y
> +CONFIG_ARM_QCOM_CPUFREQ=y
> +# CONFIG_ARM_SMMU is not set
> +# CONFIG_ARM_SP805_WATCHDOG is not set
> +CONFIG_ARM_THUMB=y
> +# CONFIG_ARM_THUMBEE is not set
> +CONFIG_ARM_UNWIND=y
> +CONFIG_ARM_VIRT_EXT=y
> +CONFIG_AT803X_PHY=y
> +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
> +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
> +CONFIG_BOUNCE=y
> +CONFIG_BUILD_BIN2C=y
> +# CONFIG_CACHE_L2X0 is not set
> +CONFIG_CLEANCACHE=y
> +CONFIG_CLKDEV_LOOKUP=y
> +CONFIG_CLKSRC_OF=y
> +CONFIG_CLKSRC_QCOM=y
> +CONFIG_CLONE_BACKWARDS=y
> +CONFIG_COMMON_CLK=y
> +CONFIG_COMMON_CLK_QCOM=y
> +CONFIG_COMPACTION=y
> +CONFIG_COREDUMP=y
> +# CONFIG_CPUFREQ_DT is not set
> +CONFIG_CPU_32v6K=y
> +CONFIG_CPU_32v7=y
> +CONFIG_CPU_ABRT_EV7=y
> +# CONFIG_CPU_BIG_ENDIAN is not set
> +# CONFIG_CPU_BPREDICT_DISABLE is not set
> +CONFIG_CPU_CACHE_V7=y
> +CONFIG_CPU_CACHE_VIPT=y
> +CONFIG_CPU_COPY_V6=y
> +CONFIG_CPU_CP15=y
> +CONFIG_CPU_CP15_MMU=y
> +CONFIG_CPU_FREQ=y
> +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
> +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
> +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
> +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
> +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
> +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
> +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
> +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
> +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
> +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
> +CONFIG_CPU_FREQ_STAT=y
> +# CONFIG_CPU_FREQ_STAT_DETAILS is not set
> +CONFIG_CPU_HAS_ASID=y
> +# CONFIG_CPU_ICACHE_DISABLE is not set
> +CONFIG_CPU_IDLE=y
> +CONFIG_CPU_IDLE_GOV_LADDER=y
> +CONFIG_CPU_IDLE_GOV_MENU=y
> +CONFIG_CPU_PABRT_V7=y
> +CONFIG_CPU_PM=y
> +CONFIG_CPU_RMAP=y
> +# CONFIG_CPU_THERMAL is not set
> +CONFIG_CPU_TLB_V7=y
> +CONFIG_CPU_V7=y
> +CONFIG_CRC16=y
> +# CONFIG_CRC32_SARWATE is not set
> +CONFIG_CRC32_SLICEBY8=y
> +CONFIG_CROSS_MEMORY_ATTACH=y
> +CONFIG_CRYPTO_DEFLATE=y
> +CONFIG_CRYPTO_LZO=y
> +CONFIG_CRYPTO_RNG2=y
> +CONFIG_CRYPTO_WORKQUEUE=y
> +CONFIG_CRYPTO_XZ=y
> +CONFIG_DCACHE_WORD_ACCESS=y
> +CONFIG_DEBUG_BUGVERBOSE=y
> +CONFIG_DEBUG_GPIO=y
> +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
> +CONFIG_DEBUG_PREEMPT=y
> +# CONFIG_DEBUG_UART_8250 is not set
> +# CONFIG_DEBUG_USER is not set
> +CONFIG_DECOMPRESS_GZIP=y
> +CONFIG_DEVMEM=y
> +CONFIG_DMADEVICES=y
> +CONFIG_DMA_ENGINE=y
> +CONFIG_DMA_OF=y
> +CONFIG_DMA_VIRTUAL_CHANNELS=y
> +CONFIG_DTC=y
> +# CONFIG_DWMAC_GENERIC is not set
> +CONFIG_DWMAC_IPQ806X=y
> +# CONFIG_DWMAC_LPC18XX is not set
> +# CONFIG_DWMAC_MESON is not set
> +# CONFIG_DWMAC_ROCKCHIP is not set
> +# CONFIG_DWMAC_SOCFPGA is not set
> +# CONFIG_DWMAC_STI is not set
> +# CONFIG_DWMAC_SUNXI is not set
> +# CONFIG_DW_DMAC_PCI is not set
> +CONFIG_DYNAMIC_DEBUG=y
> +CONFIG_ETHERNET_PACKET_MANGLE=y
> +CONFIG_FIXED_PHY=y
> +CONFIG_FREEZER=y
> +CONFIG_GENERIC_ALLOCATOR=y
> +CONFIG_GENERIC_BUG=y
> +CONFIG_GENERIC_CLOCKEVENTS=y
> +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
> +CONFIG_GENERIC_CPUFREQ_KRAIT=y
> +CONFIG_GENERIC_IDLE_POLL_SETUP=y
> +CONFIG_GENERIC_IO=y
> +CONFIG_GENERIC_IRQ_SHOW=y
> +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
> +CONFIG_GENERIC_MSI_IRQ=y
> +CONFIG_GENERIC_PCI_IOMAP=y
> +CONFIG_GENERIC_PHY=y
> +CONFIG_GENERIC_PINCONF=y
> +CONFIG_GENERIC_SCHED_CLOCK=y
> +CONFIG_GENERIC_SMP_IDLE_THREAD=y
> +CONFIG_GENERIC_STRNCPY_FROM_USER=y
> +CONFIG_GENERIC_STRNLEN_USER=y
> +CONFIG_GENERIC_TIME_VSYSCALL=y
> +CONFIG_GPIOLIB=y
> +CONFIG_GPIOLIB_IRQCHIP=y
> +CONFIG_GPIO_DEVRES=y
> +# CONFIG_GPIO_MSM_V2 is not set
> +CONFIG_GPIO_SYSFS=y
> +CONFIG_HANDLE_DOMAIN_IRQ=y
> +CONFIG_HARDIRQS_SW_RESEND=y
> +CONFIG_HAS_DMA=y
> +CONFIG_HAS_IOMEM=y
> +CONFIG_HAS_IOPORT_MAP=y
> +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
> +CONFIG_HAVE_ARCH_AUDITSYSCALL=y
> +CONFIG_HAVE_ARCH_BITREVERSE=y
> +CONFIG_HAVE_ARCH_JUMP_LABEL=y
> +CONFIG_HAVE_ARCH_KGDB=y
> +CONFIG_HAVE_ARCH_PFN_VALID=y
> +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
> +CONFIG_HAVE_ARCH_TRACEHOOK=y
> +CONFIG_HAVE_ARM_ARCH_TIMER=y
> +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
> +CONFIG_HAVE_BPF_JIT=y
> +CONFIG_HAVE_CC_STACKPROTECTOR=y
> +CONFIG_HAVE_CLK=y
> +CONFIG_HAVE_CLK_PREPARE=y
> +CONFIG_HAVE_CONTEXT_TRACKING=y
> +CONFIG_HAVE_C_RECORDMCOUNT=y
> +CONFIG_HAVE_DEBUG_KMEMLEAK=y
> +CONFIG_HAVE_DMA_API_DEBUG=y
> +CONFIG_HAVE_DMA_ATTRS=y
> +CONFIG_HAVE_DMA_CONTIGUOUS=y
> +CONFIG_HAVE_DYNAMIC_FTRACE=y
> +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
> +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
> +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
> +CONFIG_HAVE_FUNCTION_TRACER=y
> +CONFIG_HAVE_GENERIC_DMA_COHERENT=y
> +CONFIG_HAVE_HW_BREAKPOINT=y
> +CONFIG_HAVE_IDE=y
> +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
> +CONFIG_HAVE_KERNEL_GZIP=y
> +CONFIG_HAVE_KERNEL_LZ4=y
> +CONFIG_HAVE_KERNEL_LZMA=y
> +CONFIG_HAVE_KERNEL_LZO=y
> +CONFIG_HAVE_KERNEL_XZ=y
> +CONFIG_HAVE_MEMBLOCK=y
> +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
> +CONFIG_HAVE_NET_DSA=y
> +CONFIG_HAVE_OPROFILE=y
> +CONFIG_HAVE_OPTPROBES=y
> +CONFIG_HAVE_PERF_EVENTS=y
> +CONFIG_HAVE_PERF_REGS=y
> +CONFIG_HAVE_PERF_USER_STACK_DUMP=y
> +CONFIG_HAVE_PROC_CPU=y
> +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
> +CONFIG_HAVE_SMP=y
> +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
> +CONFIG_HAVE_UID16=y
> +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
> +CONFIG_HIGHMEM=y
> +CONFIG_HIGHPTE=y
> +CONFIG_HOTPLUG_CPU=y
> +# CONFIG_HSU_DMA_PCI is not set
> +CONFIG_HWMON=y
> +CONFIG_HWSPINLOCK=y
> +CONFIG_HWSPINLOCK_QCOM=y
> +CONFIG_HW_RANDOM=y
> +CONFIG_HW_RANDOM_MSM=y
> +CONFIG_HZ_FIXED=0
> +CONFIG_I2C=y
> +CONFIG_I2C_BOARDINFO=y
> +CONFIG_I2C_CHARDEV=y
> +CONFIG_I2C_COMPAT=y
> +CONFIG_I2C_HELPER_AUTO=y
> +CONFIG_I2C_QUP=y
> +CONFIG_IKCONFIG=y
> +CONFIG_IKCONFIG_PROC=y
> +CONFIG_INITRAMFS_SOURCE=""
> +CONFIG_IOMMU_HELPER=y
> +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
> +CONFIG_IOMMU_SUPPORT=y
> +CONFIG_IPQ_GCC_806X=y
> +# CONFIG_IPQ_LCC_806X is not set
> +CONFIG_IRQCHIP=y
> +CONFIG_IRQ_DOMAIN=y
> +CONFIG_IRQ_DOMAIN_HIERARCHY=y
> +CONFIG_IRQ_FORCED_THREADING=y
> +CONFIG_IRQ_WORK=y
> +CONFIG_KPSS_XCC=y
> +CONFIG_KRAITCC=y
> +CONFIG_KRAIT_CLOCKS=y
> +CONFIG_KRAIT_L2_ACCESSORS=y
> +CONFIG_LIBFDT=y
> +CONFIG_LOCKUP_DETECTOR=y
> +CONFIG_LOCK_SPIN_ON_OWNER=y
> +CONFIG_LZO_COMPRESS=y
> +CONFIG_LZO_DECOMPRESS=y
> +CONFIG_MDIO_BITBANG=y
> +CONFIG_MDIO_BOARDINFO=y
> +CONFIG_MDIO_GPIO=y
> +CONFIG_MFD_QCOM_RPM=y
> +# CONFIG_MFD_SPMI_PMIC is not set
> +CONFIG_MFD_SYSCON=y
> +CONFIG_MIGHT_HAVE_CACHE_L2X0=y
> +CONFIG_MIGHT_HAVE_PCI=y
> +CONFIG_MIGRATION=y
> +CONFIG_MODULES_USE_ELF_REL=y
> +CONFIG_MSM_GCC_8660=y
> +# CONFIG_MSM_GCC_8916 is not set
> +CONFIG_MSM_GCC_8960=y
> +CONFIG_MSM_GCC_8974=y
> +# CONFIG_MSM_LCC_8960 is not set
> +CONFIG_MSM_MMCC_8960=y
> +CONFIG_MSM_MMCC_8974=y
> +CONFIG_MTD_CMDLINE_PARTS=y
> +CONFIG_MTD_M25P80=y
> +CONFIG_MTD_NAND=y
> +CONFIG_MTD_NAND_ECC=y
> +CONFIG_MTD_NAND_QCOM=y
> +CONFIG_MTD_QCOM_SMEM_PARTS=y
> +CONFIG_MTD_SPI_NOR=y
> +CONFIG_MTD_SPLIT_FIRMWARE=y
> +CONFIG_MTD_SPLIT_FIT_FW=y
> +CONFIG_MTD_UBI=y
> +CONFIG_MTD_UBI_BEB_LIMIT=20
> +CONFIG_MTD_UBI_BLOCK=y
> +# CONFIG_MTD_UBI_FASTMAP is not set
> +# CONFIG_MTD_UBI_GLUEBI is not set
> +CONFIG_MTD_UBI_WL_THRESHOLD=4096
> +CONFIG_MULTI_IRQ_HANDLER=y
> +CONFIG_MUTEX_SPIN_ON_OWNER=y
> +CONFIG_NEED_DMA_MAP_STATE=y
> +CONFIG_NEON=y
> +CONFIG_NET_FLOW_LIMIT=y
> +CONFIG_NET_PTP_CLASSIFY=y
> +CONFIG_NET_VENDOR_WIZNET=y
> +CONFIG_NO_BOOTMEM=y
> +CONFIG_NO_HZ=y
> +CONFIG_NO_HZ_COMMON=y
> +CONFIG_NO_HZ_IDLE=y
> +CONFIG_NR_CPUS=4
> +CONFIG_OF=y
> +CONFIG_OF_ADDRESS=y
> +CONFIG_OF_ADDRESS_PCI=y
> +CONFIG_OF_EARLY_FLATTREE=y
> +CONFIG_OF_FLATTREE=y
> +CONFIG_OF_GPIO=y
> +CONFIG_OF_IRQ=y
> +CONFIG_OF_MDIO=y
> +CONFIG_OF_MTD=y
> +CONFIG_OF_NET=y
> +CONFIG_OF_PCI=y
> +CONFIG_OF_PCI_IRQ=y
> +CONFIG_OF_RESERVED_MEM=y
> +CONFIG_OLD_SIGACTION=y
> +CONFIG_OLD_SIGSUSPEND3=y
> +CONFIG_PAGEFLAGS_EXTENDED=y
> +CONFIG_PAGE_OFFSET=0xC0000000
> +CONFIG_PCI=y
> +CONFIG_PCIEAER=y
> +CONFIG_PCIEPORTBUS=y
> +CONFIG_PCIE_DW=y
> +# CONFIG_PCIE_IPROC is not set
> +CONFIG_PCIE_PME=y
> +CONFIG_PCIE_QCOM=y
> +CONFIG_PCI_DEBUG=y
> +CONFIG_PCI_DOMAINS=y
> +CONFIG_PCI_DOMAINS_GENERIC=y
> +CONFIG_PCI_MSI=y
> +CONFIG_PERF_EVENTS=y
> +CONFIG_PERF_USE_VMALLOC=y
> +CONFIG_PGTABLE_LEVELS=2
> +CONFIG_PHYLIB=y
> +# CONFIG_PHY_QCOM_APQ8064_SATA is not set
> +CONFIG_PHY_QCOM_IPQ806X_SATA=y
> +# CONFIG_PHY_QCOM_UFS is not set
> +CONFIG_PINCTRL=y
> +CONFIG_PINCTRL_APQ8064=y
> +# CONFIG_PINCTRL_APQ8084 is not set
> +CONFIG_PINCTRL_IPQ8064=y
> +CONFIG_PINCTRL_MSM=y
> +# CONFIG_PINCTRL_MSM8660 is not set
> +# CONFIG_PINCTRL_MSM8916 is not set
> +# CONFIG_PINCTRL_MSM8960 is not set
> +CONFIG_PINCTRL_MSM8X74=y
> +# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set
> +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set
> +# CONFIG_PL330_DMA is not set
> +CONFIG_PM=y
> +CONFIG_PM_CLK=y
> +# CONFIG_PM_DEBUG is not set
> +CONFIG_PM_OPP=y
> +CONFIG_PM_SLEEP=y
> +CONFIG_PM_SLEEP_SMP=y
> +CONFIG_POWER_RESET=y
> +# CONFIG_POWER_RESET_BRCMSTB is not set
> +CONFIG_POWER_RESET_MSM=y
> +CONFIG_POWER_SUPPLY=y
> +CONFIG_PPS=y
> +CONFIG_PREEMPT=y
> +CONFIG_PREEMPT_COUNT=y
> +# CONFIG_PREEMPT_NONE is not set
> +CONFIG_PREEMPT_RCU=y
> +CONFIG_PRINTK_TIME=y
> +CONFIG_PROC_PAGE_MONITOR=y
> +CONFIG_PTP_1588_CLOCK=y
> +CONFIG_QCOM_ADM=y
> +CONFIG_QCOM_BAM_DMA=y
> +CONFIG_QCOM_GSBI=y
> +CONFIG_QCOM_HFPLL=y
> +# CONFIG_QCOM_PM is not set
> +CONFIG_QCOM_SCM=y
> +# CONFIG_QCOM_SMD is not set
> +CONFIG_QCOM_SMEM=y
> +CONFIG_QCOM_WDT=y
> +CONFIG_RAS=y
> +# CONFIG_RCU_BOOST is not set
> +CONFIG_RCU_CPU_STALL_TIMEOUT=21
> +CONFIG_RCU_STALL_COMMON=y
> +CONFIG_RD_GZIP=y
> +CONFIG_REGMAP=y
> +CONFIG_REGMAP_MMIO=y
> +CONFIG_REGULATOR=y
> +CONFIG_REGULATOR_QCOM_RPM=y
> +# CONFIG_REGULATOR_QCOM_SPMI is not set
> +CONFIG_RESET_CONTROLLER=y
> +CONFIG_RFS_ACCEL=y
> +CONFIG_RPS=y
> +CONFIG_RTC_CLASS=y
> +# CONFIG_RTC_DRV_CMOS is not set
> +CONFIG_RWSEM_SPIN_ON_OWNER=y
> +CONFIG_RWSEM_XCHGADD_ALGORITHM=y
> +CONFIG_SCHED_HRTICK=y
> +# CONFIG_SCSI_DMA is not set
> +# CONFIG_SERIAL_AMBA_PL010 is not set
> +# CONFIG_SERIAL_AMBA_PL011 is not set
> +CONFIG_SERIAL_MSM=y
> +CONFIG_SERIAL_MSM_CONSOLE=y
> +# CONFIG_SLAB is not set
> +CONFIG_SLUB=y
> +CONFIG_SLUB_CPU_PARTIAL=y
> +CONFIG_SMP=y
> +CONFIG_SMP_ON_UP=y
> +CONFIG_SPARSE_IRQ=y
> +CONFIG_SPI=y
> +CONFIG_SPI_MASTER=y
> +CONFIG_SPI_QUP=y
> +CONFIG_SPMI=y
> +CONFIG_SPMI_MSM_PMIC_ARB=y
> +CONFIG_SRCU=y
> +CONFIG_STMMAC_ETH=y
> +CONFIG_STMMAC_PLATFORM=y
> +CONFIG_STOP_MACHINE=y
> +# CONFIG_STRIP_ASM_SYMS is not set
> +CONFIG_SUSPEND=y
> +CONFIG_SUSPEND_FREEZER=y
> +CONFIG_SWCONFIG=y
> +CONFIG_SWIOTLB=y
> +CONFIG_SWP_EMULATE=y
> +CONFIG_SYS_SUPPORTS_APM_EMULATION=y
> +CONFIG_THERMAL=y
> +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
> +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
> +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
> +# CONFIG_THERMAL_EMULATION is not set
> +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
> +CONFIG_THERMAL_GOV_STEP_WISE=y
> +# CONFIG_THERMAL_GOV_USER_SPACE is not set
> +CONFIG_THERMAL_HWMON=y
> +CONFIG_THERMAL_OF=y
> +# CONFIG_THUMB2_KERNEL is not set
> +CONFIG_TICK_CPU_ACCOUNTING=y
> +CONFIG_TIMER_STATS=y
> +CONFIG_UBIFS_FS=y
> +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
> +CONFIG_UBIFS_FS_LZO=y
> +CONFIG_UBIFS_FS_XZ=y
> +CONFIG_UBIFS_FS_ZLIB=y
> +CONFIG_UEVENT_HELPER_PATH=""
> +CONFIG_UID16=y
> +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
> +CONFIG_UNINLINE_SPIN_UNLOCK=y
> +CONFIG_USB_SUPPORT=y
> +CONFIG_USE_OF=y
> +CONFIG_VDSO=y
> +CONFIG_VECTORS_BASE=0xffff0000
> +CONFIG_VFP=y
> +CONFIG_VFPv3=y
> +CONFIG_VM_EVENT_COUNTERS=y
> +CONFIG_WATCHDOG_CORE=y
> +# CONFIG_WIZNET_W5100 is not set
> +# CONFIG_WIZNET_W5300 is not set
> +# CONFIG_WL_TI is not set
> +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
> +CONFIG_XPS=y
> +CONFIG_XZ_DEC_ARM=y
> +CONFIG_XZ_DEC_BCJ=y
> +CONFIG_ZBOOT_ROM_BSS=0
> +CONFIG_ZBOOT_ROM_TEXT=0
> +CONFIG_ZLIB_DEFLATE=y
> +CONFIG_ZLIB_INFLATE=y
> +CONFIG_ZONE_DMA_FLAG=0
> diff --git a/target/linux/ipq806x/modules.mk b/target/linux/ipq806x/modules.mk
> index 04a2d4f..274f344 100644
> --- a/target/linux/ipq806x/modules.mk
> +++ b/target/linux/ipq806x/modules.mk
> @@ -1,6 +1,6 @@
>  define KernelPackage/usb-phy-qcom-dwc3
>    TITLE:=DWC3 USB QCOM PHY driver
> -  DEPENDS:=@TARGET_ipq806x
> +  DEPENDS:=@TARGET_ipq806x +kmod-usb-dwc3-of-simple
>    KCONFIG:= CONFIG_PHY_QCOM_DWC3
>    FILES:= $(LINUX_DIR)/drivers/phy/phy-qcom-dwc3.ko
>    AUTOLOAD:=$(call AutoLoad,45,phy-qcom-dwc3,1)
> @@ -14,6 +14,21 @@ endef
>
>  $(eval $(call KernelPackage,usb-phy-qcom-dwc3))
>
> +define KernelPackage/usb-dwc3-of-simple
> +  TITLE:=DWC3 USB simple OF driver
> +  DEPENDS:=+kmod-usb-dwc3
> +  KCONFIG:= CONFIG_USB_DWC3_OF_SIMPLE
> +  FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-of-simple.ko
> +  AUTOLOAD:=$(call AutoLoad,53,dwc3-of-simple,1)
> +  $(call AddDepends/usb)
> +endef
> +
> +define KernelPackage/usb-dwc3-of-simple/description
> + This driver provides generic platform glue for the integrated DesignWare
> + USB3 IP Core.
> +endef
> +
> +$(eval $(call KernelPackage,usb-dwc3-of-simple))
>
>  define KernelPackage/usb-dwc3-qcom
>    TITLE:=DWC3 USB QCOM controller driver
> diff --git a/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch
> new file mode 100644
> index 0000000..9c33bad
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch
> @@ -0,0 +1,61 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -4,14 +4,6 @@
> +       model = "Qualcomm IPQ8064/AP148";
> +       compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
> +
> +-      aliases {
> +-              serial0 = &gsbi4_serial;
> +-      };
> +-
> +-      chosen {
> +-              stdout-path = "serial0:115200n8";
> +-      };
> +-
> +       reserved-memory {
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +@@ -22,6 +14,14 @@
> +               };
> +       };
> +
> ++      alias {
> ++              serial0 = &uart4;
> ++      };
> ++
> ++      chosen {
> ++              linux,stdout-path = "serial0:115200n8";
> ++      };
> ++
> +       soc {
> +               pinmux at 800000 {
> +                       i2c4_pins: i2c4_pinmux {
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -159,7 +159,7 @@
> +
> +                       syscon-tcsr = <&tcsr>;
> +
> +-                      serial at 12490000 {
> ++                      uart2: serial at 12490000 {
> +                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
> +                               reg = <0x12490000 0x1000>,
> +                                     <0x12480000 0x1000>;
> +@@ -197,7 +197,7 @@
> +
> +                       syscon-tcsr = <&tcsr>;
> +
> +-                      gsbi4_serial: serial at 16340000 {
> ++                      uart4: serial at 16340000 {
> +                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
> +                               reg = <0x16340000 0x1000>,
> +                                     <0x16300000 0x1000>;
> +@@ -234,7 +234,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>;
> diff --git a/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch
> new file mode 100644
> index 0000000..bfdb30f
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch
> @@ -0,0 +1,19 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -77,15 +77,7 @@
> +                                       spi-max-frequency = <50000000>;
> +                                       reg = <0>;
> +
> +-                                      partition at 0 {
> +-                                              label = "rootfs";
> +-                                              reg = <0x0 0x1000000>;
> +-                                      };
> +-
> +-                                      partition at 1 {
> +-                                              label = "scratch";
> +-                                              reg = <0x1000000 0x1000000>;
> +-                                      };
> ++                                      linux,part-probe = "qcom-smem";
> +                               };
> +                       };
> +               };
> diff --git a/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch
> new file mode 100644
> index 0000000..4d3e827
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch
> @@ -0,0 +1,160 @@
> +From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Tue, 7 Apr 2015 19:58:58 -0700
> +Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree
> +
> +Add basic DB149 (IPQ806x based platform) device-tree. It supports UART,
> +SATA, USB2, USB3 and NOR flash.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/Makefile               |   1 +
> + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++
> + 2 files changed, 133 insertions(+)
> + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts
> +
> +--- a/arch/arm/boot/dts/Makefile
> ++++ b/arch/arm/boot/dts/Makefile
> +@@ -506,6 +506,7 @@
> +       qcom-apq8084-ifc6540.dtb \
> +       qcom-apq8084-mtp.dtb \
> +       qcom-ipq8064-ap148.dtb \
> ++      qcom-ipq8064-db149.dtb \
> +       qcom-msm8660-surf.dtb \
> +       qcom-msm8960-cdp.dtb \
> +       qcom-msm8974-sony-xperia-honami.dtb
> +--- /dev/null
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> +@@ -0,0 +1,132 @@
> ++#include "qcom-ipq8064-v1.0.dtsi"
> ++
> ++/ {
> ++      model = "Qualcomm IPQ8064/DB149";
> ++      compatible = "qcom,ipq8064-db149", "qcom,ipq8064";
> ++
> ++      reserved-memory {
> ++              #address-cells = <1>;
> ++              #size-cells = <1>;
> ++              ranges;
> ++              rsvd at 41200000 {
> ++                      reg = <0x41200000 0x300000>;
> ++                      no-map;
> ++              };
> ++      };
> ++
> ++      alias {
> ++              serial0 = &uart2;
> ++      };
> ++
> ++      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;
> ++                              };
> ++                      };
> ++              };
> ++
> ++              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";
> ++              };
> ++      };
> ++};
> diff --git a/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
> new file mode 100644
> index 0000000..b8c527b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
> @@ -0,0 +1,52 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -47,14 +47,12 @@
> +                               status = "ok";
> +                       };
> +
> +-                      i2c4: i2c 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 {
> +--- a/drivers/clk/qcom/gcc-ipq806x.c
> ++++ b/drivers/clk/qcom/gcc-ipq806x.c
> +@@ -294,7 +294,7 @@
> +                       .parent_names = gcc_pxo_pll8,
> +                       .num_parents = 2,
> +                       .ops = &clk_rcg_ops,
> +-                      .flags = CLK_SET_PARENT_GATE,
> ++                      .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
> +               },
> +       },
> + };
> +@@ -312,7 +312,7 @@
> +                       },
> +                       .num_parents = 1,
> +                       .ops = &clk_branch_ops,
> +-                      .flags = CLK_SET_RATE_PARENT,
> ++                      .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +               },
> +       },
> + };
> +@@ -890,7 +890,7 @@
> +               .hw.init = &(struct clk_init_data){
> +                       .name = "gsbi1_h_clk",
> +                       .ops = &clk_branch_ops,
> +-                      .flags = CLK_IS_ROOT,
> ++                      .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
> +               },
> +       },
> + };
> diff --git a/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch
> new file mode 100644
> index 0000000..f026ed9
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch
> @@ -0,0 +1,14 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -4,6 +4,11 @@
> +       model = "Qualcomm IPQ8064/AP148";
> +       compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
> +
> ++      memory at 0 {
> ++              reg = <0x42000000 0x1e000000>;
> ++              device_type = "memory";
> ++      };
> ++
> +       reserved-memory {
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> diff --git a/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
> new file mode 100644
> index 0000000..e1d317d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
> @@ -0,0 +1,33 @@
> +From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Thu, 13 Aug 2015 09:45:26 -0700
> +Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts
> +
> +Add one new node to the ipq806x.dtsi file to declare & register the
> +hardware spinlock devices. This mechanism is required to be used by
> +other drivers such as SMEM.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++
> + 1 file changed, 11 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -329,5 +329,16 @@
> +                       #reset-cells = <1>;
> +               };
> +
> ++              sfpb_mutex_block: syscon at 1200600 {
> ++                      compatible = "syscon";
> ++                      reg = <0x01200600 0x100>;
> ++              };
> ++      };
> ++
> ++      sfpb_mutex: sfpb-mutex {
> ++              compatible = "qcom,sfpb-mutex";
> ++              syscon = <&sfpb_mutex_block 4 4>;
> ++
> ++              #hwlock-cells = <1>;
> +       };
> + };
> diff --git a/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
> new file mode 100644
> index 0000000..06b1405
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
> @@ -0,0 +1,36 @@
> +From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Fri, 14 Aug 2015 11:17:20 -0700
> +Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts
> +
> +SMEM is used on IPQ806x to store various board related information such
> +as boot device and flash partition layout. We'll declare it as a device
> +so we can make use of it thanks to the new SMEM soc driver.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++-
> + 1 file changed, 7 insertions(+), 1 deletion(-)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -55,7 +55,7 @@
> +                       no-map;
> +               };
> +
> +-              smem at 41000000 {
> ++              smem: smem at 41000000 {
> +                       reg = <0x41000000 0x200000>;
> +                       no-map;
> +               };
> +@@ -341,4 +341,10 @@
> +
> +               #hwlock-cells = <1>;
> +       };
> ++
> ++      smem {
> ++              compatible = "qcom,smem";
> ++              memory-region = <&smem>;
> ++              hwlocks = <&sfpb_mutex 3>;
> ++      };
> + };
> diff --git a/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
> new file mode 100644
> index 0000000..d80eb86
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
> @@ -0,0 +1,282 @@
> +From 61e8e1b1af77f24339da3f0822a76fa65ed635c6 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Thu, 13 Aug 2015 09:53:14 -0700
> +Subject: [PATCH] mtd: add SMEM parser for QCOM platforms
> +
> +On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
> +used to store partition layout. This new parser can now be used to read
> +SMEM and use it to register an MTD layout according to its content.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/mtd/Kconfig          |   7 ++
> + drivers/mtd/Makefile         |   1 +
> + drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
> + 3 files changed, 236 insertions(+)
> + create mode 100644 drivers/mtd/qcom_smem_part.c
> +
> +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
> +index a03ad29..debc887 100644
> +--- a/drivers/mtd/Kconfig
> ++++ b/drivers/mtd/Kconfig
> +@@ -190,6 +190,13 @@
> +         You will still need the parsing functions to be called by the driver
> +         for your particular device. It won't happen automatically.
> +
> ++config MTD_QCOM_SMEM_PARTS
> ++      tristate "QCOM SMEM partitioning support"
> ++      depends on QCOM_SMEM
> ++      help
> ++        This provides partitions parser for QCOM devices using SMEM
> ++        such as IPQ806x.
> ++
> + comment "User Modules And Translation Layers"
> +
> + #
> +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
> +index 99bb9a1..2a44a64 100644
> +--- a/drivers/mtd/Makefile
> ++++ b/drivers/mtd/Makefile
> +@@ -16,6 +16,7 @@
> + obj-$(CONFIG_MTD_BCM63XX_PARTS)       += bcm63xxpart.o
> + obj-$(CONFIG_MTD_BCM47XX_PARTS)       += bcm47xxpart.o
> + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
> ++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
> +
> + # 'Users' - code which presents functionality to userspace.
> + obj-$(CONFIG_MTD_BLKDEVS)     += mtd_blkdevs.o
> +diff --git a/drivers/mtd/qcom_smem_part.c b/drivers/mtd/qcom_smem_part.c
> +new file mode 100644
> +index 0000000..f9c1bca
> +--- /dev/null
> ++++ b/drivers/mtd/qcom_smem_part.c
> +@@ -0,0 +1,228 @@
> ++/*
> ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/device.h>
> ++#include <linux/slab.h>
> ++
> ++#include <linux/mtd/mtd.h>
> ++#include <linux/mtd/partitions.h>
> ++#include <linux/spi/spi.h>
> ++#include <linux/module.h>
> ++
> ++#include <linux/soc/qcom/smem.h>
> ++
> ++/* Processor/host identifier for the application processor */
> ++#define SMEM_HOST_APPS                        0
> ++
> ++/* SMEM items index */
> ++#define SMEM_AARM_PARTITION_TABLE     9
> ++#define SMEM_BOOT_FLASH_TYPE          421
> ++#define SMEM_BOOT_FLASH_BLOCK_SIZE    424
> ++
> ++/* SMEM Flash types */
> ++#define SMEM_FLASH_NAND                       2
> ++#define SMEM_FLASH_SPI                        6
> ++
> ++#define SMEM_PART_NAME_SZ             16
> ++#define SMEM_PARTS_MAX                        32
> ++
> ++struct smem_partition {
> ++      char name[SMEM_PART_NAME_SZ];
> ++      __le32 start;
> ++      __le32 size;
> ++      __le32 attr;
> ++};
> ++
> ++struct smem_partition_table {
> ++      u8 magic[8];
> ++      __le32 version;
> ++      __le32 len;
> ++      struct smem_partition parts[SMEM_PARTS_MAX];
> ++};
> ++
> ++/* SMEM Magic values in partition table */
> ++static const u8 SMEM_PTABLE_MAGIC[] = {
> ++      0xaa, 0x73, 0xee, 0x55,
> ++      0xdb, 0xbd, 0x5e, 0xe3,
> ++};
> ++
> ++static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
> ++{
> ++      size_t size;
> ++
> ++      *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
> ++                                  &size);
> ++
> ++      if (IS_ERR(*smem_blksz)) {
> ++              pr_err("Unable to read flash blksz from SMEM\n");
> ++              return -ENOENT;
> ++      }
> ++
> ++      if (size != sizeof(**smem_blksz)) {
> ++              pr_err("Invalid flash blksz size in SMEM\n");
> ++              return -EINVAL;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_smem_get_flash_type(u64 **smem_flash_type)
> ++{
> ++      size_t size;
> ++
> ++      *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
> ++                                      &size);
> ++
> ++      if (IS_ERR(*smem_flash_type)) {
> ++              pr_err("Unable to read flash type from SMEM\n");
> ++              return -ENOENT;
> ++      }
> ++
> ++      if (size != sizeof(**smem_flash_type)) {
> ++              pr_err("Invalid flash type size in SMEM\n");
> ++              return -EINVAL;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
> ++{
> ++      size_t size;
> ++
> ++      *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
> ++                              &size);
> ++
> ++      if (IS_ERR(*pparts)) {
> ++              pr_err("Unable to read partition table from SMEM\n");
> ++              return -ENOENT;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int of_dev_node_match(struct device *dev, void *data)
> ++{
> ++      return dev->of_node == data;
> ++}
> ++
> ++static bool is_spi_device(struct device_node *np)
> ++{
> ++      struct device *dev;
> ++
> ++      dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
> ++      if (!dev)
> ++              return false;
> ++
> ++      put_device(dev);
> ++      return true;
> ++}
> ++
> ++static int parse_qcom_smem_partitions(struct mtd_info *master,
> ++                                    struct mtd_partition **pparts,
> ++                                    struct mtd_part_parser_data *data)
> ++{
> ++      struct smem_partition_table *smem_parts;
> ++      u64 *smem_flash_type, *smem_blksz;
> ++      struct mtd_partition *mtd_parts;
> ++      struct device_node *of_node = data->of_node;
> ++      int i, ret;
> ++
> ++      /*
> ++       * SMEM will only store the partition table of the boot device.
> ++       * If this is not the boot device, do not return any partition.
> ++       */
> ++      ret = qcom_smem_get_flash_type(&smem_flash_type);
> ++      if (ret < 0)
> ++              return ret;
> ++
> ++      if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
> ++          || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
> ++              return 0;
> ++
> ++      /*
> ++       * Just for sanity purpose, make sure the block size in SMEM matches the
> ++       * block size of the MTD device
> ++       */
> ++      ret = qcom_smem_get_flash_blksz(&smem_blksz);
> ++      if (ret < 0)
> ++              return ret;
> ++
> ++      if (*smem_blksz != master->erasesize) {
> ++              pr_err("SMEM block size differs from MTD block size\n");
> ++              return -EINVAL;
> ++      }
> ++
> ++      /* Get partition pointer from SMEM */
> ++      ret = qcom_smem_get_flash_partitions(&smem_parts);
> ++      if (ret < 0)
> ++              return ret;
> ++
> ++      if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
> ++                 sizeof(SMEM_PTABLE_MAGIC))) {
> ++              pr_err("SMEM partition magic invalid\n");
> ++              return -EINVAL;
> ++      }
> ++
> ++      /* Allocate and populate the mtd structures */
> ++      mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
> ++                          GFP_KERNEL);
> ++      if (!mtd_parts)
> ++              return -ENOMEM;
> ++
> ++      for (i = 0; i < smem_parts->len; i++) {
> ++              struct smem_partition *s_part = &smem_parts->parts[i];
> ++              struct mtd_partition *m_part = &mtd_parts[i];
> ++
> ++              m_part->name = s_part->name;
> ++              m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
> ++              m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
> ++
> ++              /*
> ++               * The last SMEM partition may have its size marked as
> ++               * something like 0xffffffff, which means "until the end of the
> ++               * flash device". In this case, truncate it.
> ++               */
> ++              if (m_part->offset + m_part->size > master->size)
> ++                      m_part->size = master->size - m_part->offset;
> ++      }
> ++
> ++      *pparts = mtd_parts;
> ++
> ++      return smem_parts->len;
> ++}
> ++
> ++static struct mtd_part_parser qcom_smem_parser = {
> ++      .owner = THIS_MODULE,
> ++      .parse_fn = parse_qcom_smem_partitions,
> ++      .name = "qcom-smem",
> ++};
> ++
> ++static int __init qcom_smem_parser_init(void)
> ++{
> ++      register_mtd_parser(&qcom_smem_parser);
> ++      return 0;
> ++}
> ++
> ++static void __exit qcom_smem_parser_exit(void)
> ++{
> ++      deregister_mtd_parser(&qcom_smem_parser);
> ++}
> ++
> ++module_init(qcom_smem_parser_init);
> ++module_exit(qcom_smem_parser_exit);
> ++
> ++MODULE_LICENSE("GPL");
> ++MODULE_AUTHOR("Mathieu Olivari <mathieu at codeaurora.org>");
> ++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
> diff --git a/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch b/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch
> new file mode 100644
> index 0000000..96e9859
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch
> @@ -0,0 +1,244 @@
> +From 41c2b5280cd2fa3e198c422cdf223ba6e48f857a Mon Sep 17 00:00:00 2001
> +From: Felipe Balbi <balbi at ti.com>
> +Date: Wed, 18 Nov 2015 13:15:20 -0600
> +Subject: [PATCH] usb: dwc3: add generic OF glue layer
> +
> +For simple platforms which merely enable some clocks
> +and populate its children, we can use this generic
> +glue layer to avoid boilerplate code duplication.
> +
> +For now this supports Qcom and Xilinx, but if we
> +find a way to add generic handling of regulators and
> +optional PHYs, we can absorb exynos as well.
> +
> +Tested-by: Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta at xilinx.com>
> +Signed-off-by: Felipe Balbi <balbi at ti.com>
> +(cherry picked from commit 16adc674d0d68a50dfc725574738d7ae11cf5d7e)
> +
> +Change-Id: I6fd260442997b198dc12ca726814b7a9518e6353
> +Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
> +---
> + drivers/usb/dwc3/Kconfig          |   9 ++
> + drivers/usb/dwc3/Makefile         |   1 +
> + drivers/usb/dwc3/dwc3-of-simple.c | 178 ++++++++++++++++++++++++++++++++++++++
> + 3 files changed, 188 insertions(+)
> + create mode 100644 drivers/usb/dwc3/dwc3-of-simple.c
> +
> +diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
> +index 5a42c45..070e704 100644
> +--- a/drivers/usb/dwc3/Kconfig
> ++++ b/drivers/usb/dwc3/Kconfig
> +@@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE
> +         Support of USB2/3 functionality in TI Keystone2 platforms.
> +         Say 'Y' or 'M' here if you have one such device
> +
> ++config USB_DWC3_OF_SIMPLE
> ++       tristate "Generic OF Simple Glue Layer"
> ++       depends on OF && COMMON_CLK
> ++       default USB_DWC3
> ++       help
> ++         Support USB2/3 functionality in simple SoC integrations.
> ++       Currently supports Xilinx and Qualcomm DWC USB3 IP.
> ++       Say 'Y' or 'M' if you have one such device.
> ++
> + config USB_DWC3_ST
> +       tristate "STMicroelectronics Platforms"
> +       depends on ARCH_STI && OF
> +diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> +index acc951d..6491f9b 100644
> +--- a/drivers/usb/dwc3/Makefile
> ++++ b/drivers/usb/dwc3/Makefile
> +@@ -37,5 +37,6 @@ obj-$(CONFIG_USB_DWC3_OMAP)          += dwc3-omap.o
> + obj-$(CONFIG_USB_DWC3_EXYNOS)         += dwc3-exynos.o
> + obj-$(CONFIG_USB_DWC3_PCI)            += dwc3-pci.o
> + obj-$(CONFIG_USB_DWC3_KEYSTONE)               += dwc3-keystone.o
> ++obj-$(CONFIG_USB_DWC3_OF_SIMPLE)      += dwc3-of-simple.o
> + obj-$(CONFIG_USB_DWC3_QCOM)           += dwc3-qcom.o
> + obj-$(CONFIG_USB_DWC3_ST)             += dwc3-st.o
> +diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
> +new file mode 100644
> +index 0000000..60c4c5a
> +--- /dev/null
> ++++ b/drivers/usb/dwc3/dwc3-of-simple.c
> +@@ -0,0 +1,178 @@
> ++/**
> ++ * dwc3-of-simple.c - OF glue layer for simple integrations
> ++ *
> ++ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
> ++ *
> ++ * Author: Felipe Balbi <balbi at ti.com>
> ++ *
> ++ * This program is free software: you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2  of
> ++ * the License 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.
> ++ *
> ++ * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
> ++ * <iivanov at mm-sol.com> and the original patch adding support for Xilinx' SoC
> ++ * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta at xilinx.com>
> ++ */
> ++
> ++#include <linux/module.h>
> ++#include <linux/kernel.h>
> ++#include <linux/slab.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/dma-mapping.h>
> ++#include <linux/clk.h>
> ++#include <linux/clk-provider.h>
> ++#include <linux/of.h>
> ++#include <linux/of_platform.h>
> ++#include <linux/pm_runtime.h>
> ++
> ++struct dwc3_of_simple {
> ++      struct device           *dev;
> ++      struct clk              **clks;
> ++      int                     num_clocks;
> ++};
> ++
> ++static int dwc3_of_simple_probe(struct platform_device *pdev)
> ++{
> ++      struct dwc3_of_simple   *simple;
> ++      struct device           *dev = &pdev->dev;
> ++      struct device_node      *np = dev->of_node;
> ++
> ++      int                     ret;
> ++      int                     i;
> ++
> ++      simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
> ++      if (!simple)
> ++              return -ENOMEM;
> ++
> ++      ret = of_clk_get_parent_count(np);
> ++      if (ret < 0)
> ++              return ret;
> ++
> ++      simple->num_clocks = ret;
> ++
> ++      simple->clks = devm_kcalloc(dev, simple->num_clocks,
> ++                      sizeof(struct clk *), GFP_KERNEL);
> ++      if (!simple->clks)
> ++              return -ENOMEM;
> ++
> ++      simple->dev = dev;
> ++
> ++      for (i = 0; i < simple->num_clocks; i++) {
> ++              struct clk      *clk;
> ++
> ++              clk = of_clk_get(np, i);
> ++              if (IS_ERR(clk)) {
> ++                      while (--i >= 0)
> ++                              clk_put(simple->clks[i]);
> ++                      return PTR_ERR(clk);
> ++              }
> ++
> ++              ret = clk_prepare_enable(clk);
> ++              if (ret < 0) {
> ++                      while (--i >= 0) {
> ++                              clk_disable_unprepare(simple->clks[i]);
> ++                              clk_put(simple->clks[i]);
> ++                      }
> ++                      clk_put(clk);
> ++
> ++                      return ret;
> ++              }
> ++
> ++              simple->clks[i] = clk;
> ++      }
> ++
> ++      ret = of_platform_populate(np, NULL, NULL, dev);
> ++      if (ret) {
> ++              for (i = 0; i < simple->num_clocks; i++) {
> ++                      clk_disable_unprepare(simple->clks[i]);
> ++                      clk_put(simple->clks[i]);
> ++              }
> ++
> ++              return ret;
> ++      }
> ++
> ++      pm_runtime_set_active(dev);
> ++      pm_runtime_enable(dev);
> ++      pm_runtime_get_sync(dev);
> ++
> ++      return 0;
> ++}
> ++
> ++static int dwc3_of_simple_remove(struct platform_device *pdev)
> ++{
> ++      struct dwc3_of_simple   *simple = platform_get_drvdata(pdev);
> ++      struct device           *dev = &pdev->dev;
> ++      int                     i;
> ++
> ++      for (i = 0; i < simple->num_clocks; i++) {
> ++              clk_unprepare(simple->clks[i]);
> ++              clk_put(simple->clks[i]);
> ++      }
> ++
> ++      of_platform_depopulate(dev);
> ++
> ++      pm_runtime_put_sync(dev);
> ++      pm_runtime_disable(dev);
> ++
> ++      return 0;
> ++}
> ++
> ++static int dwc3_of_simple_runtime_suspend(struct device *dev)
> ++{
> ++      struct dwc3_of_simple   *simple = dev_get_drvdata(dev);
> ++      int                     i;
> ++
> ++      for (i = 0; i < simple->num_clocks; i++)
> ++              clk_disable(simple->clks[i]);
> ++
> ++      return 0;
> ++}
> ++
> ++static int dwc3_of_simple_runtime_resume(struct device *dev)
> ++{
> ++      struct dwc3_of_simple   *simple = dev_get_drvdata(dev);
> ++      int                     ret;
> ++      int                     i;
> ++
> ++      for (i = 0; i < simple->num_clocks; i++) {
> ++              ret = clk_enable(simple->clks[i]);
> ++              if (ret < 0) {
> ++                      while (--i >= 0)
> ++                              clk_disable(simple->clks[i]);
> ++                      return ret;
> ++              }
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
> ++      SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
> ++                      dwc3_of_simple_runtime_resume, NULL)
> ++};
> ++
> ++static const struct of_device_id of_dwc3_simple_match[] = {
> ++      { .compatible = "qcom,dwc3" },
> ++      { .compatible = "xlnx,zynqmp-dwc3" },
> ++      { /* Sentinel */ }
> ++};
> ++MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
> ++
> ++static struct platform_driver dwc3_of_simple_driver = {
> ++      .probe          = dwc3_of_simple_probe,
> ++      .remove         = dwc3_of_simple_remove,
> ++      .driver         = {
> ++              .name   = "dwc3-of-simple",
> ++              .of_match_table = of_dwc3_simple_match,
> ++      },
> ++};
> ++
> ++module_platform_driver(dwc3_of_simple_driver);
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
> ++MODULE_AUTHOR("Felipe Balbi <balbi at ti.com>");
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch b/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch
> new file mode 100644
> index 0000000..2fbdd1b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch
> @@ -0,0 +1,41 @@
> +From 131386d63ca3177d471aa93808c69b85fdac520d Mon Sep 17 00:00:00 2001
> +From: Felipe Balbi <balbi at ti.com>
> +Date: Tue, 22 Dec 2015 21:56:10 -0600
> +Subject: [PATCH] usb: dwc3: of-simple: fix build warning on !PM
> +
> +if we have a !PM kernel build, our runtime
> +suspend/resume callbacks will be left defined but
> +unused. Add a ifdef CONFIG_PM guard.
> +
> +Signed-off-by: Felipe Balbi <balbi at ti.com>
> +(cherry picked from commit 5072cfc40a80cea3749fd3413b3896630d8c787e)
> +
> +Change-Id: I088186c33aa917ec8da2985372ceefc289b24242
> +Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
> +---
> + drivers/usb/dwc3/dwc3-of-simple.c | 2 ++
> + 1 file changed, 2 insertions(+)
> +
> +diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
> +index 60c4c5a..9c9f741 100644
> +--- a/drivers/usb/dwc3/dwc3-of-simple.c
> ++++ b/drivers/usb/dwc3/dwc3-of-simple.c
> +@@ -122,6 +122,7 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
> +       return 0;
> + }
> +
> ++#ifdef CONFIG_PM
> + static int dwc3_of_simple_runtime_suspend(struct device *dev)
> + {
> +       struct dwc3_of_simple   *simple = dev_get_drvdata(dev);
> +@@ -150,6 +151,7 @@ static int dwc3_of_simple_runtime_resume(struct device *dev)
> +
> +       return 0;
> + }
> ++#endif
> +
> + static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
> +       SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch b/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch
> new file mode 100644
> index 0000000..44506c1
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch
> @@ -0,0 +1,52 @@
> +From 07c8b15688055d81ac8e1c8c964b9e4c302287f1 Mon Sep 17 00:00:00 2001
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +Date: Mon, 22 Feb 2016 11:12:47 -0800
> +Subject: [PATCH] usb: dwc3: Remove impossible check for
> + of_clk_get_parent_count() < 0
> +
> +The check for < 0 is impossible now that
> +of_clk_get_parent_count() returns an unsigned int. Simplify the
> +code and update the types.
> +
> +Acked-by: Felipe Balbi <balbi at kernel.org>
> +Cc: <linux-usb at vger.kernel.org>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +(cherry picked from commit 3d755dcc20dd452b52532eca17da40ebbd12aee9)
> +
> +Change-Id: Iaa38e064d801fb36c855fea51c0443840368e0d3
> +Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
> +---
> + drivers/usb/dwc3/dwc3-of-simple.c | 9 +++++----
> + 1 file changed, 5 insertions(+), 4 deletions(-)
> +
> +diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
> +index 9c9f741..9743353 100644
> +--- a/drivers/usb/dwc3/dwc3-of-simple.c
> ++++ b/drivers/usb/dwc3/dwc3-of-simple.c
> +@@ -42,6 +42,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
> +       struct device           *dev = &pdev->dev;
> +       struct device_node      *np = dev->of_node;
> +
> ++      unsigned int            count;
> +       int                     ret;
> +       int                     i;
> +
> +@@ -49,11 +50,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
> +       if (!simple)
> +               return -ENOMEM;
> +
> +-      ret = of_clk_get_parent_count(np);
> +-      if (ret < 0)
> +-              return ret;
> ++      count = of_clk_get_parent_count(np);
> ++      if (!count)
> ++              return -ENOENT;
> +
> +-      simple->num_clocks = ret;
> ++      simple->num_clocks = count;
> +
> +       simple->clks = devm_kcalloc(dev, simple->num_clocks,
> +                       sizeof(struct clk *), GFP_KERNEL);
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
> new file mode 100644
> index 0000000..d554919
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
> @@ -0,0 +1,512 @@
> +--- a/drivers/phy/Kconfig
> ++++ b/drivers/phy/Kconfig
> +@@ -390,4 +390,15 @@
> +         Enable this to support the Broadcom Cygnus PCIe PHY.
> +         If unsure, say N.
> +
> ++config PHY_QCOM_DWC3
> ++      tristate "QCOM DWC3 USB PHY support"
> ++      depends on ARCH_QCOM
> ++      depends on HAS_IOMEM
> ++      depends on OF
> ++      select GENERIC_PHY
> ++      help
> ++        This option enables support for the Synopsis PHYs present inside the
> ++        Qualcomm USB3.0 DWC3 controller.  This driver supports both HS and SS
> ++        PHY controllers.
> ++
> + endmenu
> +--- a/drivers/phy/Makefile
> ++++ b/drivers/phy/Makefile
> +@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210)   +=
> + obj-$(CONFIG_PHY_BRCMSTB_SATA)                += phy-brcmstb-sata.o
> + obj-$(CONFIG_PHY_PISTACHIO_USB)               += phy-pistachio-usb.o
> + obj-$(CONFIG_PHY_CYGNUS_PCIE)         += phy-bcm-cygnus-pcie.o
> ++obj-$(CONFIG_PHY_QCOM_DWC3)           += phy-qcom-dwc3.o
> +--- /dev/null
> ++++ b/drivers/phy/phy-qcom-dwc3.c
> +@@ -0,0 +1,484 @@
> ++/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++* This program is distributed in the hope that it will be useful,
> ++* but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++* GNU General Public License for more details.
> ++*/
> ++
> ++#include <linux/clk.h>
> ++#include <linux/err.h>
> ++#include <linux/io.h>
> ++#include <linux/module.h>
> ++#include <linux/of.h>
> ++#include <linux/phy/phy.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/delay.h>
> ++
> ++/**
> ++ *  USB QSCRATCH Hardware registers
> ++ */
> ++#define QSCRATCH_GENERAL_CFG          (0x08)
> ++#define HSUSB_PHY_CTRL_REG            (0x10)
> ++
> ++/* PHY_CTRL_REG */
> ++#define HSUSB_CTRL_DMSEHV_CLAMP                       BIT(24)
> ++#define HSUSB_CTRL_USB2_SUSPEND                       BIT(23)
> ++#define HSUSB_CTRL_UTMI_CLK_EN                        BIT(21)
> ++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID                BIT(20)
> ++#define HSUSB_CTRL_USE_CLKCORE                        BIT(18)
> ++#define HSUSB_CTRL_DPSEHV_CLAMP                       BIT(17)
> ++#define HSUSB_CTRL_COMMONONN                  BIT(11)
> ++#define HSUSB_CTRL_ID_HV_CLAMP                        BIT(9)
> ++#define HSUSB_CTRL_OTGSESSVLD_CLAMP           BIT(8)
> ++#define HSUSB_CTRL_CLAMP_EN                   BIT(7)
> ++#define HSUSB_CTRL_RETENABLEN                 BIT(1)
> ++#define HSUSB_CTRL_POR                                BIT(0)
> ++
> ++/* QSCRATCH_GENERAL_CFG */
> ++#define HSUSB_GCFG_XHCI_REV           BIT(2)
> ++
> ++/**
> ++ *  USB QSCRATCH Hardware registers
> ++ */
> ++#define SSUSB_PHY_CTRL_REG            (0x00)
> ++#define SSUSB_PHY_PARAM_CTRL_1                (0x04)
> ++#define SSUSB_PHY_PARAM_CTRL_2                (0x08)
> ++#define CR_PROTOCOL_DATA_IN_REG               (0x0c)
> ++#define CR_PROTOCOL_DATA_OUT_REG      (0x10)
> ++#define CR_PROTOCOL_CAP_ADDR_REG      (0x14)
> ++#define CR_PROTOCOL_CAP_DATA_REG      (0x18)
> ++#define CR_PROTOCOL_READ_REG          (0x1c)
> ++#define CR_PROTOCOL_WRITE_REG         (0x20)
> ++
> ++/* PHY_CTRL_REG */
> ++#define SSUSB_CTRL_REF_USE_PAD                BIT(28)
> ++#define SSUSB_CTRL_TEST_POWERDOWN     BIT(27)
> ++#define SSUSB_CTRL_LANE0_PWR_PRESENT  BIT(24)
> ++#define SSUSB_CTRL_SS_PHY_EN          BIT(8)
> ++#define SSUSB_CTRL_SS_PHY_RESET               BIT(7)
> ++
> ++/* SSPHY control registers */
> ++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)        (0x1006 + 0x100 * lane)
> ++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)       (0x1002 + 0x100 * lane)
> ++
> ++/* RX OVRD IN HI bits */
> ++#define RX_OVRD_IN_HI_RX_RESET_OVRD           BIT(13)
> ++#define RX_OVRD_IN_HI_RX_RX_RESET             BIT(12)
> ++#define RX_OVRD_IN_HI_RX_EQ_OVRD              BIT(11)
> ++#define RX_OVRD_IN_HI_RX_EQ_MASK              0x0700
> ++#define RX_OVRD_IN_HI_RX_EQ_SHIFT             8
> ++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD           BIT(7)
> ++#define RX_OVRD_IN_HI_RX_EQ_EN                        BIT(6)
> ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD      BIT(5)
> ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK      0x0018
> ++#define RX_OVRD_IN_HI_RX_RATE_OVRD            BIT(2)
> ++#define RX_OVRD_IN_HI_RX_RATE_MASK            0x0003
> ++
> ++/* TX OVRD DRV LO register bits */
> ++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
> ++#define TX_OVRD_DRV_LO_PREEMPH_MASK   0x3F80
> ++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT  7
> ++#define TX_OVRD_DRV_LO_EN             BIT(14)
> ++
> ++/* SS CAP register bits */
> ++#define SS_CR_CAP_ADDR_REG            BIT(0)
> ++#define SS_CR_CAP_DATA_REG            BIT(0)
> ++#define SS_CR_READ_REG                        BIT(0)
> ++#define SS_CR_WRITE_REG                       BIT(0)
> ++
> ++struct qcom_dwc3_usb_phy {
> ++      void __iomem            *base;
> ++      struct device           *dev;
> ++      struct clk              *xo_clk;
> ++      struct clk              *ref_clk;
> ++};
> ++
> ++struct qcom_dwc3_phy_drvdata {
> ++      struct phy_ops  ops;
> ++      u32             clk_rate;
> ++};
> ++
> ++/**
> ++ * Write register and read back masked value to confirm it is written
> ++ *
> ++ * @base - QCOM DWC3 PHY base virtual address.
> ++ * @offset - register offset.
> ++ * @mask - register bitmask specifying what should be updated
> ++ * @val - value to write.
> ++ */
> ++static inline void qcom_dwc3_phy_write_readback(
> ++      struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
> ++      const u32 mask, u32 val)
> ++{
> ++      u32 write_val, tmp = readl(phy_dwc3->base + offset);
> ++
> ++      tmp &= ~mask;           /* retain other bits */
> ++      write_val = tmp | val;
> ++
> ++      writel(write_val, phy_dwc3->base + offset);
> ++
> ++      /* Read back to see if val was written */
> ++      tmp = readl(phy_dwc3->base + offset);
> ++      tmp &= mask;            /* clear other bits */
> ++
> ++      if (tmp != val)
> ++              dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
> ++                      val, offset);
> ++}
> ++
> ++static int wait_for_latch(void __iomem *addr)
> ++{
> ++      u32 retry = 10;
> ++
> ++      while (true) {
> ++              if (!readl(addr))
> ++                      break;
> ++
> ++              if (--retry == 0)
> ++                      return -ETIMEDOUT;
> ++
> ++              usleep_range(10, 20);
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++/**
> ++ * Write SSPHY register
> ++ *
> ++ * @base - QCOM DWC3 PHY base virtual address.
> ++ * @addr - SSPHY address to write.
> ++ * @val - value to write.
> ++ */
> ++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3,
> ++                                      u32 addr, u32 val)
> ++{
> ++      int ret;
> ++
> ++      writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
> ++      writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
> ++
> ++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
> ++      if (ret)
> ++              goto err_wait;
> ++
> ++      writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
> ++      writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
> ++
> ++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
> ++      if (ret)
> ++              goto err_wait;
> ++
> ++      writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
> ++
> ++      ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
> ++
> ++err_wait:
> ++      if (ret)
> ++              dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
> ++      return ret;
> ++}
> ++
> ++/**
> ++ * Read SSPHY register.
> ++ *
> ++ * @base - QCOM DWC3 PHY base virtual address.
> ++ * @addr - SSPHY address to read.
> ++ */
> ++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
> ++{
> ++      int ret;
> ++
> ++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
> ++      writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG);
> ++
> ++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
> ++      if (ret)
> ++              goto err_wait;
> ++
> ++      /*
> ++       * Due to hardware bug, first read of SSPHY register might be
> ++       * incorrect. Hence as workaround, SW should perform SSPHY register
> ++       * read twice, but use only second read and ignore first read.
> ++       */
> ++      writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
> ++
> ++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
> ++      if (ret)
> ++              goto err_wait;
> ++
> ++      /* throwaway read */
> ++      readl(base + CR_PROTOCOL_DATA_OUT_REG);
> ++
> ++      writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
> ++
> ++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
> ++      if (ret)
> ++              goto err_wait;
> ++
> ++      *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
> ++
> ++err_wait:
> ++      return ret;
> ++}
> ++
> ++static int qcom_dwc3_phy_power_on(struct phy *phy)
> ++{
> ++      int ret;
> ++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
> ++
> ++      ret = clk_prepare_enable(phy_dwc3->xo_clk);
> ++      if (ret)
> ++              return ret;
> ++
> ++      ret = clk_prepare_enable(phy_dwc3->ref_clk);
> ++      if (ret)
> ++              clk_disable_unprepare(phy_dwc3->xo_clk);
> ++
> ++      return ret;
> ++}
> ++
> ++static int qcom_dwc3_phy_power_off(struct phy *phy)
> ++{
> ++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
> ++
> ++      clk_disable_unprepare(phy_dwc3->ref_clk);
> ++      clk_disable_unprepare(phy_dwc3->xo_clk);
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_dwc3_hs_phy_init(struct phy *phy)
> ++{
> ++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
> ++      u32 val;
> ++
> ++      /*
> ++       * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
> ++       * enable clamping, and disable RETENTION (power-on default is ENABLED)
> ++       */
> ++      val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
> ++              HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN |
> ++              HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
> ++              HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
> ++              HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
> ++
> ++      /* use core clock if external reference is not present */
> ++      if (!phy_dwc3->xo_clk)
> ++              val |= HSUSB_CTRL_USE_CLKCORE;
> ++
> ++      writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
> ++      usleep_range(2000, 2200);
> ++
> ++      /* Disable (bypass) VBUS and ID filters */
> ++      writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_dwc3_ss_phy_init(struct phy *phy)
> ++{
> ++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
> ++      int ret;
> ++      u32 data = 0;
> ++
> ++      /* reset phy */
> ++      data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
> ++      writel(data | SSUSB_CTRL_SS_PHY_RESET,
> ++              phy_dwc3->base + SSUSB_PHY_CTRL_REG);
> ++      usleep_range(2000, 2200);
> ++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
> ++
> ++      /* clear REF_PAD if we don't have XO clk */
> ++      if (!phy_dwc3->xo_clk)
> ++              data &= ~SSUSB_CTRL_REF_USE_PAD;
> ++      else
> ++              data |= SSUSB_CTRL_REF_USE_PAD;
> ++
> ++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
> ++
> ++      /* wait for ref clk to become stable, this can take up to 30ms */
> ++      msleep(30);
> ++
> ++      data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
> ++      writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
> ++
> ++      /*
> ++       * Fix RX Equalization setting as follows
> ++       * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
> ++       * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
> ++       * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
> ++       * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
> ++       */
> ++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
> ++                      SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
> ++      if (ret)
> ++              goto err_phy_trans;
> ++
> ++      data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
> ++      data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
> ++      data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
> ++      data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
> ++      data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
> ++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
> ++              SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
> ++      if (ret)
> ++              goto err_phy_trans;
> ++
> ++      /*
> ++       * Set EQ and TX launch amplitudes as follows
> ++       * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
> ++       * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
> ++       * LANE0.TX_OVRD_DRV_LO.EN set to 1.
> ++       */
> ++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
> ++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
> ++      if (ret)
> ++              goto err_phy_trans;
> ++
> ++      data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
> ++      data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
> ++      data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
> ++      data |= 0x7f;
> ++      data |= TX_OVRD_DRV_LO_EN;
> ++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
> ++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
> ++      if (ret)
> ++              goto err_phy_trans;
> ++
> ++      /*
> ++       * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
> ++       * TX_FULL_SWING [26:20] amplitude to 127
> ++       * TX_DEEMPH_3_5DB [13:8] to 22
> ++       * LOS_BIAS [2:0] to 0x5
> ++       */
> ++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
> ++                                 0x07f03f07, 0x07f01605);
> ++
> ++err_phy_trans:
> ++      return ret;
> ++}
> ++
> ++static int qcom_dwc3_ss_phy_exit(struct phy *phy)
> ++{
> ++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
> ++
> ++      /* Sequence to put SSPHY in low power state:
> ++       * 1. Clear REF_PHY_EN in PHY_CTRL_REG
> ++       * 2. Clear REF_USE_PAD in PHY_CTRL_REG
> ++       * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
> ++       */
> ++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
> ++              SSUSB_CTRL_SS_PHY_EN, 0x0);
> ++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
> ++              SSUSB_CTRL_REF_USE_PAD, 0x0);
> ++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
> ++              0x0, SSUSB_CTRL_TEST_POWERDOWN);
> ++
> ++      return 0;
> ++}
> ++
> ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = {
> ++      .ops = {
> ++              .init           = qcom_dwc3_hs_phy_init,
> ++              .power_on       = qcom_dwc3_phy_power_on,
> ++              .power_off      = qcom_dwc3_phy_power_off,
> ++              .owner          = THIS_MODULE,
> ++      },
> ++      .clk_rate = 60000000,
> ++};
> ++
> ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = {
> ++      .ops = {
> ++              .init           = qcom_dwc3_ss_phy_init,
> ++              .exit           = qcom_dwc3_ss_phy_exit,
> ++              .power_on       = qcom_dwc3_phy_power_on,
> ++              .power_off      = qcom_dwc3_phy_power_off,
> ++              .owner          = THIS_MODULE,
> ++      },
> ++      .clk_rate = 125000000,
> ++};
> ++
> ++static const struct of_device_id qcom_dwc3_phy_table[] = {
> ++      { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata },
> ++      { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata },
> ++      { /* Sentinel */ }
> ++};
> ++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
> ++
> ++static int qcom_dwc3_phy_probe(struct platform_device *pdev)
> ++{
> ++      struct qcom_dwc3_usb_phy        *phy_dwc3;
> ++      struct phy_provider             *phy_provider;
> ++      struct phy                      *generic_phy;
> ++      struct resource                 *res;
> ++      const struct of_device_id *match;
> ++      const struct qcom_dwc3_phy_drvdata *data;
> ++
> ++      phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
> ++      if (!phy_dwc3)
> ++              return -ENOMEM;
> ++
> ++      match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node);
> ++      data = match->data;
> ++
> ++      phy_dwc3->dev = &pdev->dev;
> ++
> ++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
> ++      if (IS_ERR(phy_dwc3->base))
> ++              return PTR_ERR(phy_dwc3->base);
> ++
> ++      phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
> ++      if (IS_ERR(phy_dwc3->ref_clk)) {
> ++              dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
> ++              return PTR_ERR(phy_dwc3->ref_clk);
> ++      }
> ++
> ++      clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
> ++
> ++      phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
> ++      if (IS_ERR(phy_dwc3->xo_clk)) {
> ++              dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
> ++              phy_dwc3->xo_clk = NULL;
> ++      }
> ++
> ++      generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node,
> ++                                    &data->ops);
> ++
> ++      if (IS_ERR(generic_phy))
> ++              return PTR_ERR(generic_phy);
> ++
> ++      phy_set_drvdata(generic_phy, phy_dwc3);
> ++      platform_set_drvdata(pdev, phy_dwc3);
> ++
> ++      phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
> ++                      of_phy_simple_xlate);
> ++
> ++      if (IS_ERR(phy_provider))
> ++              return PTR_ERR(phy_provider);
> ++
> ++      return 0;
> ++}
> ++
> ++static struct platform_driver qcom_dwc3_phy_driver = {
> ++      .probe          = qcom_dwc3_phy_probe,
> ++      .driver         = {
> ++              .name   = "qcom-dwc3-usb-phy",
> ++              .owner  = THIS_MODULE,
> ++              .of_match_table = qcom_dwc3_phy_table,
> ++      },
> ++};
> ++
> ++module_platform_driver(qcom_dwc3_phy_driver);
> ++
> ++MODULE_ALIAS("platform:phy-qcom-dwc3");
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_AUTHOR("Andy Gross <agross at codeaurora.org>");
> ++MODULE_AUTHOR("Ivan T. Ivanov <iivanov at mm-sol.com>");
> ++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
> diff --git a/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
> new file mode 100644
> index 0000000..6e6f10d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
> @@ -0,0 +1,125 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -92,5 +92,29 @@
> +               sata 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";
> ++              };
> +       };
> + };
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -333,6 +333,90 @@
> +                       compatible = "syscon";
> +                       reg = <0x01200600 0x100>;
> +               };
> ++
> ++              hs_phy_1: phy 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";
> ++
> ++                      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";
> ++                              tx-fifo-resize;
> ++                              dr_mode = "host";
> ++                      };
> ++              };
> ++
> ++              usb3_1: usb30 at 1 {
> ++                      compatible = "qcom,dwc3";
> ++                      #address-cells = <1>;
> ++                      #size-cells = <1>;
> ++                      clocks = <&gcc USB30_1_MASTER_CLK>;
> ++                      clock-names = "core";
> ++
> ++                      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";
> ++                              tx-fifo-resize;
> ++                              dr_mode = "host";
> ++                      };
> ++              };
> +       };
> +
> +       sfpb_mutex: sfpb-mutex {
> diff --git a/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
> new file mode 100644
> index 0000000..41f91fa
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
> @@ -0,0 +1,263 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings
> +From: Stanimir Varbanov <svarbanov at mm-sol.com>
> +X-Patchwork-Id: 6326181
> +Message-Id: <1430743338-10441-4-git-send-email-svarbanov at mm-sol.com>
> +To: Rob Herring <robh+dt at kernel.org>, Kumar Gala <galak at codeaurora.org>,
> +       Mark Rutland <mark.rutland at arm.com>,
> +       Grant Likely <grant.likely at linaro.org>,
> +       Bjorn Helgaas <bhelgaas at google.com>,
> +       Kishon Vijay Abraham I <kishon at ti.com>,
> +       Russell King <linux at arm.linux.org.uk>, Arnd Bergmann <arnd at arndb.de>
> +Cc: linux-arm-msm at vger.kernel.org, linux-kernel at vger.kernel.org,
> +       linux-arm-kernel at lists.infradead.org, devicetree at vger.kernel.org,
> +       linux-pci at vger.kernel.org, Mathieu Olivari <mathieu at codeaurora.org>,
> +       Srinivas Kandagatla <srinivas.kandagatla at linaro.org>,
> +       Stanimir Varbanov <svarbanov at mm-sol.com>
> +Date: Mon,  4 May 2015 15:42:16 +0300
> +
> +Document Qualcomm PCIe driver devicetree bindings.
> +
> +Signed-off-by: Stanimir Varbanov <svarbanov at mm-sol.com>
> +
> +---
> +.../devicetree/bindings/pci/qcom,pcie.txt          |  231 ++++++++++++++++++++
> + 1 files changed, 231 insertions(+), 0 deletions(-)
> + create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt
> +
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> +@@ -0,0 +1,231 @@
> ++* Qualcomm PCI express root complex
> ++
> ++- compatible:
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: Value shall include
> ++                  - "qcom,pcie-v0" for apq/ipq8064
> ++                  - "qcom,pcie-v1" for apq8084
> ++
> ++- reg:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: Register ranges as listed in the reg-names property
> ++
> ++- reg-names:
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: Must include the following entries
> ++                  - "parf"   Qualcomm specific registers
> ++                  - "dbi"    Designware PCIe registers
> ++                  - "elbi"   External local bus interface registers
> ++                  - "config" PCIe configuration space
> ++
> ++- device_type:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: Should be "pci". As specified in designware-pcie.txt
> ++
> ++- #address-cells:
> ++      Usage: required
> ++      Value type: <u32>
> ++      Definition: Should be set to 3. As specified in designware-pcie.txt
> ++
> ++- #size-cells:
> ++      Usage: required
> ++      Value type: <u32>
> ++      Definition: Should be set 2. As specified in designware-pcie.txt
> ++
> ++- ranges:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: As specified in designware-pcie.txt
> ++
> ++- interrupts:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: MSI interrupt
> ++
> ++- interrupt-names:
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: Should contain "msi"
> ++
> ++- #interrupt-cells:
> ++      Usage: required
> ++      Value type: <u32>
> ++      Definition: Should be 1. As specified in designware-pcie.txt
> ++
> ++- interrupt-map-mask:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: As specified in designware-pcie.txt
> ++
> ++- interrupt-map:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: As specified in designware-pcie.txt
> ++
> ++- clocks:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: List of phandle and clock specifier pairs as listed
> ++                  in clock-names property
> ++
> ++- clock-names:
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: Should contain the following entries
> ++                  * should be populated for v0 and v1
> ++                      - "iface"      Configuration AHB clock
> ++
> ++                  * should be populated for v0
> ++                      - "core"       Clocks the pcie hw block
> ++                      - "phy"        Clocks the pcie PHY block
> ++
> ++                  * should be populated for v1
> ++                      - "aux"        Auxiliary (AUX) clock
> ++                      - "bus_master" Master AXI clock
> ++                      - "bus_slave"  Slave AXI clock
> ++
> ++- resets:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: List of phandle and reset specifier pairs as listed
> ++                  in reset-names property
> ++
> ++- reset-names:
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: Should contain the following entries
> ++                  * should be populated for v0
> ++                      - "axi"  AXI reset
> ++                      - "ahb"  AHB reset
> ++                      - "por"  POR reset
> ++                      - "pci"  PCI reset
> ++                      - "phy"  PHY reset
> ++
> ++                  * should be populated for v1
> ++                      - "core" Core reset
> ++
> ++- power-domains:
> ++      Usage: required (for v1 only)
> ++      Value type: <prop-encoded-array>
> ++      Definition: A phandle and power domain specifier pair to the
> ++                  power domain which is responsible for collapsing
> ++                  and restoring power to the peripheral
> ++
> ++- <name>-supply:
> ++      Usage: required
> ++      Value type: <phandle>
> ++      Definition: List of phandles to the power supply regulator(s)
> ++                  * should be populated for v0 and v1
> ++                      - "vdda"        core analog power supply
> ++
> ++                  * should be populated for v0
> ++                      - "vdda_phy"    analog power supply for PHY
> ++                      - "vdda_refclk" analog power supply for IC which generate
> ++                                      reference clock
> ++
> ++- phys:
> ++      Usage: required (for v1 only)
> ++      Value type: <phandle>
> ++      Definition: List of phandle(s) as listed in phy-names property
> ++
> ++- phy-names:
> ++      Usage: required (for v1 only)
> ++      Value type: <stringlist>
> ++      Definition: Should contain "pciephy"
> ++
> ++- <name>-gpio:
> ++      Usage: optional
> ++      Value type: <prop-encoded-array>
> ++      Definition: List of phandle and gpio specifier pairs. Should contain
> ++                  - "perst"  PCIe endpoint reset signal line
> ++                  - "pewake" PCIe endpoint wake signal line
> ++
> ++- pinctrl-0:
> ++      Usage: required
> ++      Value type: <phandle>
> ++      Definition: List of phandles pointing at a pin(s) configuration
> ++
> ++- pinctrl-names
> ++      Usage: required
> ++      Value type: <stringlist>
> ++      Definition: List of names of pinctrl-0 state
> ++
> ++* Example for v0
> ++      pcie0: pci 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 0          0x0fe00000 0 0x00100000   /* I/O */
> ++                        0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */
> ++              interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
> ++              interrupt-names = "msi";
> ++              #interrupt-cells = <1>;
> ++              interrupt-map-mask = <0 0 0 0x7>;
> ++              interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
> ++                              <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
> ++                              <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
> ++                              <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
> ++              clocks = <&gcc PCIE_A_CLK>,
> ++                       <&gcc PCIE_H_CLK>,
> ++                       <&gcc PCIE_PHY_CLK>;
> ++              clock-names = "core", "iface", "phy";
> ++              resets = <&gcc PCIE_ACLK_RESET>,
> ++                       <&gcc PCIE_HCLK_RESET>,
> ++                       <&gcc PCIE_POR_RESET>,
> ++                       <&gcc PCIE_PCI_RESET>,
> ++                       <&gcc PCIE_PHY_RESET>;
> ++              reset-names = "axi", "ahb", "por", "pci", "phy";
> ++      };
> ++
> ++* Example for v1
> ++      pcie0 at fc520000 {
> ++              compatible = "qcom,pcie-v1";
> ++              reg = <0xfc520000 0x2000>,
> ++                    <0xff000000 0x1000>,
> ++                    <0xff001000 0x1000>,
> ++                    <0xff002000 0x2000>;
> ++              reg-names = "parf", "dbi", "elbi", "config";
> ++              device_type = "pci";
> ++              linux,pci-domain = <0>;
> ++              bus-range = <0x00 0xff>;
> ++              num-lanes = <1>;
> ++              #address-cells = <3>;
> ++              #size-cells = <2>;
> ++              ranges = <0x81000000 0 0          0xff200000 0 0x00100000   /* I/O */
> ++                        0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
> ++              interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
> ++              interrupt-names = "msi";
> ++              #interrupt-cells = <1>;
> ++              interrupt-map-mask = <0 0 0 0x7>;
> ++              interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
> ++                              <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
> ++                              <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
> ++                              <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
> ++              clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
> ++                       <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
> ++                       <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
> ++                       <&gcc GCC_PCIE_0_AUX_CLK>;
> ++              clock-names = "iface", "master_bus", "slave_bus", "aux";
> ++              resets = <&gcc GCC_PCIE_0_BCR>;
> ++              reset-names = "core";
> ++              power-domains = <&gcc PCIE0_GDSC>;
> ++              vdda-supply = <&pma8084_l3>;
> ++              phys = <&pciephy0>;
> ++              phy-names = "pciephy";
> ++              perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
> ++              pinctrl-0 = <&pcie0_pins_default>;
> ++              pinctrl-names = "default";
> ++      };
> diff --git a/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
> new file mode 100644
> index 0000000..ad1a1b9
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
> @@ -0,0 +1,752 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver
> +From: Stanimir Varbanov <svarbanov at mm-sol.com>
> +X-Patchwork-Id: 6326161
> +Message-Id: <1430743338-10441-5-git-send-email-svarbanov at mm-sol.com>
> +To: Rob Herring <robh+dt at kernel.org>, Kumar Gala <galak at codeaurora.org>,
> +       Mark Rutland <mark.rutland at arm.com>,
> +       Grant Likely <grant.likely at linaro.org>,
> +       Bjorn Helgaas <bhelgaas at google.com>,
> +       Kishon Vijay Abraham I <kishon at ti.com>,
> +       Russell King <linux at arm.linux.org.uk>, Arnd Bergmann <arnd at arndb.de>
> +Cc: linux-arm-msm at vger.kernel.org, linux-kernel at vger.kernel.org,
> +       linux-arm-kernel at lists.infradead.org, devicetree at vger.kernel.org,
> +       linux-pci at vger.kernel.org, Mathieu Olivari <mathieu at codeaurora.org>,
> +       Srinivas Kandagatla <srinivas.kandagatla at linaro.org>,
> +       Stanimir Varbanov <svarbanov at mm-sol.com>
> +Date: Mon,  4 May 2015 15:42:17 +0300
> +
> +The PCIe driver reuse the Designware common code for host
> +and MSI initialization, and also program the Qualcomm
> +application specific registers.
> +
> +Signed-off-by: Stanimir Varbanov <svarbanov at mm-sol.com>
> +
> +---
> +MAINTAINERS                  |    7 +
> + drivers/pci/host/Kconfig     |    9 +
> + drivers/pci/host/Makefile    |    1 +
> + drivers/pci/host/pcie-qcom.c |  677 ++++++++++++++++++++++++++++++++++++++++++
> + 4 files changed, 694 insertions(+), 0 deletions(-)
> + create mode 100644 drivers/pci/host/pcie-qcom.c
> +
> +--- a/MAINTAINERS
> ++++ b/MAINTAINERS
> +@@ -8253,6 +8253,13 @@
> + F:    Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
> + F:    drivers/pci/host/pcie-hisi.c
> +
> ++PCIE DRIVER FOR QUALCOMM MSM
> ++M:     Stanimir Varbanov <svarbanov at mm-sol.com>
> ++L:     linux-pci at vger.kernel.org
> ++L:     linux-arm-msm at vger.kernel.org
> ++S:     Maintained
> ++F:     drivers/pci/host/*qcom*
> ++
> + PCMCIA SUBSYSTEM
> + P:    Linux PCMCIA Team
> + L:    linux-pcmcia at lists.infradead.org
> +--- a/drivers/pci/host/Kconfig
> ++++ b/drivers/pci/host/Kconfig
> +@@ -173,4 +173,13 @@
> +       help
> +         Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
> +
> ++config PCIE_QCOM
> ++      bool "Qualcomm PCIe controller"
> ++      depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
> ++      select PCIE_DW
> ++      select PCIEPORTBUS
> ++      help
> ++        Say Y here to enable PCIe controller support on Qualcomm SoCs. The
> ++        PCIe controller use Designware core plus Qualcomm specific hardware
> ++        wrappers.
> + endmenu
> +--- /dev/null
> ++++ b/drivers/pci/host/pcie-qcom.c
> +@@ -0,0 +1,676 @@
> ++/*
> ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/clk.h>
> ++#include <linux/delay.h>
> ++#include <linux/gpio.h>
> ++#include <linux/interrupt.h>
> ++#include <linux/io.h>
> ++#include <linux/kernel.h>
> ++#include <linux/module.h>
> ++#include <linux/of_gpio.h>
> ++#include <linux/pci.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/phy/phy.h>
> ++#include <linux/regulator/consumer.h>
> ++#include <linux/reset.h>
> ++#include <linux/slab.h>
> ++#include <linux/types.h>
> ++
> ++#include "pcie-designware.h"
> ++
> ++#define PCIE20_PARF_PHY_CTRL                  0x40
> ++#define PCIE20_PARF_PHY_REFCLK                        0x4C
> ++#define PCIE20_PARF_DBI_BASE_ADDR             0x168
> ++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE               0x16c
> ++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT     0x178
> ++
> ++#define PCIE20_ELBI_SYS_CTRL                  0x04
> ++#define PCIE20_ELBI_SYS_STTS                  0x08
> ++#define XMLH_LINK_UP                          BIT(10)
> ++
> ++#define PCIE20_CAP                            0x70
> ++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
> ++
> ++#define PERST_DELAY_MIN_US                    1000
> ++#define PERST_DELAY_MAX_US                    1005
> ++
> ++#define LINKUP_DELAY_MIN_US                   5000
> ++#define LINKUP_DELAY_MAX_US                   5100
> ++#define LINKUP_RETRIES_COUNT                  20
> ++
> ++#define PCIE_V0                                       0       /* apq8064 */
> ++#define PCIE_V1                                       1       /* apq8084 */
> ++
> ++struct qcom_pcie_resources_v0 {
> ++      struct clk *iface_clk;
> ++      struct clk *core_clk;
> ++      struct clk *phy_clk;
> ++      struct reset_control *pci_reset;
> ++      struct reset_control *axi_reset;
> ++      struct reset_control *ahb_reset;
> ++      struct reset_control *por_reset;
> ++      struct reset_control *phy_reset;
> ++      struct regulator *vdda;
> ++      struct regulator *vdda_phy;
> ++      struct regulator *vdda_refclk;
> ++};
> ++
> ++struct qcom_pcie_resources_v1 {
> ++      struct clk *iface;
> ++      struct clk *aux;
> ++      struct clk *master_bus;
> ++      struct clk *slave_bus;
> ++      struct reset_control *core;
> ++      struct regulator *vdda;
> ++};
> ++
> ++union pcie_resources {
> ++      struct qcom_pcie_resources_v0 v0;
> ++      struct qcom_pcie_resources_v1 v1;
> ++};
> ++
> ++struct qcom_pcie {
> ++      struct pcie_port pp;
> ++      struct device *dev;
> ++      union pcie_resources res;
> ++      void __iomem *parf;
> ++      void __iomem *dbi;
> ++      void __iomem *elbi;
> ++      struct phy *phy;
> ++      struct gpio_desc *reset;
> ++      unsigned int version;
> ++};
> ++
> ++#define to_qcom_pcie(x)               container_of(x, struct qcom_pcie, pp)
> ++
> ++static inline void
> ++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
> ++{
> ++      u32 val = readl(addr);
> ++
> ++      val &= ~clear_mask;
> ++      val |= set_mask;
> ++      writel(val, addr);
> ++}
> ++
> ++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
> ++{
> ++      int val, active_low;
> ++
> ++      if (IS_ERR_OR_NULL(pcie->reset))
> ++              return;
> ++
> ++      active_low = gpiod_is_active_low(pcie->reset);
> ++
> ++      if (assert)
> ++              val = !!active_low;
> ++      else
> ++              val = !active_low;
> ++
> ++      gpiod_set_value(pcie->reset, val);
> ++
> ++      usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
> ++}
> ++
> ++static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
> ++{
> ++      qcom_ep_reset_assert_deassert(pcie, 1);
> ++}
> ++
> ++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
> ++{
> ++      qcom_ep_reset_assert_deassert(pcie, 0);
> ++}
> ++
> ++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
> ++{
> ++      struct pcie_port *pp = arg;
> ++
> ++      return dw_handle_msi_irq(pp);
> ++}
> ++
> ++static int qcom_pcie_link_up(struct pcie_port *pp)
> ++{
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
> ++      u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
> ++
> ++      return val & BIT(29) ? 1 : 0;
> ++}
> ++
> ++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
> ++
> ++      reset_control_assert(res->pci_reset);
> ++      reset_control_assert(res->axi_reset);
> ++      reset_control_assert(res->ahb_reset);
> ++      reset_control_assert(res->por_reset);
> ++      reset_control_assert(res->pci_reset);
> ++      clk_disable_unprepare(res->iface_clk);
> ++      clk_disable_unprepare(res->core_clk);
> ++      clk_disable_unprepare(res->phy_clk);
> ++      regulator_disable(res->vdda);
> ++      regulator_disable(res->vdda_phy);
> ++      regulator_disable(res->vdda_refclk);
> ++}
> ++
> ++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
> ++
> ++      reset_control_assert(res->core);
> ++      clk_disable_unprepare(res->slave_bus);
> ++      clk_disable_unprepare(res->master_bus);
> ++      clk_disable_unprepare(res->iface);
> ++      clk_disable_unprepare(res->aux);
> ++      regulator_disable(res->vdda);
> ++}
> ++
> ++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
> ++      struct device *dev = pcie->dev;
> ++      int ret;
> ++
> ++      ret = regulator_enable(res->vdda);
> ++      if (ret) {
> ++              dev_err(dev, "cannot enable vdda regulator\n");
> ++              return ret;
> ++      }
> ++
> ++      ret = regulator_enable(res->vdda_refclk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot enable vdda_refclk regulator\n");
> ++              goto err_refclk;
> ++      }
> ++
> ++      ret = regulator_enable(res->vdda_phy);
> ++      if (ret) {
> ++              dev_err(dev, "cannot enable vdda_phy regulator\n");
> ++              goto err_vdda_phy;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->iface_clk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable iface clock\n");
> ++              goto err_iface;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->core_clk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable core clock\n");
> ++              goto err_clk_core;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->phy_clk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable phy clock\n");
> ++              goto err_clk_phy;
> ++      }
> ++
> ++      ret = reset_control_deassert(res->ahb_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert ahb reset\n");
> ++              goto err_reset_ahb;
> ++      }
> ++
> ++      return 0;
> ++
> ++err_reset_ahb:
> ++      clk_disable_unprepare(res->phy_clk);
> ++err_clk_phy:
> ++      clk_disable_unprepare(res->core_clk);
> ++err_clk_core:
> ++      clk_disable_unprepare(res->iface_clk);
> ++err_iface:
> ++      regulator_disable(res->vdda_phy);
> ++err_vdda_phy:
> ++      regulator_disable(res->vdda_refclk);
> ++err_refclk:
> ++      regulator_disable(res->vdda);
> ++      return ret;
> ++}
> ++
> ++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
> ++      struct device *dev = pcie->dev;
> ++      int ret;
> ++
> ++      ret = reset_control_deassert(res->core);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert core reset\n");
> ++              return ret;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->aux);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable aux clock\n");
> ++              goto err_res;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->iface);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable iface clock\n");
> ++              goto err_aux;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->master_bus);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable master_bus clock\n");
> ++              goto err_iface;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->slave_bus);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable slave_bus clock\n");
> ++              goto err_master;
> ++      }
> ++
> ++      ret = regulator_enable(res->vdda);
> ++      if (ret) {
> ++              dev_err(dev, "cannot enable vdda regulator\n");
> ++              goto err_slave;
> ++      }
> ++
> ++      return 0;
> ++
> ++err_slave:
> ++      clk_disable_unprepare(res->slave_bus);
> ++err_master:
> ++      clk_disable_unprepare(res->master_bus);
> ++err_iface:
> ++      clk_disable_unprepare(res->iface);
> ++err_aux:
> ++      clk_disable_unprepare(res->aux);
> ++err_res:
> ++      reset_control_assert(res->core);
> ++
> ++      return ret;
> ++}
> ++
> ++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
> ++      struct device *dev = pcie->dev;
> ++
> ++      res->vdda = devm_regulator_get(dev, "vdda");
> ++      if (IS_ERR(res->vdda))
> ++              return PTR_ERR(res->vdda);
> ++
> ++      res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
> ++      if (IS_ERR(res->vdda_phy))
> ++              return PTR_ERR(res->vdda_phy);
> ++
> ++      res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
> ++      if (IS_ERR(res->vdda_refclk))
> ++              return PTR_ERR(res->vdda_refclk);
> ++
> ++      res->iface_clk = devm_clk_get(dev, "iface");
> ++      if (IS_ERR(res->iface_clk))
> ++              return PTR_ERR(res->iface_clk);
> ++
> ++      res->core_clk = devm_clk_get(dev, "core");
> ++      if (IS_ERR(res->core_clk))
> ++              return PTR_ERR(res->core_clk);
> ++
> ++      res->phy_clk = devm_clk_get(dev, "phy");
> ++      if (IS_ERR(res->phy_clk))
> ++              return PTR_ERR(res->phy_clk);
> ++
> ++      res->pci_reset = devm_reset_control_get(dev, "pci");
> ++      if (IS_ERR(res->pci_reset))
> ++              return PTR_ERR(res->pci_reset);
> ++
> ++      res->axi_reset = devm_reset_control_get(dev, "axi");
> ++      if (IS_ERR(res->axi_reset))
> ++              return PTR_ERR(res->axi_reset);
> ++
> ++      res->ahb_reset = devm_reset_control_get(dev, "ahb");
> ++      if (IS_ERR(res->ahb_reset))
> ++              return PTR_ERR(res->ahb_reset);
> ++
> ++      res->por_reset = devm_reset_control_get(dev, "por");
> ++      if (IS_ERR(res->por_reset))
> ++              return PTR_ERR(res->por_reset);
> ++
> ++      res->phy_reset = devm_reset_control_get(dev, "phy");
> ++      if (IS_ERR(res->phy_reset))
> ++              return PTR_ERR(res->phy_reset);
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
> ++{
> ++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
> ++      struct device *dev = pcie->dev;
> ++
> ++      res->vdda = devm_regulator_get(dev, "vdda");
> ++      if (IS_ERR(res->vdda))
> ++              return PTR_ERR(res->vdda);
> ++
> ++      res->iface = devm_clk_get(dev, "iface");
> ++      if (IS_ERR(res->iface))
> ++              return PTR_ERR(res->iface);
> ++
> ++      res->aux = devm_clk_get(dev, "aux");
> ++      if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER)
> ++              return -EPROBE_DEFER;
> ++      else if (IS_ERR(res->aux))
> ++              res->aux = NULL;
> ++
> ++      res->master_bus = devm_clk_get(dev, "master_bus");
> ++      if (IS_ERR(res->master_bus))
> ++              return PTR_ERR(res->master_bus);
> ++
> ++      res->slave_bus = devm_clk_get(dev, "slave_bus");
> ++      if (IS_ERR(res->slave_bus))
> ++              return PTR_ERR(res->slave_bus);
> ++
> ++      res->core = devm_reset_control_get(dev, "core");
> ++      if (IS_ERR(res->core))
> ++              return PTR_ERR(res->core);
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_pcie_enable_link_training(struct pcie_port *pp)
> ++{
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
> ++      struct device *dev = pp->dev;
> ++      int retries;
> ++      u32 val;
> ++
> ++      /* enable link training */
> ++      writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
> ++
> ++      /* wait for up to 100ms for the link to come up */
> ++      retries = LINKUP_RETRIES_COUNT;
> ++      do {
> ++              val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
> ++              if (val & XMLH_LINK_UP)
> ++                      break;
> ++              usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
> ++      } while (retries--);
> ++
> ++      if (retries < 0 || !dw_pcie_link_up(pp)) {
> ++              dev_err(dev, "link initialization failed\n");
> ++              return -ENXIO;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static void qcom_pcie_host_init_v1(struct pcie_port *pp)
> ++{
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
> ++      int ret;
> ++
> ++      qcom_ep_reset_assert(pcie);
> ++
> ++      ret = qcom_pcie_enable_resources_v1(pcie);
> ++      if (ret)
> ++              return;
> ++
> ++      /* change DBI base address */
> ++      writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
> ++
> ++      if (IS_ENABLED(CONFIG_PCI_MSI))
> ++              writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
> ++                            0, BIT(31));
> ++
> ++      ret = phy_init(pcie->phy);
> ++      if (ret)
> ++              goto err_res;
> ++
> ++      ret = phy_power_on(pcie->phy);
> ++      if (ret)
> ++              goto err_phy;
> ++
> ++      dw_pcie_setup_rc(pp);
> ++
> ++      if (IS_ENABLED(CONFIG_PCI_MSI))
> ++              dw_pcie_msi_init(pp);
> ++
> ++      qcom_ep_reset_deassert(pcie);
> ++
> ++      ret = qcom_pcie_enable_link_training(pp);
> ++      if (ret)
> ++              goto err;
> ++
> ++      return;
> ++
> ++err:
> ++      qcom_ep_reset_assert(pcie);
> ++      phy_power_off(pcie->phy);
> ++err_phy:
> ++      phy_exit(pcie->phy);
> ++err_res:
> ++      qcom_pcie_disable_resources_v1(pcie);
> ++}
> ++
> ++static void qcom_pcie_host_init_v0(struct pcie_port *pp)
> ++{
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
> ++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
> ++      struct device *dev = pcie->dev;
> ++      int ret;
> ++
> ++      qcom_ep_reset_assert(pcie);
> ++
> ++      ret = qcom_pcie_enable_resources_v0(pcie);
> ++      if (ret)
> ++              return;
> ++
> ++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
> ++
> ++      /* enable external reference clock */
> ++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
> ++
> ++      ret = reset_control_deassert(res->phy_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert phy reset\n");
> ++              return;
> ++      }
> ++
> ++      ret = reset_control_deassert(res->pci_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert pci reset\n");
> ++              return;
> ++      }
> ++
> ++      ret = reset_control_deassert(res->por_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert por reset\n");
> ++              return;
> ++      }
> ++
> ++      ret = reset_control_deassert(res->axi_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot deassert axi reset\n");
> ++              return;
> ++      }
> ++
> ++      /* wait 150ms for clock acquisition */
> ++      usleep_range(10000, 15000);
> ++
> ++      dw_pcie_setup_rc(pp);
> ++
> ++      if (IS_ENABLED(CONFIG_PCI_MSI))
> ++              dw_pcie_msi_init(pp);
> ++
> ++      qcom_ep_reset_deassert(pcie);
> ++
> ++      ret = qcom_pcie_enable_link_training(pp);
> ++      if (ret)
> ++              goto err;
> ++
> ++      return;
> ++err:
> ++      qcom_ep_reset_assert(pcie);
> ++      qcom_pcie_disable_resources_v0(pcie);
> ++}
> ++
> ++static void qcom_pcie_host_init(struct pcie_port *pp)
> ++{
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
> ++
> ++      if (pcie->version == PCIE_V0)
> ++              return qcom_pcie_host_init_v0(pp);
> ++      else
> ++              return qcom_pcie_host_init_v1(pp);
> ++}
> ++
> ++static int
> ++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val)
> ++{
> ++      /* the device class is not reported correctly from the register */
> ++      if (where == PCI_CLASS_REVISION && size == 4) {
> ++              *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
> ++              *val &= ~(0xffff << 16);
> ++              *val |= PCI_CLASS_BRIDGE_PCI << 16;
> ++              return PCIBIOS_SUCCESSFUL;
> ++      }
> ++
> ++      return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
> ++}
> ++
> ++static struct pcie_host_ops qcom_pcie_ops = {
> ++      .link_up = qcom_pcie_link_up,
> ++      .host_init = qcom_pcie_host_init,
> ++      .rd_own_conf = qcom_pcie_rd_own_conf,
> ++};
> ++
> ++static const struct of_device_id qcom_pcie_match[] = {
> ++      { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
> ++      { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
> ++      { }
> ++};
> ++
> ++static int qcom_pcie_probe(struct platform_device *pdev)
> ++{
> ++      struct device *dev = &pdev->dev;
> ++      const struct of_device_id *match;
> ++      struct resource *res;
> ++      struct qcom_pcie *pcie;
> ++      struct pcie_port *pp;
> ++      int ret;
> ++
> ++      match = of_match_node(qcom_pcie_match, dev->of_node);
> ++      if (!match)
> ++              return -ENXIO;
> ++
> ++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> ++      if (!pcie)
> ++              return -ENOMEM;
> ++
> ++      pcie->version = (unsigned int)match->data;
> ++
> ++      pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
> ++      if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
> ++              return PTR_ERR(pcie->reset);
> ++
> ++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
> ++      pcie->parf = devm_ioremap_resource(dev, res);
> ++      if (IS_ERR(pcie->parf))
> ++              return PTR_ERR(pcie->parf);
> ++
> ++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
> ++      pcie->dbi = devm_ioremap_resource(dev, res);
> ++      if (IS_ERR(pcie->dbi))
> ++              return PTR_ERR(pcie->dbi);
> ++
> ++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
> ++      pcie->elbi = devm_ioremap_resource(dev, res);
> ++      if (IS_ERR(pcie->elbi))
> ++              return PTR_ERR(pcie->elbi);
> ++
> ++      pcie->phy = devm_phy_optional_get(dev, "pciephy");
> ++      if (IS_ERR(pcie->phy))
> ++              return PTR_ERR(pcie->phy);
> ++
> ++      pcie->dev = dev;
> ++
> ++      if (pcie->version == PCIE_V0)
> ++              ret = qcom_pcie_get_resources_v0(pcie);
> ++      else
> ++              ret = qcom_pcie_get_resources_v1(pcie);
> ++
> ++      if (ret)
> ++              return ret;
> ++
> ++      pp = &pcie->pp;
> ++      pp->dev = dev;
> ++      pp->dbi_base = pcie->dbi;
> ++      pp->root_bus_nr = -1;
> ++      pp->ops = &qcom_pcie_ops;
> ++
> ++      if (IS_ENABLED(CONFIG_PCI_MSI)) {
> ++              pp->msi_irq = platform_get_irq_byname(pdev, "msi");
> ++              if (pp->msi_irq < 0) {
> ++                      dev_err(dev, "cannot get msi irq\n");
> ++                      return pp->msi_irq;
> ++              }
> ++
> ++              ret = devm_request_irq(dev, pp->msi_irq,
> ++                                     qcom_pcie_msi_irq_handler,
> ++                                     IRQF_SHARED, "qcom-pcie-msi", pp);
> ++              if (ret) {
> ++                      dev_err(dev, "cannot request msi irq\n");
> ++                      return ret;
> ++              }
> ++      }
> ++
> ++      ret = dw_pcie_host_init(pp);
> ++      if (ret) {
> ++              dev_err(dev, "cannot initialize host\n");
> ++              return ret;
> ++      }
> ++
> ++      platform_set_drvdata(pdev, pcie);
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_pcie_remove(struct platform_device *pdev)
> ++{
> ++      struct qcom_pcie *pcie = platform_get_drvdata(pdev);
> ++
> ++      qcom_ep_reset_assert(pcie);
> ++      phy_power_off(pcie->phy);
> ++      phy_exit(pcie->phy);
> ++      if (pcie->version == PCIE_V0)
> ++              qcom_pcie_disable_resources_v0(pcie);
> ++      else
> ++              qcom_pcie_disable_resources_v1(pcie);
> ++
> ++      return 0;
> ++}
> ++
> ++static struct platform_driver qcom_pcie_driver = {
> ++      .probe = qcom_pcie_probe,
> ++      .remove = qcom_pcie_remove,
> ++      .driver = {
> ++              .name = "qcom-pcie",
> ++              .of_match_table = qcom_pcie_match,
> ++      },
> ++};
> ++
> ++module_platform_driver(qcom_pcie_driver);
> ++
> ++MODULE_AUTHOR("Stanimir Varbanov <svarbanov at mm-sol.com>");
> ++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_ALIAS("platform:qcom-pcie");
> +--- a/drivers/pci/host/Makefile
> ++++ b/drivers/pci/host/Makefile
> +@@ -20,3 +20,4 @@
> + obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
> + obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
> + obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
> ++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
> diff --git a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
> new file mode 100644
> index 0000000..1246bfe
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
> @@ -0,0 +1,244 @@
> +From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Tue, 21 Apr 2015 19:01:42 -0700
> +Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms
> +
> +qcom-pcie driver now supports version 0 of the controller. This change
> +adds the corresponding entries to the IPQ806x dtsi file and
> +corresponding platform (AP148).
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064-ap148.dts |  30 ++++++++
> + arch/arm/boot/dts/qcom-ipq8064.dtsi      | 124 +++++++++++++++++++++++++++++++
> + 2 files changed, 154 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -116,5 +116,15 @@
> +               usb30 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>;
> ++              };
> +       };
> + };
> +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> +@@ -128,5 +128,17 @@
> +               usb30 at 1 {
> +                       status = "ok";
> +               };
> ++
> ++              pcie0: pci at 1b500000 {
> ++                      status = "ok";
> ++              };
> ++
> ++              pcie1: pci at 1b700000 {
> ++                      status = "ok";
> ++              };
> ++
> ++              pcie2: pci at 1b900000 {
> ++                      status = "ok";
> ++              };
> +       };
> + };
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -4,6 +4,9 @@
> + #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
> + #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
> + #include <dt-bindings/soc/qcom,gsbi.h>
> ++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
> ++#include <dt-bindings/interrupt-controller/arm-gic.h>
> ++#include <dt-bindings/gpio/gpio.h>
> +
> + / {
> +       model = "Qualcomm IPQ8064";
> +@@ -99,6 +102,33 @@
> +                       interrupt-controller;
> +                       #interrupt-cells = <2>;
> +                       interrupts = <0 16 0x4>;
> ++
> ++                      pcie0_pins: pcie0_pinmux {
> ++                              mux {
> ++                                      pins = "gpio3";
> ++                                      function = "pcie1_rst";
> ++                                      drive-strength = <12>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> ++
> ++                      pcie1_pins: pcie1_pinmux {
> ++                              mux {
> ++                                      pins = "gpio48";
> ++                                      function = "pcie2_rst";
> ++                                      drive-strength = <12>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> ++
> ++                      pcie2_pins: pcie2_pinmux {
> ++                              mux {
> ++                                      pins = "gpio63";
> ++                                      function = "pcie3_rst";
> ++                                      drive-strength = <12>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> +               };
> +
> +               intc: interrupt-controller at 2000000 {
> +@@ -417,6 +447,144 @@
> +                               dr_mode = "host";
> +                       };
> +               };
> ++
> ++              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>;
> ++                      clock-names = "core", "iface", "phy";
> ++
> ++                      resets = <&gcc PCIE_ACLK_RESET>,
> ++                               <&gcc PCIE_HCLK_RESET>,
> ++                               <&gcc PCIE_POR_RESET>,
> ++                               <&gcc PCIE_PCI_RESET>,
> ++                               <&gcc PCIE_PHY_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++
> ++                      pinctrl-0 = <&pcie0_pins>;
> ++                      pinctrl-names = "default";
> ++
> ++                      perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
> ++
> ++                      status = "disabled";
> ++              };
> ++
> ++              pcie1: pci 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>;
> ++                      clock-names = "core", "iface", "phy";
> ++
> ++                      resets = <&gcc PCIE_1_ACLK_RESET>,
> ++                               <&gcc PCIE_1_HCLK_RESET>,
> ++                               <&gcc PCIE_1_POR_RESET>,
> ++                               <&gcc PCIE_1_PCI_RESET>,
> ++                               <&gcc PCIE_1_PHY_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++
> ++                      pinctrl-0 = <&pcie1_pins>;
> ++                      pinctrl-names = "default";
> ++
> ++                      perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
> ++
> ++                      status = "disabled";
> ++              };
> ++
> ++              pcie2: pci 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>;
> ++                      clock-names = "core", "iface", "phy";
> ++
> ++                      resets = <&gcc PCIE_2_ACLK_RESET>,
> ++                               <&gcc PCIE_2_HCLK_RESET>,
> ++                               <&gcc PCIE_2_POR_RESET>,
> ++                               <&gcc PCIE_2_PCI_RESET>,
> ++                               <&gcc PCIE_2_PHY_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++
> ++                      pinctrl-0 = <&pcie2_pins>;
> ++                      pinctrl-names = "default";
> ++
> ++                      perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>;
> ++
> ++                      status = "disabled";
> ++              };
> +       };
> +
> +       sfpb_mutex: sfpb-mutex {
> diff --git a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
> new file mode 100644
> index 0000000..e2d3135
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
> @@ -0,0 +1,29 @@
> +From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Tue, 21 Apr 2015 19:09:07 -0700
> +Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is
> + enabled
> +
> +If multiple PCIe devices are present in the system, the kernel will
> +panic at boot time when trying to scan the PCI buses. This happens on
> +IPQ806x based platforms, which has 3 PCIe ports.
> +
> +Enabling this option allows the kernel to assign the pci-domains
> +according to the device-tree content. This allows multiple PCIe
> +controllers to coexist in the system.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/mach-qcom/Kconfig | 1 +
> + 1 file changed, 1 insertion(+)
> +
> +--- a/arch/arm/mach-qcom/Kconfig
> ++++ b/arch/arm/mach-qcom/Kconfig
> +@@ -5,6 +5,7 @@ menuconfig ARCH_QCOM
> +       select ARM_AMBA
> +       select PINCTRL
> +       select QCOM_SCM if SMP
> ++      select PCI_DOMAINS if PCI
> +       help
> +         Support for Qualcomm's devicetree based systems.
> +
> diff --git a/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch
> new file mode 100644
> index 0000000..6328113
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch
> @@ -0,0 +1,311 @@
> +--- a/drivers/pci/host/pcie-qcom.c
> ++++ b/drivers/pci/host/pcie-qcom.c
> +@@ -29,8 +29,53 @@
> +
> + #include "pcie-designware.h"
> +
> ++/* DBI registers */
> ++#define PCIE20_CAP                            0x70
> ++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
> ++
> ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0               0x818
> ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1               0x81c
> ++
> ++#define PCIE20_PLR_IATU_VIEWPORT              0x900
> ++#define PCIE20_PLR_IATU_REGION_OUTBOUND               (0x0 << 31)
> ++#define PCIE20_PLR_IATU_REGION_INDEX(x)               (x << 0)
> ++
> ++#define PCIE20_PLR_IATU_CTRL1                 0x904
> ++#define PCIE20_PLR_IATU_TYPE_CFG0             (0x4 << 0)
> ++#define PCIE20_PLR_IATU_TYPE_MEM              (0x0 << 0)
> ++
> ++#define PCIE20_PLR_IATU_CTRL2                 0x908
> ++#define PCIE20_PLR_IATU_ENABLE                        BIT(31)
> ++
> ++#define PCIE20_PLR_IATU_LBAR                  0x90C
> ++#define PCIE20_PLR_IATU_UBAR                  0x910
> ++#define PCIE20_PLR_IATU_LAR                   0x914
> ++#define PCIE20_PLR_IATU_LTAR                  0x918
> ++#define PCIE20_PLR_IATU_UTAR                  0x91c
> ++
> ++#define MSM_PCIE_DEV_CFG_ADDR                 0x01000000
> ++
> ++/* PARF registers */
> ++#define PCIE20_PARF_PCS_DEEMPH                        0x34
> ++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x)          (x << 16)
> ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x)    (x << 8)
> ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x)      (x << 0)
> ++
> ++#define PCIE20_PARF_PCS_SWING                 0x38
> ++#define PCS_SWING_TX_SWING_FULL(x)            (x << 8)
> ++#define PCS_SWING_TX_SWING_LOW(x)             (x << 0)
> ++
> + #define PCIE20_PARF_PHY_CTRL                  0x40
> ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK     (0x1f << 16)
> ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x)               (x << 16)
> ++
> + #define PCIE20_PARF_PHY_REFCLK                        0x4C
> ++#define REF_SSP_EN                            BIT(16)
> ++#define REF_USE_PAD                           BIT(12)
> ++
> ++#define PCIE20_PARF_CONFIG_BITS                       0x50
> ++#define PHY_RX0_EQ(x)                         (x << 24)
> ++
> + #define PCIE20_PARF_DBI_BASE_ADDR             0x168
> + #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE               0x16c
> + #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT     0x178
> +@@ -39,9 +84,6 @@
> + #define PCIE20_ELBI_SYS_STTS                  0x08
> + #define XMLH_LINK_UP                          BIT(10)
> +
> +-#define PCIE20_CAP                            0x70
> +-#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
> +-
> + #define PERST_DELAY_MIN_US                    1000
> + #define PERST_DELAY_MAX_US                    1005
> +
> +@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 {
> +       struct clk *iface_clk;
> +       struct clk *core_clk;
> +       struct clk *phy_clk;
> ++      struct clk *aux_clk;
> ++      struct clk *ref_clk;
> +       struct reset_control *pci_reset;
> +       struct reset_control *axi_reset;
> +       struct reset_control *ahb_reset;
> +       struct reset_control *por_reset;
> +       struct reset_control *phy_reset;
> ++      struct reset_control *ext_reset;
> +       struct regulator *vdda;
> +       struct regulator *vdda_phy;
> +       struct regulator *vdda_refclk;
> ++      uint8_t phy_tx0_term_offset;
> + };
> +
> + struct qcom_pcie_resources_v1 {
> +@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl
> +
> + static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
> + {
> +-      int val, active_low;
> +-
> +       if (IS_ERR_OR_NULL(pcie->reset))
> +               return;
> +
> +-      active_low = gpiod_is_active_low(pcie->reset);
> +-
> +-      if (assert)
> +-              val = !!active_low;
> +-      else
> +-              val = !active_low;
> +-
> +-      gpiod_set_value(pcie->reset, val);
> +-
> ++      gpiod_set_value(pcie->reset, assert);
> +       usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
> + }
> +
> +@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_
> +       reset_control_assert(res->axi_reset);
> +       reset_control_assert(res->ahb_reset);
> +       reset_control_assert(res->por_reset);
> +-      reset_control_assert(res->pci_reset);
> ++      reset_control_assert(res->phy_reset);
> ++      reset_control_assert(res->ext_reset);
> +       clk_disable_unprepare(res->iface_clk);
> +       clk_disable_unprepare(res->core_clk);
> +       clk_disable_unprepare(res->phy_clk);
> ++      clk_disable_unprepare(res->aux_clk);
> ++      clk_disable_unprepare(res->ref_clk);
> +       regulator_disable(res->vdda);
> +       regulator_disable(res->vdda_phy);
> +       regulator_disable(res->vdda_refclk);
> +@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0
> +               goto err_vdda_phy;
> +       }
> +
> ++      ret = reset_control_deassert(res->ext_reset);
> ++      if (ret) {
> ++              dev_err(dev, "cannot assert ext reset\n");
> ++              goto err_reset_ext;
> ++      }
> ++
> +       ret = clk_prepare_enable(res->iface_clk);
> +       if (ret) {
> +               dev_err(dev, "cannot prepare/enable iface clock\n");
> +@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0
> +               goto err_clk_phy;
> +       }
> +
> ++      ret = clk_prepare_enable(res->aux_clk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable aux clock\n");
> ++              goto err_clk_aux;
> ++      }
> ++
> ++      ret = clk_prepare_enable(res->ref_clk);
> ++      if (ret) {
> ++              dev_err(dev, "cannot prepare/enable ref clock\n");
> ++              goto err_clk_ref;
> ++      }
> ++
> +       ret = reset_control_deassert(res->ahb_reset);
> +       if (ret) {
> +               dev_err(dev, "cannot deassert ahb reset\n");
> +               goto err_reset_ahb;
> +       }
> ++      udelay(1);
> +
> +       return 0;
> +
> + err_reset_ahb:
> ++      clk_disable_unprepare(res->ref_clk);
> ++err_clk_ref:
> ++      clk_disable_unprepare(res->aux_clk);
> ++err_clk_aux:
> +       clk_disable_unprepare(res->phy_clk);
> + err_clk_phy:
> +       clk_disable_unprepare(res->core_clk);
> + err_clk_core:
> +       clk_disable_unprepare(res->iface_clk);
> + err_iface:
> ++      reset_control_assert(res->ext_reset);
> ++err_reset_ext:
> +       regulator_disable(res->vdda_phy);
> + err_vdda_phy:
> +       regulator_disable(res->vdda_refclk);
> +@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st
> +       if (IS_ERR(res->phy_clk))
> +               return PTR_ERR(res->phy_clk);
> +
> ++      res->aux_clk = devm_clk_get(dev, "aux");
> ++      if (IS_ERR(res->aux_clk))
> ++              return PTR_ERR(res->aux_clk);
> ++
> ++      res->ref_clk = devm_clk_get(dev, "ref");
> ++      if (IS_ERR(res->ref_clk))
> ++              return PTR_ERR(res->ref_clk);
> ++
> +       res->pci_reset = devm_reset_control_get(dev, "pci");
> +       if (IS_ERR(res->pci_reset))
> +               return PTR_ERR(res->pci_reset);
> +@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st
> +       if (IS_ERR(res->phy_reset))
> +               return PTR_ERR(res->phy_reset);
> +
> ++      res->ext_reset = devm_reset_control_get(dev, "ext");
> ++      if (IS_ERR(res->ext_reset))
> ++              return PTR_ERR(res->ext_reset);
> ++
> ++      if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
> ++                              &res->phy_tx0_term_offset))
> ++              res->phy_tx0_term_offset = 0;
> ++
> +       return 0;
> + }
> +
> +@@ -461,6 +541,57 @@ err_res:
> +       qcom_pcie_disable_resources_v1(pcie);
> + }
> +
> ++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev)
> ++{
> ++      struct pcie_port *pp = &pcie->pp;
> ++
> ++      /*
> ++       * program and enable address translation region 0 (device config
> ++       * address space); region type config;
> ++       * axi config address range to device config address range
> ++       */
> ++      writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
> ++             PCIE20_PLR_IATU_REGION_INDEX(0),
> ++             pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
> ++
> ++      writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
> ++      writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
> ++      writel(pp->cfg0_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
> ++      writel((pp->cfg0_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
> ++      writel((pp->cfg0_base + pp->cfg0_size - 1),
> ++             pcie->dbi + PCIE20_PLR_IATU_LAR);
> ++      writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR);
> ++      writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR);
> ++}
> ++
> ++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie)
> ++{
> ++      struct pcie_port *pp = &pcie->pp;
> ++
> ++      /*
> ++       * program and enable address translation region 2 (device resource
> ++       * address space); region type memory;
> ++       * axi device bar address range to device bar address range
> ++       */
> ++      writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
> ++             PCIE20_PLR_IATU_REGION_INDEX(2),
> ++             pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
> ++
> ++      writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
> ++      writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
> ++      writel(pp->mem_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
> ++      writel((pp->mem_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
> ++      writel(pp->mem_base + pp->mem_size - 1,
> ++             pcie->dbi + PCIE20_PLR_IATU_LAR);
> ++      writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR);
> ++      writel(upper_32_bits(pp->mem_bus_addr),
> ++             pcie->dbi + PCIE20_PLR_IATU_UTAR);
> ++
> ++      /* 256B PCIE buffer setting */
> ++      writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
> ++      writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
> ++}
> ++
> + static void qcom_pcie_host_init_v0(struct pcie_port *pp)
> + {
> +       struct qcom_pcie *pcie = to_qcom_pcie(pp);
> +@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc
> +
> +       qcom_ep_reset_assert(pcie);
> +
> ++      reset_control_assert(res->ahb_reset);
> ++
> +       ret = qcom_pcie_enable_resources_v0(pcie);
> +       if (ret)
> +               return;
> +
> +       writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
> +
> +-      /* enable external reference clock */
> +-      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
> ++      /* Set Tx termination offset */
> ++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL,
> ++                    PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
> ++                    PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
> ++
> ++      /* PARF programming */
> ++      writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
> ++             PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
> ++             PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
> ++             pcie->parf + PCIE20_PARF_PCS_DEEMPH);
> ++      writel(PCS_SWING_TX_SWING_FULL(0x78) |
> ++             PCS_SWING_TX_SWING_LOW(0x78),
> ++             pcie->parf + PCIE20_PARF_PCS_SWING);
> ++      writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
> ++
> ++      /* Enable reference clock */
> ++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK,
> ++                    REF_USE_PAD, REF_SSP_EN);
> +
> ++      /* De-assert PHY, PCIe, POR and AXI resets */
> +       ret = reset_control_deassert(res->phy_reset);
> +       if (ret) {
> +               dev_err(dev, "cannot deassert phy reset\n");
> +@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc
> +       if (ret)
> +               goto err;
> +
> ++      qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR);
> ++      qcom_pcie_prog_viewport_mem2_outbound(pcie);
> ++
> +       return;
> + err:
> +       qcom_ep_reset_assert(pcie);
> diff --git a/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch
> new file mode 100644
> index 0000000..8ceace9
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch
> @@ -0,0 +1,80 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -476,15 +476,21 @@
> +
> +                       clocks = <&gcc PCIE_A_CLK>,
> +                                <&gcc PCIE_H_CLK>,
> +-                               <&gcc PCIE_PHY_CLK>;
> +-                      clock-names = "core", "iface", "phy";
> ++                               <&gcc PCIE_PHY_CLK>,
> ++                               <&gcc PCIE_AUX_CLK>,
> ++                               <&gcc PCIE_ALT_REF_CLK>;
> ++                      clock-names = "core", "iface", "phy", "aux", "ref";
> ++
> ++                      assigned-clocks = <&gcc PCIE_ALT_REF_CLK>;
> ++                      assigned-clock-rates = <100000000>;
> +
> +                       resets = <&gcc PCIE_ACLK_RESET>,
> +                                <&gcc PCIE_HCLK_RESET>,
> +                                <&gcc PCIE_POR_RESET>,
> +                                <&gcc PCIE_PCI_RESET>,
> +-                               <&gcc PCIE_PHY_RESET>;
> +-                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++                               <&gcc PCIE_PHY_RESET>,
> ++                               <&gcc PCIE_EXT_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
> +
> +                       pinctrl-0 = <&pcie0_pins>;
> +                       pinctrl-names = "default";
> +@@ -522,15 +528,21 @@
> +
> +                       clocks = <&gcc PCIE_1_A_CLK>,
> +                                <&gcc PCIE_1_H_CLK>,
> +-                               <&gcc PCIE_1_PHY_CLK>;
> +-                      clock-names = "core", "iface", "phy";
> ++                               <&gcc PCIE_1_PHY_CLK>,
> ++                               <&gcc PCIE_1_AUX_CLK>,
> ++                               <&gcc PCIE_1_ALT_REF_CLK>;
> ++                      clock-names = "core", "iface", "phy", "aux", "ref";
> ++
> ++                      assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>;
> ++                      assigned-clock-rates = <100000000>;
> +
> +                       resets = <&gcc PCIE_1_ACLK_RESET>,
> +                                <&gcc PCIE_1_HCLK_RESET>,
> +                                <&gcc PCIE_1_POR_RESET>,
> +                                <&gcc PCIE_1_PCI_RESET>,
> +-                               <&gcc PCIE_1_PHY_RESET>;
> +-                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++                               <&gcc PCIE_1_PHY_RESET>,
> ++                               <&gcc PCIE_1_EXT_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
> +
> +                       pinctrl-0 = <&pcie1_pins>;
> +                       pinctrl-names = "default";
> +@@ -568,15 +580,21 @@
> +
> +                       clocks = <&gcc PCIE_2_A_CLK>,
> +                                <&gcc PCIE_2_H_CLK>,
> +-                               <&gcc PCIE_2_PHY_CLK>;
> +-                      clock-names = "core", "iface", "phy";
> ++                               <&gcc PCIE_2_PHY_CLK>,
> ++                               <&gcc PCIE_2_AUX_CLK>,
> ++                               <&gcc PCIE_2_ALT_REF_CLK>;
> ++                      clock-names = "core", "iface", "phy", "aux", "ref";
> ++
> ++                      assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>;
> ++                      assigned-clock-rates = <100000000>;
> +
> +                       resets = <&gcc PCIE_2_ACLK_RESET>,
> +                                <&gcc PCIE_2_HCLK_RESET>,
> +                                <&gcc PCIE_2_POR_RESET>,
> +                                <&gcc PCIE_2_PCI_RESET>,
> +-                               <&gcc PCIE_2_PHY_RESET>;
> +-                      reset-names = "axi", "ahb", "por", "pci", "phy";
> ++                               <&gcc PCIE_2_PHY_RESET>,
> ++                               <&gcc PCIE_2_EXT_RESET>;
> ++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
> +
> +                       pinctrl-0 = <&pcie2_pins>;
> +                       pinctrl-names = "default";
> diff --git a/target/linux/ipq806x/patches-4.4/126-regulator-qcom-Update-support-for-RPM-controller-SMB.patch b/target/linux/ipq806x/patches-4.4/126-regulator-qcom-Update-support-for-RPM-controller-SMB.patch
> new file mode 100644
> index 0000000..c583c5b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/126-regulator-qcom-Update-support-for-RPM-controller-SMB.patch
> @@ -0,0 +1,147 @@
> +From 3461762edee0d912bfc7971576490446ebc156ce Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangi at codeaurora.org>
> +Date: Fri, 10 Jun 2016 19:43:36 +0530
> +Subject: [PATCH] regulator: qcom: Update support for RPM-controller SMB208
> +
> +RPM regulator driver has been reworked to single platform device
> +in last commit "087a1b5 regulator: qcom: Rework to single platform device"
> +but it seems SMB208 was missed to be updated in that commit.
> +
> +The IPQ8064 reference boards make use of SMB208 regulators, so this
> +change will update the SMB208 regulator according to new framework
> +and updated Docs and IPQ8064 DT too.
> +
> +Change-Id: I05af71a7dce81ec8142667ac90b4f097bfeeb677
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + Documentation/devicetree/bindings/mfd/qcom-rpm.txt |  4 ++
> + arch/arm/boot/dts/qcom-ipq8064.dtsi                | 54 +++++++++-------------
> + drivers/regulator/qcom_rpm-regulator.c             |  9 ++++
> + 3 files changed, 35 insertions(+), 32 deletions(-)
> +
> +diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
> +index 5e97a95..0a4a239 100644
> +--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
> ++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
> +@@ -59,6 +59,7 @@
> +                   "qcom,rpm-pm8058-regulators"
> +                   "qcom,rpm-pm8901-regulators"
> +                   "qcom,rpm-pm8921-regulators"
> ++                  "qcom,rpm-smb208-regulators"
> +
> + - vdd_l0_l1_lvs-supply:
> + - vdd_l2_l11_l12-supply:
> +@@ -156,6 +157,9 @@
> +       l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch,
> +       ncp
> +
> ++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:
> +
> +diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c
> +index e254272..fb1d21c 100644
> +--- a/drivers/regulator/qcom_rpm-regulator.c
> ++++ b/drivers/regulator/qcom_rpm-regulator.c
> +@@ -869,10 +869,19 @@
> +       { }
> + };
> +
> ++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-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/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +index 112e930..cc490c2 100644
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -2,6 +2,7 @@
> +
> + #include "skeleton.dtsi"
> + #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
> ++#include <dt-bindings/mfd/qcom-rpm.h>
> + #include <dt-bindings/clock/qcom,lcc-ipq806x.h>
> + #include <dt-bindings/soc/qcom,gsbi.h>
> + #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
> +@@ -93,6 +94,53 @@
> +                       reg-names = "lpass-lpaif";
> +               };
> +
> ++              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";
> ++
> ++                      #address-cells = <1>;
> ++                      #size-cells = <0>;
> ++
> ++                      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>;
> ++                              };
> ++                      };
> ++              };
> ++
> +               qcom_pinmux: pinmux at 800000 {
> +                       compatible = "qcom,ipq8064-pinctrl";
> +                       reg = <0x800000 0x4000>;
> +@@ -164,6 +222,12 @@
> +                       reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
> +               };
> +
> ++              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";
> +                       reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
> diff --git a/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch
> new file mode 100644
> index 0000000..01bd749
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch
> @@ -0,0 +1,144 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063051
> +Message-Id: <1426920332-9340-2-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>,
> +       Mark Rutland <mark.rutland at arm.com>, Russell King <linux at arm.linux.org.uk>,
> +       Courtney Cavin <courtney.cavin at sonymobile.com>
> +Date: Fri, 20 Mar 2015 23:45:20 -0700
> +
> +Krait CPUs have a handful of L2 cache controller registers that
> +live behind a cp15 based indirection register. First you program
> +the indirection register (l2cpselr) to point the L2 'window'
> +register (l2cpdr) at what you want to read/write.  Then you
> +read/write the 'window' register to do what you want. The
> +l2cpselr register is not banked per-cpu so we must lock around
> +accesses to it to prevent other CPUs from re-pointing l2cpdr
> +underneath us.
> +
> +Cc: Mark Rutland <mark.rutland at arm.com>
> +Cc: Russell King <linux at arm.linux.org.uk>
> +Cc: Courtney Cavin <courtney.cavin at sonymobile.com>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +arch/arm/common/Kconfig                   |  3 ++
> + arch/arm/common/Makefile                  |  1 +
> + arch/arm/common/krait-l2-accessors.c      | 58 +++++++++++++++++++++++++++++++
> + arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
> + 4 files changed, 82 insertions(+)
> + create mode 100644 arch/arm/common/krait-l2-accessors.c
> + create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
> +
> +--- a/arch/arm/common/Kconfig
> ++++ b/arch/arm/common/Kconfig
> +@@ -9,6 +9,9 @@
> +       bool
> +       select ZONE_DMA
> +
> ++config KRAIT_L2_ACCESSORS
> ++      bool
> ++
> + config SHARP_LOCOMO
> +       bool
> +
> +--- a/arch/arm/common/Makefile
> ++++ b/arch/arm/common/Makefile
> +@@ -7,6 +7,7 @@
> + obj-$(CONFIG_ICST)            += icst.o
> + obj-$(CONFIG_SA1111)          += sa1111.o
> + obj-$(CONFIG_DMABOUNCE)               += dmabounce.o
> ++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
> + obj-$(CONFIG_SHARP_LOCOMO)    += locomo.o
> + obj-$(CONFIG_SHARP_PARAM)     += sharpsl_param.o
> + obj-$(CONFIG_SHARP_SCOOP)     += scoop.o
> +--- /dev/null
> ++++ b/arch/arm/common/krait-l2-accessors.c
> +@@ -0,0 +1,58 @@
> ++/*
> ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/spinlock.h>
> ++#include <linux/export.h>
> ++
> ++#include <asm/barrier.h>
> ++#include <asm/krait-l2-accessors.h>
> ++
> ++static DEFINE_RAW_SPINLOCK(krait_l2_lock);
> ++
> ++void krait_set_l2_indirect_reg(u32 addr, u32 val)
> ++{
> ++      unsigned long flags;
> ++
> ++      raw_spin_lock_irqsave(&krait_l2_lock, flags);
> ++      /*
> ++       * Select the L2 window by poking l2cpselr, then write to the window
> ++       * via l2cpdr.
> ++       */
> ++      asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
> ++      isb();
> ++      asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
> ++      isb();
> ++
> ++      raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
> ++}
> ++EXPORT_SYMBOL(krait_set_l2_indirect_reg);
> ++
> ++u32 krait_get_l2_indirect_reg(u32 addr)
> ++{
> ++      u32 val;
> ++      unsigned long flags;
> ++
> ++      raw_spin_lock_irqsave(&krait_l2_lock, flags);
> ++      /*
> ++       * Select the L2 window by poking l2cpselr, then read from the window
> ++       * via l2cpdr.
> ++       */
> ++      asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
> ++      isb();
> ++      asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
> ++
> ++      raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
> ++
> ++      return val;
> ++}
> ++EXPORT_SYMBOL(krait_get_l2_indirect_reg);
> +--- /dev/null
> ++++ b/arch/arm/include/asm/krait-l2-accessors.h
> +@@ -0,0 +1,20 @@
> ++/*
> ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
> ++#define __ASMARM_KRAIT_L2_ACCESSORS_H
> ++
> ++extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
> ++extern u32 krait_get_l2_indirect_reg(u32 addr);
> ++
> ++#endif
> diff --git a/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch
> new file mode 100644
> index 0000000..acf5820
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch
> @@ -0,0 +1,184 @@
> +From 4c28a15ea536281c8d619e5c6716ade914c79a6e Mon Sep 17 00:00:00 2001
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +Date: Fri, 20 Mar 2015 23:45:21 -0700
> +Subject: [PATCH 1/2] clk: mux: Split out register accessors for reuse
> +
> +We want to reuse the logic in clk-mux.c for other clock drivers
> +that don't use readl as register accessors. Fortunately, there
> +really isn't much to the mux code besides the table indirection
> +and quirk flags if you assume any bit shifting and masking has
> +been done already. Pull that logic out into reusable functions
> +that operate on an optional table and some flags so that other
> +drivers can use the same logic.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/clk/clk-mux.c        | 74 +++++++++++++++++++++++++++-----------------
> + include/linux/clk-provider.h |  9 ++++--
> + 2 files changed, 53 insertions(+), 30 deletions(-)
> +
> +diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
> +index 7129c86..b03a34d 100644
> +--- a/drivers/clk/clk-mux.c
> ++++ b/drivers/clk/clk-mux.c
> +@@ -28,35 +28,24 @@
> +
> + #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
> +
> +-static u8 clk_mux_get_parent(struct clk_hw *hw)
> ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
> ++                              unsigned int *table, unsigned long flags)
> + {
> +-      struct clk_mux *mux = to_clk_mux(hw);
> +       int num_parents = clk_hw_get_num_parents(hw);
> +-      u32 val;
> +
> +-      /*
> +-       * FIXME need a mux-specific flag to determine if val is bitwise or numeric
> +-       * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
> +-       * to 0x7 (index starts at one)
> +-       * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
> +-       * val = 0x4 really means "bit 2, index starts at bit 0"
> +-       */
> +-      val = clk_readl(mux->reg) >> mux->shift;
> +-      val &= mux->mask;
> +-
> +-      if (mux->table) {
> ++      if (table) {
> +               int i;
> +
> +               for (i = 0; i < num_parents; i++)
> +-                      if (mux->table[i] == val)
> ++                      if (table[i] == val)
> +                               return i;
> +               return -EINVAL;
> +       }
> +
> +-      if (val && (mux->flags & CLK_MUX_INDEX_BIT))
> ++      if (val && (flags & CLK_MUX_INDEX_BIT))
> +               val = ffs(val) - 1;
> +
> +-      if (val && (mux->flags & CLK_MUX_INDEX_ONE))
> ++      if (val && (flags & CLK_MUX_INDEX_ONE))
> +               val--;
> +
> +       if (val >= num_parents)
> +@@ -64,24 +53,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
> +
> +       return val;
> + }
> ++EXPORT_SYMBOL_GPL(clk_mux_get_parent);
> +
> +-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
> ++static u8 _clk_mux_get_parent(struct clk_hw *hw)
> + {
> +       struct clk_mux *mux = to_clk_mux(hw);
> +       u32 val;
> +-      unsigned long flags = 0;
> +
> +-      if (mux->table)
> +-              index = mux->table[index];
> ++      /*
> ++      * FIXME need a mux-specific flag to determine if val is bitwise or numeric
> ++      * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
> ++      * to 0x7 (index starts at one)
> ++      * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
> ++      * val = 0x4 really means "bit 2, index starts at bit 0"
> ++      */
> ++      val = clk_readl(mux->reg) >> mux->shift;
> ++      val &= mux->mask;
> +
> +-      else {
> +-              if (mux->flags & CLK_MUX_INDEX_BIT)
> +-                      index = 1 << index;
> ++      return clk_mux_get_parent(hw, val, mux->table, mux->flags);
> ++}
> ++
> ++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
> ++                          unsigned long flags)
> ++{
> ++      unsigned int val = index;
> +
> +-              if (mux->flags & CLK_MUX_INDEX_ONE)
> +-                      index++;
> ++      if (table) {
> ++              val = table[val];
> ++      } else {
> ++              if (flags & CLK_MUX_INDEX_BIT)
> ++                      val = 1 << index;
> ++
> ++      if (flags & CLK_MUX_INDEX_ONE)
> ++              val++;
> +       }
> +
> ++      return val;
> ++}
> ++EXPORT_SYMBOL_GPL(clk_mux_reindex);
> ++
> ++static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
> ++{
> ++      struct clk_mux *mux = to_clk_mux(hw);
> ++      u32 val;
> ++      unsigned long flags = 0;
> ++
> ++      index = clk_mux_reindex(index, mux->table, mux->flags);
> ++
> +       if (mux->lock)
> +               spin_lock_irqsave(mux->lock, flags);
> +       else
> +@@ -105,7 +123,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
> + }
> +
> + const struct clk_ops clk_mux_ops = {
> +-      .get_parent = clk_mux_get_parent,
> ++      .get_parent = _clk_mux_get_parent,
> +       .set_parent = clk_mux_set_parent,
> +       .determine_rate = __clk_mux_determine_rate,
> + };
> +@@ -120,7 +138,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
> +               const char * const *parent_names, u8 num_parents,
> +               unsigned long flags,
> +               void __iomem *reg, u8 shift, u32 mask,
> +-              u8 clk_mux_flags, u32 *table, spinlock_t *lock)
> ++              u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
> + {
> +       struct clk_mux *mux;
> +       struct clk *clk;
> +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> +index c56988a..b6b17b5 100644
> +--- a/include/linux/clk-provider.h
> ++++ b/include/linux/clk-provider.h
> +@@ -432,7 +432,7 @@ void clk_unregister_divider(struct clk *clk);
> + struct clk_mux {
> +       struct clk_hw   hw;
> +       void __iomem    *reg;
> +-      u32             *table;
> ++      unsigned int    *table;
> +       u32             mask;
> +       u8              shift;
> +       u8              flags;
> +@@ -448,6 +448,11 @@ struct clk_mux {
> + extern const struct clk_ops clk_mux_ops;
> + extern const struct clk_ops clk_mux_ro_ops;
> +
> ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
> ++                              unsigned int *table, unsigned long flags);
> ++unsigned int clk_mux_reindex(u8 index, unsigned int *table,
> ++                          unsigned long flags);
> ++
> + struct clk *clk_register_mux(struct device *dev, const char *name,
> +               const char * const *parent_names, u8 num_parents,
> +               unsigned long flags,
> +@@ -458,7 +463,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
> +               const char * const *parent_names, u8 num_parents,
> +               unsigned long flags,
> +               void __iomem *reg, u8 shift, u32 mask,
> +-              u8 clk_mux_flags, u32 *table, spinlock_t *lock);
> ++              u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
> +
> + void clk_unregister_mux(struct clk *clk);
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
> new file mode 100644
> index 0000000..5df0a56
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch
> @@ -0,0 +1,127 @@
> +From 39d42ce5031d2a4f92fa203b87acfbab340b15a2 Mon Sep 17 00:00:00 2001
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +Date: Fri, 20 Mar 2015 23:45:22 -0700
> +Subject: [PATCH 2/2] clk: Avoid sending high rates to downstream clocks during
> + set_rate
> +
> +If a clock is on and we call clk_set_rate() on it we may get into
> +a situation where the clock temporarily increases in rate
> +dramatically while we walk the tree and call .set_rate() ops. For
> +example, consider a case where a PLL feeds into a divider.
> +Initially the divider is set to divide by 1 and the PLL is
> +running fairly slow (100MHz). The downstream consumer of the
> +divider output can only handle rates =< 400 MHz, but the divider
> +can only choose between divisors of 1 and 4.
> +
> + +-----+   +----------------+
> + | PLL |-->| div 1 or div 4 |---> consumer device
> + +-----+   +----------------+
> +
> +To achieve a rate of 400MHz on the output of the divider, we
> +would have to set the rate of the PLL to 1.6 GHz and then divide
> +it by 4. The current code would set the PLL to 1.6GHz first while
> +the divider is still set to 1, thus causing the downstream
> +consumer of the clock to receive a few clock cycles of 1.6GHz
> +clock (far beyond it's maximum acceptable rate). We should be
> +changing the divider first before increasing the PLL rate to
> +avoid this problem.
> +
> +Therefore, set the rate of any child clocks that are increasing
> +in rate from their current rate so that they can increase their
> +dividers if necessary. We assume that there isn't such a thing as
> +minimum rate requirements.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
> + 1 file changed, 22 insertions(+), 12 deletions(-)
> +
> +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> +index f13c3f4..8404c3c 100644
> +--- a/drivers/clk/clk.c
> ++++ b/drivers/clk/clk.c
> +@@ -1427,21 +1427,24 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
> +  * walk down a subtree and set the new rates notifying the rate
> +  * change on the way
> +  */
> +-static void clk_change_rate(struct clk_core *core)
> ++static void
> ++clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
> + {
> +       struct clk_core *child;
> +       struct hlist_node *tmp;
> +       unsigned long old_rate;
> +-      unsigned long best_parent_rate = 0;
> +       bool skip_set_rate = false;
> +       struct clk_core *old_parent;
> +
> +-      old_rate = core->rate;
> ++      hlist_for_each_entry(child, &core->children, child_node) {
> ++              /* Skip children who will be reparented to another clock */
> ++              if (child->new_parent && child->new_parent != core)
> ++                      continue;
> ++              if (child->new_rate > child->rate)
> ++                      clk_change_rate(child, core->new_rate);
> ++      }
> +
> +-      if (core->new_parent)
> +-              best_parent_rate = core->new_parent->rate;
> +-      else if (core->parent)
> +-              best_parent_rate = core->parent->rate;
> ++      old_rate = core->rate;
> +
> +       if (core->new_parent && core->new_parent != core->parent) {
> +               old_parent = __clk_set_parent_before(core, core->new_parent);
> +@@ -1467,7 +1470,7 @@ static void clk_change_rate(struct clk_core *core)
> +
> +       trace_clk_set_rate_complete(core, core->new_rate);
> +
> +-      core->rate = clk_recalc(core, best_parent_rate);
> ++      core->rate = core->new_rate;
> +
> +       if (core->notifier_count && old_rate != core->rate)
> +               __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
> +@@ -1483,12 +1486,13 @@ static void clk_change_rate(struct clk_core *core)
> +               /* Skip children who will be reparented to another clock */
> +               if (child->new_parent && child->new_parent != core)
> +                       continue;
> +-              clk_change_rate(child);
> ++              if (child->new_rate != child->rate)
> ++                      clk_change_rate(child, core->new_rate);
> +       }
> +
> +       /* handle the new child who might not be in core->children yet */
> +-      if (core->new_child)
> +-              clk_change_rate(core->new_child);
> ++      if (core->new_child && core->new_child->new_rate != core->new_child->rate)
> ++              clk_change_rate(core->new_child, core->new_rate);
> + }
> +
> + static int clk_core_set_rate_nolock(struct clk_core *core,
> +@@ -1497,6 +1501,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
> +       struct clk_core *top, *fail_clk;
> +       unsigned long rate = req_rate;
> +       int ret = 0;
> ++      unsigned long parent_rate;
> +
> +       if (!core)
> +               return 0;
> +@@ -1522,8 +1527,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
> +               return -EBUSY;
> +       }
> +
> ++      if (top->parent)
> ++              parent_rate = top->parent->rate;
> ++      else
> ++              parent_rate = 0;
> ++
> +       /* change the rates */
> +-      clk_change_rate(top);
> ++      clk_change_rate(top, parent_rate);
> +
> +       core->req_rate = req_rate;
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch
> new file mode 100644
> index 0000000..e969f6b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch
> @@ -0,0 +1,158 @@
> +From f7a00ea959be31f9b742042294a359d508edce94 Mon Sep 17 00:00:00 2001
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +Date: Fri, 20 Mar 2015 23:45:23 -0700
> +Subject: [PATCH] clk: Add safe switch hook
> +
> +Sometimes clocks can't accept their parent source turning off
> +while the source is reprogrammed to a different rate. Most
> +notably CPU clocks require a way to switch away from the current
> +PLL they're running on, reprogram that PLL to a new rate, and
> +then switch back to the PLL with the new rate once they're done.
> +Add a hook that drivers can implement allowing them to return a
> +'safe parent' that they can switch their parent to while the
> +upstream source is reprogrammed to support this.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/clk/clk.c            | 61 ++++++++++++++++++++++++++++++++++++++------
> + include/linux/clk-provider.h |  1 +
> + 2 files changed, 54 insertions(+), 8 deletions(-)
> +
> +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> +index 8404c3c..a29319a 100644
> +--- a/drivers/clk/clk.c
> ++++ b/drivers/clk/clk.c
> +@@ -51,9 +51,12 @@ struct clk_core {
> +       struct clk_core         **parents;
> +       u8                      num_parents;
> +       u8                      new_parent_index;
> ++      u8                      safe_parent_index;
> +       unsigned long           rate;
> +       unsigned long           req_rate;
> ++      unsigned long           old_rate;
> +       unsigned long           new_rate;
> ++      struct clk_core         *safe_parent;
> +       struct clk_core         *new_parent;
> +       struct clk_core         *new_child;
> +       unsigned long           flags;
> +@@ -1271,7 +1274,8 @@ out:
> + static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
> +                            struct clk_core *new_parent, u8 p_index)
> + {
> +-      struct clk_core *child;
> ++      struct clk_core *child, *parent;
> ++      struct clk_hw *parent_hw;
> +
> +       core->new_rate = new_rate;
> +       core->new_parent = new_parent;
> +@@ -1281,6 +1285,18 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
> +       if (new_parent && new_parent != core->parent)
> +               new_parent->new_child = core;
> +
> ++      if (core->ops->get_safe_parent) {
> ++              parent_hw = core->ops->get_safe_parent(core->hw);
> ++              if (parent_hw) {
> ++                      parent = parent_hw->core;
> ++                      p_index = clk_fetch_parent_index(core, parent);
> ++                      core->safe_parent_index = p_index;
> ++                      core->safe_parent = parent;
> ++              }
> ++      } else {
> ++              core->safe_parent = NULL;
> ++      }
> ++
> +       hlist_for_each_entry(child, &core->children, child_node) {
> +               child->new_rate = clk_recalc(child, new_rate);
> +               clk_calc_subtree(child, child->new_rate, NULL, 0);
> +@@ -1393,14 +1409,43 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
> +                                                 unsigned long event)
> + {
> +       struct clk_core *child, *tmp_clk, *fail_clk = NULL;
> ++      struct clk_core *old_parent;
> +       int ret = NOTIFY_DONE;
> +
> +-      if (core->rate == core->new_rate)
> ++      if (core->rate == core->new_rate && event != POST_RATE_CHANGE)
> +               return NULL;
> +
> ++      switch (event) {
> ++      case PRE_RATE_CHANGE:
> ++              if (core->safe_parent)
> ++                      core->ops->set_parent(core->hw, core->safe_parent_index);
> ++              core->old_rate = core->rate;
> ++              break;
> ++      case POST_RATE_CHANGE:
> ++              if (core->safe_parent) {
> ++                      old_parent = __clk_set_parent_before(core,
> ++                                                          core->new_parent);
> ++                      if (core->ops->set_rate_and_parent) {
> ++                              core->ops->set_rate_and_parent(core->hw,
> ++                                              core->new_rate,
> ++                                              core->new_parent ?
> ++                                              core->new_parent->rate : 0,
> ++                                              core->new_parent_index);
> ++                      } else if (core->ops->set_parent) {
> ++                              core->ops->set_parent(core->hw,
> ++                                              core->new_parent_index);
> ++                      }
> ++                      __clk_set_parent_after(core, core->new_parent,
> ++                                              old_parent);
> ++              }
> ++              break;
> ++      }
> ++
> +       if (core->notifier_count) {
> +-              ret = __clk_notify(core, event, core->rate, core->new_rate);
> +-              if (ret & NOTIFY_STOP_MASK)
> ++              if (event != POST_RATE_CHANGE || core->old_rate != core->rate)
> ++                      ret = __clk_notify(core, event, core->old_rate,
> ++                                         core->new_rate);
> ++              if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
> +                       fail_clk = core;
> +       }
> +
> +@@ -1446,7 +1491,8 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
> +
> +       old_rate = core->rate;
> +
> +-      if (core->new_parent && core->new_parent != core->parent) {
> ++      if (core->new_parent && core->new_parent != core->parent &&
> ++                      !core->safe_parent) {
> +               old_parent = __clk_set_parent_before(core, core->new_parent);
> +               trace_clk_set_parent(core, core->new_parent);
> +
> +@@ -1472,9 +1518,6 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
> +
> +       core->rate = core->new_rate;
> +
> +-      if (core->notifier_count && old_rate != core->rate)
> +-              __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
> +-
> +       if (core->flags & CLK_RECALC_NEW_RATES)
> +               (void)clk_calc_new_rates(core, core->new_rate);
> +
> +@@ -1537,6 +1580,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
> +
> +       core->req_rate = req_rate;
> +
> ++      clk_propagate_rate_change(top, POST_RATE_CHANGE);
> ++
> +       return ret;
> + }
> +
> +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> +index b6b17b5..5d49262 100644
> +--- a/include/linux/clk-provider.h
> ++++ b/include/linux/clk-provider.h
> +@@ -202,6 +202,7 @@ struct clk_ops {
> +                                         struct clk_rate_request *req);
> +       int             (*set_parent)(struct clk_hw *hw, u8 index);
> +       u8              (*get_parent)(struct clk_hw *hw);
> ++      struct clk_hw   *(*get_safe_parent)(struct clk_hw *hw);
> +       int             (*set_rate)(struct clk_hw *hw, unsigned long rate,
> +                                   unsigned long parent_rate);
> +       int             (*set_rate_and_parent)(struct clk_hw *hw,
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
> new file mode 100644
> index 0000000..a1b1f4b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
> @@ -0,0 +1,351 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063261
> +Message-Id: <1426920332-9340-6-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>
> +Date: Fri, 20 Mar 2015 23:45:24 -0700
> +
> +HFPLLs are the main frequency source for Krait CPU clocks. Add
> +support for changing the rate of these PLLs.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +I'd really like to get rid of __clk_hfpll_init_once() if possible...
> +
> + drivers/clk/qcom/Makefile    |   1 +
> + drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
> + drivers/clk/qcom/clk-hfpll.h |  54 +++++++++
> + 3 files changed, 308 insertions(+)
> + create mode 100644 drivers/clk/qcom/clk-hfpll.c
> + create mode 100644 drivers/clk/qcom/clk-hfpll.h
> +
> +--- a/drivers/clk/qcom/Makefile
> ++++ b/drivers/clk/qcom/Makefile
> +@@ -8,6 +8,7 @@
> + clk-qcom-y += clk-branch.o
> + clk-qcom-y += clk-regmap-divider.o
> + clk-qcom-y += clk-regmap-mux.o
> ++clk-qcom-y += clk-hfpll.o
> + clk-qcom-y += reset.o
> + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
> +
> +--- /dev/null
> ++++ b/drivers/clk/qcom/clk-hfpll.c
> +@@ -0,0 +1,253 @@
> ++/*
> ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++#include <linux/kernel.h>
> ++#include <linux/export.h>
> ++#include <linux/regmap.h>
> ++#include <linux/delay.h>
> ++#include <linux/err.h>
> ++#include <linux/clk-provider.h>
> ++#include <linux/spinlock.h>
> ++
> ++#include "clk-regmap.h"
> ++#include "clk-hfpll.h"
> ++
> ++#define PLL_OUTCTRL   BIT(0)
> ++#define PLL_BYPASSNL  BIT(1)
> ++#define PLL_RESET_N   BIT(2)
> ++
> ++/* Initialize a HFPLL at a given rate and enable it. */
> ++static void __clk_hfpll_init_once(struct clk_hw *hw)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++
> ++      if (likely(h->init_done))
> ++              return;
> ++
> ++      /* Configure PLL parameters for integer mode. */
> ++      if (hd->config_val)
> ++              regmap_write(regmap, hd->config_reg, hd->config_val);
> ++      regmap_write(regmap, hd->m_reg, 0);
> ++      regmap_write(regmap, hd->n_reg, 1);
> ++
> ++      if (hd->user_reg) {
> ++              u32 regval = hd->user_val;
> ++              unsigned long rate;
> ++
> ++              rate = clk_hw_get_rate(hw->clk);
> ++
> ++              /* Pick the right VCO. */
> ++              if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
> ++                      regval |= hd->user_vco_mask;
> ++              regmap_write(regmap, hd->user_reg, regval);
> ++      }
> ++
> ++      if (hd->droop_reg)
> ++              regmap_write(regmap, hd->droop_reg, hd->droop_val);
> ++
> ++      h->init_done = true;
> ++}
> ++
> ++static void __clk_hfpll_enable(struct clk_hw *hw)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      u32 val;
> ++
> ++      __clk_hfpll_init_once(hw);
> ++
> ++      /* Disable PLL bypass mode. */
> ++      regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
> ++
> ++      /*
> ++       * H/W requires a 5us delay between disabling the bypass and
> ++       * de-asserting the reset. Delay 10us just to be safe.
> ++       */
> ++      udelay(10);
> ++
> ++      /* De-assert active-low PLL reset. */
> ++      regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
> ++
> ++      /* Wait for PLL to lock. */
> ++      if (hd->status_reg) {
> ++              do {
> ++                      regmap_read(regmap, hd->status_reg, &val);
> ++              } while (!(val & BIT(hd->lock_bit)));
> ++      } else {
> ++              udelay(60);
> ++      }
> ++
> ++      /* Enable PLL output. */
> ++      regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
> ++}
> ++
> ++/* Enable an already-configured HFPLL. */
> ++static int clk_hfpll_enable(struct clk_hw *hw)
> ++{
> ++      unsigned long flags;
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      u32 mode;
> ++
> ++      spin_lock_irqsave(&h->lock, flags);
> ++      regmap_read(regmap, hd->mode_reg, &mode);
> ++      if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
> ++              __clk_hfpll_enable(hw);
> ++      spin_unlock_irqrestore(&h->lock, flags);
> ++
> ++      return 0;
> ++}
> ++
> ++static void __clk_hfpll_disable(struct clk_hfpll *h)
> ++{
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++
> ++      /*
> ++       * Disable the PLL output, disable test mode, enable the bypass mode,
> ++       * and assert the reset.
> ++       */
> ++      regmap_update_bits(regmap, hd->mode_reg,
> ++                      PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
> ++}
> ++
> ++static void clk_hfpll_disable(struct clk_hw *hw)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      unsigned long flags;
> ++
> ++      spin_lock_irqsave(&h->lock, flags);
> ++      __clk_hfpll_disable(h);
> ++      spin_unlock_irqrestore(&h->lock, flags);
> ++}
> ++
> ++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
> ++                               unsigned long *parent_rate)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      unsigned long rrate;
> ++
> ++      rate = clamp(rate, hd->min_rate, hd->max_rate);
> ++
> ++      rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
> ++      if (rrate > hd->max_rate)
> ++              rrate -= *parent_rate;
> ++
> ++      return rrate;
> ++}
> ++
> ++/*
> ++ * For optimization reasons, assumes no downstream clocks are actively using
> ++ * it.
> ++ */
> ++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
> ++                            unsigned long parent_rate)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      unsigned long flags;
> ++      u32 l_val, val;
> ++      bool enabled;
> ++
> ++      l_val = rate / parent_rate;
> ++
> ++      spin_lock_irqsave(&h->lock, flags);
> ++
> ++      enabled = __clk_is_enabled(hw->clk);
> ++      if (enabled)
> ++              __clk_hfpll_disable(h);
> ++
> ++      /* Pick the right VCO. */
> ++      if (hd->user_reg && hd->user_vco_mask) {
> ++              regmap_read(regmap, hd->user_reg, &val);
> ++              if (rate <= hd->low_vco_max_rate)
> ++                      val &= ~hd->user_vco_mask;
> ++              else
> ++                      val |= hd->user_vco_mask;
> ++              regmap_write(regmap, hd->user_reg, val);
> ++      }
> ++
> ++      regmap_write(regmap, hd->l_reg, l_val);
> ++
> ++      if (enabled)
> ++              __clk_hfpll_enable(hw);
> ++
> ++      spin_unlock_irqrestore(&h->lock, flags);
> ++
> ++      return 0;
> ++}
> ++
> ++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
> ++                                         unsigned long parent_rate)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      u32 l_val;
> ++
> ++      regmap_read(regmap, hd->l_reg, &l_val);
> ++
> ++      return l_val * parent_rate;
> ++}
> ++
> ++static void clk_hfpll_init(struct clk_hw *hw)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      u32 mode, status;
> ++
> ++      regmap_read(regmap, hd->mode_reg, &mode);
> ++      if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
> ++              __clk_hfpll_init_once(hw);
> ++              return;
> ++      }
> ++
> ++      if (hd->status_reg) {
> ++              regmap_read(regmap, hd->status_reg, &status);
> ++              if (!(status & BIT(hd->lock_bit))) {
> ++                      WARN(1, "HFPLL %s is ON, but not locked!\n",
> ++                                      __clk_get_name(hw->clk));
> ++                      clk_hfpll_disable(hw);
> ++                      __clk_hfpll_init_once(hw);
> ++              }
> ++      }
> ++}
> ++
> ++static int hfpll_is_enabled(struct clk_hw *hw)
> ++{
> ++      struct clk_hfpll *h = to_clk_hfpll(hw);
> ++      struct hfpll_data const *hd = h->d;
> ++      struct regmap *regmap = h->clkr.regmap;
> ++      u32 mode;
> ++
> ++      regmap_read(regmap, hd->mode_reg, &mode);
> ++      mode &= 0x7;
> ++      return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
> ++}
> ++
> ++const struct clk_ops clk_ops_hfpll = {
> ++      .enable = clk_hfpll_enable,
> ++      .disable = clk_hfpll_disable,
> ++      .is_enabled = hfpll_is_enabled,
> ++      .round_rate = clk_hfpll_round_rate,
> ++      .set_rate = clk_hfpll_set_rate,
> ++      .recalc_rate = clk_hfpll_recalc_rate,
> ++      .init = clk_hfpll_init,
> ++};
> ++EXPORT_SYMBOL_GPL(clk_ops_hfpll);
> +--- /dev/null
> ++++ b/drivers/clk/qcom/clk-hfpll.h
> +@@ -0,0 +1,54 @@
> ++/*
> ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++#ifndef __QCOM_CLK_HFPLL_H__
> ++#define __QCOM_CLK_HFPLL_H__
> ++
> ++#include <linux/clk-provider.h>
> ++#include <linux/spinlock.h>
> ++#include "clk-regmap.h"
> ++
> ++struct hfpll_data {
> ++      u32 mode_reg;
> ++      u32 l_reg;
> ++      u32 m_reg;
> ++      u32 n_reg;
> ++      u32 user_reg;
> ++      u32 droop_reg;
> ++      u32 config_reg;
> ++      u32 status_reg;
> ++      u8  lock_bit;
> ++
> ++      u32 droop_val;
> ++      u32 config_val;
> ++      u32 user_val;
> ++      u32 user_vco_mask;
> ++      unsigned long low_vco_max_rate;
> ++
> ++      unsigned long min_rate;
> ++      unsigned long max_rate;
> ++};
> ++
> ++struct clk_hfpll {
> ++      struct hfpll_data const *d;
> ++      int init_done;
> ++
> ++      struct clk_regmap clkr;
> ++      spinlock_t lock;
> ++};
> ++
> ++#define to_clk_hfpll(_hw) \
> ++      container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
> ++
> ++extern const struct clk_ops clk_ops_hfpll;
> ++
> ++#endif
> diff --git a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch
> new file mode 100644
> index 0000000..5a452db
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch
> @@ -0,0 +1,206 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,06/13] clk: qcom: Add HFPLL driver
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063231
> +Message-Id: <1426920332-9340-7-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>, <devicetree at vger.kernel.org>
> +Date: Fri, 20 Mar 2015 23:45:25 -0700
> +
> +On some devices (MSM8974 for example), the HFPLLs are
> +instantiated within the Krait processor subsystem as separate
> +register regions. Add a driver for these PLLs so that we can
> +provide HFPLL clocks for use by the system.
> +
> +Cc: <devicetree at vger.kernel.org>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +.../devicetree/bindings/clock/qcom,hfpll.txt       |  40 ++++++++
> + drivers/clk/qcom/Kconfig                           |   8 ++
> + drivers/clk/qcom/Makefile                          |   1 +
> + drivers/clk/qcom/hfpll.c                           | 109 +++++++++++++++++++++
> + 4 files changed, 158 insertions(+)
> + create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
> + create mode 100644 drivers/clk/qcom/hfpll.c
> +
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
> +@@ -0,0 +1,40 @@
> ++High-Frequency PLL (HFPLL)
> ++
> ++PROPERTIES
> ++
> ++- compatible:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: must be "qcom,hfpll"
> ++
> ++- reg:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: address and size of HPLL registers. An optional second
> ++                  element specifies the address and size of the alias
> ++                  register region.
> ++
> ++- clock-output-names:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: Name of the PLL. Typically hfpllX where X is a CPU number
> ++                  starting at 0. Otherwise hfpll_Y where Y is more specific
> ++                  such as "l2".
> ++
> ++Example:
> ++
> ++1) An HFPLL for the L2 cache.
> ++
> ++      clock-controller at f9016000 {
> ++              compatible = "qcom,hfpll";
> ++              reg = <0xf9016000 0x30>;
> ++              clock-output-names = "hfpll_l2";
> ++      };
> ++
> ++2) An HFPLL for CPU0. This HFPLL has the alias register region.
> ++
> ++      clock-controller at f908a000 {
> ++              compatible = "qcom,hfpll";
> ++              reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
> ++              clock-output-names = "hfpll0";
> ++      };
> +--- a/drivers/clk/qcom/Kconfig
> ++++ b/drivers/clk/qcom/Kconfig
> +@@ -106,3 +106,11 @@
> +         Support for the multimedia clock controller on msm8974 devices.
> +         Say Y if you want to support multimedia devices such as display,
> +         graphics, video encode/decode, camera, etc.
> ++
> ++config QCOM_HFPLL
> ++      tristate "High-Frequency PLL (HFPLL) Clock Controller"
> ++      depends on COMMON_CLK_QCOM
> ++      help
> ++        Support for the high-frequency PLLs present on Qualcomm devices.
> ++        Say Y if you want to support CPU frequency scaling on devices
> ++        such as MSM8974, APQ8084, etc.
> +--- a/drivers/clk/qcom/Makefile
> ++++ b/drivers/clk/qcom/Makefile
> +@@ -23,3 +23,4 @@
> + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
> + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
> + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
> ++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
> +--- /dev/null
> ++++ b/drivers/clk/qcom/hfpll.c
> +@@ -0,0 +1,109 @@
> ++/*
> ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/init.h>
> ++#include <linux/module.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/of.h>
> ++#include <linux/clk.h>
> ++#include <linux/clk-provider.h>
> ++#include <linux/regmap.h>
> ++
> ++#include "clk-regmap.h"
> ++#include "clk-hfpll.h"
> ++
> ++static const struct hfpll_data hdata = {
> ++      .mode_reg = 0x00,
> ++      .l_reg = 0x04,
> ++      .m_reg = 0x08,
> ++      .n_reg = 0x0c,
> ++      .user_reg = 0x10,
> ++      .config_reg = 0x14,
> ++      .config_val = 0x430405d,
> ++      .status_reg = 0x1c,
> ++      .lock_bit = 16,
> ++
> ++      .user_val = 0x8,
> ++      .user_vco_mask = 0x100000,
> ++      .low_vco_max_rate = 1248000000,
> ++      .min_rate = 537600000UL,
> ++      .max_rate = 2900000000UL,
> ++};
> ++
> ++static const struct of_device_id qcom_hfpll_match_table[] = {
> ++      { .compatible = "qcom,hfpll" },
> ++      { }
> ++};
> ++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
> ++
> ++static const struct regmap_config hfpll_regmap_config = {
> ++      .reg_bits       = 32,
> ++      .reg_stride     = 4,
> ++      .val_bits       = 32,
> ++      .max_register   = 0x30,
> ++      .fast_io        = true,
> ++};
> ++
> ++static int qcom_hfpll_probe(struct platform_device *pdev)
> ++{
> ++      struct clk *clk;
> ++      struct resource *res;
> ++      struct device *dev = &pdev->dev;
> ++      void __iomem *base;
> ++      struct regmap *regmap;
> ++      struct clk_hfpll *h;
> ++      struct clk_init_data init = {
> ++              .parent_names = (const char *[]){ "xo" },
> ++              .num_parents = 1,
> ++              .ops = &clk_ops_hfpll,
> ++      };
> ++
> ++      h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
> ++      if (!h)
> ++              return -ENOMEM;
> ++
> ++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      base = devm_ioremap_resource(dev, res);
> ++      if (IS_ERR(base))
> ++              return PTR_ERR(base);
> ++
> ++      regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
> ++      if (IS_ERR(regmap))
> ++              return PTR_ERR(regmap);
> ++
> ++      if (of_property_read_string_index(dev->of_node, "clock-output-names",
> ++                                                0, &init.name))
> ++              return -ENODEV;
> ++
> ++      h->d = &hdata;
> ++      h->clkr.hw.init = &init;
> ++      spin_lock_init(&h->lock);
> ++
> ++      clk = devm_clk_register_regmap(&pdev->dev, &h->clkr);
> ++
> ++      return PTR_ERR_OR_ZERO(clk);
> ++}
> ++
> ++static struct platform_driver qcom_hfpll_driver = {
> ++      .probe          = qcom_hfpll_probe,
> ++      .driver         = {
> ++              .name   = "qcom-hfpll",
> ++              .of_match_table = qcom_hfpll_match_table,
> ++      },
> ++};
> ++module_platform_driver(qcom_hfpll_driver);
> ++
> ++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_ALIAS("platform:qcom-hfpll");
> diff --git a/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
> new file mode 100644
> index 0000000..5b191b5
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch
> @@ -0,0 +1,127 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063241
> +Message-Id: <1426920332-9340-9-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>
> +Date: Fri, 20 Mar 2015 23:45:27 -0700
> +
> +Describe the HFPLLs present on IPQ806X devices.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
> + 1 file changed, 83 insertions(+)
> +
> +--- a/drivers/clk/qcom/gcc-ipq806x.c
> ++++ b/drivers/clk/qcom/gcc-ipq806x.c
> +@@ -30,6 +30,7 @@
> + #include "clk-pll.h"
> + #include "clk-rcg.h"
> + #include "clk-branch.h"
> ++#include "clk-hfpll.h"
> + #include "reset.h"
> +
> + static struct clk_pll pll0 = {
> +@@ -113,6 +114,85 @@
> +       },
> + };
> +
> ++static struct hfpll_data hfpll0_data = {
> ++      .mode_reg = 0x3200,
> ++      .l_reg = 0x3208,
> ++      .m_reg = 0x320c,
> ++      .n_reg = 0x3210,
> ++      .config_reg = 0x3204,
> ++      .status_reg = 0x321c,
> ++      .config_val = 0x7845c665,
> ++      .droop_reg = 0x3214,
> ++      .droop_val = 0x0108c000,
> ++      .min_rate = 600000000UL,
> ++      .max_rate = 1800000000UL,
> ++};
> ++
> ++static struct clk_hfpll hfpll0 = {
> ++      .d = &hfpll0_data,
> ++      .clkr.hw.init = &(struct clk_init_data){
> ++              .parent_names = (const char *[]){ "pxo" },
> ++              .num_parents = 1,
> ++              .name = "hfpll0",
> ++              .ops = &clk_ops_hfpll,
> ++              .flags = CLK_IGNORE_UNUSED,
> ++      },
> ++      .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
> ++};
> ++
> ++static struct hfpll_data hfpll1_data = {
> ++      .mode_reg = 0x3240,
> ++      .l_reg = 0x3248,
> ++      .m_reg = 0x324c,
> ++      .n_reg = 0x3250,
> ++      .config_reg = 0x3244,
> ++      .status_reg = 0x325c,
> ++      .config_val = 0x7845c665,
> ++      .droop_reg = 0x3314,
> ++      .droop_val = 0x0108c000,
> ++      .min_rate = 600000000UL,
> ++      .max_rate = 1800000000UL,
> ++};
> ++
> ++static struct clk_hfpll hfpll1 = {
> ++      .d = &hfpll1_data,
> ++      .clkr.hw.init = &(struct clk_init_data){
> ++              .parent_names = (const char *[]){ "pxo" },
> ++              .num_parents = 1,
> ++              .name = "hfpll1",
> ++              .ops = &clk_ops_hfpll,
> ++              .flags = CLK_IGNORE_UNUSED,
> ++      },
> ++      .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
> ++};
> ++
> ++static struct hfpll_data hfpll_l2_data = {
> ++      .mode_reg = 0x3300,
> ++      .l_reg = 0x3308,
> ++      .m_reg = 0x330c,
> ++      .n_reg = 0x3310,
> ++      .config_reg = 0x3304,
> ++      .status_reg = 0x331c,
> ++      .config_val = 0x7845c665,
> ++      .droop_reg = 0x3314,
> ++      .droop_val = 0x0108c000,
> ++      .min_rate = 600000000UL,
> ++      .max_rate = 1800000000UL,
> ++};
> ++
> ++static struct clk_hfpll hfpll_l2 = {
> ++              .d = &hfpll_l2_data,
> ++              .clkr.hw.init = &(struct clk_init_data){
> ++              .parent_names = (const char *[]){ "pxo" },
> ++              .num_parents = 1,
> ++              .name = "hfpll_l2",
> ++              .ops = &clk_ops_hfpll,
> ++              .flags = CLK_IGNORE_UNUSED,
> ++      },
> ++      .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
> ++};
> ++
> ++
> + static struct clk_pll pll14 = {
> +       .l_reg = 0x31c4,
> +       .m_reg = 0x31c8,
> +@@ -2837,6 +2917,9 @@
> +       [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
> +       [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
> +       [NSSTCM_CLK] = &nss_tcm_clk.clkr,
> ++      [PLL9] = &hfpll0.clkr,
> ++      [PLL10] = &hfpll1.clkr,
> ++      [PLL12] = &hfpll_l2.clkr,
> + };
> +
> + static const struct qcom_reset_map gcc_ipq806x_resets[] = {
> diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
> new file mode 100644
> index 0000000..522482d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
> @@ -0,0 +1,272 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,09/13] clk: qcom: Add support for Krait clocks
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063251
> +Message-Id: <1426920332-9340-10-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>
> +Date: Fri, 20 Mar 2015 23:45:28 -0700
> +
> +The Krait clocks are made up of a series of muxes and a divider
> +that choose between a fixed rate clock and dedicated HFPLLs for
> +each CPU. Instead of using mmio accesses to remux parents, the
> +Krait implementation exposes the remux control via cp15
> +registers. Support these clocks.
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +drivers/clk/qcom/Kconfig     |   4 ++
> + drivers/clk/qcom/Makefile    |   1 +
> + drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++
> + drivers/clk/qcom/clk-krait.h |  49 +++++++++++++
> + 4 files changed, 220 insertions(+)
> + create mode 100644 drivers/clk/qcom/clk-krait.c
> + create mode 100644 drivers/clk/qcom/clk-krait.h
> +
> +--- a/drivers/clk/qcom/Kconfig
> ++++ b/drivers/clk/qcom/Kconfig
> +@@ -114,3 +114,7 @@
> +         Support for the high-frequency PLLs present on Qualcomm devices.
> +         Say Y if you want to support CPU frequency scaling on devices
> +         such as MSM8974, APQ8084, etc.
> ++
> ++config KRAIT_CLOCKS
> ++      bool
> ++      select KRAIT_L2_ACCESSORS
> +--- a/drivers/clk/qcom/Makefile
> ++++ b/drivers/clk/qcom/Makefile
> +@@ -8,6 +8,7 @@
> + clk-qcom-y += clk-branch.o
> + clk-qcom-y += clk-regmap-divider.o
> + clk-qcom-y += clk-regmap-mux.o
> ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
> + clk-qcom-y += clk-hfpll.o
> + clk-qcom-y += reset.o
> + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
> +
> +--- /dev/null
> ++++ b/drivers/clk/qcom/clk-krait.c
> +@@ -0,0 +1,166 @@
> ++/*
> ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/module.h>
> ++#include <linux/init.h>
> ++#include <linux/io.h>
> ++#include <linux/delay.h>
> ++#include <linux/err.h>
> ++#include <linux/clk-provider.h>
> ++#include <linux/spinlock.h>
> ++
> ++#include <asm/krait-l2-accessors.h>
> ++
> ++#include "clk-krait.h"
> ++
> ++/* Secondary and primary muxes share the same cp15 register */
> ++static DEFINE_SPINLOCK(krait_clock_reg_lock);
> ++
> ++#define LPL_SHIFT     8
> ++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
> ++{
> ++      unsigned long flags;
> ++      u32 regval;
> ++
> ++      spin_lock_irqsave(&krait_clock_reg_lock, flags);
> ++      regval = krait_get_l2_indirect_reg(mux->offset);
> ++      regval &= ~(mux->mask << mux->shift);
> ++      regval |= (sel & mux->mask) << mux->shift;
> ++      if (mux->lpl) {
> ++              regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
> ++              regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
> ++      }
> ++      krait_set_l2_indirect_reg(mux->offset, regval);
> ++      spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
> ++
> ++      /* Wait for switch to complete. */
> ++      mb();
> ++      udelay(1);
> ++}
> ++
> ++static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
> ++{
> ++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
> ++      u32 sel;
> ++
> ++      sel = clk_mux_reindex(index, mux->parent_map, 0);
> ++      mux->en_mask = sel;
> ++      /* Don't touch mux if CPU is off as it won't work */
> ++      if (__clk_is_enabled(hw->clk))
> ++              __krait_mux_set_sel(mux, sel);
> ++      return 0;
> ++}
> ++
> ++static u8 krait_mux_get_parent(struct clk_hw *hw)
> ++{
> ++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
> ++      u32 sel;
> ++
> ++      sel = krait_get_l2_indirect_reg(mux->offset);
> ++      sel >>= mux->shift;
> ++      sel &= mux->mask;
> ++      mux->en_mask = sel;
> ++
> ++      return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
> ++}
> ++
> ++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw)
> ++{
> ++      int i;
> ++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
> ++      int num_parents = clk_hw_get_num_parents(hw->clk);
> ++
> ++      i = mux->safe_sel;
> ++      for (i = 0; i < num_parents; i++)
> ++              if (mux->safe_sel == mux->parent_map[i])
> ++                      break;
> ++
> ++      return __clk_get_hw(clk_hw_get_parent_by_index(hw->clk, i));
> ++}
> ++
> ++static int krait_mux_enable(struct clk_hw *hw)
> ++{
> ++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
> ++
> ++      __krait_mux_set_sel(mux, mux->en_mask);
> ++
> ++      return 0;
> ++}
> ++
> ++static void krait_mux_disable(struct clk_hw *hw)
> ++{
> ++      struct krait_mux_clk *mux = to_krait_mux_clk(hw);
> ++
> ++      __krait_mux_set_sel(mux, mux->safe_sel);
> ++}
> ++
> ++const struct clk_ops krait_mux_clk_ops = {
> ++      .enable = krait_mux_enable,
> ++      .disable = krait_mux_disable,
> ++      .set_parent = krait_mux_set_parent,
> ++      .get_parent = krait_mux_get_parent,
> ++      .determine_rate = __clk_mux_determine_rate_closest,
> ++      .get_safe_parent = krait_mux_get_safe_parent,
> ++};
> ++EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
> ++
> ++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
> ++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
> ++                                unsigned long *parent_rate)
> ++{
> ++      *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw->clk), rate * 2);
> ++      return DIV_ROUND_UP(*parent_rate, 2);
> ++}
> ++
> ++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
> ++                      unsigned long parent_rate)
> ++{
> ++      struct krait_div2_clk *d = to_krait_div2_clk(hw);
> ++      unsigned long flags;
> ++      u32 val;
> ++      u32 mask = BIT(d->width) - 1;
> ++
> ++      if (d->lpl)
> ++              mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
> ++
> ++      spin_lock_irqsave(&krait_clock_reg_lock, flags);
> ++      val = krait_get_l2_indirect_reg(d->offset);
> ++      val &= ~mask;
> ++      krait_set_l2_indirect_reg(d->offset, val);
> ++      spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
> ++
> ++      return 0;
> ++}
> ++
> ++static unsigned long
> ++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> ++{
> ++      struct krait_div2_clk *d = to_krait_div2_clk(hw);
> ++      u32 mask = BIT(d->width) - 1;
> ++      u32 div;
> ++
> ++      div = krait_get_l2_indirect_reg(d->offset);
> ++      div >>= d->shift;
> ++      div &= mask;
> ++      div = (div + 1) * 2;
> ++
> ++      return DIV_ROUND_UP(parent_rate, div);
> ++}
> ++
> ++const struct clk_ops krait_div2_clk_ops = {
> ++      .round_rate = krait_div2_round_rate,
> ++      .set_rate = krait_div2_set_rate,
> ++      .recalc_rate = krait_div2_recalc_rate,
> ++};
> ++EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
> +--- /dev/null
> ++++ b/drivers/clk/qcom/clk-krait.h
> +@@ -0,0 +1,49 @@
> ++/*
> ++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#ifndef __QCOM_CLK_KRAIT_H
> ++#define __QCOM_CLK_KRAIT_H
> ++
> ++#include <linux/clk-provider.h>
> ++
> ++struct krait_mux_clk {
> ++      unsigned int    *parent_map;
> ++      bool            has_safe_parent;
> ++      u8              safe_sel;
> ++      u32             offset;
> ++      u32             mask;
> ++      u32             shift;
> ++      u32             en_mask;
> ++      bool            lpl;
> ++
> ++      struct clk_hw   hw;
> ++};
> ++
> ++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
> ++
> ++extern const struct clk_ops krait_mux_clk_ops;
> ++
> ++struct krait_div2_clk {
> ++      u32             offset;
> ++      u8              width;
> ++      u32             shift;
> ++      bool            lpl;
> ++
> ++      struct clk_hw   hw;
> ++};
> ++
> ++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
> ++
> ++extern const struct clk_ops krait_div2_clk_ops;
> ++
> ++#endif
> diff --git a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
> new file mode 100644
> index 0000000..b2ddbc8
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
> @@ -0,0 +1,207 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063201
> +Message-Id: <1426920332-9340-11-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>, <devicetree at vger.kernel.org>
> +Date: Fri, 20 Mar 2015 23:45:29 -0700
> +
> +The ACC and GCC regions present in KPSSv1 contain registers to
> +control clocks and power to each Krait CPU and L2. For CPUfreq
> +purposes probe these devices and expose a mux clock that chooses
> +between PXO and PLL8.
> +
> +Cc: <devicetree at vger.kernel.org>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt  |  7 ++
> + .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt  | 28 +++++++
> + drivers/clk/qcom/Kconfig                           |  8 ++
> + drivers/clk/qcom/Makefile                          |  1 +
> + drivers/clk/qcom/kpss-xcc.c                        | 95 ++++++++++++++++++++++
> + 5 files changed, 139 insertions(+)
> + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
> + create mode 100644 drivers/clk/qcom/kpss-xcc.c
> +
> +--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
> ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
> +@@ -21,10 +21,17 @@
> +                   the register region. An optional second element specifies
> +                   the base address and size of the alias register region.
> +
> ++- clock-output-names:
> ++      Usage: optional
> ++      Value type: <string>
> ++      Definition: Name of the output clock. Typically acpuX_aux where X is a
> ++                  CPU number starting at 0.
> ++
> + Example:
> +
> +       clock-controller at 2088000 {
> +               compatible = "qcom,kpss-acc-v2";
> +               reg = <0x02088000 0x1000>,
> +                     <0x02008000 0x1000>;
> ++              clock-output-names = "acpu0_aux";
> +       };
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
> +@@ -0,0 +1,28 @@
> ++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
> ++
> ++PROPERTIES
> ++
> ++- compatible:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: should be one of:
> ++                      "qcom,kpss-gcc"
> ++
> ++- reg:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: base address and size of the register region
> ++
> ++- clock-output-names:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: Name of the output clock. Typically acpu_l2_aux indicating
> ++                  an L2 cache auxiliary clock.
> ++
> ++Example:
> ++
> ++      l2cc: clock-controller at 2011000 {
> ++              compatible = "qcom,kpss-gcc";
> ++              reg = <0x2011000 0x1000>;
> ++              clock-output-names = "acpu_l2_aux";
> ++      };
> +--- a/drivers/clk/qcom/Kconfig
> ++++ b/drivers/clk/qcom/Kconfig
> +@@ -115,6 +115,14 @@
> +         Say Y if you want to support CPU frequency scaling on devices
> +         such as MSM8974, APQ8084, etc.
> +
> ++config KPSS_XCC
> ++      tristate "KPSS Clock Controller"
> ++      depends on COMMON_CLK_QCOM
> ++      help
> ++        Support for the Krait ACC and GCC clock controllers. Say Y
> ++        if you want to support CPU frequency scaling on devices such
> ++        as MSM8960, APQ8064, etc.
> ++
> + config KRAIT_CLOCKS
> +       bool
> +       select KRAIT_L2_ACCESSORS
> +--- a/drivers/clk/qcom/Makefile
> ++++ b/drivers/clk/qcom/Makefile
> +@@ -9,6 +9,7 @@
> + clk-qcom-y += clk-regmap-divider.o
> + clk-qcom-y += clk-regmap-mux.o
> + clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
> ++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
> + clk-qcom-y += clk-hfpll.o
> + clk-qcom-y += reset.o
> + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
> +--- /dev/null
> ++++ b/drivers/clk/qcom/kpss-xcc.c
> +@@ -0,0 +1,95 @@
> ++/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/init.h>
> ++#include <linux/module.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/err.h>
> ++#include <linux/io.h>
> ++#include <linux/of.h>
> ++#include <linux/of_device.h>
> ++#include <linux/clk.h>
> ++#include <linux/clk-provider.h>
> ++
> ++static const char *aux_parents[] = {
> ++      "pll8_vote",
> ++      "pxo",
> ++};
> ++
> ++static unsigned int aux_parent_map[] = {
> ++      3,
> ++      0,
> ++};
> ++
> ++static const struct of_device_id kpss_xcc_match_table[] = {
> ++      { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
> ++      { .compatible = "qcom,kpss-gcc" },
> ++      {}
> ++};
> ++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
> ++
> ++static int kpss_xcc_driver_probe(struct platform_device *pdev)
> ++{
> ++      const struct of_device_id *id;
> ++      struct clk *clk;
> ++      struct resource *res;
> ++      void __iomem *base;
> ++      const char *name;
> ++
> ++      id = of_match_device(kpss_xcc_match_table, &pdev->dev);
> ++      if (!id)
> ++              return -ENODEV;
> ++
> ++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      base = devm_ioremap_resource(&pdev->dev, res);
> ++      if (IS_ERR(base))
> ++              return PTR_ERR(base);
> ++
> ++      if (id->data) {
> ++              if (of_property_read_string_index(pdev->dev.of_node,
> ++                                      "clock-output-names", 0, &name))
> ++                      return -ENODEV;
> ++              base += 0x14;
> ++      } else {
> ++              name = "acpu_l2_aux";
> ++              base += 0x28;
> ++      }
> ++
> ++      clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
> ++                                   ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
> ++                                   0, aux_parent_map, NULL);
> ++
> ++      platform_set_drvdata(pdev, clk);
> ++
> ++      return PTR_ERR_OR_ZERO(clk);
> ++}
> ++
> ++static int kpss_xcc_driver_remove(struct platform_device *pdev)
> ++{
> ++      clk_unregister_mux(platform_get_drvdata(pdev));
> ++      return 0;
> ++}
> ++
> ++static struct platform_driver kpss_xcc_driver = {
> ++      .probe = kpss_xcc_driver_probe,
> ++      .remove = kpss_xcc_driver_remove,
> ++      .driver = {
> ++              .name = "kpss-xcc",
> ++              .of_match_table = kpss_xcc_match_table,
> ++      },
> ++};
> ++module_platform_driver(kpss_xcc_driver);
> ++
> ++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_ALIAS("platform:kpss-xcc");
> diff --git a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch
> new file mode 100644
> index 0000000..f0e9467
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch
> @@ -0,0 +1,435 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063121
> +Message-Id: <1426920332-9340-12-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>, <devicetree at vger.kernel.org>
> +Date: Fri, 20 Mar 2015 23:45:30 -0700
> +
> +The Krait CPU clocks are made up of a primary mux and secondary
> +mux for each CPU and the L2, controlled via cp15 accessors. For
> +Kraits within KPSSv1 each secondary mux accepts a different aux
> +source, but on KPSSv2 each secondary mux accepts the same aux
> +source.
> +
> +Cc: <devicetree at vger.kernel.org>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +.../devicetree/bindings/clock/qcom,krait-cc.txt    |  22 ++
> + drivers/clk/qcom/Kconfig                           |   8 +
> + drivers/clk/qcom/Makefile                          |   1 +
> + drivers/clk/qcom/krait-cc.c                        | 352 +++++++++++++++++++++
> + 4 files changed, 383 insertions(+)
> + create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
> + create mode 100644 drivers/clk/qcom/krait-cc.c
> +
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
> +@@ -0,0 +1,22 @@
> ++Krait Clock Controller
> ++
> ++PROPERTIES
> ++
> ++- compatible:
> ++      Usage: required
> ++      Value type: <string>
> ++      Definition: must be one of:
> ++                      "qcom,krait-cc-v1"
> ++                      "qcom,krait-cc-v2"
> ++
> ++- #clock-cells:
> ++      Usage: required
> ++      Value type: <u32>
> ++      Definition: must be 1
> ++
> ++Example:
> ++
> ++      kraitcc: clock-controller {
> ++              compatible = "qcom,krait-cc-v1";
> ++              #clock-cells = <1>;
> ++      };
> +--- a/drivers/clk/qcom/Kconfig
> ++++ b/drivers/clk/qcom/Kconfig
> +@@ -123,6 +123,14 @@
> +         if you want to support CPU frequency scaling on devices such
> +         as MSM8960, APQ8064, etc.
> +
> ++config KRAITCC
> ++      tristate "Krait Clock Controller"
> ++      depends on COMMON_CLK_QCOM && ARM
> ++      select KRAIT_CLOCKS
> ++      help
> ++        Support for the Krait CPU clocks on Qualcomm devices.
> ++        Say Y if you want to support CPU frequency scaling.
> ++
> + config KRAIT_CLOCKS
> +       bool
> +       select KRAIT_L2_ACCESSORS
> +--- a/drivers/clk/qcom/Makefile
> ++++ b/drivers/clk/qcom/Makefile
> +@@ -26,3 +26,4 @@
> + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
> + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
> + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
> ++obj-$(CONFIG_KRAITCC) += krait-cc.o
> +--- /dev/null
> ++++ b/drivers/clk/qcom/krait-cc.c
> +@@ -0,0 +1,352 @@
> ++/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/init.h>
> ++#include <linux/module.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/err.h>
> ++#include <linux/io.h>
> ++#include <linux/of.h>
> ++#include <linux/of_device.h>
> ++#include <linux/clk.h>
> ++#include <linux/clk-provider.h>
> ++#include <linux/slab.h>
> ++
> ++#include "clk-krait.h"
> ++
> ++static unsigned int sec_mux_map[] = {
> ++      2,
> ++      0,
> ++};
> ++
> ++static unsigned int pri_mux_map[] = {
> ++      1,
> ++      2,
> ++      0,
> ++};
> ++
> ++static int
> ++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
> ++{
> ++      struct krait_div2_clk *div;
> ++      struct clk_init_data init = {
> ++              .num_parents = 1,
> ++              .ops = &krait_div2_clk_ops,
> ++              .flags = CLK_SET_RATE_PARENT,
> ++      };
> ++      const char *p_names[1];
> ++      struct clk *clk;
> ++
> ++      div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
> ++      if (!div)
> ++              return -ENOMEM;
> ++
> ++      div->width = 2;
> ++      div->shift = 6;
> ++      div->lpl = id >= 0;
> ++      div->offset = offset;
> ++      div->hw.init = &init;
> ++
> ++      init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
> ++      if (!init.name)
> ++              return -ENOMEM;
> ++
> ++      init.parent_names = p_names;
> ++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
> ++      if (!p_names[0]) {
> ++              kfree(init.name);
> ++              return -ENOMEM;
> ++      }
> ++
> ++      clk = devm_clk_register(dev, &div->hw);
> ++      kfree(p_names[0]);
> ++      kfree(init.name);
> ++
> ++      return PTR_ERR_OR_ZERO(clk);
> ++}
> ++
> ++static int
> ++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
> ++                bool unique_aux)
> ++{
> ++      struct krait_mux_clk *mux;
> ++      static const char *sec_mux_list[] = {
> ++              "acpu_aux",
> ++              "qsb",
> ++      };
> ++      struct clk_init_data init = {
> ++              .parent_names = sec_mux_list,
> ++              .num_parents = ARRAY_SIZE(sec_mux_list),
> ++              .ops = &krait_mux_clk_ops,
> ++              .flags = CLK_SET_RATE_PARENT,
> ++      };
> ++      struct clk *clk;
> ++
> ++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> ++      if (!mux)
> ++              return -ENOMEM;
> ++
> ++      mux->offset = offset;
> ++      mux->lpl = id >= 0;
> ++      mux->has_safe_parent = true;
> ++      mux->safe_sel = 2;
> ++      mux->mask = 0x3;
> ++      mux->shift = 2;
> ++      mux->parent_map = sec_mux_map;
> ++      mux->hw.init = &init;
> ++
> ++      init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
> ++      if (!init.name)
> ++              return -ENOMEM;
> ++
> ++      if (unique_aux) {
> ++              sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
> ++              if (!sec_mux_list[0]) {
> ++                      clk = ERR_PTR(-ENOMEM);
> ++                      goto err_aux;
> ++              }
> ++      }
> ++
> ++      clk = devm_clk_register(dev, &mux->hw);
> ++
> ++      if (unique_aux)
> ++              kfree(sec_mux_list[0]);
> ++err_aux:
> ++      kfree(init.name);
> ++      return PTR_ERR_OR_ZERO(clk);
> ++}
> ++
> ++static struct clk *
> ++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
> ++{
> ++      struct krait_mux_clk *mux;
> ++      const char *p_names[3];
> ++      struct clk_init_data init = {
> ++              .parent_names = p_names,
> ++              .num_parents = ARRAY_SIZE(p_names),
> ++              .ops = &krait_mux_clk_ops,
> ++              .flags = CLK_SET_RATE_PARENT,
> ++      };
> ++      struct clk *clk;
> ++
> ++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> ++      if (!mux)
> ++              return ERR_PTR(-ENOMEM);
> ++
> ++      mux->has_safe_parent = true;
> ++      mux->safe_sel = 0;
> ++      mux->mask = 0x3;
> ++      mux->shift = 0;
> ++      mux->offset = offset;
> ++      mux->lpl = id >= 0;
> ++      mux->parent_map = pri_mux_map;
> ++      mux->hw.init = &init;
> ++
> ++      init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
> ++      if (!init.name)
> ++              return ERR_PTR(-ENOMEM);
> ++
> ++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
> ++      if (!p_names[0]) {
> ++              clk = ERR_PTR(-ENOMEM);
> ++              goto err_p0;
> ++      }
> ++
> ++      p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
> ++      if (!p_names[1]) {
> ++              clk = ERR_PTR(-ENOMEM);
> ++              goto err_p1;
> ++      }
> ++
> ++      p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
> ++      if (!p_names[2]) {
> ++              clk = ERR_PTR(-ENOMEM);
> ++              goto err_p2;
> ++      }
> ++
> ++      clk = devm_clk_register(dev, &mux->hw);
> ++
> ++      kfree(p_names[2]);
> ++err_p2:
> ++      kfree(p_names[1]);
> ++err_p1:
> ++      kfree(p_names[0]);
> ++err_p0:
> ++      kfree(init.name);
> ++      return clk;
> ++}
> ++
> ++/* id < 0 for L2, otherwise id == physical CPU number */
> ++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
> ++{
> ++      int ret;
> ++      unsigned offset;
> ++      void *p = NULL;
> ++      const char *s;
> ++      struct clk *clk;
> ++
> ++      if (id >= 0) {
> ++              offset = 0x4501 + (0x1000 * id);
> ++              s = p = kasprintf(GFP_KERNEL, "%d", id);
> ++              if (!s)
> ++                      return ERR_PTR(-ENOMEM);
> ++      } else {
> ++              offset = 0x500;
> ++              s = "_l2";
> ++      }
> ++
> ++      ret = krait_add_div(dev, id, s, offset);
> ++      if (ret) {
> ++              clk = ERR_PTR(ret);
> ++              goto err;
> ++      }
> ++
> ++      ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
> ++      if (ret) {
> ++              clk = ERR_PTR(ret);
> ++              goto err;
> ++      }
> ++
> ++      clk = krait_add_pri_mux(dev, id, s, offset);
> ++err:
> ++      kfree(p);
> ++      return clk;
> ++}
> ++
> ++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
> ++{
> ++      unsigned int idx = clkspec->args[0];
> ++      struct clk **clks = data;
> ++
> ++      if (idx >= 5) {
> ++              pr_err("%s: invalid clock index %d\n", __func__, idx);
> ++              return ERR_PTR(-EINVAL);
> ++      }
> ++
> ++      return clks[idx] ? : ERR_PTR(-ENODEV);
> ++}
> ++
> ++static const struct of_device_id krait_cc_match_table[] = {
> ++      { .compatible = "qcom,krait-cc-v1", (void *)1UL },
> ++      { .compatible = "qcom,krait-cc-v2" },
> ++      {}
> ++};
> ++MODULE_DEVICE_TABLE(of, krait_cc_match_table);
> ++
> ++static int krait_cc_probe(struct platform_device *pdev)
> ++{
> ++      struct device *dev = &pdev->dev;
> ++      const struct of_device_id *id;
> ++      unsigned long cur_rate, aux_rate;
> ++      int cpu;
> ++      struct clk *clk;
> ++      struct clk **clks;
> ++      struct clk *l2_pri_mux_clk;
> ++
> ++      id = of_match_device(krait_cc_match_table, dev);
> ++      if (!id)
> ++              return -ENODEV;
> ++
> ++      /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
> ++      clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
> ++      if (IS_ERR(clk))
> ++              return PTR_ERR(clk);
> ++
> ++      if (!id->data) {
> ++              clk = clk_register_fixed_factor(dev, "acpu_aux",
> ++                                              "gpll0_vote", 0, 1, 2);
> ++              if (IS_ERR(clk))
> ++                      return PTR_ERR(clk);
> ++      }
> ++
> ++      /* Krait configurations have at most 4 CPUs and one L2 */
> ++      clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
> ++      if (!clks)
> ++              return -ENOMEM;
> ++
> ++      for_each_possible_cpu(cpu) {
> ++              clk = krait_add_clks(dev, cpu, id->data);
> ++              if (IS_ERR(clk))
> ++                      return PTR_ERR(clk);
> ++              clks[cpu] = clk;
> ++      }
> ++
> ++      l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
> ++      if (IS_ERR(l2_pri_mux_clk))
> ++              return PTR_ERR(l2_pri_mux_clk);
> ++      clks[4] = l2_pri_mux_clk;
> ++
> ++      /*
> ++       * We don't want the CPU or L2 clocks to be turned off at late init
> ++       * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
> ++       * refcount of these clocks. Any cpufreq/hotplug manager can assume
> ++       * that the clocks have already been prepared and enabled by the time
> ++       * they take over.
> ++       */
> ++      for_each_online_cpu(cpu) {
> ++              clk_prepare_enable(l2_pri_mux_clk);
> ++              WARN(clk_prepare_enable(clks[cpu]),
> ++                      "Unable to turn on CPU%d clock", cpu);
> ++      }
> ++
> ++      /*
> ++       * Force reinit of HFPLLs and muxes to overwrite any potential
> ++       * incorrect configuration of HFPLLs and muxes by the bootloader.
> ++       * While at it, also make sure the cores are running at known rates
> ++       * and print the current rate.
> ++       *
> ++       * The clocks are set to aux clock rate first to make sure the
> ++       * secondary mux is not sourcing off of QSB. The rate is then set to
> ++       * two different rates to force a HFPLL reinit under all
> ++       * circumstances.
> ++       */
> ++      cur_rate = clk_get_rate(l2_pri_mux_clk);
> ++      aux_rate = 384000000;
> ++      if (cur_rate == 1) {
> ++              pr_info("L2 @ QSB rate. Forcing new rate.\n");
> ++              cur_rate = aux_rate;
> ++      }
> ++      clk_set_rate(l2_pri_mux_clk, aux_rate);
> ++      clk_set_rate(l2_pri_mux_clk, 2);
> ++      clk_set_rate(l2_pri_mux_clk, cur_rate);
> ++      pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
> ++      for_each_possible_cpu(cpu) {
> ++              clk = clks[cpu];
> ++              cur_rate = clk_get_rate(clk);
> ++              if (cur_rate == 1) {
> ++                      pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
> ++                      cur_rate = aux_rate;
> ++              }
> ++              clk_set_rate(clk, aux_rate);
> ++              clk_set_rate(clk, 2);
> ++              clk_set_rate(clk, cur_rate);
> ++              pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
> ++      }
> ++
> ++      of_clk_add_provider(dev->of_node, krait_of_get, clks);
> ++
> ++      return 0;
> ++}
> ++
> ++static struct platform_driver krait_cc_driver = {
> ++      .probe = krait_cc_probe,
> ++      .driver = {
> ++              .name = "krait-cc",
> ++              .of_match_table = krait_cc_match_table,
> ++      },
> ++};
> ++module_platform_driver(krait_cc_driver);
> ++
> ++MODULE_DESCRIPTION("Krait CPU Clock Driver");
> ++MODULE_LICENSE("GPL v2");
> ++MODULE_ALIAS("platform:krait-cc");
> diff --git a/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
> new file mode 100644
> index 0000000..d4c43f4
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch
> @@ -0,0 +1,304 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +X-Patchwork-Id: 6063191
> +Message-Id: <1426920332-9340-13-git-send-email-sboyd at codeaurora.org>
> +To: Mike Turquette <mturquette at linaro.org>, Stephen Boyd <sboyd at codeaurora.org>
> +Cc: linux-kernel at vger.kernel.org, linux-arm-msm at vger.kernel.org,
> +       linux-pm at vger.kernel.org, linux-arm-kernel at lists.infradead.org,
> +       Viresh Kumar <viresh.kumar at linaro.org>, <devicetree at vger.kernel.org>
> +Date: Fri, 20 Mar 2015 23:45:31 -0700
> +
> +Register a cpufreq-generic device whenever we detect that a
> +"qcom,krait" compatible CPU is present in DT.
> +
> +Cc: <devicetree at vger.kernel.org>
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> +.../devicetree/bindings/arm/msm/qcom,pvs.txt       |  38 ++++
> + drivers/cpufreq/Kconfig.arm                        |   9 +
> + drivers/cpufreq/Makefile                           |   1 +
> + drivers/cpufreq/qcom-cpufreq.c                     | 204 +++++++++++++++++++++
> + 4 files changed, 252 insertions(+)
> + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
> + create mode 100644 drivers/cpufreq/qcom-cpufreq.c
> +
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
> +@@ -0,0 +1,38 @@
> ++Qualcomm Process Voltage Scaling Tables
> ++
> ++The node name is required to be "qcom,pvs". There shall only be one
> ++such node present in the root of the tree.
> ++
> ++PROPERTIES
> ++
> ++- qcom,pvs-format-a or qcom,pvs-format-b:
> ++      Usage: required
> ++      Value type: <empty>
> ++      Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
> ++                  If qcom,pvs-format-a is used the table is two columns
> ++                  (frequency and voltage in that order). If qcom,pvs-format-b                     is used the table is three columns (frequency, voltage,
> ++                  and current in that order).
> ++
> ++- qcom,speedX-pvsY-bin-vZ:
> ++      Usage: required
> ++      Value type: <prop-encoded-array>
> ++      Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
> ++                  and version Z.
> ++Example:
> ++
> ++      qcom,pvs {
> ++              qcom,pvs-format-a;
> ++              qcom,speed0-pvs0-bin-v0 =
> ++                      <  384000000  950000 >,
> ++                      <  486000000  975000 >,
> ++                      <  594000000 1000000 >,
> ++                      <  702000000 1025000 >,
> ++                      <  810000000 1075000 >,
> ++                      <  918000000 1100000 >,
> ++                      < 1026000000 1125000 >,
> ++                      < 1134000000 1175000 >,
> ++                      < 1242000000 1200000 >,
> ++                      < 1350000000 1225000 >,
> ++                      < 1458000000 1237500 >,
> ++                      < 1512000000 1250000 >;
> ++      };
> +--- a/drivers/cpufreq/Kconfig.arm
> ++++ b/drivers/cpufreq/Kconfig.arm
> +@@ -95,6 +95,15 @@
> +       depends on ARCH_OMAP2PLUS
> +       default ARCH_OMAP2PLUS
> +
> ++config ARM_QCOM_CPUFREQ
> ++      tristate "Qualcomm based"
> ++      depends on ARCH_QCOM
> ++      select PM_OPP
> ++      help
> ++        This adds the CPUFreq driver for Qualcomm SoC based boards.
> ++
> ++        If in doubt, say N.
> ++
> + config ARM_S3C_CPUFREQ
> +       bool
> +       help
> +--- a/drivers/cpufreq/Makefile
> ++++ b/drivers/cpufreq/Makefile
> +@@ -61,6 +61,7 @@
> + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)   += omap-cpufreq.o
> + obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)      += pxa2xx-cpufreq.o
> + obj-$(CONFIG_PXA3xx)                  += pxa3xx-cpufreq.o
> ++obj-$(CONFIG_ARM_QCOM_CPUFREQ)         += qcom-cpufreq.o
> + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ)     += s3c24xx-cpufreq.o
> + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
> + obj-$(CONFIG_ARM_S3C2410_CPUFREQ)     += s3c2410-cpufreq.o
> +--- /dev/null
> ++++ b/drivers/cpufreq/qcom-cpufreq.c
> +@@ -0,0 +1,204 @@
> ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/cpu.h>
> ++#include <linux/err.h>
> ++#include <linux/init.h>
> ++#include <linux/io.h>
> ++#include <linux/kernel.h>
> ++#include <linux/module.h>
> ++#include <linux/of.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/pm_opp.h>
> ++#include <linux/slab.h>
> ++#include <linux/cpufreq-dt.h>
> ++
> ++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
> ++{
> ++      void __iomem *base;
> ++      u32 pte_efuse;
> ++
> ++      *speed = *pvs = *pvs_ver = 0;
> ++
> ++      base = ioremap(0x007000c0, 4);
> ++      if (!base) {
> ++              pr_warn("Unable to read efuse data. Defaulting to 0!\n");
> ++              return;
> ++      }
> ++
> ++      pte_efuse = readl_relaxed(base);
> ++      iounmap(base);
> ++
> ++      *speed = pte_efuse & 0xf;
> ++      if (*speed == 0xf)
> ++              *speed = (pte_efuse >> 4) & 0xf;
> ++
> ++      if (*speed == 0xf) {
> ++              *speed = 0;
> ++              pr_warn("Speed bin: Defaulting to %d\n", *speed);
> ++      } else {
> ++              pr_info("Speed bin: %d\n", *speed);
> ++      }
> ++
> ++      *pvs = (pte_efuse >> 10) & 0x7;
> ++      if (*pvs == 0x7)
> ++              *pvs = (pte_efuse >> 13) & 0x7;
> ++
> ++      if (*pvs == 0x7) {
> ++              *pvs = 0;
> ++              pr_warn("PVS bin: Defaulting to %d\n", *pvs);
> ++      } else {
> ++              pr_info("PVS bin: %d\n", *pvs);
> ++      }
> ++}
> ++
> ++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
> ++{
> ++      u32 pte_efuse, redundant_sel;
> ++      void __iomem *base;
> ++
> ++      *speed = 0;
> ++      *pvs = 0;
> ++      *pvs_ver = 0;
> ++
> ++      base = ioremap(0xfc4b80b0, 8);
> ++      if (!base) {
> ++              pr_warn("Unable to read efuse data. Defaulting to 0!\n");
> ++              return;
> ++      }
> ++
> ++      pte_efuse = readl_relaxed(base);
> ++      redundant_sel = (pte_efuse >> 24) & 0x7;
> ++      *speed = pte_efuse & 0x7;
> ++      /* 4 bits of PVS are in efuse register bits 31, 8-6. */
> ++      *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
> ++      *pvs_ver = (pte_efuse >> 4) & 0x3;
> ++
> ++      switch (redundant_sel) {
> ++      case 1:
> ++              *speed = (pte_efuse >> 27) & 0xf;
> ++              break;
> ++      case 2:
> ++              *pvs = (pte_efuse >> 27) & 0xf;
> ++              break;
> ++      }
> ++
> ++      /* Check SPEED_BIN_BLOW_STATUS */
> ++      if (pte_efuse & BIT(3)) {
> ++              pr_info("Speed bin: %d\n", *speed);
> ++      } else {
> ++              pr_warn("Speed bin not set. Defaulting to 0!\n");
> ++              *speed = 0;
> ++      }
> ++
> ++      /* Check PVS_BLOW_STATUS */
> ++      pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
> ++      if (pte_efuse) {
> ++              pr_info("PVS bin: %d\n", *pvs);
> ++      } else {
> ++              pr_warn("PVS bin not set. Defaulting to 0!\n");
> ++              *pvs = 0;
> ++      }
> ++
> ++      pr_info("PVS version: %d\n", *pvs_ver);
> ++      iounmap(base);
> ++}
> ++
> ++static int __init qcom_cpufreq_populate_opps(void)
> ++{
> ++      int len, rows, cols, i, k, speed, pvs, pvs_ver;
> ++      char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
> ++      struct device_node *np;
> ++      struct device *dev;
> ++      int cpu = 0;
> ++
> ++      np = of_find_node_by_name(NULL, "qcom,pvs");
> ++      if (!np)
> ++              return -ENODEV;
> ++
> ++      if (of_property_read_bool(np, "qcom,pvs-format-a")) {
> ++              get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
> ++              cols = 2;
> ++      } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
> ++              get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
> ++              cols = 3;
> ++      } else {
> ++              return -ENODEV;
> ++      }
> ++
> ++      snprintf(table_name, sizeof(table_name),
> ++                      "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
> ++
> ++      if (!of_find_property(np, table_name, &len))
> ++              return -EINVAL;
> ++
> ++      len /= sizeof(u32);
> ++      if (len % cols || len == 0)
> ++              return -EINVAL;
> ++
> ++      rows = len / cols;
> ++
> ++      for (i = 0, k = 0; i < rows; i++) {
> ++              u32 freq, volt;
> ++
> ++              of_property_read_u32_index(np, table_name, k++, &freq);
> ++              of_property_read_u32_index(np, table_name, k++, &volt);
> ++              while (k % cols)
> ++                      k++; /* Skip uA entries if present */
> ++              for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
> ++                      dev = get_cpu_device(cpu);
> ++                      if (!dev)
> ++                              return -ENODEV;
> ++                      if (dev_pm_opp_add(dev, freq, volt))
> ++                              pr_warn("failed to add OPP %u\n", freq);
> ++              }
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int __init qcom_cpufreq_driver_init(void)
> ++{
> ++      struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
> ++      struct platform_device_info devinfo = {
> ++              .name = "cpufreq-dt",
> ++              .data = &pdata,
> ++              .size_data = sizeof(pdata),
> ++      };
> ++      struct device *cpu_dev;
> ++      struct device_node *np;
> ++      int ret;
> ++
> ++      cpu_dev = get_cpu_device(0);
> ++      if (!cpu_dev)
> ++              return -ENODEV;
> ++
> ++      np = of_node_get(cpu_dev->of_node);
> ++      if (!np)
> ++              return -ENOENT;
> ++
> ++      if (!of_device_is_compatible(np, "qcom,krait")) {
> ++              of_node_put(np);
> ++              return -ENODEV;
> ++      }
> ++      of_node_put(np);
> ++
> ++      ret = qcom_cpufreq_populate_opps();
> ++      if (ret)
> ++              return ret;
> ++
> ++      return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
> ++}
> ++module_init(qcom_cpufreq_driver_init);
> ++
> ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
> ++MODULE_LICENSE("GPL v2");
> diff --git a/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
> new file mode 100644
> index 0000000..b2e737b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch
> @@ -0,0 +1,100 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -26,6 +26,11 @@
> +                       next-level-cache = <&L2>;
> +                       qcom,acc = <&acc0>;
> +                       qcom,saw = <&saw0>;
> ++                      clocks = <&kraitcc 0>;
> ++                      clock-names = "cpu";
> ++                      clock-latency = <100000>;
> ++                      core-supply = <&smb208_s2a>;
> ++                      voltage-tolerance = <5>;
> +               };
> +
> +               cpu at 1 {
> +@@ -36,11 +41,24 @@
> +                       next-level-cache = <&L2>;
> +                       qcom,acc = <&acc1>;
> +                       qcom,saw = <&saw1>;
> ++                      clocks = <&kraitcc 1>;
> ++                      clock-names = "cpu";
> ++                      clock-latency = <100000>;
> ++                      core-supply = <&smb208_s2b>;
> +               };
> +
> +               L2: l2-cache {
> +                       compatible = "cache";
> +                       cache-level = <2>;
> ++                      clocks = <&kraitcc 4>;
> ++                      clock-names = "cache";
> ++                      cache-points-kHz = <
> ++                              /* kHz    uV    CPU kHz */
> ++                              1200000 1150000 1200000
> ++                              1000000 1100000  600000
> ++                               384000 1100000  384000
> ++                      >;
> ++                      vdd_dig-supply = <&smb208_s1a>;
> +               };
> +       };
> +
> +@@ -73,6 +91,46 @@
> +               };
> +       };
> +
> ++      kraitcc: clock-controller {
> ++              compatible = "qcom,krait-cc-v1";
> ++              #clock-cells = <1>;
> ++      };
> ++
> ++      qcom,pvs {
> ++              qcom,pvs-format-a;
> ++              qcom,speed0-pvs0-bin-v0 =
> ++                      < 1400000000 1250000 >,
> ++                      < 1200000000 1200000 >,
> ++                      < 1000000000 1150000 >,
> ++                       < 800000000 1100000 >,
> ++                       < 600000000 1050000 >,
> ++                       < 384000000 1000000 >;
> ++
> ++              qcom,speed0-pvs1-bin-v0 =
> ++                      < 1400000000 1175000 >,
> ++                      < 1200000000 1125000 >,
> ++                      < 1000000000 1075000 >,
> ++                       < 800000000 1025000 >,
> ++                       < 600000000  975000 >,
> ++                       < 384000000  925000 >;
> ++
> ++              qcom,speed0-pvs2-bin-v0 =
> ++                      < 1400000000 1125000 >,
> ++                      < 1200000000 1075000 >,
> ++                      < 1000000000 1025000 >,
> ++                       < 800000000  995000 >,
> ++                       < 600000000  925000 >,
> ++                       < 384000000  875000 >;
> ++
> ++              qcom,speed0-pvs3-bin-v0 =
> ++                      < 1400000000 1050000 >,
> ++                      < 1200000000 1000000 >,
> ++                      < 1000000000  950000 >,
> ++                       < 800000000  900000 >,
> ++                       < 600000000  850000 >,
> ++                       < 384000000  800000 >;
> ++      };
> ++
> +       soc: soc {
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +@@ -205,11 +263,13 @@
> +               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 {
> diff --git a/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
> new file mode 100644
> index 0000000..f33c9e0
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch
> @@ -0,0 +1,461 @@
> +From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001
> +From: Stephen Boyd <sboyd at codeaurora.org>
> +Date: Fri, 30 May 2014 16:36:11 -0700
> +Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
> +
> +Krait processors have individual clocks for each CPU that can
> +scale independently from one another. cpufreq-cpu0 is fairly
> +close to this, but assumes that there is only one clock for all
> +CPUs. Add a driver to support the Krait configuration.
> +
> +TODO: Merge into cpufreq-cpu0? Or make generic?
> +
> +Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
> +
> +---
> + drivers/cpufreq/Kconfig         |  13 +++
> + drivers/cpufreq/Makefile        |   1 +
> + drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++
> + 3 files changed, 204 insertions(+)
> + create mode 100644 drivers/cpufreq/cpufreq-krait.c
> +
> +--- a/drivers/cpufreq/Kconfig
> ++++ b/drivers/cpufreq/Kconfig
> +@@ -198,6 +198,19 @@
> +
> +         If in doubt, say N.
> +
> ++config GENERIC_CPUFREQ_KRAIT
> ++      tristate "Krait cpufreq driver"
> ++      depends on HAVE_CLK && OF
> ++      # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
> ++      depends on !CPU_THERMAL || THERMAL
> ++      select PM_OPP
> ++      help
> ++        This adds a generic cpufreq driver for CPU0 frequency management.
> ++        It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
> ++        systems which share clock and voltage across all CPUs.
> ++
> ++        If in doubt, say N.
> ++
> + if X86
> + source "drivers/cpufreq/Kconfig.x86"
> + endif
> +--- a/drivers/cpufreq/Makefile
> ++++ b/drivers/cpufreq/Makefile
> +@@ -13,6 +13,7 @@
> + obj-$(CONFIG_CPU_FREQ_GOV_COMMON)             += cpufreq_governor.o
> +
> + obj-$(CONFIG_CPUFREQ_DT)              += cpufreq-dt.o
> ++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT)    += cpufreq-krait.o
> +
> + ##################################################################################
> + # x86 drivers.
> +--- /dev/null
> ++++ b/drivers/cpufreq/cpufreq-krait.c
> +@@ -0,0 +1,390 @@
> ++/*
> ++ * Copyright (C) 2012 Freescale Semiconductor, Inc.
> ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
> ++ *
> ++ * The OPP code in function krait_set_target() is reused from
> ++ * drivers/cpufreq/omap-cpufreq.c
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 as
> ++ * published by the Free Software Foundation.
> ++ */
> ++
> ++#include <linux/clk.h>
> ++#include <linux/cpu.h>
> ++#include <linux/cpu_cooling.h>
> ++#include <linux/cpufreq.h>
> ++#include <linux/cpumask.h>
> ++#include <linux/err.h>
> ++#include <linux/module.h>
> ++#include <linux/of.h>
> ++#include <linux/pm_opp.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/regulator/consumer.h>
> ++#include <linux/slab.h>
> ++#include <linux/thermal.h>
> ++
> ++static unsigned int transition_latency;
> ++static unsigned int voltage_tolerance; /* in percentage */
> ++
> ++static struct device *cpu_dev;
> ++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks);
> ++static DEFINE_PER_CPU(struct regulator *, krait_supply_core);
> ++static struct cpufreq_frequency_table *freq_table;
> ++static struct thermal_cooling_device *cdev;
> ++
> ++struct cache_points {
> ++      unsigned long cache_freq;
> ++      unsigned int cache_volt;
> ++      unsigned long cpu_freq;
> ++};
> ++
> ++static struct regulator *krait_l2_reg;
> ++static struct clk *krait_l2_clk;
> ++static struct cache_points *krait_l2_points;
> ++static int nr_krait_l2_points;
> ++
> ++static int krait_parse_cache_points(struct device *dev,
> ++              struct device_node *of_node)
> ++{
> ++      const struct property *prop;
> ++      const __be32 *val;
> ++      int nr, i;
> ++
> ++      prop = of_find_property(of_node, "cache-points-kHz", NULL);
> ++      if (!prop)
> ++              return -ENODEV;
> ++      if (!prop->value)
> ++              return -ENODATA;
> ++
> ++      /*
> ++       * Each OPP is a set of tuples consisting of frequency and
> ++       * cpu-frequency like <freq-kHz volt-uV freq-kHz>.
> ++       */
> ++      nr = prop->length / sizeof(u32);
> ++      if (nr % 3) {
> ++              dev_err(dev, "%s: Invalid cache points\n", __func__);
> ++              return -EINVAL;
> ++      }
> ++      nr /= 3;
> ++
> ++      krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points),
> ++                                     GFP_KERNEL);
> ++      if (!krait_l2_points)
> ++              return -ENOMEM;
> ++      nr_krait_l2_points = nr;
> ++
> ++      for (i = 0, val = prop->value; i < nr; i++) {
> ++              unsigned long cache_freq = be32_to_cpup(val++) * 1000;
> ++              unsigned int cache_volt = be32_to_cpup(val++);
> ++              unsigned long cpu_freq = be32_to_cpup(val++) * 1000;
> ++
> ++              krait_l2_points[i].cache_freq = cache_freq;
> ++              krait_l2_points[i].cache_volt = cache_volt;
> ++              krait_l2_points[i].cpu_freq = cpu_freq;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index)
> ++{
> ++      struct dev_pm_opp *opp;
> ++      unsigned long volt = 0, volt_old = 0, tol = 0;
> ++      unsigned long freq, max_cpu_freq = 0;
> ++      unsigned int old_freq, new_freq;
> ++      long freq_Hz, freq_exact;
> ++      int ret, i;
> ++      struct clk *cpu_clk;
> ++      struct regulator *core;
> ++      unsigned int cpu;
> ++
> ++      cpu_clk = per_cpu(krait_cpu_clks, policy->cpu);
> ++
> ++      freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
> ++      if (freq_Hz <= 0)
> ++              freq_Hz = freq_table[index].frequency * 1000;
> ++
> ++      freq_exact = freq_Hz;
> ++      new_freq = freq_Hz / 1000;
> ++      old_freq = clk_get_rate(cpu_clk) / 1000;
> ++
> ++      core = per_cpu(krait_supply_core, policy->cpu);
> ++
> ++      rcu_read_lock();
> ++      opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
> ++      if (IS_ERR(opp)) {
> ++              rcu_read_unlock();
> ++              pr_err("failed to find OPP for %ld\n", freq_Hz);
> ++              return PTR_ERR(opp);
> ++      }
> ++      volt = dev_pm_opp_get_voltage(opp);
> ++      rcu_read_unlock();
> ++      tol = volt * voltage_tolerance / 100;
> ++      volt_old = regulator_get_voltage(core);
> ++
> ++      pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
> ++               old_freq / 1000, volt_old ? volt_old / 1000 : -1,
> ++               new_freq / 1000, volt ? volt / 1000 : -1);
> ++
> ++      /* scaling up?  scale voltage before frequency */
> ++      if (new_freq > old_freq) {
> ++              ret = regulator_set_voltage_tol(core, volt, tol);
> ++              if (ret) {
> ++                      pr_err("failed to scale voltage up: %d\n", ret);
> ++                      return ret;
> ++              }
> ++      }
> ++
> ++      ret = clk_set_rate(cpu_clk, freq_exact);
> ++      if (ret) {
> ++              pr_err("failed to set clock rate: %d\n", ret);
> ++              return ret;
> ++      }
> ++
> ++      /* scaling down?  scale voltage after frequency */
> ++      if (new_freq < old_freq) {
> ++              ret = regulator_set_voltage_tol(core, volt, tol);
> ++              if (ret) {
> ++                      pr_err("failed to scale voltage down: %d\n", ret);
> ++                      clk_set_rate(cpu_clk, old_freq * 1000);
> ++              }
> ++      }
> ++
> ++      for_each_possible_cpu(cpu) {
> ++              freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu));
> ++              max_cpu_freq = max(max_cpu_freq, freq);
> ++      }
> ++
> ++      for (i = 0; i < nr_krait_l2_points; i++) {
> ++              if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
> ++                      if (krait_l2_reg) {
> ++                              ret = regulator_set_voltage_tol(krait_l2_reg,
> ++                                              krait_l2_points[i].cache_volt,
> ++                                              tol);
> ++                              if (ret) {
> ++                                      pr_err("failed to scale l2 voltage: %d\n",
> ++                                              ret);
> ++                              }
> ++                      }
> ++                      ret = clk_set_rate(krait_l2_clk,
> ++                                      krait_l2_points[i].cache_freq);
> ++                      if (ret)
> ++                              pr_err("failed to scale l2 clk: %d\n", ret);
> ++                      break;
> ++              }
> ++
> ++      }
> ++
> ++      return ret;
> ++}
> ++
> ++static int krait_cpufreq_init(struct cpufreq_policy *policy)
> ++{
> ++      int ret;
> ++
> ++      policy->clk = per_cpu(krait_cpu_clks, policy->cpu);
> ++
> ++      ret = cpufreq_table_validate_and_show(policy, freq_table);
> ++      if (ret) {
> ++              pr_err("%s: invalid frequency table: %d\n", __func__, ret);
> ++              return ret;
> ++      }
> ++
> ++      policy->cpuinfo.transition_latency = transition_latency;
> ++
> ++      return 0;
> ++}
> ++
> ++static struct cpufreq_driver krait_cpufreq_driver = {
> ++      .flags = CPUFREQ_STICKY,
> ++      .verify = cpufreq_generic_frequency_table_verify,
> ++      .target_index = krait_set_target,
> ++      .get = cpufreq_generic_get,
> ++      .init = krait_cpufreq_init,
> ++      .name = "generic_krait",
> ++      .attr = cpufreq_generic_attr,
> ++};
> ++
> ++static int krait_cpufreq_probe(struct platform_device *pdev)
> ++{
> ++      struct device_node *np, *cache;
> ++      int ret, i;
> ++      unsigned int cpu;
> ++      struct device *dev;
> ++      struct clk *clk;
> ++      struct regulator *core;
> ++      unsigned long freq_Hz, freq, max_cpu_freq = 0;
> ++      struct dev_pm_opp *opp;
> ++      unsigned long volt, tol;
> ++
> ++      cpu_dev = get_cpu_device(0);
> ++      if (!cpu_dev) {
> ++              pr_err("failed to get krait device\n");
> ++              return -ENODEV;
> ++      }
> ++
> ++      np = of_node_get(cpu_dev->of_node);
> ++      if (!np) {
> ++              pr_err("failed to find krait node\n");
> ++              return -ENOENT;
> ++      }
> ++
> ++      ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
> ++      if (ret) {
> ++              pr_err("failed to init cpufreq table: %d\n", ret);
> ++              goto out_put_node;
> ++      }
> ++
> ++      of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
> ++
> ++      if (of_property_read_u32(np, "clock-latency", &transition_latency))
> ++              transition_latency = CPUFREQ_ETERNAL;
> ++
> ++      cache = of_find_next_cache_node(np);
> ++      if (cache) {
> ++              struct device_node *vdd;
> ++
> ++              vdd = of_parse_phandle(cache, "vdd_dig-supply", 0);
> ++              if (vdd) {
> ++                      krait_l2_reg = regulator_get(NULL, vdd->name);
> ++                      if (IS_ERR(krait_l2_reg)) {
> ++                              pr_warn("failed to get l2 vdd_dig supply\n");
> ++                              krait_l2_reg = NULL;
> ++                      }
> ++                      of_node_put(vdd);
> ++              }
> ++
> ++              krait_l2_clk = of_clk_get(cache, 0);
> ++              if (!IS_ERR(krait_l2_clk)) {
> ++                      ret = krait_parse_cache_points(&pdev->dev, cache);
> ++                      if (ret)
> ++                              clk_put(krait_l2_clk);
> ++              }
> ++              if (IS_ERR(krait_l2_clk) || ret)
> ++                      krait_l2_clk = NULL;
> ++      }
> ++
> ++      for_each_possible_cpu(cpu) {
> ++              dev = get_cpu_device(cpu);
> ++              if (!dev) {
> ++                      pr_err("failed to get krait device\n");
> ++                      ret = -ENOENT;
> ++                      goto out_free_table;
> ++              }
> ++              per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL);
> ++              if (IS_ERR(clk)) {
> ++                      ret = PTR_ERR(clk);
> ++                      goto out_free_table;
> ++              }
> ++              core = devm_regulator_get(dev, "core");
> ++              if (IS_ERR(core)) {
> ++                      pr_debug("failed to get core regulator\n");
> ++                      ret = PTR_ERR(core);
> ++                      goto out_free_table;
> ++              }
> ++              per_cpu(krait_supply_core, cpu) = core;
> ++
> ++              freq = freq_Hz = clk_get_rate(clk);
> ++
> ++              rcu_read_lock();
> ++              opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
> ++              if (IS_ERR(opp)) {
> ++                      rcu_read_unlock();
> ++                      pr_err("failed to find OPP for %ld\n", freq_Hz);
> ++                      ret = PTR_ERR(opp);
> ++                      goto out_free_table;
> ++              }
> ++              volt = dev_pm_opp_get_voltage(opp);
> ++              rcu_read_unlock();
> ++
> ++              tol = volt * voltage_tolerance / 100;
> ++              ret = regulator_set_voltage_tol(core, volt, tol);
> ++              if (ret) {
> ++                      pr_err("failed to scale voltage up: %d\n", ret);
> ++                      goto out_free_table;
> ++              }
> ++              ret = regulator_enable(core);
> ++              if (ret) {
> ++                      pr_err("failed to enable regulator: %d\n", ret);
> ++                      goto out_free_table;
> ++              }
> ++              max_cpu_freq = max(max_cpu_freq, freq);
> ++      }
> ++
> ++      for (i = 0; i < nr_krait_l2_points; i++) {
> ++              if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
> ++                      if (krait_l2_reg) {
> ++                              ret = regulator_set_voltage_tol(krait_l2_reg,
> ++                                              krait_l2_points[i].cache_volt,
> ++                                              tol);
> ++                              if (ret)
> ++                                      pr_err("failed to scale l2 voltage: %d\n",
> ++                                                      ret);
> ++                              ret = regulator_enable(krait_l2_reg);
> ++                              if (ret)
> ++                                      pr_err("failed to enable l2 voltage: %d\n",
> ++                                                      ret);
> ++                      }
> ++                      break;
> ++              }
> ++
> ++      }
> ++
> ++      ret = cpufreq_register_driver(&krait_cpufreq_driver);
> ++      if (ret) {
> ++              pr_err("failed register driver: %d\n", ret);
> ++              goto out_free_table;
> ++      }
> ++      of_node_put(np);
> ++
> ++      /*
> ++       * For now, just loading the cooling device;
> ++       * thermal DT code takes care of matching them.
> ++       */
> ++      for_each_possible_cpu(cpu) {
> ++              dev = get_cpu_device(cpu);
> ++              np = of_node_get(dev->of_node);
> ++              if (of_find_property(np, "#cooling-cells", NULL)) {
> ++                      cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu));
> ++                      if (IS_ERR(cdev))
> ++                              pr_err("running cpufreq without cooling device: %ld\n",
> ++                                     PTR_ERR(cdev));
> ++              }
> ++              of_node_put(np);
> ++      }
> ++
> ++      return 0;
> ++
> ++out_free_table:
> ++      regulator_put(krait_l2_reg);
> ++      clk_put(krait_l2_clk);
> ++      dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
> ++out_put_node:
> ++      of_node_put(np);
> ++      return ret;
> ++}
> ++
> ++static int krait_cpufreq_remove(struct platform_device *pdev)
> ++{
> ++      cpufreq_cooling_unregister(cdev);
> ++      cpufreq_unregister_driver(&krait_cpufreq_driver);
> ++      dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
> ++      clk_put(krait_l2_clk);
> ++      regulator_put(krait_l2_reg);
> ++
> ++      return 0;
> ++}
> ++
> ++static struct platform_driver krait_cpufreq_platdrv = {
> ++      .driver = {
> ++              .name   = "cpufreq-krait",
> ++              .owner  = THIS_MODULE,
> ++      },
> ++      .probe          = krait_cpufreq_probe,
> ++      .remove         = krait_cpufreq_remove,
> ++};
> ++module_platform_driver(krait_cpufreq_platdrv);
> ++
> ++MODULE_DESCRIPTION("Krait CPUfreq driver");
> ++MODULE_LICENSE("GPL v2");
> +--- a/drivers/cpufreq/qcom-cpufreq.c
> ++++ b/drivers/cpufreq/qcom-cpufreq.c
> +@@ -168,11 +168,8 @@
> +
> + static int __init qcom_cpufreq_driver_init(void)
> + {
> +-      struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
> +       struct platform_device_info devinfo = {
> +-              .name = "cpufreq-dt",
> +-              .data = &pdata,
> +-              .size_data = sizeof(pdata),
> ++              .name = "cpufreq-krait",
> +       };
> +       struct device *cpu_dev;
> +       struct device_node *np;
> diff --git a/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
> new file mode 100644
> index 0000000..4f5c0ef
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch
> @@ -0,0 +1,76 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers
> +From: Andy Gross <agross at codeaurora.org>
> +X-Patchwork-Id: 6027361
> +Message-Id: <1426571172-9711-2-git-send-email-agross at codeaurora.org>
> +To: Vinod Koul <vinod.koul at intel.com>
> +Cc: devicetree at vger.kernel.org, dmaengine at vger.kernel.org,
> +       linux-arm-msm at vger.kernel.org, linux-kernel at vger.kernel.org,
> +       linux-arm-kernel at lists.infradead.org, Kumar Gala <galak at codeaurora.org>,
> +       Bjorn Andersson <bjorn.andersson at sonymobile.com>,
> +       Andy Gross <agross at codeaurora.org>
> +Date: Tue, 17 Mar 2015 00:46:11 -0500
> +
> +This patch removes the crci information from the dma channel property.  At least
> +one client device requires using more than one CRCI value for a channel.  This
> +does not match the current binding and the crci information needs to be removed.
> +
> +Instead, the client device will provide this information via other means.
> +
> +Signed-off-by: Andy Gross <agross 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.4/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch
> new file mode 100644
> index 0000000..805f28f
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch
> @@ -0,0 +1,964 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v6,2/2] dmaengine: Add ADM driver
> +From: Andy Gross <agross at codeaurora.org>
> +X-Patchwork-Id: 6027351
> +Message-Id: <1426571172-9711-3-git-send-email-agross at codeaurora.org>
> +To: Vinod Koul <vinod.koul at intel.com>
> +Cc: devicetree at vger.kernel.org, dmaengine at vger.kernel.org,
> +       linux-arm-msm at vger.kernel.org, linux-kernel at vger.kernel.org,
> +       linux-arm-kernel at lists.infradead.org, Kumar Gala <galak at codeaurora.org>,
> +       Bjorn Andersson <bjorn.andersson at sonymobile.com>,
> +       Andy Gross <agross at codeaurora.org>
> +Date: Tue, 17 Mar 2015 00:46:12 -0500
> +
> +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
> +controller found in the MSM8x60 and IPQ/APQ8064 platforms.
> +
> +The ADM supports both memory to memory transactions and memory
> +to/from peripheral device transactions.  The controller also provides flow
> +control capabilities for transactions to/from peripheral devices.
> +
> +The initial release of this driver supports slave transfers to/from peripherals
> +and also incorporates CRCI (client rate control interface) flow control.
> +
> +Signed-off-by: Andy Gross <agross at codeaurora.org>
> +Reviewed-by: sricharan <sricharan at codeaurora.org>
> +
> +---
> +drivers/dma/Kconfig    |   10 +
> + drivers/dma/Makefile   |    1 +
> + drivers/dma/qcom_adm.c |  900 ++++++++++++++++++++++++++++++++++++++++++++++++
> + 3 files changed, 911 insertions(+)
> + create mode 100644 drivers/dma/qcom_adm.c
> +
> +--- a/drivers/dma/Kconfig
> ++++ b/drivers/dma/Kconfig
> +@@ -558,4 +558,14 @@
> + config DMA_ENGINE_RAID
> +       bool
> +
> ++config QCOM_ADM
> ++      tristate "Qualcomm ADM support"
> ++      depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
> ++      select DMA_ENGINE
> ++      select DMA_VIRTUAL_CHANNELS
> ++      ---help---
> ++        Enable support for the Qualcomm ADM DMA controller.  This controller
> ++        provides DMA capabilities for both general purpose and on-chip
> ++        peripheral devices.
> ++
> + endif
> +--- /dev/null
> ++++ b/drivers/dma/qcom_adm.c
> +@@ -0,0 +1,900 @@
> ++/*
> ++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This program is free software; you can redistribute it and/or modify
> ++ * it under the terms of the GNU General Public License version 2 and
> ++ * only version 2 as published by the Free Software Foundation.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ *
> ++ */
> ++
> ++#include <linux/kernel.h>
> ++#include <linux/io.h>
> ++#include <linux/init.h>
> ++#include <linux/slab.h>
> ++#include <linux/module.h>
> ++#include <linux/interrupt.h>
> ++#include <linux/dma-mapping.h>
> ++#include <linux/scatterlist.h>
> ++#include <linux/device.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/of.h>
> ++#include <linux/of_address.h>
> ++#include <linux/of_irq.h>
> ++#include <linux/of_dma.h>
> ++#include <linux/reset.h>
> ++#include <linux/clk.h>
> ++#include <linux/dmaengine.h>
> ++
> ++#include "dmaengine.h"
> ++#include "virt-dma.h"
> ++
> ++/* ADM registers - calculated from channel number and security domain */
> ++#define ADM_CHAN_MULTI                        0x4
> ++#define ADM_CI_MULTI                  0x4
> ++#define ADM_CRCI_MULTI                        0x4
> ++#define ADM_EE_MULTI                  0x800
> ++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
> ++#define ADM_EE_OFFS(ee)                       (ADM_EE_MULTI * ee)
> ++#define ADM_CHAN_EE_OFFS(chan, ee)    (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
> ++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
> ++#define ADM_CI_OFFS(ci)                       (ADM_CHAN_OFF(ci))
> ++#define ADM_CH_CMD_PTR(chan, ee)      (ADM_CHAN_EE_OFFS(chan, ee))
> ++#define ADM_CH_RSLT(chan, ee)         (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
> ++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
> ++#define ADM_CH_STATUS_SD(chan, ee)    (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
> ++#define ADM_CH_CONF(chan)             (0x240 + ADM_CHAN_OFFS(chan))
> ++#define ADM_CH_RSLT_CONF(chan, ee)    (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
> ++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
> ++#define ADM_CI_CONF(ci)                       (0x390 + ci * ADM_CI_MULTI)
> ++#define ADM_GP_CTL                    0x3d8
> ++#define ADM_CRCI_CTL(crci, ee)                (0x400 + crci * ADM_CRCI_MULTI + \
> ++                                              ADM_EE_OFFS(ee))
> ++
> ++/* channel status */
> ++#define ADM_CH_STATUS_VALID   BIT(1)
> ++
> ++/* channel result */
> ++#define ADM_CH_RSLT_VALID     BIT(31)
> ++#define ADM_CH_RSLT_ERR               BIT(3)
> ++#define ADM_CH_RSLT_FLUSH     BIT(2)
> ++#define ADM_CH_RSLT_TPD               BIT(1)
> ++
> ++/* channel conf */
> ++#define ADM_CH_CONF_SHADOW_EN         BIT(12)
> ++#define ADM_CH_CONF_MPU_DISABLE               BIT(11)
> ++#define ADM_CH_CONF_PERM_MPU_CONF     BIT(9)
> ++#define ADM_CH_CONF_FORCE_RSLT_EN     BIT(7)
> ++#define ADM_CH_CONF_SEC_DOMAIN(ee)    (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
> ++
> ++/* channel result conf */
> ++#define ADM_CH_RSLT_CONF_FLUSH_EN     BIT(1)
> ++#define ADM_CH_RSLT_CONF_IRQ_EN               BIT(0)
> ++
> ++/* CRCI CTL */
> ++#define ADM_CRCI_CTL_MUX_SEL  BIT(18)
> ++#define ADM_CRCI_CTL_RST      BIT(17)
> ++
> ++/* CI configuration */
> ++#define ADM_CI_RANGE_END(x)   (x << 24)
> ++#define ADM_CI_RANGE_START(x) (x << 16)
> ++#define ADM_CI_BURST_4_WORDS  BIT(2)
> ++#define ADM_CI_BURST_8_WORDS  BIT(3)
> ++
> ++/* GP CTL */
> ++#define ADM_GP_CTL_LP_EN      BIT(12)
> ++#define ADM_GP_CTL_LP_CNT(x)  (x << 8)
> ++
> ++/* Command pointer list entry */
> ++#define ADM_CPLE_LP           BIT(31)
> ++#define ADM_CPLE_CMD_PTR_LIST BIT(29)
> ++
> ++/* Command list entry */
> ++#define ADM_CMD_LC            BIT(31)
> ++#define ADM_CMD_DST_CRCI(n)   (((n) & 0xf) << 7)
> ++#define ADM_CMD_SRC_CRCI(n)   (((n) & 0xf) << 3)
> ++
> ++#define ADM_CMD_TYPE_SINGLE   0x0
> ++#define ADM_CMD_TYPE_BOX      0x3
> ++
> ++#define ADM_CRCI_MUX_SEL      BIT(4)
> ++#define ADM_DESC_ALIGN                8
> ++#define ADM_MAX_XFER          (SZ_64K-1)
> ++#define ADM_MAX_ROWS          (SZ_64K-1)
> ++#define ADM_MAX_CHANNELS      16
> ++
> ++struct adm_desc_hw_box {
> ++      u32 cmd;
> ++      u32 src_addr;
> ++      u32 dst_addr;
> ++      u32 row_len;
> ++      u32 num_rows;
> ++      u32 row_offset;
> ++};
> ++
> ++struct adm_desc_hw_single {
> ++      u32 cmd;
> ++      u32 src_addr;
> ++      u32 dst_addr;
> ++      u32 len;
> ++};
> ++
> ++struct adm_async_desc {
> ++      struct virt_dma_desc vd;
> ++      struct adm_device *adev;
> ++
> ++      size_t length;
> ++      enum dma_transfer_direction dir;
> ++      dma_addr_t dma_addr;
> ++      size_t dma_len;
> ++
> ++      void *cpl;
> ++      dma_addr_t cp_addr;
> ++      u32 crci;
> ++      u32 mux;
> ++      u32 blk_size;
> ++};
> ++
> ++struct adm_chan {
> ++      struct virt_dma_chan vc;
> ++      struct adm_device *adev;
> ++
> ++      /* parsed from DT */
> ++      u32 id;                 /* channel id */
> ++
> ++      struct adm_async_desc *curr_txd;
> ++      struct dma_slave_config slave;
> ++      struct list_head node;
> ++
> ++      int error;
> ++      int initialized;
> ++};
> ++
> ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
> ++{
> ++      return container_of(common, struct adm_chan, vc.chan);
> ++}
> ++
> ++struct adm_device {
> ++      void __iomem *regs;
> ++      struct device *dev;
> ++      struct dma_device common;
> ++      struct device_dma_parameters dma_parms;
> ++      struct adm_chan *channels;
> ++
> ++      u32 ee;
> ++
> ++      struct clk *core_clk;
> ++      struct clk *iface_clk;
> ++
> ++      struct reset_control *clk_reset;
> ++      struct reset_control *c0_reset;
> ++      struct reset_control *c1_reset;
> ++      struct reset_control *c2_reset;
> ++      int irq;
> ++};
> ++
> ++/**
> ++ * adm_free_chan - Frees dma resources associated with the specific channel
> ++ *
> ++ * Free all allocated descriptors associated with this channel
> ++ *
> ++ */
> ++static void adm_free_chan(struct dma_chan *chan)
> ++{
> ++      /* free all queued descriptors */
> ++      vchan_free_chan_resources(to_virt_chan(chan));
> ++}
> ++
> ++/**
> ++ * adm_get_blksize - Get block size from burst value
> ++ *
> ++ */
> ++static int adm_get_blksize(unsigned int burst)
> ++{
> ++      int ret;
> ++
> ++      switch (burst) {
> ++      case 16:
> ++      case 32:
> ++      case 64:
> ++      case 128:
> ++              ret = ffs(burst>>4) - 1;
> ++              break;
> ++      case 192:
> ++              ret = 4;
> ++              break;
> ++      case 256:
> ++              ret = 5;
> ++              break;
> ++      default:
> ++              ret = -EINVAL;
> ++              break;
> ++      }
> ++
> ++      return ret;
> ++}
> ++
> ++/**
> ++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
> ++ *
> ++ * @achan: ADM channel
> ++ * @desc: Descriptor memory pointer
> ++ * @sg: Scatterlist entry
> ++ * @crci: CRCI value
> ++ * @burst: Burst size of transaction
> ++ * @direction: DMA transfer direction
> ++ */
> ++static void *adm_process_fc_descriptors(struct adm_chan *achan,
> ++      void *desc, struct scatterlist *sg, u32 crci, u32 burst,
> ++      enum dma_transfer_direction direction)
> ++{
> ++      struct adm_desc_hw_box *box_desc = NULL;
> ++      struct adm_desc_hw_single *single_desc;
> ++      u32 remainder = sg_dma_len(sg);
> ++      u32 rows, row_offset, crci_cmd;
> ++      u32 mem_addr = sg_dma_address(sg);
> ++      u32 *incr_addr = &mem_addr;
> ++      u32 *src, *dst;
> ++
> ++      if (direction == DMA_DEV_TO_MEM) {
> ++              crci_cmd = ADM_CMD_SRC_CRCI(crci);
> ++              row_offset = burst;
> ++              src = &achan->slave.src_addr;
> ++              dst = &mem_addr;
> ++      } else {
> ++              crci_cmd = ADM_CMD_DST_CRCI(crci);
> ++              row_offset = burst << 16;
> ++              src = &mem_addr;
> ++              dst = &achan->slave.dst_addr;
> ++      }
> ++
> ++      while (remainder >= burst) {
> ++              box_desc = desc;
> ++              box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
> ++              box_desc->row_offset = row_offset;
> ++              box_desc->src_addr = *src;
> ++              box_desc->dst_addr = *dst;
> ++
> ++              rows = remainder / burst;
> ++              rows = min_t(u32, rows, ADM_MAX_ROWS);
> ++              box_desc->num_rows = rows << 16 | rows;
> ++              box_desc->row_len = burst << 16 | burst;
> ++
> ++              *incr_addr += burst * rows;
> ++              remainder -= burst * rows;
> ++              desc += sizeof(*box_desc);
> ++      }
> ++
> ++      /* if leftover bytes, do one single descriptor */
> ++      if (remainder) {
> ++              single_desc = desc;
> ++              single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
> ++              single_desc->len = remainder;
> ++              single_desc->src_addr = *src;
> ++              single_desc->dst_addr = *dst;
> ++              desc += sizeof(*single_desc);
> ++
> ++              if (sg_is_last(sg))
> ++                      single_desc->cmd |= ADM_CMD_LC;
> ++      } else {
> ++              if (box_desc && sg_is_last(sg))
> ++                      box_desc->cmd |= ADM_CMD_LC;
> ++      }
> ++
> ++      return desc;
> ++}
> ++
> ++/**
> ++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
> ++ *
> ++ * @achan: ADM channel
> ++ * @desc: Descriptor memory pointer
> ++ * @sg: Scatterlist entry
> ++ * @direction: DMA transfer direction
> ++ */
> ++static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
> ++      void *desc, struct scatterlist *sg,
> ++      enum dma_transfer_direction direction)
> ++{
> ++      struct adm_desc_hw_single *single_desc;
> ++      u32 remainder = sg_dma_len(sg);
> ++      u32 mem_addr = sg_dma_address(sg);
> ++      u32 *incr_addr = &mem_addr;
> ++      u32 *src, *dst;
> ++
> ++      if (direction == DMA_DEV_TO_MEM) {
> ++              src = &achan->slave.src_addr;
> ++              dst = &mem_addr;
> ++      } else {
> ++              src = &mem_addr;
> ++              dst = &achan->slave.dst_addr;
> ++      }
> ++
> ++      do {
> ++              single_desc = desc;
> ++              single_desc->cmd = ADM_CMD_TYPE_SINGLE;
> ++              single_desc->src_addr = *src;
> ++              single_desc->dst_addr = *dst;
> ++              single_desc->len = (remainder > ADM_MAX_XFER) ?
> ++                              ADM_MAX_XFER : remainder;
> ++
> ++              remainder -= single_desc->len;
> ++              *incr_addr += single_desc->len;
> ++              desc += sizeof(*single_desc);
> ++      } while (remainder);
> ++
> ++      /* set last command if this is the end of the whole transaction */
> ++      if (sg_is_last(sg))
> ++              single_desc->cmd |= ADM_CMD_LC;
> ++
> ++      return desc;
> ++}
> ++
> ++/**
> ++ * adm_prep_slave_sg - Prep slave sg transaction
> ++ *
> ++ * @chan: dma channel
> ++ * @sgl: scatter gather list
> ++ * @sg_len: length of sg
> ++ * @direction: DMA transfer direction
> ++ * @flags: DMA flags
> ++ * @context: transfer context (unused)
> ++ */
> ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
> ++      struct scatterlist *sgl, unsigned int sg_len,
> ++      enum dma_transfer_direction direction, unsigned long flags,
> ++      void *context)
> ++{
> ++      struct adm_chan *achan = to_adm_chan(chan);
> ++      struct adm_device *adev = achan->adev;
> ++      struct adm_async_desc *async_desc;
> ++      struct scatterlist *sg;
> ++      u32 i, burst;
> ++      u32 single_count = 0, box_count = 0, crci = 0;
> ++      void *desc;
> ++      u32 *cple;
> ++      int blk_size = 0;
> ++
> ++      if (!is_slave_direction(direction)) {
> ++              dev_err(adev->dev, "invalid dma direction\n");
> ++              return NULL;
> ++      }
> ++
> ++      /*
> ++       * get burst value from slave configuration
> ++       */
> ++      burst = (direction == DMA_MEM_TO_DEV) ?
> ++              achan->slave.dst_maxburst :
> ++              achan->slave.src_maxburst;
> ++
> ++      /* if using flow control, validate burst and crci values */
> ++      if (achan->slave.device_fc) {
> ++
> ++              blk_size = adm_get_blksize(burst);
> ++              if (blk_size < 0) {
> ++                      dev_err(adev->dev, "invalid burst value: %d\n",
> ++                              burst);
> ++                      return ERR_PTR(-EINVAL);
> ++              }
> ++
> ++              crci = achan->slave.slave_id & 0xf;
> ++              if (!crci || achan->slave.slave_id > 0x1f) {
> ++                      dev_err(adev->dev, "invalid crci value\n");
> ++                      return ERR_PTR(-EINVAL);
> ++              }
> ++      }
> ++
> ++      /* iterate through sgs and compute allocation size of structures */
> ++      for_each_sg(sgl, sg, sg_len, i) {
> ++              if (achan->slave.device_fc) {
> ++                      box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
> ++                                                ADM_MAX_ROWS);
> ++                      if (sg_dma_len(sg) % burst)
> ++                              single_count++;
> ++              } else {
> ++                      single_count += DIV_ROUND_UP(sg_dma_len(sg),
> ++                                                   ADM_MAX_XFER);
> ++              }
> ++      }
> ++
> ++      async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
> ++      if (!async_desc)
> ++              return ERR_PTR(-ENOMEM);
> ++
> ++      if (crci)
> ++              async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
> ++                                      ADM_CRCI_CTL_MUX_SEL : 0;
> ++      async_desc->crci = crci;
> ++      async_desc->blk_size = blk_size;
> ++      async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
> ++                              box_count * sizeof(struct adm_desc_hw_box) +
> ++                              sizeof(*cple) + 2 * ADM_DESC_ALIGN;
> ++
> ++      async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len,
> ++                              &async_desc->dma_addr, GFP_NOWAIT);
> ++
> ++      if (!async_desc->cpl) {
> ++              kfree(async_desc);
> ++              return ERR_PTR(-ENOMEM);
> ++      }
> ++
> ++      async_desc->adev = adev;
> ++
> ++      /* both command list entry and descriptors must be 8 byte aligned */
> ++      cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
> ++      desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
> ++
> ++      /* init cmd list */
> ++      *cple = ADM_CPLE_LP;
> ++      *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3;
> ++
> ++      for_each_sg(sgl, sg, sg_len, i) {
> ++              async_desc->length += sg_dma_len(sg);
> ++
> ++              if (achan->slave.device_fc)
> ++                      desc = adm_process_fc_descriptors(achan, desc, sg, crci,
> ++                                                      burst, direction);
> ++              else
> ++                      desc = adm_process_non_fc_descriptors(achan, desc, sg,
> ++                                                         direction);
> ++      }
> ++
> ++      return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
> ++}
> ++
> ++/**
> ++ * adm_terminate_all - terminate all transactions on a channel
> ++ * @achan: adm dma channel
> ++ *
> ++ * Dequeues and frees all transactions, aborts current transaction
> ++ * No callbacks are done
> ++ *
> ++ */
> ++static int adm_terminate_all(struct dma_chan *chan)
> ++{
> ++      struct adm_chan *achan = to_adm_chan(chan);
> ++      struct adm_device *adev = achan->adev;
> ++      unsigned long flags;
> ++      LIST_HEAD(head);
> ++
> ++      spin_lock_irqsave(&achan->vc.lock, flags);
> ++      vchan_get_all_descriptors(&achan->vc, &head);
> ++
> ++      /* send flush command to terminate current transaction */
> ++      writel_relaxed(0x0,
> ++              adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
> ++
> ++      spin_unlock_irqrestore(&achan->vc.lock, flags);
> ++
> ++      vchan_dma_desc_free_list(&achan->vc, &head);
> ++
> ++      return 0;
> ++}
> ++
> ++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
> ++{
> ++      struct adm_chan *achan = to_adm_chan(chan);
> ++      unsigned long flag;
> ++
> ++      spin_lock_irqsave(&achan->vc.lock, flag);
> ++      memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
> ++      spin_unlock_irqrestore(&achan->vc.lock, flag);
> ++
> ++      return 0;
> ++}
> ++
> ++/**
> ++ * adm_start_dma - start next transaction
> ++ * @achan - ADM dma channel
> ++ */
> ++static void adm_start_dma(struct adm_chan *achan)
> ++{
> ++      struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
> ++      struct adm_device *adev = achan->adev;
> ++      struct adm_async_desc *async_desc;
> ++
> ++      lockdep_assert_held(&achan->vc.lock);
> ++
> ++      if (!vd)
> ++              return;
> ++
> ++      list_del(&vd->node);
> ++
> ++      /* write next command list out to the CMD FIFO */
> ++      async_desc = container_of(vd, struct adm_async_desc, vd);
> ++      achan->curr_txd = async_desc;
> ++
> ++      /* reset channel error */
> ++      achan->error = 0;
> ++
> ++      if (!achan->initialized) {
> ++              /* enable interrupts */
> ++              writel(ADM_CH_CONF_SHADOW_EN |
> ++                     ADM_CH_CONF_PERM_MPU_CONF |
> ++                     ADM_CH_CONF_MPU_DISABLE |
> ++                     ADM_CH_CONF_SEC_DOMAIN(adev->ee),
> ++                     adev->regs + ADM_CH_CONF(achan->id));
> ++
> ++              writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
> ++                      adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
> ++
> ++              achan->initialized = 1;
> ++      }
> ++
> ++      /* set the crci block size if this transaction requires CRCI */
> ++      if (async_desc->crci) {
> ++              writel(async_desc->mux | async_desc->blk_size,
> ++                      adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
> ++      }
> ++
> ++      /* make sure IRQ enable doesn't get reordered */
> ++      wmb();
> ++
> ++      /* write next command list out to the CMD FIFO */
> ++      writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
> ++              adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
> ++}
> ++
> ++/**
> ++ * adm_dma_irq - irq handler for ADM controller
> ++ * @irq: IRQ of interrupt
> ++ * @data: callback data
> ++ *
> ++ * IRQ handler for the bam controller
> ++ */
> ++static irqreturn_t adm_dma_irq(int irq, void *data)
> ++{
> ++      struct adm_device *adev = data;
> ++      u32 srcs, i;
> ++      struct adm_async_desc *async_desc;
> ++      unsigned long flags;
> ++
> ++      srcs = readl_relaxed(adev->regs +
> ++                      ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
> ++
> ++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
> ++              struct adm_chan *achan = &adev->channels[i];
> ++              u32 status, result;
> ++
> ++              if (srcs & BIT(i)) {
> ++                      status = readl_relaxed(adev->regs +
> ++                              ADM_CH_STATUS_SD(i, adev->ee));
> ++
> ++                      /* if no result present, skip */
> ++                      if (!(status & ADM_CH_STATUS_VALID))
> ++                              continue;
> ++
> ++                      result = readl_relaxed(adev->regs +
> ++                              ADM_CH_RSLT(i, adev->ee));
> ++
> ++                      /* no valid results, skip */
> ++                      if (!(result & ADM_CH_RSLT_VALID))
> ++                              continue;
> ++
> ++                      /* flag error if transaction was flushed or failed */
> ++                      if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
> ++                              achan->error = 1;
> ++
> ++                      spin_lock_irqsave(&achan->vc.lock, flags);
> ++                      async_desc = achan->curr_txd;
> ++
> ++                      achan->curr_txd = NULL;
> ++
> ++                      if (async_desc) {
> ++                              vchan_cookie_complete(&async_desc->vd);
> ++
> ++                              /* kick off next DMA */
> ++                              adm_start_dma(achan);
> ++                      }
> ++
> ++                      spin_unlock_irqrestore(&achan->vc.lock, flags);
> ++              }
> ++      }
> ++
> ++      return IRQ_HANDLED;
> ++}
> ++
> ++/**
> ++ * adm_tx_status - returns status of transaction
> ++ * @chan: dma channel
> ++ * @cookie: transaction cookie
> ++ * @txstate: DMA transaction state
> ++ *
> ++ * Return status of dma transaction
> ++ */
> ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
> ++      struct dma_tx_state *txstate)
> ++{
> ++      struct adm_chan *achan = to_adm_chan(chan);
> ++      struct virt_dma_desc *vd;
> ++      enum dma_status ret;
> ++      unsigned long flags;
> ++      size_t residue = 0;
> ++
> ++      ret = dma_cookie_status(chan, cookie, txstate);
> ++      if (ret == DMA_COMPLETE || !txstate)
> ++              return ret;
> ++
> ++      spin_lock_irqsave(&achan->vc.lock, flags);
> ++
> ++      vd = vchan_find_desc(&achan->vc, cookie);
> ++      if (vd)
> ++              residue = container_of(vd, struct adm_async_desc, vd)->length;
> ++
> ++      spin_unlock_irqrestore(&achan->vc.lock, flags);
> ++
> ++      /*
> ++       * residue is either the full length if it is in the issued list, or 0
> ++       * if it is in progress.  We have no reliable way of determining
> ++       * anything inbetween
> ++      */
> ++      dma_set_residue(txstate, residue);
> ++
> ++      if (achan->error)
> ++              return DMA_ERROR;
> ++
> ++      return ret;
> ++}
> ++
> ++/**
> ++ * adm_issue_pending - starts pending transactions
> ++ * @chan: dma channel
> ++ *
> ++ * Issues all pending transactions and starts DMA
> ++ */
> ++static void adm_issue_pending(struct dma_chan *chan)
> ++{
> ++      struct adm_chan *achan = to_adm_chan(chan);
> ++      unsigned long flags;
> ++
> ++      spin_lock_irqsave(&achan->vc.lock, flags);
> ++
> ++      if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
> ++              adm_start_dma(achan);
> ++      spin_unlock_irqrestore(&achan->vc.lock, flags);
> ++}
> ++
> ++/**
> ++ * adm_dma_free_desc - free descriptor memory
> ++ * @vd: virtual descriptor
> ++ *
> ++ */
> ++static void adm_dma_free_desc(struct virt_dma_desc *vd)
> ++{
> ++      struct adm_async_desc *async_desc = container_of(vd,
> ++                      struct adm_async_desc, vd);
> ++
> ++      dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len,
> ++              async_desc->cpl, async_desc->dma_addr);
> ++      kfree(async_desc);
> ++}
> ++
> ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
> ++      u32 index)
> ++{
> ++      achan->id = index;
> ++      achan->adev = adev;
> ++
> ++      vchan_init(&achan->vc, &adev->common);
> ++      achan->vc.desc_free = adm_dma_free_desc;
> ++}
> ++
> ++static int adm_dma_probe(struct platform_device *pdev)
> ++{
> ++      struct adm_device *adev;
> ++      struct resource *iores;
> ++      int ret;
> ++      u32 i;
> ++
> ++      adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
> ++      if (!adev)
> ++              return -ENOMEM;
> ++
> ++      adev->dev = &pdev->dev;
> ++
> ++      iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      adev->regs = devm_ioremap_resource(&pdev->dev, iores);
> ++      if (IS_ERR(adev->regs))
> ++              return PTR_ERR(adev->regs);
> ++
> ++      adev->irq = platform_get_irq(pdev, 0);
> ++      if (adev->irq < 0)
> ++              return adev->irq;
> ++
> ++      ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
> ++      if (ret) {
> ++              dev_err(adev->dev, "Execution environment unspecified\n");
> ++              return ret;
> ++      }
> ++
> ++      adev->core_clk = devm_clk_get(adev->dev, "core");
> ++      if (IS_ERR(adev->core_clk))
> ++              return PTR_ERR(adev->core_clk);
> ++
> ++      ret = clk_prepare_enable(adev->core_clk);
> ++      if (ret) {
> ++              dev_err(adev->dev, "failed to prepare/enable core clock\n");
> ++              return ret;
> ++      }
> ++
> ++      adev->iface_clk = devm_clk_get(adev->dev, "iface");
> ++      if (IS_ERR(adev->iface_clk)) {
> ++              ret = PTR_ERR(adev->iface_clk);
> ++              goto err_disable_core_clk;
> ++      }
> ++
> ++      ret = clk_prepare_enable(adev->iface_clk);
> ++      if (ret) {
> ++              dev_err(adev->dev, "failed to prepare/enable iface clock\n");
> ++              goto err_disable_core_clk;
> ++      }
> ++
> ++      adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
> ++      if (IS_ERR(adev->clk_reset)) {
> ++              dev_err(adev->dev, "failed to get ADM0 reset\n");
> ++              ret = PTR_ERR(adev->clk_reset);
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
> ++      if (IS_ERR(adev->c0_reset)) {
> ++              dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
> ++              ret = PTR_ERR(adev->c0_reset);
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
> ++      if (IS_ERR(adev->c1_reset)) {
> ++              dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
> ++              ret = PTR_ERR(adev->c1_reset);
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
> ++      if (IS_ERR(adev->c2_reset)) {
> ++              dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
> ++              ret = PTR_ERR(adev->c2_reset);
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      reset_control_assert(adev->clk_reset);
> ++      reset_control_assert(adev->c0_reset);
> ++      reset_control_assert(adev->c1_reset);
> ++      reset_control_assert(adev->c2_reset);
> ++
> ++      reset_control_deassert(adev->clk_reset);
> ++      reset_control_deassert(adev->c0_reset);
> ++      reset_control_deassert(adev->c1_reset);
> ++      reset_control_deassert(adev->c2_reset);
> ++
> ++      adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
> ++                              sizeof(*adev->channels), GFP_KERNEL);
> ++
> ++      if (!adev->channels) {
> ++              ret = -ENOMEM;
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      /* allocate and initialize channels */
> ++      INIT_LIST_HEAD(&adev->common.channels);
> ++
> ++      for (i = 0; i < ADM_MAX_CHANNELS; i++)
> ++              adm_channel_init(adev, &adev->channels[i], i);
> ++
> ++      /* reset CRCIs */
> ++      for (i = 0; i < 16; i++)
> ++              writel(ADM_CRCI_CTL_RST, adev->regs +
> ++                      ADM_CRCI_CTL(i, adev->ee));
> ++
> ++      /* configure client interfaces */
> ++      writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
> ++              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
> ++      writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
> ++              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
> ++      writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
> ++              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
> ++      writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
> ++              adev->regs + ADM_GP_CTL);
> ++
> ++      ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
> ++                      0, "adm_dma", adev);
> ++      if (ret)
> ++              goto err_disable_clks;
> ++
> ++      platform_set_drvdata(pdev, adev);
> ++
> ++      adev->common.dev = adev->dev;
> ++      adev->common.dev->dma_parms = &adev->dma_parms;
> ++
> ++      /* set capabilities */
> ++      dma_cap_zero(adev->common.cap_mask);
> ++      dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
> ++      dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
> ++
> ++      /* initialize dmaengine apis */
> ++      adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
> ++      adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
> ++      adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
> ++      adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
> ++      adev->common.device_free_chan_resources = adm_free_chan;
> ++      adev->common.device_prep_slave_sg = adm_prep_slave_sg;
> ++      adev->common.device_issue_pending = adm_issue_pending;
> ++      adev->common.device_tx_status = adm_tx_status;
> ++      adev->common.device_terminate_all = adm_terminate_all;
> ++      adev->common.device_config = adm_slave_config;
> ++
> ++      ret = dma_async_device_register(&adev->common);
> ++      if (ret) {
> ++              dev_err(adev->dev, "failed to register dma async device\n");
> ++              goto err_disable_clks;
> ++      }
> ++
> ++      ret = of_dma_controller_register(pdev->dev.of_node,
> ++                                       of_dma_xlate_by_chan_id,
> ++                                       &adev->common);
> ++      if (ret)
> ++              goto err_unregister_dma;
> ++
> ++      return 0;
> ++
> ++err_unregister_dma:
> ++      dma_async_device_unregister(&adev->common);
> ++err_disable_clks:
> ++      clk_disable_unprepare(adev->iface_clk);
> ++err_disable_core_clk:
> ++      clk_disable_unprepare(adev->core_clk);
> ++
> ++      return ret;
> ++}
> ++
> ++static int adm_dma_remove(struct platform_device *pdev)
> ++{
> ++      struct adm_device *adev = platform_get_drvdata(pdev);
> ++      struct adm_chan *achan;
> ++      u32 i;
> ++
> ++      of_dma_controller_free(pdev->dev.of_node);
> ++      dma_async_device_unregister(&adev->common);
> ++
> ++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
> ++              achan = &adev->channels[i];
> ++
> ++              /* mask IRQs for this channel/EE pair */
> ++              writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
> ++
> ++              adm_terminate_all(&adev->channels[i].vc.chan);
> ++      }
> ++
> ++      devm_free_irq(adev->dev, adev->irq, adev);
> ++
> ++      clk_disable_unprepare(adev->core_clk);
> ++      clk_disable_unprepare(adev->iface_clk);
> ++
> ++      return 0;
> ++}
> ++
> ++static const struct of_device_id adm_of_match[] = {
> ++      { .compatible = "qcom,adm", },
> ++      {}
> ++};
> ++MODULE_DEVICE_TABLE(of, adm_of_match);
> ++
> ++static struct platform_driver adm_dma_driver = {
> ++      .probe = adm_dma_probe,
> ++      .remove = adm_dma_remove,
> ++      .driver = {
> ++              .name = "adm-dma-engine",
> ++              .of_match_table = adm_of_match,
> ++      },
> ++};
> ++
> ++module_platform_driver(adm_dma_driver);
> ++
> ++MODULE_AUTHOR("Andy Gross <agross at codeaurora.org>");
> ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
> ++MODULE_LICENSE("GPL v2");
> +--- a/drivers/dma/Makefile
> ++++ b/drivers/dma/Makefile
> +@@ -65,5 +65,6 @@
> + obj-$(CONFIG_TI_EDMA) += edma.o
> + obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
> + obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
> ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
> +
> + obj-y += xilinx/
> diff --git a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
> new file mode 100644
> index 0000000..91fc90d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch
> @@ -0,0 +1,42 @@
> +From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001
> +From: Andy Gross <agross at codeaurora.org>
> +Date: Wed, 20 May 2015 15:41:07 +0530
> +Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node
> +
> +This patch adds support for the ADM DMA on the IPQ8064 SOC
> +
> +Signed-off-by: Andy Gross <agross at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064-ap148.dts |  4 ++++
> + arch/arm/boot/dts/qcom-ipq8064.dtsi      | 21 +++++++++++++++++++++
> + 2 files changed, 25 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -717,6 +717,26 @@
> +
> +                       status = "disabled";
> +               };
> ++
> ++              adm_dma: dma at 18300000 {
> ++                      compatible = "qcom,adm";
> ++                      reg = <0x18300000 0x100000>;
> ++                      interrupts = <0 170 0>;
> ++                      #dma-cells = <1>;
> ++
> ++                      clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
> ++                      clock-names = "core", "iface";
> ++
> ++                      resets = <&gcc ADM0_RESET>,
> ++                               <&gcc ADM0_PBUS_RESET>,
> ++                               <&gcc ADM0_C0_RESET>,
> ++                               <&gcc ADM0_C1_RESET>,
> ++                               <&gcc ADM0_C2_RESET>;
> ++                      reset-names = "clk", "pbus", "c0", "c1", "c2";
> ++                      qcom,ee = <0>;
> ++
> ++                      status = "disabled";
> ++              };
> +       };
> +
> +       sfpb_mutex: sfpb-mutex {
> diff --git a/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
> new file mode 100644
> index 0000000..21fe405
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
> @@ -0,0 +1,83 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,
> +       1/5] mtd: nand: Create a BBT flag to access bad block markers in raw
> +       mode
> +From: Archit Taneja <architt at codeaurora.org>
> +X-Patchwork-Id: 6927081
> +Message-Id: <1438578498-32254-2-git-send-email-architt at codeaurora.org>
> +To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
> +       cernekee at gmail.com, computersforpeace at gmail.com
> +Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
> +       sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
> +       Archit Taneja <architt at codeaurora.org>
> +Date: Mon,  3 Aug 2015 10:38:14 +0530
> +
> +Some controllers can access the factory bad block marker from OOB only
> +when they read it in raw mode. When ECC is enabled, these controllers
> +discard reading/writing bad block markers, preventing access to them
> +altogether.
> +
> +The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
> +This results in the nand driver's ecc->read_oob() op to be called, which
> +works with ECC enabled.
> +
> +Create a new BBT option flag that tells nand_bbt to force the mode to
> +MTD_OPS_RAW. This would result in the correct op being called for the
> +underlying nand controller driver.
> +
> +Reviewed-by: Andy Gross <agross at codeaurora.org>
> +Signed-off-by: Archit Taneja <architt at codeaurora.org>
> +
> +---
> +drivers/mtd/nand/nand_base.c | 6 +++++-
> + drivers/mtd/nand/nand_bbt.c  | 6 +++++-
> + include/linux/mtd/bbm.h      | 7 +++++++
> + 3 files changed, 17 insertions(+), 2 deletions(-)
> +
> +--- a/drivers/mtd/nand/nand_base.c
> ++++ b/drivers/mtd/nand/nand_base.c
> +@@ -394,7 +394,11 @@
> +       } else {
> +               ops.len = ops.ooblen = 1;
> +       }
> +-      ops.mode = MTD_OPS_PLACE_OOB;
> ++
> ++      if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
> ++              ops.mode = MTD_OPS_RAW;
> ++      else
> ++              ops.mode = MTD_OPS_PLACE_OOB;
> +
> +       /* Write to first/last page(s) if necessary */
> +       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
> +--- a/drivers/mtd/nand/nand_bbt.c
> ++++ b/drivers/mtd/nand/nand_bbt.c
> +@@ -420,7 +420,11 @@
> +       ops.oobbuf = buf;
> +       ops.ooboffs = 0;
> +       ops.datbuf = NULL;
> +-      ops.mode = MTD_OPS_PLACE_OOB;
> ++
> ++      if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
> ++              ops.mode = MTD_OPS_RAW;
> ++      else
> ++              ops.mode = MTD_OPS_PLACE_OOB;
> +
> +       for (j = 0; j < numpages; j++) {
> +               /*
> +--- a/include/linux/mtd/bbm.h
> ++++ b/include/linux/mtd/bbm.h
> +@@ -116,6 +116,12 @@
> + #define NAND_BBT_NO_OOB_BBM   0x00080000
> +
> + /*
> ++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
> ++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
> ++ * when in RAW access mode
> ++ */
> ++#define NAND_BBT_ACCESS_BBM_RAW        0x00100000
> ++/*
> +  * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
> +  * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
> +  * in nand_chip.bbt_options.
> diff --git a/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
> new file mode 100644
> index 0000000..19e5f91
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
> @@ -0,0 +1,2024 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver
> +From: Archit Taneja <architt at codeaurora.org>
> +X-Patchwork-Id: 6927101
> +Message-Id: <1438578498-32254-3-git-send-email-architt at codeaurora.org>
> +To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
> +       cernekee at gmail.com, computersforpeace at gmail.com
> +Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
> +       sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
> +       Archit Taneja <architt at codeaurora.org>
> +Date: Mon,  3 Aug 2015 10:38:15 +0530
> +
> +The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx,
> +MDM9x15 series.
> +
> +It exists as a sub block inside the IPs EBI2 (External Bus Interface 2)
> +and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a
> +broader interface for external slow peripheral devices such as LCD and
> +NAND/NOR flash memory or SRAM like interfaces.
> +
> +We add support for the NAND controller found within EBI2. For the SoCs
> +of our interest, we only use the NAND controller within EBI2. Therefore,
> +it's safe for us to assume that the NAND controller is a standalone block
> +within the SoC.
> +
> +The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND
> +flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and
> +16 bit correction/step) and RS ECC(4 bit correction/step) that covers main
> +and spare data. The controller contains an internal 512 byte page buffer
> +to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA
> +for register read/write and data transfers. The controller performs page
> +reads and writes at a codeword/step level of 512 bytes. It can support up
> +to 2 external chips of different configurations.
> +
> +The driver prepares register read and write configuration descriptors for
> +each codeword, followed by data descriptors to read or write data from the
> +controller's internal buffer. It uses a single ADM DMA channel that we get
> +via dmaengine API. The controller requires 2 ADM CRCIs for command and
> +data flow control. These are passed via DT.
> +
> +The ecc layout used by the controller is syndrome like, but we can't use
> +the standard syndrome ecc ops because of several reasons. First, the amount
> +of data bytes covered by ecc isn't same in each step. Second, writing to
> +free oob space requires us writing to the entire step in which the oob
> +lies. This forces us to create our own ecc ops.
> +
> +One more difference is how the controller accesses the bad block marker.
> +The controller ignores reading the marker when ECC is enabled. ECC needs
> +to be explicity disabled to read or write to the bad block marker. For
> +this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to
> +read the factory provided bad block markers.
> +
> +v3:
> +- Refactor dma functions for maximum reuse
> +- Use dma_slave_confing on stack
> +- optimize and clean upempty_page_fixup using memchr_inv
> +- ensure portability with dma register reads using le32_* funcs
> +- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves
> +- fix handling of return values of dmaengine funcs
> +- constify wherever possible
> +- Remove dependency on ADM DMA in Kconfig
> +- Misc fixes and clean ups
> +
> +v2:
> +- Use new BBT flag that allows us to read BBM in raw mode
> +- reduce memcpy-s in the driver
> +- some refactor and clean ups because of above changes
> +
> +Reviewed-by: Andy Gross <agross at codeaurora.org>
> +Signed-off-by: Archit Taneja <architt at codeaurora.org>
> +
> +---
> +drivers/mtd/nand/Kconfig      |    7 +
> + drivers/mtd/nand/Makefile     |    1 +
> + drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++
> + 3 files changed, 1921 insertions(+)
> + create mode 100644 drivers/mtd/nand/qcom_nandc.c
> +
> +--- a/drivers/mtd/nand/Kconfig
> ++++ b/drivers/mtd/nand/Kconfig
> +@@ -546,4 +546,11 @@
> +       help
> +         Enables support for NAND controller on Hisilicon SoC Hip04.
> +
> ++config MTD_NAND_QCOM
> ++      tristate "Support for NAND on QCOM SoCs"
> ++      depends on ARCH_QCOM
> ++      help
> ++        Enables support for NAND flash chips on SoCs containing the EBI2 NAND
> ++        controller. This controller is found on IPQ806x SoC.
> ++
> + endif # MTD_NAND
> +--- /dev/null
> ++++ b/drivers/mtd/nand/qcom_nandc.c
> +@@ -0,0 +1,1918 @@
> ++/*
> ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> ++ *
> ++ * This software is licensed under the terms of the GNU General Public
> ++ * License version 2, as published by the Free Software Foundation, and
> ++ * may be copied, distributed, and modified under those terms.
> ++ *
> ++ * This program is distributed in the hope that it will be useful,
> ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
> ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ++ * GNU General Public License for more details.
> ++ */
> ++
> ++#include <linux/clk.h>
> ++#include <linux/slab.h>
> ++#include <linux/bitops.h>
> ++#include <linux/dma-mapping.h>
> ++#include <linux/dmaengine.h>
> ++#include <linux/module.h>
> ++#include <linux/mtd/nand.h>
> ++#include <linux/mtd/partitions.h>
> ++#include <linux/of.h>
> ++#include <linux/of_device.h>
> ++#include <linux/of_mtd.h>
> ++#include <linux/delay.h>
> ++
> ++/* NANDc reg offsets */
> ++#define NAND_FLASH_CMD                        0x00
> ++#define NAND_ADDR0                    0x04
> ++#define NAND_ADDR1                    0x08
> ++#define NAND_FLASH_CHIP_SELECT                0x0c
> ++#define NAND_EXEC_CMD                 0x10
> ++#define NAND_FLASH_STATUS             0x14
> ++#define NAND_BUFFER_STATUS            0x18
> ++#define NAND_DEV0_CFG0                        0x20
> ++#define NAND_DEV0_CFG1                        0x24
> ++#define NAND_DEV0_ECC_CFG             0x28
> ++#define NAND_DEV1_ECC_CFG             0x2c
> ++#define NAND_DEV1_CFG0                        0x30
> ++#define NAND_DEV1_CFG1                        0x34
> ++#define NAND_READ_ID                  0x40
> ++#define NAND_READ_STATUS              0x44
> ++#define NAND_DEV_CMD0                 0xa0
> ++#define NAND_DEV_CMD1                 0xa4
> ++#define NAND_DEV_CMD2                 0xa8
> ++#define NAND_DEV_CMD_VLD              0xac
> ++#define SFLASHC_BURST_CFG             0xe0
> ++#define NAND_ERASED_CW_DETECT_CFG     0xe8
> ++#define NAND_ERASED_CW_DETECT_STATUS  0xec
> ++#define NAND_EBI2_ECC_BUF_CFG         0xf0
> ++#define FLASH_BUF_ACC                 0x100
> ++
> ++#define NAND_CTRL                     0xf00
> ++#define NAND_VERSION                  0xf08
> ++#define NAND_READ_LOCATION_0          0xf20
> ++#define NAND_READ_LOCATION_1          0xf24
> ++
> ++/* dummy register offsets, used by write_reg_dma */
> ++#define NAND_DEV_CMD1_RESTORE         0xdead
> ++#define NAND_DEV_CMD_VLD_RESTORE      0xbeef
> ++
> ++/* NAND_FLASH_CMD bits */
> ++#define PAGE_ACC                      BIT(4)
> ++#define LAST_PAGE                     BIT(5)
> ++
> ++/* NAND_FLASH_CHIP_SELECT bits */
> ++#define NAND_DEV_SEL                  0
> ++#define DM_EN                         BIT(2)
> ++
> ++/* NAND_FLASH_STATUS bits */
> ++#define FS_OP_ERR                     BIT(4)
> ++#define FS_READY_BSY_N                        BIT(5)
> ++#define FS_MPU_ERR                    BIT(8)
> ++#define FS_DEVICE_STS_ERR             BIT(16)
> ++#define FS_DEVICE_WP                  BIT(23)
> ++
> ++/* NAND_BUFFER_STATUS bits */
> ++#define BS_UNCORRECTABLE_BIT          BIT(8)
> ++#define BS_CORRECTABLE_ERR_MSK                0x1f
> ++
> ++/* NAND_DEVn_CFG0 bits */
> ++#define DISABLE_STATUS_AFTER_WRITE    4
> ++#define CW_PER_PAGE                   6
> ++#define UD_SIZE_BYTES                 9
> ++#define ECC_PARITY_SIZE_BYTES_RS      19
> ++#define SPARE_SIZE_BYTES              23
> ++#define NUM_ADDR_CYCLES                       27
> ++#define STATUS_BFR_READ                       30
> ++#define SET_RD_MODE_AFTER_STATUS      31
> ++
> ++/* NAND_DEVn_CFG0 bits */
> ++#define DEV0_CFG1_ECC_DISABLE         0
> ++#define WIDE_FLASH                    1
> ++#define NAND_RECOVERY_CYCLES          2
> ++#define CS_ACTIVE_BSY                 5
> ++#define BAD_BLOCK_BYTE_NUM            6
> ++#define BAD_BLOCK_IN_SPARE_AREA               16
> ++#define WR_RD_BSY_GAP                 17
> ++#define ENABLE_BCH_ECC                        27
> ++
> ++/* NAND_DEV0_ECC_CFG bits */
> ++#define ECC_CFG_ECC_DISABLE           0
> ++#define ECC_SW_RESET                  1
> ++#define ECC_MODE                      4
> ++#define ECC_PARITY_SIZE_BYTES_BCH     8
> ++#define ECC_NUM_DATA_BYTES            16
> ++#define ECC_FORCE_CLK_OPEN            30
> ++
> ++/* NAND_DEV_CMD1 bits */
> ++#define READ_ADDR                     0
> ++
> ++/* NAND_DEV_CMD_VLD bits */
> ++#define READ_START_VLD                        0
> ++
> ++/* NAND_EBI2_ECC_BUF_CFG bits */
> ++#define NUM_STEPS                     0
> ++
> ++/* NAND_ERASED_CW_DETECT_CFG bits */
> ++#define ERASED_CW_ECC_MASK            1
> ++#define AUTO_DETECT_RES                       0
> ++#define MASK_ECC                      (1 << ERASED_CW_ECC_MASK)
> ++#define RESET_ERASED_DET              (1 << AUTO_DETECT_RES)
> ++#define ACTIVE_ERASED_DET             (0 << AUTO_DETECT_RES)
> ++#define CLR_ERASED_PAGE_DET           (RESET_ERASED_DET | MASK_ECC)
> ++#define SET_ERASED_PAGE_DET           (ACTIVE_ERASED_DET | MASK_ECC)
> ++
> ++/* NAND_ERASED_CW_DETECT_STATUS bits */
> ++#define PAGE_ALL_ERASED                       BIT(7)
> ++#define CODEWORD_ALL_ERASED           BIT(6)
> ++#define PAGE_ERASED                   BIT(5)
> ++#define CODEWORD_ERASED                       BIT(4)
> ++#define ERASED_PAGE                   (PAGE_ALL_ERASED | PAGE_ERASED)
> ++#define ERASED_CW                     (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
> ++
> ++/* Version Mask */
> ++#define NAND_VERSION_MAJOR_MASK               0xf0000000
> ++#define NAND_VERSION_MAJOR_SHIFT      28
> ++#define NAND_VERSION_MINOR_MASK               0x0fff0000
> ++#define NAND_VERSION_MINOR_SHIFT      16
> ++
> ++/* NAND OP_CMDs */
> ++#define PAGE_READ                     0x2
> ++#define PAGE_READ_WITH_ECC            0x3
> ++#define PAGE_READ_WITH_ECC_SPARE      0x4
> ++#define PROGRAM_PAGE                  0x6
> ++#define PAGE_PROGRAM_WITH_ECC         0x7
> ++#define PROGRAM_PAGE_SPARE            0x9
> ++#define BLOCK_ERASE                   0xa
> ++#define FETCH_ID                      0xb
> ++#define RESET_DEVICE                  0xd
> ++
> ++/*
> ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
> ++ * the driver calls the chunks 'step' or 'codeword' interchangeably
> ++ */
> ++#define NANDC_STEP_SIZE                       512
> ++
> ++/*
> ++ * the largest page size we support is 8K, this will have 16 steps/codewords
> ++ * of 512 bytes each
> ++ */
> ++#define       MAX_NUM_STEPS                   (SZ_8K / NANDC_STEP_SIZE)
> ++
> ++/* we read at most 3 registers per codeword scan */
> ++#define MAX_REG_RD                    (3 * MAX_NUM_STEPS)
> ++
> ++/* ECC modes */
> ++#define ECC_NONE      BIT(0)
> ++#define ECC_RS_4BIT   BIT(1)
> ++#define       ECC_BCH_4BIT    BIT(2)
> ++#define       ECC_BCH_8BIT    BIT(3)
> ++
> ++struct desc_info {
> ++      struct list_head list;
> ++
> ++      enum dma_transfer_direction dir;
> ++      struct scatterlist sgl;
> ++      struct dma_async_tx_descriptor *dma_desc;
> ++};
> ++
> ++/*
> ++ * holds the current register values that we want to write. acts as a contiguous
> ++ * chunk of memory which we use to write the controller registers through DMA.
> ++ */
> ++struct nandc_regs {
> ++      u32 cmd;
> ++      u32 addr0;
> ++      u32 addr1;
> ++      u32 chip_sel;
> ++      u32 exec;
> ++
> ++      u32 cfg0;
> ++      u32 cfg1;
> ++      u32 ecc_bch_cfg;
> ++
> ++      u32 clrflashstatus;
> ++      u32 clrreadstatus;
> ++
> ++      u32 cmd1;
> ++      u32 vld;
> ++
> ++      u32 orig_cmd1;
> ++      u32 orig_vld;
> ++
> ++      u32 ecc_buf_cfg;
> ++};
> ++
> ++/*
> ++ * @cmd_crci:                 ADM DMA CRCI for command flow control
> ++ * @data_crci:                        ADM DMA CRCI for data flow control
> ++ * @list:                     DMA descriptor list (list of desc_infos)
> ++ * @dma_done:                 completion param to denote end of last
> ++ *                            descriptor in the list
> ++ * @data_buffer:              our local DMA buffer for page read/writes,
> ++ *                            used when we can't use the buffer provided
> ++ *                            by upper layers directly
> ++ * @buf_size/count/start:     markers for chip->read_buf/write_buf functions
> ++ * @reg_read_buf:             buffer for reading register data via DMA
> ++ * @reg_read_pos:             marker for data read in reg_read_buf
> ++ * @cfg0, cfg1, cfg0_raw..:   NANDc register configurations needed for
> ++ *                            ecc/non-ecc mode for the current nand flash
> ++ *                            device
> ++ * @regs:                     a contiguous chunk of memory for DMA register
> ++ *                            writes
> ++ * @ecc_strength:             4 bit or 8 bit ecc, received via DT
> ++ * @bus_width:                        8 bit or 16 bit NAND bus width, received via DT
> ++ * @ecc_modes:                        supported ECC modes by the current controller,
> ++ *                            initialized via DT match data
> ++ * @cw_size:                  the number of bytes in a single step/codeword
> ++ *                            of a page, consisting of all data, ecc, spare
> ++ *                            and reserved bytes
> ++ * @cw_data:                  the number of bytes within a codeword protected
> ++ *                            by ECC
> ++ * @bch_enabled:              flag to tell whether BCH or RS ECC mode is used
> ++ * @status:                   value to be returned if NAND_CMD_STATUS command
> ++ *                            is executed
> ++ */
> ++struct qcom_nandc_data {
> ++      struct platform_device *pdev;
> ++      struct device *dev;
> ++
> ++      void __iomem *base;
> ++      struct resource *res;
> ++
> ++      struct clk *core_clk;
> ++      struct clk *aon_clk;
> ++
> ++      /* DMA stuff */
> ++      struct dma_chan *chan;
> ++      struct dma_slave_config slave_conf;
> ++      unsigned int cmd_crci;
> ++      unsigned int data_crci;
> ++      struct list_head list;
> ++      struct completion dma_done;
> ++
> ++      /* MTD stuff */
> ++      struct nand_chip chip;
> ++      struct mtd_info mtd;
> ++
> ++      /* local data buffer and markers */
> ++      u8              *data_buffer;
> ++      int             buf_size;
> ++      int             buf_count;
> ++      int             buf_start;
> ++
> ++      /* local buffer to read back registers */
> ++      u32 *reg_read_buf;
> ++      int reg_read_pos;
> ++
> ++      /* required configs */
> ++      u32 cfg0, cfg1;
> ++      u32 cfg0_raw, cfg1_raw;
> ++      u32 ecc_buf_cfg;
> ++      u32 ecc_bch_cfg;
> ++      u32 clrflashstatus;
> ++      u32 clrreadstatus;
> ++      u32 sflashc_burst_cfg;
> ++      u32 cmd1, vld;
> ++
> ++      /* register state */
> ++      struct nandc_regs *regs;
> ++
> ++      /* things we get from DT */
> ++      int ecc_strength;
> ++      int bus_width;
> ++
> ++      u32 ecc_modes;
> ++
> ++      /* misc params */
> ++      int cw_size;
> ++      int cw_data;
> ++      bool use_ecc;
> ++      bool bch_enabled;
> ++      u8 status;
> ++      int last_command;
> ++};
> ++
> ++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset)
> ++{
> ++      return ioread32(this->base + offset);
> ++}
> ++
> ++static inline void nandc_write(struct qcom_nandc_data *this, int offset,
> ++                             u32 val)
> ++{
> ++      iowrite32(val, this->base + offset);
> ++}
> ++
> ++/* helper to configure address register values */
> ++static void set_address(struct qcom_nandc_data *this, u16 column, int page)
> ++{
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      if (chip->options & NAND_BUSWIDTH_16)
> ++              column >>= 1;
> ++
> ++      regs->addr0 = page << 16 | column;
> ++      regs->addr1 = page >> 16 & 0xff;
> ++}
> ++
> ++/*
> ++ * update_rw_regs:    set up read/write register values, these will be
> ++ *                    written to the NAND controller registers via DMA
> ++ *
> ++ * @num_cw:           number of steps for the read/write operation
> ++ * @read:             read or write operation
> ++ */
> ++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read)
> ++{
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      if (read) {
> ++              if (this->use_ecc)
> ++                      regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
> ++              else
> ++                      regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
> ++      } else {
> ++                      regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
> ++      }
> ++
> ++      if (this->use_ecc) {
> ++              regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) |
> ++                              (num_cw - 1) << CW_PER_PAGE;
> ++
> ++              regs->cfg1 = this->cfg1;
> ++              regs->ecc_bch_cfg = this->ecc_bch_cfg;
> ++      } else {
> ++              regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) |
> ++                              (num_cw - 1) << CW_PER_PAGE;
> ++
> ++              regs->cfg1 = this->cfg1_raw;
> ++              regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
> ++      }
> ++
> ++      regs->ecc_buf_cfg = this->ecc_buf_cfg;
> ++      regs->clrflashstatus = this->clrflashstatus;
> ++      regs->clrreadstatus = this->clrreadstatus;
> ++      regs->exec = 1;
> ++}
> ++
> ++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off,
> ++                       const void *vaddr, int size, bool flow_control)
> ++{
> ++      struct desc_info *desc;
> ++      struct dma_async_tx_descriptor *dma_desc;
> ++      struct scatterlist *sgl;
> ++      struct dma_slave_config slave_conf;
> ++      int r;
> ++
> ++      desc = kzalloc(sizeof(*desc), GFP_KERNEL);
> ++      if (!desc)
> ++              return -ENOMEM;
> ++
> ++      list_add_tail(&desc->list, &this->list);
> ++
> ++      sgl = &desc->sgl;
> ++
> ++      sg_init_one(sgl, vaddr, size);
> ++
> ++      desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> ++
> ++      r = dma_map_sg(this->dev, sgl, 1, desc->dir);
> ++      if (r == 0) {
> ++              r = -ENOMEM;
> ++              goto err;
> ++      }
> ++
> ++      memset(&slave_conf, 0x00, sizeof(slave_conf));
> ++
> ++      slave_conf.device_fc = flow_control;
> ++      if (read) {
> ++              slave_conf.src_maxburst = 16;
> ++              slave_conf.src_addr = this->res->start + reg_off;
> ++              slave_conf.slave_id = this->data_crci;
> ++      } else {
> ++              slave_conf.dst_maxburst = 16;
> ++              slave_conf.dst_addr = this->res->start + reg_off;
> ++              slave_conf.slave_id = this->cmd_crci;
> ++      }
> ++
> ++      r = dmaengine_slave_config(this->chan, &slave_conf);
> ++      if (r) {
> ++              dev_err(this->dev, "failed to configure dma channel\n");
> ++              goto err;
> ++      }
> ++
> ++      dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0);
> ++      if (!dma_desc) {
> ++              dev_err(this->dev, "failed to prepare desc\n");
> ++              r = -EINVAL;
> ++              goto err;
> ++      }
> ++
> ++      desc->dma_desc = dma_desc;
> ++
> ++      return 0;
> ++err:
> ++      kfree(desc);
> ++
> ++      return r;
> ++}
> ++
> ++/*
> ++ * read_reg_dma:      prepares a descriptor to read a given number of
> ++ *                    contiguous registers to the reg_read_buf pointer
> ++ *
> ++ * @first:            offset of the first register in the contiguous block
> ++ * @num_regs:         number of registers to read
> ++ */
> ++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
> ++{
> ++      bool flow_control = false;
> ++      void *vaddr;
> ++      int size;
> ++
> ++      if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
> ++              flow_control = true;
> ++
> ++      size = num_regs * sizeof(u32);
> ++      vaddr = this->reg_read_buf + this->reg_read_pos;
> ++      this->reg_read_pos += num_regs;
> ++
> ++      return prep_dma_desc(this, true, first, vaddr, size, flow_control);
> ++}
> ++
> ++/*
> ++ * write_reg_dma:     prepares a descriptor to write a given number of
> ++ *                    contiguous registers
> ++ *
> ++ * @first:            offset of the first register in the contiguous block
> ++ * @num_regs:         number of registers to write
> ++ */
> ++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
> ++{
> ++      bool flow_control = false;
> ++      struct nandc_regs *regs = this->regs;
> ++      void *vaddr;
> ++      int size;
> ++
> ++      switch (first) {
> ++      case NAND_FLASH_CMD:
> ++              vaddr = &regs->cmd;
> ++              flow_control = true;
> ++              break;
> ++      case NAND_EXEC_CMD:
> ++              vaddr = &regs->exec;
> ++              break;
> ++      case NAND_FLASH_STATUS:
> ++              vaddr = &regs->clrflashstatus;
> ++              break;
> ++      case NAND_DEV0_CFG0:
> ++              vaddr = &regs->cfg0;
> ++              break;
> ++      case NAND_READ_STATUS:
> ++              vaddr = &regs->clrreadstatus;
> ++              break;
> ++      case NAND_DEV_CMD1:
> ++              vaddr = &regs->cmd1;
> ++              break;
> ++      case NAND_DEV_CMD1_RESTORE:
> ++              first = NAND_DEV_CMD1;
> ++              vaddr = &regs->orig_cmd1;
> ++              break;
> ++      case NAND_DEV_CMD_VLD:
> ++              vaddr = &regs->vld;
> ++              break;
> ++      case NAND_DEV_CMD_VLD_RESTORE:
> ++              first = NAND_DEV_CMD_VLD;
> ++              vaddr = &regs->orig_vld;
> ++              break;
> ++      case NAND_EBI2_ECC_BUF_CFG:
> ++              vaddr = &regs->ecc_buf_cfg;
> ++              break;
> ++      default:
> ++              dev_err(this->dev, "invalid starting register\n");
> ++              return -EINVAL;
> ++      }
> ++
> ++      size = num_regs * sizeof(u32);
> ++
> ++      return prep_dma_desc(this, false, first, vaddr, size, flow_control);
> ++}
> ++
> ++/*
> ++ * read_data_dma:     prepares a DMA descriptor to transfer data from the
> ++ *                    controller's internal buffer to the buffer 'vaddr'
> ++ *
> ++ * @reg_off:          offset within the controller's data buffer
> ++ * @vaddr:            virtual address of the buffer we want to write to
> ++ * @size:             DMA transaction size in bytes
> ++ */
> ++static int read_data_dma(struct qcom_nandc_data *this, int reg_off,
> ++                       const u8 *vaddr, int size)
> ++{
> ++      return prep_dma_desc(this, true, reg_off, vaddr, size, false);
> ++}
> ++
> ++/*
> ++ * write_data_dma:    prepares a DMA descriptor to transfer data from
> ++ *                    'vaddr' to the controller's internal buffer
> ++ *
> ++ * @reg_off:          offset within the controller's data buffer
> ++ * @vaddr:            virtual address of the buffer we want to read from
> ++ * @size:             DMA transaction size in bytes
> ++ */
> ++static int write_data_dma(struct qcom_nandc_data *this, int reg_off,
> ++                        const u8 *vaddr, int size)
> ++{
> ++      return prep_dma_desc(this, false, reg_off, vaddr, size, false);
> ++}
> ++
> ++/*
> ++ * helper to prepare dma descriptors to configure registers needed for reading a
> ++ * codeword/step in a page
> ++ */
> ++static void config_cw_read(struct qcom_nandc_data *this)
> ++{
> ++      write_reg_dma(this, NAND_FLASH_CMD, 3);
> ++      write_reg_dma(this, NAND_DEV0_CFG0, 3);
> ++      write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
> ++
> ++      write_reg_dma(this, NAND_EXEC_CMD, 1);
> ++
> ++      read_reg_dma(this, NAND_FLASH_STATUS, 2);
> ++      read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1);
> ++}
> ++
> ++/*
> ++ * helpers to prepare dma descriptors used to configure registers needed for
> ++ * writing a codeword/step in a page
> ++ */
> ++static void config_cw_write_pre(struct qcom_nandc_data *this)
> ++{
> ++      write_reg_dma(this, NAND_FLASH_CMD, 3);
> ++      write_reg_dma(this, NAND_DEV0_CFG0, 3);
> ++      write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
> ++}
> ++
> ++static void config_cw_write_post(struct qcom_nandc_data *this)
> ++{
> ++      write_reg_dma(this, NAND_EXEC_CMD, 1);
> ++
> ++      read_reg_dma(this, NAND_FLASH_STATUS, 1);
> ++
> ++      write_reg_dma(this, NAND_FLASH_STATUS, 1);
> ++      write_reg_dma(this, NAND_READ_STATUS, 1);
> ++}
> ++
> ++/*
> ++ * the following functions are used within chip->cmdfunc() to perform different
> ++ * NAND_CMD_* commands
> ++ */
> ++
> ++/* sets up descriptors for NAND_CMD_PARAM */
> ++static int nandc_param(struct qcom_nandc_data *this)
> ++{
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      /*
> ++       * NAND_CMD_PARAM is called before we know much about the FLASH chip
> ++       * in use. we configure the controller to perform a raw read of 512
> ++       * bytes to read onfi params
> ++       */
> ++      regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
> ++      regs->addr0 = 0;
> ++      regs->addr1 = 0;
> ++      regs->cfg0 =  0 << CW_PER_PAGE
> ++                      | 512 << UD_SIZE_BYTES
> ++                      | 5 << NUM_ADDR_CYCLES
> ++                      | 0 << SPARE_SIZE_BYTES;
> ++
> ++      regs->cfg1 =  7 << NAND_RECOVERY_CYCLES
> ++                      | 0 << CS_ACTIVE_BSY
> ++                      | 17 << BAD_BLOCK_BYTE_NUM
> ++                      | 1 << BAD_BLOCK_IN_SPARE_AREA
> ++                      | 2 << WR_RD_BSY_GAP
> ++                      | 0 << WIDE_FLASH
> ++                      | 1 << DEV0_CFG1_ECC_DISABLE;
> ++
> ++      regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
> ++
> ++      /* configure CMD1 and VLD for ONFI param probing */
> ++      regs->vld = (this->vld & ~(1 << READ_START_VLD))
> ++                      | 0 << READ_START_VLD;
> ++
> ++      regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR))
> ++                      | NAND_CMD_PARAM << READ_ADDR;
> ++
> ++      regs->exec = 1;
> ++
> ++      regs->orig_cmd1 = this->cmd1;
> ++      regs->orig_vld = this->vld;
> ++
> ++      write_reg_dma(this, NAND_DEV_CMD_VLD, 1);
> ++      write_reg_dma(this, NAND_DEV_CMD1, 1);
> ++
> ++      this->buf_count = 512;
> ++      memset(this->data_buffer, 0xff, this->buf_count);
> ++
> ++      config_cw_read(this);
> ++
> ++      read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count);
> ++
> ++      /* restore CMD1 and VLD regs */
> ++      write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1);
> ++      write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1);
> ++
> ++      return 0;
> ++}
> ++
> ++/* sets up descriptors for NAND_CMD_ERASE1 */
> ++static int erase_block(struct qcom_nandc_data *this, int page_addr)
> ++{
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE;
> ++      regs->addr0 = page_addr;
> ++      regs->addr1 = 0;
> ++      regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE);
> ++      regs->cfg1 = this->cfg1_raw;
> ++      regs->exec = 1;
> ++      regs->clrflashstatus = this->clrflashstatus;
> ++      regs->clrreadstatus = this->clrreadstatus;
> ++
> ++      write_reg_dma(this, NAND_FLASH_CMD, 3);
> ++      write_reg_dma(this, NAND_DEV0_CFG0, 2);
> ++      write_reg_dma(this, NAND_EXEC_CMD, 1);
> ++
> ++      read_reg_dma(this, NAND_FLASH_STATUS, 1);
> ++
> ++      write_reg_dma(this, NAND_FLASH_STATUS, 1);
> ++      write_reg_dma(this, NAND_READ_STATUS, 1);
> ++
> ++      return 0;
> ++}
> ++
> ++/* sets up descriptors for NAND_CMD_READID */
> ++static int read_id(struct qcom_nandc_data *this, int column)
> ++{
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      if (column == -1)
> ++              return 0;
> ++
> ++      regs->cmd = FETCH_ID;
> ++      regs->addr0 = column;
> ++      regs->addr1 = 0;
> ++      regs->chip_sel = DM_EN;
> ++      regs->exec = 1;
> ++
> ++      write_reg_dma(this, NAND_FLASH_CMD, 4);
> ++      write_reg_dma(this, NAND_EXEC_CMD, 1);
> ++
> ++      read_reg_dma(this, NAND_READ_ID, 1);
> ++
> ++      return 0;
> ++}
> ++
> ++/* sets up descriptors for NAND_CMD_RESET */
> ++static int reset(struct qcom_nandc_data *this)
> ++{
> ++      struct nandc_regs *regs = this->regs;
> ++
> ++      regs->cmd = RESET_DEVICE;
> ++      regs->exec = 1;
> ++
> ++      write_reg_dma(this, NAND_FLASH_CMD, 1);
> ++      write_reg_dma(this, NAND_EXEC_CMD, 1);
> ++
> ++      read_reg_dma(this, NAND_FLASH_STATUS, 1);
> ++
> ++      return 0;
> ++}
> ++
> ++/* helpers to submit/free our list of dma descriptors */
> ++static void dma_callback(void *param)
> ++{
> ++      struct qcom_nandc_data *this = param;
> ++      struct completion *c = &this->dma_done;
> ++
> ++      complete(c);
> ++}
> ++
> ++static int submit_descs(struct qcom_nandc_data *this)
> ++{
> ++      struct completion *c = &this->dma_done;
> ++      struct desc_info *desc;
> ++      int r;
> ++
> ++      init_completion(c);
> ++
> ++      list_for_each_entry(desc, &this->list, list) {
> ++              /*
> ++               * we add a callback to the last descriptor in our list to
> ++               * notify completion of command
> ++               */
> ++              if (list_is_last(&desc->list, &this->list)) {
> ++                      desc->dma_desc->callback = dma_callback;
> ++                      desc->dma_desc->callback_param = this;
> ++              }
> ++
> ++              dmaengine_submit(desc->dma_desc);
> ++      }
> ++
> ++      dma_async_issue_pending(this->chan);
> ++
> ++      r = wait_for_completion_timeout(c, msecs_to_jiffies(500));
> ++      if (!r)
> ++              return -ETIMEDOUT;
> ++
> ++      return 0;
> ++}
> ++
> ++static void free_descs(struct qcom_nandc_data *this)
> ++{
> ++      struct desc_info *desc, *n;
> ++
> ++      list_for_each_entry_safe(desc, n, &this->list, list) {
> ++              list_del(&desc->list);
> ++              dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir);
> ++              kfree(desc);
> ++      }
> ++}
> ++
> ++/* reset the register read buffer for next NAND operation */
> ++static void clear_read_regs(struct qcom_nandc_data *this)
> ++{
> ++      this->reg_read_pos = 0;
> ++      memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf));
> ++}
> ++
> ++static void pre_command(struct qcom_nandc_data *this, int command)
> ++{
> ++      this->buf_count = 0;
> ++      this->buf_start = 0;
> ++      this->use_ecc = false;
> ++      this->last_command = command;
> ++
> ++      clear_read_regs(this);
> ++}
> ++
> ++/*
> ++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
> ++ * privately maintained status byte, this status byte can be read after
> ++ * NAND_CMD_STATUS is called
> ++ */
> ++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command)
> ++{
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int num_cw;
> ++      int i;
> ++
> ++      num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
> ++
> ++      for (i = 0; i < num_cw; i++) {
> ++              __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]);
> ++
> ++              if (flash_status & FS_MPU_ERR)
> ++                      this->status &= ~NAND_STATUS_WP;
> ++
> ++              if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
> ++                              (flash_status & FS_DEVICE_STS_ERR)))
> ++                      this->status |= NAND_STATUS_FAIL;
> ++      }
> ++}
> ++
> ++static void post_command(struct qcom_nandc_data *this, int command)
> ++{
> ++      switch (command) {
> ++      case NAND_CMD_READID:
> ++              memcpy(this->data_buffer, this->reg_read_buf, this->buf_count);
> ++              break;
> ++      case NAND_CMD_PAGEPROG:
> ++      case NAND_CMD_ERASE1:
> ++              parse_erase_write_errors(this, command);
> ++              break;
> ++      default:
> ++              break;
> ++      }
> ++}
> ++
> ++/*
> ++ * Implements chip->cmdfunc. It's  only used for a limited set of commands.
> ++ * The rest of the commands wouldn't be called by upper layers. For example,
> ++ * NAND_CMD_READOOB would never be called because we have our own versions
> ++ * of read_oob ops for nand_ecc_ctrl.
> ++ */
> ++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
> ++                       int column, int page_addr)
> ++{
> ++      struct nand_chip *chip = mtd->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      bool wait = false;
> ++      int r = 0;
> ++
> ++      pre_command(this, command);
> ++
> ++      switch (command) {
> ++      case NAND_CMD_RESET:
> ++              r = reset(this);
> ++              wait = true;
> ++              break;
> ++
> ++      case NAND_CMD_READID:
> ++              this->buf_count = 4;
> ++              r = read_id(this, column);
> ++              wait = true;
> ++              break;
> ++
> ++      case NAND_CMD_PARAM:
> ++              r = nandc_param(this);
> ++              wait = true;
> ++              break;
> ++
> ++      case NAND_CMD_ERASE1:
> ++              r = erase_block(this, page_addr);
> ++              wait = true;
> ++              break;
> ++
> ++      case NAND_CMD_READ0:
> ++              /* we read the entire page for now */
> ++              WARN_ON(column != 0);
> ++
> ++              this->use_ecc = true;
> ++              set_address(this, 0, page_addr);
> ++              update_rw_regs(this, ecc->steps, true);
> ++              break;
> ++
> ++      case NAND_CMD_SEQIN:
> ++              WARN_ON(column != 0);
> ++              set_address(this, 0, page_addr);
> ++              break;
> ++
> ++      case NAND_CMD_PAGEPROG:
> ++      case NAND_CMD_STATUS:
> ++      case NAND_CMD_NONE:
> ++      default:
> ++              break;
> ++      }
> ++
> ++      if (r) {
> ++              dev_err(this->dev, "failure executing command %d\n",
> ++                      command);
> ++              free_descs(this);
> ++              return;
> ++      }
> ++
> ++      if (wait) {
> ++              r = submit_descs(this);
> ++              if (r)
> ++                      dev_err(this->dev,
> ++                              "failure submitting descs for command %d\n",
> ++                              command);
> ++      }
> ++
> ++      free_descs(this);
> ++
> ++      post_command(this, command);
> ++}
> ++
> ++/*
> ++ * when using RS ECC, the NAND controller flags an error when reading an
> ++ * erased page. however, there are special characters at certain offsets when
> ++ * we read the erased page. we check here if the page is really empty. if so,
> ++ * we replace the magic characters with 0xffs
> ++ */
> ++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf)
> ++{
> ++      struct mtd_info *mtd = &this->mtd;
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int cwperpage = ecc->steps;
> ++      u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS];
> ++      int i, j;
> ++
> ++      /* if BCH is enabled, HW will take care of detecting erased pages */
> ++      if (this->bch_enabled || !this->use_ecc)
> ++              return false;
> ++
> ++      for (i = 0; i < cwperpage; i++) {
> ++              u8 *empty1, *empty2;
> ++              __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]);
> ++
> ++              /*
> ++               * an erased page flags an error in NAND_FLASH_STATUS, check if
> ++               * the page is erased by looking for 0x54s at offsets 3 and 175
> ++               * from the beginning of each codeword
> ++               */
> ++              if (!(flash_status & FS_OP_ERR))
> ++                      break;
> ++
> ++              empty1 = &data_buf[3 + i * this->cw_data];
> ++              empty2 = &data_buf[175 + i * this->cw_data];
> ++
> ++              /*
> ++               * if the error wasn't because of an erased page, bail out and
> ++               * and let someone else do the error checking
> ++               */
> ++              if ((*empty1 == 0x54 && *empty2 == 0xff) ||
> ++                              (*empty1 == 0xff && *empty2 == 0x54)) {
> ++                      orig1[i] = *empty1;
> ++                      orig2[i] = *empty2;
> ++
> ++                      *empty1 = 0xff;
> ++                      *empty2 = 0xff;
> ++              } else {
> ++                      break;
> ++              }
> ++      }
> ++
> ++      if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize))
> ++              goto not_empty;
> ++
> ++      /*
> ++       * tell the caller that the page was empty and is fixed up, so that
> ++       * parse_read_errors() doesn't think it's an error
> ++       */
> ++      return true;
> ++
> ++not_empty:
> ++      /* restore original values if not empty*/
> ++      for (j = 0; j < i; j++) {
> ++              data_buf[3 + j * this->cw_data] = orig1[j];
> ++              data_buf[175 + j * this->cw_data] = orig2[j];
> ++      }
> ++
> ++      return false;
> ++}
> ++
> ++struct read_stats {
> ++      __le32 flash;
> ++      __le32 buffer;
> ++      __le32 erased_cw;
> ++};
> ++
> ++/*
> ++ * reads back status registers set by the controller to notify page read
> ++ * errors. this is equivalent to what 'ecc->correct()' would do.
> ++ */
> ++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page)
> ++{
> ++      struct mtd_info *mtd = &this->mtd;
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int cwperpage = ecc->steps;
> ++      unsigned int max_bitflips = 0;
> ++      int i;
> ++
> ++      for (i = 0; i < cwperpage; i++) {
> ++              int stat;
> ++              struct read_stats *buf;
> ++
> ++              buf = (struct read_stats *) (this->reg_read_buf + 3 * i);
> ++
> ++              buf->flash = le32_to_cpu(buf->flash);
> ++              buf->buffer = le32_to_cpu(buf->buffer);
> ++              buf->erased_cw = le32_to_cpu(buf->erased_cw);
> ++
> ++              if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) {
> ++
> ++                      /* ignore erased codeword errors */
> ++                      if (this->bch_enabled) {
> ++                              if ((buf->erased_cw & ERASED_CW) == ERASED_CW)
> ++                                      continue;
> ++                      } else if (erased_page) {
> ++                              continue;
> ++                      }
> ++
> ++                      if (buf->buffer & BS_UNCORRECTABLE_BIT) {
> ++                              mtd->ecc_stats.failed++;
> ++                              continue;
> ++                      }
> ++              }
> ++
> ++              stat = buf->buffer & BS_CORRECTABLE_ERR_MSK;
> ++              mtd->ecc_stats.corrected += stat;
> ++
> ++              max_bitflips = max_t(unsigned int, max_bitflips, stat);
> ++      }
> ++
> ++      return max_bitflips;
> ++}
> ++
> ++/*
> ++ * helper to perform the actual page read operation, used by ecc->read_page()
> ++ * and ecc->read_oob()
> ++ */
> ++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf,
> ++                       u8 *oob_buf)
> ++{
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int i, r;
> ++
> ++      /* queue cmd descs for each codeword */
> ++      for (i = 0; i < ecc->steps; i++) {
> ++              int data_size, oob_size;
> ++
> ++              if (i == (ecc->steps - 1)) {
> ++                      data_size = ecc->size - ((ecc->steps - 1) << 2);
> ++                      oob_size = (ecc->steps << 2) + ecc->bytes;
> ++              } else {
> ++                      data_size = this->cw_data;
> ++                      oob_size = ecc->bytes;
> ++              }
> ++
> ++              config_cw_read(this);
> ++
> ++              if (data_buf)
> ++                      read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
> ++
> ++              if (oob_buf)
> ++                      read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf,
> ++                                      oob_size);
> ++
> ++              if (data_buf)
> ++                      data_buf += data_size;
> ++              if (oob_buf)
> ++                      oob_buf += oob_size;
> ++      }
> ++
> ++      r = submit_descs(this);
> ++      if (r)
> ++              dev_err(this->dev, "failure to read page/oob\n");
> ++
> ++      free_descs(this);
> ++
> ++      return r;
> ++}
> ++
> ++/*
> ++ * a helper that copies the last step/codeword of a page (containing free oob)
> ++ * into our local buffer
> ++ */
> ++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page)
> ++{
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int size;
> ++      int r;
> ++
> ++      clear_read_regs(this);
> ++
> ++      size = use_ecc ? this->cw_data : this->cw_size;
> ++
> ++      /* prepare a clean read buffer */
> ++      memset(this->data_buffer, 0xff, size);
> ++
> ++      this->use_ecc = use_ecc;
> ++      set_address(this, this->cw_size * (ecc->steps - 1), page);
> ++      update_rw_regs(this, 1, true);
> ++
> ++      config_cw_read(this);
> ++
> ++      read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size);
> ++
> ++      r = submit_descs(this);
> ++      if (r)
> ++              dev_err(this->dev, "failed to copy last codeword\n");
> ++
> ++      free_descs(this);
> ++
> ++      return r;
> ++}
> ++
> ++/* implements ecc->read_page() */
> ++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
> ++                              uint8_t *buf, int oob_required, int page)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      u8 *data_buf, *oob_buf = NULL;
> ++      bool erased_page;
> ++      int r;
> ++
> ++      data_buf = buf;
> ++      oob_buf = oob_required ? chip->oob_poi : NULL;
> ++
> ++      r = read_page_low(this, data_buf, oob_buf);
> ++      if (r) {
> ++              dev_err(this->dev, "failure to read page\n");
> ++              return r;
> ++      }
> ++
> ++      erased_page = empty_page_fixup(this, data_buf);
> ++
> ++      return parse_read_errors(this, erased_page);
> ++}
> ++
> ++/* implements ecc->read_oob() */
> ++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
> ++                             int page)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int r;
> ++
> ++      clear_read_regs(this);
> ++
> ++      this->use_ecc = true;
> ++      set_address(this, 0, page);
> ++      update_rw_regs(this, ecc->steps, true);
> ++
> ++      r = read_page_low(this, NULL, chip->oob_poi);
> ++      if (r)
> ++              dev_err(this->dev, "failure to read oob\n");
> ++
> ++      return r;
> ++}
> ++
> ++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */
> ++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
> ++                                 int page)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      uint8_t *oob = chip->oob_poi;
> ++      int start, length;
> ++      int r;
> ++
> ++      /*
> ++       * configure registers for a raw page read, the address is set to the
> ++       * beginning of the last codeword, we don't care about reading ecc
> ++       * portion of oob, just the free stuff
> ++       */
> ++      r = copy_last_cw(this, false, page);
> ++      if (r)
> ++              return r;
> ++
> ++      /*
> ++       * reading raw oob has 2 parts, first the bad block byte, then the
> ++       * actual free oob region. perform a memcpy in two steps
> ++       */
> ++      start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
> ++      length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
> ++
> ++      memcpy(oob, this->data_buffer + start, length);
> ++
> ++      oob += length;
> ++
> ++      start = this->cw_data - (ecc->steps << 2) + 1;
> ++      length = ecc->steps << 2;
> ++
> ++      memcpy(oob, this->data_buffer + start, length);
> ++
> ++      return 0;
> ++}
> ++
> ++/* implements ecc->write_page() */
> ++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
> ++                               const uint8_t *buf, int oob_required)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      u8 *data_buf, *oob_buf;
> ++      int i, r = 0;
> ++
> ++      clear_read_regs(this);
> ++
> ++      data_buf = (u8 *) buf;
> ++      oob_buf = chip->oob_poi;
> ++
> ++      this->use_ecc = true;
> ++      update_rw_regs(this, ecc->steps, false);
> ++
> ++      for (i = 0; i < ecc->steps; i++) {
> ++              int data_size, oob_size;
> ++
> ++              if (i == (ecc->steps - 1)) {
> ++                      data_size = ecc->size - ((ecc->steps - 1) << 2);
> ++                      oob_size = (ecc->steps << 2) + ecc->bytes;
> ++              } else {
> ++                      data_size = this->cw_data;
> ++                      oob_size = ecc->bytes;
> ++              }
> ++
> ++              config_cw_write_pre(this);
> ++              write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
> ++
> ++              /*
> ++               * we don't really need to write anything to oob for the
> ++               * first n - 1 codewords since these oob regions just
> ++               * contain ecc that's written by the controller itself
> ++               */
> ++              if (i == (ecc->steps - 1))
> ++                      write_data_dma(this, FLASH_BUF_ACC + data_size,
> ++                                      oob_buf, oob_size);
> ++              config_cw_write_post(this);
> ++
> ++              data_buf += data_size;
> ++              oob_buf += oob_size;
> ++      }
> ++
> ++      r = submit_descs(this);
> ++      if (r)
> ++              dev_err(this->dev, "failure to write page\n");
> ++
> ++      free_descs(this);
> ++
> ++      return r;
> ++}
> ++
> ++/*
> ++ * implements ecc->write_oob()
> ++ *
> ++ * the NAND controller cannot write only data or only oob within a codeword,
> ++ * since ecc is calculated for the combined codeword. we first copy the
> ++ * entire contents for the last codeword(data + oob), replace the old oob
> ++ * with the new one in chip->oob_poi, and then write the entire codeword.
> ++ * this read-copy-write operation results in a slight perormance loss.
> ++ */
> ++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
> ++                              int page)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      uint8_t *oob = chip->oob_poi;
> ++      int free_boff;
> ++      int data_size, oob_size;
> ++      int r, status = 0;
> ++
> ++      r = copy_last_cw(this, true, page);
> ++      if (r)
> ++              return r;
> ++
> ++      clear_read_regs(this);
> ++
> ++      /* calculate the data and oob size for the last codeword/step */
> ++      data_size = ecc->size - ((ecc->steps - 1) << 2);
> ++      oob_size = (ecc->steps << 2) + ecc->bytes;
> ++
> ++      /*
> ++       * the location of spare data in the oob buffer, we could also use
> ++       * ecc->layout.oobfree here
> ++       */
> ++      free_boff = ecc->bytes * (ecc->steps - 1);
> ++
> ++      /* override new oob content to last codeword */
> ++      memcpy(this->data_buffer + data_size, oob + free_boff, oob_size);
> ++
> ++      this->use_ecc = true;
> ++      set_address(this, this->cw_size * (ecc->steps - 1), page);
> ++      update_rw_regs(this, 1, false);
> ++
> ++      config_cw_write_pre(this);
> ++      write_data_dma(this, FLASH_BUF_ACC, this->data_buffer,
> ++              data_size + oob_size);
> ++      config_cw_write_post(this);
> ++
> ++      r = submit_descs(this);
> ++
> ++      free_descs(this);
> ++
> ++      if (r) {
> ++              dev_err(this->dev, "failure to write oob\n");
> ++              return -EIO;
> ++      }
> ++
> ++      chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
> ++
> ++      status = chip->waitfunc(mtd, chip);
> ++
> ++      return status & NAND_STATUS_FAIL ? -EIO : 0;
> ++}
> ++
> ++/* implements ecc->write_oob_raw(), used to write bad block marker flag */
> ++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd,
> ++                                  struct nand_chip *chip, int page)
> ++{
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      uint8_t *oob = chip->oob_poi;
> ++      int start, length;
> ++      int r, status = 0;
> ++
> ++      r = copy_last_cw(this, false, page);
> ++      if (r)
> ++              return r;
> ++
> ++      clear_read_regs(this);
> ++
> ++      /*
> ++       * writing raw oob has 2 parts, first the bad block region, then the
> ++       * actual free region
> ++       */
> ++      start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
> ++      length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
> ++
> ++      memcpy(this->data_buffer + start, oob, length);
> ++
> ++      oob += length;
> ++
> ++      start = this->cw_data - (ecc->steps << 2) + 1;
> ++      length = ecc->steps << 2;
> ++
> ++      memcpy(this->data_buffer + start, oob, length);
> ++
> ++      /* prepare write */
> ++      this->use_ecc = false;
> ++      set_address(this, this->cw_size * (ecc->steps - 1), page);
> ++      update_rw_regs(this, 1, false);
> ++
> ++      config_cw_write_pre(this);
> ++      write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size);
> ++      config_cw_write_post(this);
> ++
> ++      r = submit_descs(this);
> ++
> ++      free_descs(this);
> ++
> ++      if (r) {
> ++              dev_err(this->dev, "failure to write updated oob\n");
> ++              return -EIO;
> ++      }
> ++
> ++      chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
> ++
> ++      status = chip->waitfunc(mtd, chip);
> ++
> ++      return status & NAND_STATUS_FAIL ? -EIO : 0;
> ++}
> ++
> ++/*
> ++ * the three functions below implement chip->read_byte(), chip->read_buf()
> ++ * and chip->write_buf() respectively. these aren't used for
> ++ * reading/writing page data, they are used for smaller data like reading
> ++ * id, status etc
> ++ */
> ++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
> ++{
> ++      struct nand_chip *chip = mtd->priv;
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      uint8_t *buf = this->data_buffer;
> ++      uint8_t ret = 0x0;
> ++
> ++      if (this->last_command == NAND_CMD_STATUS) {
> ++              ret = this->status;
> ++
> ++              this->status = NAND_STATUS_READY | NAND_STATUS_WP;
> ++
> ++              return ret;
> ++      }
> ++
> ++      if (this->buf_start < this->buf_count)
> ++              ret = buf[this->buf_start++];
> ++
> ++      return ret;
> ++}
> ++
> ++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> ++{
> ++      struct nand_chip *chip = mtd->priv;
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
> ++
> ++      memcpy(buf, this->data_buffer + this->buf_start, real_len);
> ++      this->buf_start += real_len;
> ++}
> ++
> ++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
> ++              int len)
> ++{
> ++      struct nand_chip *chip = mtd->priv;
> ++      struct qcom_nandc_data *this = chip->priv;
> ++      int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
> ++
> ++      memcpy(this->data_buffer + this->buf_start, buf, real_len);
> ++
> ++      this->buf_start += real_len;
> ++}
> ++
> ++/* we support only one external chip for now */
> ++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
> ++{
> ++      struct nand_chip *chip = mtd->priv;
> ++      struct qcom_nandc_data *this = chip->priv;
> ++
> ++      if (chipnr <= 0)
> ++              return;
> ++
> ++      dev_warn(this->dev, "invalid chip select\n");
> ++}
> ++
> ++/*
> ++ * NAND controller page layout info
> ++ *
> ++ * |-----------------------|    |---------------------------------|
> ++ * |          xx.......xx|      |             *********xx.......xx|
> ++ * |  DATA    xx..ECC..xx|      |     DATA    **SPARE**xx..ECC..xx|
> ++ * |   (516)  xx.......xx|      |  (516-n*4)  **(n*4)**xx.......xx|
> ++ * |          xx.......xx|      |             *********xx.......xx|
> ++ * |-----------------------|    |---------------------------------|
> ++ *     codeword 1,2..n-1                      codeword n
> ++ *  <---(528/532 Bytes)---->     <-------(528/532 Bytes)---------->
> ++ *
> ++ * n = number of codewords in the page
> ++ * . = ECC bytes
> ++ * * = spare bytes
> ++ * x = unused/reserved bytes
> ++ *
> ++ * 2K page: n = 4, spare = 16 bytes
> ++ * 4K page: n = 8, spare = 32 bytes
> ++ * 8K page: n = 16, spare = 64 bytes
> ++ *
> ++ * the qcom nand controller operates at a sub page/codeword level. each
> ++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
> ++ * the number of ECC bytes vary based on the ECC strength and the bus width.
> ++ *
> ++ * the first n - 1 codewords contains 516 bytes of user data, the remaining
> ++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
> ++ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
> ++ *
> ++ * the layout described above is used by the controller when the ECC block is
> ++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are
> ++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout
> ++ * layouts defined below doesn't consider the positions occupied by the reserved
> ++ * bytes
> ++ *
> ++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width)
> ++ * in the last codeword is the position of bad block marker. the bad block
> ++ * marker cannot be accessed when ECC is enabled.
> ++ *
> ++ */
> ++
> ++/*
> ++ * Layouts for different page sizes and ecc modes. We skip the eccpos field
> ++ * since it isn't needed for this driver
> ++ */
> ++
> ++/* 2K page, 4 bit ECC */
> ++static struct nand_ecclayout layout_oob_64 = {
> ++      .eccbytes       = 40,
> ++      .oobfree        = {
> ++                              { 30, 16 },
> ++                        },
> ++};
> ++
> ++/* 4K page, 4 bit ECC, 8/16 bit bus width */
> ++static struct nand_ecclayout layout_oob_128 = {
> ++      .eccbytes       = 80,
> ++      .oobfree        = {
> ++                              { 70, 32 },
> ++                        },
> ++};
> ++
> ++/* 4K page, 8 bit ECC, 8 bit bus width */
> ++static struct nand_ecclayout layout_oob_224_x8 = {
> ++      .eccbytes       = 104,
> ++      .oobfree        = {
> ++                              { 91, 32 },
> ++                        },
> ++};
> ++
> ++/* 4K page, 8 bit ECC, 16 bit bus width */
> ++static struct nand_ecclayout layout_oob_224_x16 = {
> ++      .eccbytes       = 112,
> ++      .oobfree        = {
> ++                              { 98, 32 },
> ++                        },
> ++};
> ++
> ++/* 8K page, 4 bit ECC, 8/16 bit bus width */
> ++static struct nand_ecclayout layout_oob_256 = {
> ++      .eccbytes       = 160,
> ++      .oobfree        = {
> ++                              { 151, 64 },
> ++                        },
> ++};
> ++
> ++/*
> ++ * this is called before scan_ident, we do some minimal configurations so
> ++ * that reading ID and ONFI params work
> ++ */
> ++static void qcom_nandc_pre_init(struct qcom_nandc_data *this)
> ++{
> ++      /* kill onenand */
> ++      nandc_write(this, SFLASHC_BURST_CFG, 0);
> ++
> ++      /* enable ADM DMA */
> ++      nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN);
> ++
> ++      /* save the original values of these registers */
> ++      this->cmd1 = nandc_read(this, NAND_DEV_CMD1);
> ++      this->vld = nandc_read(this, NAND_DEV_CMD_VLD);
> ++
> ++      /* initial status value */
> ++      this->status = NAND_STATUS_READY | NAND_STATUS_WP;
> ++}
> ++
> ++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this)
> ++{
> ++      struct mtd_info *mtd = &this->mtd;
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int cwperpage;
> ++      bool wide_bus;
> ++
> ++      /* the nand controller fetches codewords/chunks of 512 bytes */
> ++      cwperpage = mtd->writesize >> 9;
> ++
> ++      ecc->strength = this->ecc_strength;
> ++
> ++      wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
> ++
> ++      if (ecc->strength >= 8) {
> ++              /* 8 bit ECC defaults to BCH ECC on all platforms */
> ++              ecc->bytes = wide_bus ? 14 : 13;
> ++      } else {
> ++              /*
> ++               * if the controller supports BCH for 4 bit ECC, the controller
> ++               * uses lesser bytes for ECC. If RS is used, the ECC bytes is
> ++               * always 10 bytes
> ++               */
> ++              if (this->ecc_modes & ECC_BCH_4BIT)
> ++                      ecc->bytes = wide_bus ? 8 : 7;
> ++              else
> ++                      ecc->bytes = 10;
> ++      }
> ++
> ++      /* each step consists of 512 bytes of data */
> ++      ecc->size = NANDC_STEP_SIZE;
> ++
> ++      ecc->read_page          = qcom_nandc_read_page;
> ++      ecc->read_oob           = qcom_nandc_read_oob;
> ++      ecc->write_page         = qcom_nandc_write_page;
> ++      ecc->write_oob          = qcom_nandc_write_oob;
> ++
> ++      /*
> ++       * the bad block marker is readable only when we read the page with ECC
> ++       * disabled. all the ops above run with ECC enabled. We need raw read
> ++       * and write function for oob in order to access bad block marker.
> ++       */
> ++      ecc->read_oob_raw       = qcom_nandc_read_oob_raw;
> ++      ecc->write_oob_raw      = qcom_nandc_write_oob_raw;
> ++
> ++      switch (mtd->oobsize) {
> ++      case 64:
> ++              ecc->layout = &layout_oob_64;
> ++              break;
> ++      case 128:
> ++              ecc->layout = &layout_oob_128;
> ++              break;
> ++      case 224:
> ++              if (wide_bus)
> ++                      ecc->layout = &layout_oob_224_x16;
> ++              else
> ++                      ecc->layout = &layout_oob_224_x8;
> ++              break;
> ++      case 256:
> ++              ecc->layout = &layout_oob_256;
> ++              break;
> ++      default:
> ++              dev_err(this->dev, "unsupported NAND device, oobsize %d\n",
> ++                      mtd->oobsize);
> ++              return -ENODEV;
> ++      }
> ++
> ++      ecc->mode = NAND_ECC_HW;
> ++
> ++      /* enable ecc by default */
> ++      this->use_ecc = true;
> ++
> ++      return 0;
> ++}
> ++
> ++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this)
> ++{
> ++      struct mtd_info *mtd = &this->mtd;
> ++      struct nand_chip *chip = &this->chip;
> ++      struct nand_ecc_ctrl *ecc = &chip->ecc;
> ++      int cwperpage = mtd->writesize / ecc->size;
> ++      int spare_bytes, bad_block_byte;
> ++      bool wide_bus;
> ++      int ecc_mode = 0;
> ++
> ++      wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
> ++
> ++      if (ecc->strength >= 8) {
> ++              this->cw_size = 532;
> ++
> ++              spare_bytes = wide_bus ? 0 : 2;
> ++
> ++              this->bch_enabled = true;
> ++              ecc_mode = 1;
> ++      } else {
> ++              this->cw_size = 528;
> ++
> ++              if (this->ecc_modes & ECC_BCH_4BIT) {
> ++                      spare_bytes = wide_bus ? 2 : 4;
> ++
> ++                      this->bch_enabled = true;
> ++                      ecc_mode = 0;
> ++              } else {
> ++                      spare_bytes = wide_bus ? 0 : 1;
> ++              }
> ++      }
> ++
> ++      /*
> ++       * DATA_UD_BYTES varies based on whether the read/write command protects
> ++       * spare data with ECC too. We protect spare data by default, so we set
> ++       * it to main + spare data, which are 512 and 4 bytes respectively.
> ++       */
> ++      this->cw_data = 516;
> ++
> ++      bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1;
> ++
> ++      this->cfg0 = (cwperpage - 1) << CW_PER_PAGE
> ++                              | this->cw_data << UD_SIZE_BYTES
> ++                              | 0 << DISABLE_STATUS_AFTER_WRITE
> ++                              | 5 << NUM_ADDR_CYCLES
> ++                              | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS
> ++                              | 0 << STATUS_BFR_READ
> ++                              | 1 << SET_RD_MODE_AFTER_STATUS
> ++                              | spare_bytes << SPARE_SIZE_BYTES;
> ++
> ++      this->cfg1 = 7 << NAND_RECOVERY_CYCLES
> ++                              | 0 <<  CS_ACTIVE_BSY
> ++                              | bad_block_byte << BAD_BLOCK_BYTE_NUM
> ++                              | 0 << BAD_BLOCK_IN_SPARE_AREA
> ++                              | 2 << WR_RD_BSY_GAP
> ++                              | wide_bus << WIDE_FLASH
> ++                              | this->bch_enabled << ENABLE_BCH_ECC;
> ++
> ++      this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
> ++                              | this->cw_size << UD_SIZE_BYTES
> ++                              | 5 << NUM_ADDR_CYCLES
> ++                              | 0 << SPARE_SIZE_BYTES;
> ++
> ++      this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
> ++                              | 0 << CS_ACTIVE_BSY
> ++                              | 17 << BAD_BLOCK_BYTE_NUM
> ++                              | 1 << BAD_BLOCK_IN_SPARE_AREA
> ++                              | 2 << WR_RD_BSY_GAP
> ++                              | wide_bus << WIDE_FLASH
> ++                              | 1 << DEV0_CFG1_ECC_DISABLE;
> ++
> ++      this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE
> ++                              | 0 << ECC_SW_RESET
> ++                              | this->cw_data << ECC_NUM_DATA_BYTES
> ++                              | 1 << ECC_FORCE_CLK_OPEN
> ++                              | ecc_mode << ECC_MODE
> ++                              | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH;
> ++
> ++      this->ecc_buf_cfg = 0x203 << NUM_STEPS;
> ++
> ++      this->clrflashstatus = FS_READY_BSY_N;
> ++      this->clrreadstatus = 0xc0;
> ++
> ++      dev_dbg(this->dev,
> ++              "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
> ++              this->cfg0, this->cfg1, this->ecc_buf_cfg,
> ++              this->ecc_bch_cfg, this->cw_size, this->cw_data,
> ++              ecc->strength, ecc->bytes, cwperpage);
> ++}
> ++
> ++static int qcom_nandc_alloc(struct qcom_nandc_data *this)
> ++{
> ++      int r;
> ++
> ++      r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32));
> ++      if (r) {
> ++              dev_err(this->dev, "failed to set DMA mask\n");
> ++              return r;
> ++      }
> ++
> ++      /*
> ++       * we use the internal buffer for reading ONFI params, reading small
> ++       * data like ID and status, and preforming read-copy-write operations
> ++       * when writing to a codeword partially. 532 is the maximum possible
> ++       * size of a codeword for our nand controller
> ++       */
> ++      this->buf_size = 532;
> ++
> ++      this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL);
> ++      if (!this->data_buffer)
> ++              return -ENOMEM;
> ++
> ++      this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL);
> ++      if (!this->regs)
> ++              return -ENOMEM;
> ++
> ++      this->reg_read_buf = devm_kzalloc(this->dev,
> ++                              MAX_REG_RD * sizeof(*this->reg_read_buf),
> ++                              GFP_KERNEL);
> ++      if (!this->reg_read_buf)
> ++              return -ENOMEM;
> ++
> ++      INIT_LIST_HEAD(&this->list);
> ++
> ++      this->chan = dma_request_slave_channel(this->dev, "rxtx");
> ++      if (!this->chan) {
> ++              dev_err(this->dev, "failed to request slave channel\n");
> ++              return -ENODEV;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static void qcom_nandc_unalloc(struct qcom_nandc_data *this)
> ++{
> ++      dma_release_channel(this->chan);
> ++}
> ++
> ++static int qcom_nandc_init(struct qcom_nandc_data *this)
> ++{
> ++      struct mtd_info *mtd = &this->mtd;
> ++      struct nand_chip *chip = &this->chip;
> ++      struct device_node *np = this->dev->of_node;
> ++      struct mtd_part_parser_data ppdata = { .of_node = np };
> ++      int r;
> ++
> ++      mtd->priv = chip;
> ++      mtd->name = "qcom-nandc";
> ++      mtd->owner = THIS_MODULE;
> ++
> ++      chip->priv = this;
> ++
> ++      chip->cmdfunc           = qcom_nandc_command;
> ++      chip->select_chip       = qcom_nandc_select_chip;
> ++      chip->read_byte         = qcom_nandc_read_byte;
> ++      chip->read_buf          = qcom_nandc_read_buf;
> ++      chip->write_buf         = qcom_nandc_write_buf;
> ++
> ++      chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
> ++      if (this->bus_width == 16)
> ++              chip->options |= NAND_BUSWIDTH_16;
> ++
> ++      chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW;
> ++      if (of_get_nand_on_flash_bbt(np))
> ++              chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
> ++
> ++      qcom_nandc_pre_init(this);
> ++
> ++      r = nand_scan_ident(mtd, 1, NULL);
> ++      if (r)
> ++              return r;
> ++
> ++      r = qcom_nandc_ecc_init(this);
> ++      if (r)
> ++              return r;
> ++
> ++      qcom_nandc_hw_post_init(this);
> ++
> ++      r = nand_scan_tail(mtd);
> ++      if (r)
> ++              return r;
> ++
> ++      return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
> ++}
> ++
> ++static int qcom_nandc_parse_dt(struct platform_device *pdev)
> ++{
> ++      struct qcom_nandc_data *this = platform_get_drvdata(pdev);
> ++      struct device_node *np = this->dev->of_node;
> ++      int r;
> ++
> ++      this->ecc_strength = of_get_nand_ecc_strength(np);
> ++      if (this->ecc_strength < 0) {
> ++              dev_warn(this->dev,
> ++                      "incorrect ecc strength, setting to 4 bits/step\n");
> ++              this->ecc_strength = 4;
> ++      }
> ++
> ++      this->bus_width = of_get_nand_bus_width(np);
> ++      if (this->bus_width < 0) {
> ++              dev_warn(this->dev, "incorrect bus width, setting to 8\n");
> ++              this->bus_width = 8;
> ++      }
> ++
> ++      r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci);
> ++      if (r) {
> ++              dev_err(this->dev, "command CRCI unspecified\n");
> ++              return r;
> ++      }
> ++
> ++      r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci);
> ++      if (r) {
> ++              dev_err(this->dev, "data CRCI unspecified\n");
> ++              return r;
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> ++static int qcom_nandc_probe(struct platform_device *pdev)
> ++{
> ++      struct qcom_nandc_data *this;
> ++      const struct of_device_id *match;
> ++      int r;
> ++
> ++      this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
> ++      if (!this)
> ++              return -ENOMEM;
> ++
> ++      platform_set_drvdata(pdev, this);
> ++
> ++      this->pdev = pdev;
> ++      this->dev  = &pdev->dev;
> ++
> ++      match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
> ++      if (!match) {
> ++              dev_err(&pdev->dev, "failed to match device\n");
> ++              return -ENODEV;
> ++      }
> ++
> ++      if (!match->data) {
> ++              dev_err(&pdev->dev, "failed to get device data\n");
> ++              return -ENODEV;
> ++      }
> ++
> ++      this->ecc_modes = (u32) match->data;
> ++
> ++      this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      this->base = devm_ioremap_resource(&pdev->dev, this->res);
> ++      if (IS_ERR(this->base))
> ++              return PTR_ERR(this->base);
> ++
> ++      this->core_clk = devm_clk_get(&pdev->dev, "core");
> ++      if (IS_ERR(this->core_clk))
> ++              return PTR_ERR(this->core_clk);
> ++
> ++      this->aon_clk = devm_clk_get(&pdev->dev, "aon");
> ++      if (IS_ERR(this->aon_clk))
> ++              return PTR_ERR(this->aon_clk);
> ++
> ++      r = qcom_nandc_parse_dt(pdev);
> ++      if (r)
> ++              return r;
> ++
> ++      r = qcom_nandc_alloc(this);
> ++      if (r)
> ++              return r;
> ++
> ++      r = clk_prepare_enable(this->core_clk);
> ++      if (r)
> ++              goto err_core_clk;
> ++
> ++      r = clk_prepare_enable(this->aon_clk);
> ++      if (r)
> ++              goto err_aon_clk;
> ++
> ++      r = qcom_nandc_init(this);
> ++      if (r)
> ++              goto err_init;
> ++
> ++      return 0;
> ++
> ++err_init:
> ++      clk_disable_unprepare(this->aon_clk);
> ++err_aon_clk:
> ++      clk_disable_unprepare(this->core_clk);
> ++err_core_clk:
> ++      qcom_nandc_unalloc(this);
> ++
> ++      return r;
> ++}
> ++
> ++static int qcom_nandc_remove(struct platform_device *pdev)
> ++{
> ++      struct qcom_nandc_data *this = platform_get_drvdata(pdev);
> ++
> ++      qcom_nandc_unalloc(this);
> ++
> ++      clk_disable_unprepare(this->aon_clk);
> ++      clk_disable_unprepare(this->core_clk);
> ++
> ++      return 0;
> ++}
> ++
> ++#define EBI2_NANDC_ECC_MODES  (ECC_RS_4BIT | ECC_BCH_8BIT)
> ++
> ++/*
> ++ * data will hold a struct pointer containing more differences once we support
> ++ * more IPs
> ++ */
> ++static const struct of_device_id qcom_nandc_of_match[] = {
> ++      {       .compatible = "qcom,ebi2-nandc",
> ++              .data = (void *) EBI2_NANDC_ECC_MODES,
> ++      },
> ++      {}
> ++};
> ++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
> ++
> ++static struct platform_driver qcom_nandc_driver = {
> ++      .driver = {
> ++              .name = "qcom-nandc",
> ++              .of_match_table = qcom_nandc_of_match,
> ++      },
> ++      .probe   = qcom_nandc_probe,
> ++      .remove  = qcom_nandc_remove,
> ++};
> ++module_platform_driver(qcom_nandc_driver);
> ++
> ++MODULE_AUTHOR("Archit Taneja <architt at codeaurora.org>");
> ++MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
> ++MODULE_LICENSE("GPL v2");
> +--- a/drivers/mtd/nand/Makefile
> ++++ b/drivers/mtd/nand/Makefile
> +@@ -55,5 +55,6 @@
> + obj-$(CONFIG_MTD_NAND_SUNXI)          += sunxi_nand.o
> + obj-$(CONFIG_MTD_NAND_HISI504)                += hisi504_nand.o
> + obj-$(CONFIG_MTD_NAND_BRCMNAND)               += brcmnand/
> ++obj-$(CONFIG_MTD_NAND_QCOM)            += qcom_nandc.o
> +
> + nand-objs := nand_base.o nand_bbt.o nand_timings.o
> diff --git a/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
> new file mode 100644
> index 0000000..6530eb1
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
> @@ -0,0 +1,82 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings
> +From: Archit Taneja <architt at codeaurora.org>
> +X-Patchwork-Id: 6927141
> +Message-Id: <1438578498-32254-4-git-send-email-architt at codeaurora.org>
> +To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
> +       cernekee at gmail.com, computersforpeace at gmail.com
> +Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
> +       sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
> +       Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
> +Date: Mon,  3 Aug 2015 10:38:16 +0530
> +
> +Add DT bindings document for the Qualcomm NAND controller driver.
> +
> +Cc: devicetree at vger.kernel.org
> +
> +v3:
> +- Don't use '0x' when specifying nand controller address space
> +- Add optional property for on-flash bbt usage
> +
> +Acked-by: Andy Gross <agross at codeaurora.org>
> +Signed-off-by: Archit Taneja <architt at codeaurora.org>
> +
> +---
> +.../devicetree/bindings/mtd/qcom_nandc.txt         | 49 ++++++++++++++++++++++
> + 1 file changed, 49 insertions(+)
> + create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt
> +
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
> +@@ -0,0 +1,49 @@
> ++* Qualcomm NAND controller
> ++
> ++Required properties:
> ++- compatible:         should be "qcom,ebi2-nand" for IPQ806x
> ++- reg:                        MMIO address range
> ++- clocks:             must contain core clock and always on clock
> ++- clock-names:                must contain "core" for the core clock and "aon" for the
> ++                      always on clock
> ++- dmas:                       DMA specifier, consisting of a phandle to the ADM DMA
> ++                      controller node and the channel number to be used for
> ++                      NAND. Refer to dma.txt and qcom_adm.txt for more details
> ++- dma-names:          must be "rxtx"
> ++- qcom,cmd-crci:      must contain the ADM command type CRCI block instance
> ++                      number specified for the NAND controller on the given
> ++                      platform
> ++- qcom,data-crci:     must contain the ADM data type CRCI block instance
> ++                      number specified for the NAND controller on the given
> ++                      platform
> ++
> ++Optional properties:
> ++- nand-bus-width:     bus width. Must be 8 or 16. If not present, 8 is chosen
> ++                      as default
> ++
> ++- nand-ecc-strength:  number of bits to correct per ECC step. Must be 4 or 8
> ++                      bits. If not present, 4 is chosen as default
> ++- nand-on-flash-bbt:  Create/use on-flash bad block table
> ++
> ++The device tree may optionally contain sub-nodes describing partitions of the
> ++address space. See partition.txt for more detail.
> ++
> ++Example:
> ++
> ++nand at 1ac00000 {
> ++      compatible = "qcom,ebi2-nandc";
> ++      reg = <0x1ac00000 0x800>;
> ++
> ++      clocks = <&gcc EBI2_CLK>,
> ++               <&gcc EBI2_AON_CLK>;
> ++      clock-names = "core", "aon";
> ++
> ++      dmas = <&adm_dma 3>;
> ++      dma-names = "rxtx";
> ++      qcom,cmd-crci = <15>;
> ++      qcom,data-crci = <3>;
> ++
> ++      partition at 0 {
> ++      ...
> ++      };
> ++};
> diff --git a/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
> new file mode 100644
> index 0000000..5b1bb4b
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
> @@ -0,0 +1,51 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x
> +From: Archit Taneja <architt at codeaurora.org>
> +X-Patchwork-Id: 6927121
> +Message-Id: <1438578498-32254-5-git-send-email-architt at codeaurora.org>
> +To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
> +       cernekee at gmail.com, computersforpeace at gmail.com
> +Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
> +       sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
> +       Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
> +Date: Mon,  3 Aug 2015 10:38:17 +0530
> +
> +The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding
> +compatible string.
> +
> +Cc: devicetree at vger.kernel.org
> +
> +Reviewed-by: Andy Gross <agross at codeaurora.org>
> +Signed-off-by: Archit Taneja <architt at codeaurora.org>
> +
> +---
> +arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++
> + 1 file changed, 15 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +@@ -737,6 +737,22 @@
> +
> +                       status = "disabled";
> +               };
> ++
> ++              nand at 1ac00000 {
> ++                      compatible = "qcom,ebi2-nandc";
> ++                      reg = <0x1ac00000 0x800>;
> ++
> ++                      clocks = <&gcc EBI2_CLK>,
> ++                               <&gcc EBI2_AON_CLK>;
> ++                      clock-names = "core", "aon";
> ++
> ++                      dmas = <&adm_dma 3>;
> ++                      dma-names = "rxtx";
> ++                      qcom,cmd-crci = <15>;
> ++                      qcom,data-crci = <3>;
> ++
> ++                      status = "disabled";
> ++              };
> +       };
> +
> +       sfpb_mutex: sfpb-mutex {
> diff --git a/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
> new file mode 100644
> index 0000000..9de3d8f
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
> @@ -0,0 +1,76 @@
> +Content-Type: text/plain; charset="utf-8"
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 7bit
> +Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform
> +From: Archit Taneja <architt at codeaurora.org>
> +X-Patchwork-Id: 6927091
> +Message-Id: <1438578498-32254-6-git-send-email-architt at codeaurora.org>
> +To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
> +       cernekee at gmail.com, computersforpeace at gmail.com
> +Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
> +       sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
> +       Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
> +Date: Mon,  3 Aug 2015 10:38:18 +0530
> +
> +Enable the NAND controller node on the AP148 platform. Provide pinmux
> +information.
> +
> +Cc: devicetree at vger.kernel.org
> +
> +Signed-off-by: Archit Taneja <architt at codeaurora.org>
> +
> +---
> +arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++
> + 1 file changed, 36 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -43,6 +43,28 @@
> +                                       bias-none;
> +                               };
> +                       };
> ++                      nand_pins: nand_pins {
> ++                              mux {
> ++                                      pins = "gpio34", "gpio35", "gpio36",
> ++                                             "gpio37", "gpio38", "gpio39",
> ++                                             "gpio40", "gpio41", "gpio42",
> ++                                             "gpio43", "gpio44", "gpio45",
> ++                                             "gpio46", "gpio47";
> ++                                      function = "nand";
> ++                                      drive-strength = <10>;
> ++                                      bias-disable;
> ++                              };
> ++                              pullups {
> ++                                      pins = "gpio39";
> ++                                      bias-pull-up;
> ++                              };
> ++                              hold {
> ++                                      pins = "gpio40", "gpio41", "gpio42",
> ++                                             "gpio43", "gpio44", "gpio45",
> ++                                             "gpio46", "gpio47";
> ++                                      bias-bus-hold;
> ++                              };
> ++                      };
> +               };
> +
> +               gsbi at 16300000 {
> +@@ -126,5 +148,19 @@
> +                       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>;
> ++              };
> +       };
> + };
> ++
> ++&adm_dma {
> ++      status = "ok";
> ++};
> diff --git a/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
> new file mode 100644
> index 0000000..7060282
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch
> @@ -0,0 +1,11 @@
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -157,6 +157,8 @@
> +
> +                       nand-ecc-strength = <4>;
> +                       nand-bus-width = <8>;
> ++
> ++                      linux,part-probe = "qcom-smem";
> +               };
> +       };
> + };
> diff --git a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
> new file mode 100644
> index 0000000..c0281ff
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch
> @@ -0,0 +1,62 @@
> +From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Wed, 29 Apr 2015 15:21:46 -0700
> +Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom
> +
> +ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
> +on most ARM architectures. This automatically calculate ZRELADDR by
> +masking PHYS_OFFSET with 0xf8000000.
> +
> +However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware
> +network accelerators, and the bootloader removes this section from the
> +layout passed from the ATAGS (when used).
> +
> +For newer bootloader, when DT is used, this is not a problem, we just
> +reserve this memory in the device tree. But if the bootloader doesn't
> +have DT support, then ATAGS have to be used. In this case, the ARM
> +decompressor will position the kernel in this low mem, which will not be
> +in the RAM section mapped by the bootloader, which means the kernel will
> +freeze in the middle of the boot process trying to map the memory.
> +
> +As a work around, this patch allows disabling AUTO_ZRELADDR when
> +ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders
> +which don't support device-tree, which is the case on certain early
> +IPQ806x based designs.
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/Kconfig                 | 2 +-
> + arch/arm/Makefile                | 2 ++
> + arch/arm/mach-qcom/Makefile.boot | 1 +
> + 3 files changed, 4 insertions(+), 1 deletion(-)
> + create mode 100644 arch/arm/mach-qcom/Makefile.boot
> +
> +--- a/arch/arm/Kconfig
> ++++ b/arch/arm/Kconfig
> +@@ -323,7 +323,7 @@
> +       select ARCH_WANT_OPTIONAL_GPIOLIB
> +       select ARM_HAS_SG_CHAIN
> +       select ARM_PATCH_PHYS_VIRT
> +-      select AUTO_ZRELADDR
> ++      select AUTO_ZRELADDR if !ARCH_QCOM
> +       select CLKSRC_OF
> +       select COMMON_CLK
> +       select GENERIC_CLOCKEVENTS
> +--- a/arch/arm/Makefile
> ++++ b/arch/arm/Makefile
> +@@ -256,9 +256,11 @@
> + else
> + MACHINE  :=
> + endif
> ++ifeq ($(CONFIG_ARCH_QCOM),)
> + ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
> + MACHINE  :=
> + endif
> ++endif
> +
> + machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
> + platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y)))
> +--- /dev/null
> ++++ b/arch/arm/mach-qcom/Makefile.boot
> +@@ -0,0 +1 @@
> ++zreladdr-y+= 0x42208000
> diff --git a/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch
> new file mode 100644
> index 0000000..8890a91
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch
> @@ -0,0 +1,13 @@
> +--- a/drivers/mtd/qcom_smem_part.c
> ++++ b/drivers/mtd/qcom_smem_part.c
> +@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st
> +               m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
> +               m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
> +
> ++              /* "rootfs" conflicts with OpenWrt auto mounting */
> ++              if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
> ++                      m_part->name = "ubi";
> ++
> +               /*
> +                * The last SMEM partition may have its size marked as
> +                * something like 0xffffffff, which means "until the end of the
> diff --git a/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
> new file mode 100644
> index 0000000..625ff0a
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
> @@ -0,0 +1,146 @@
> +From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Mon, 11 May 2015 16:32:09 -0700
> +Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++-
> + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++
> + 2 files changed, 85 insertions(+), 1 deletion(-)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -19,8 +19,9 @@
> +               };
> +       };
> +
> +-      alias {
> ++      aliases {
> +               serial0 = &uart4;
> ++              mdio-gpio0 = &mdio0;
> +       };
> +
> +       chosen {
> +@@ -65,6 +66,15 @@
> +                                       bias-bus-hold;
> +                               };
> +                       };
> ++
> ++                      mdio0_pins: mdio0_pins {
> ++                              mux {
> ++                                      pins = "gpio0", "gpio1";
> ++                                      function = "gpio";
> ++                                      drive-strength = <8>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> +               };
> +
> +               gsbi at 16300000 {
> +@@ -160,6 +170,34 @@
> +
> +                       linux,part-probe = "qcom-smem";
> +               };
> ++
> ++              mdio0: mdio {
> ++                      compatible = "virtual,mdio-gpio";
> ++                      #address-cells = <1>;
> ++                      #size-cells = <0>;
> ++                      gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
> ++                      pinctrl-0 = <&mdio0_pins>;
> ++                      pinctrl-names = "default";
> ++
> ++                      phy0: ethernet-phy 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>;
> ++                      };
> ++              };
> +       };
> + };
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> +@@ -16,6 +16,7 @@
> +
> +       alias {
> +               serial0 = &uart2;
> ++              mdio-gpio0 = &mdio0;
> +       };
> +
> +       chosen {
> +@@ -38,6 +39,15 @@
> +                                       bias-none;
> +                               };
> +                       };
> ++
> ++                      mdio0_pins: mdio0_pins {
> ++                              mux {
> ++                                      pins = "gpio0", "gpio1";
> ++                                      function = "gpio";
> ++                                      drive-strength = <8>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> +               };
> +
> +               gsbi2: gsbi at 12480000 {
> +@@ -140,5 +150,44 @@
> +               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 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>;
> ++                      };
> ++
> ++                      phy6: ethernet-phy at 6 {
> ++                              device_type = "ethernet-phy";
> ++                              reg = <6>;
> ++                      };
> ++
> ++                      phy7: ethernet-phy at 7 {
> ++                              device_type = "ethernet-phy";
> ++                              reg = <7>;
> ++                      };
> ++              };
> +       };
> + };
> diff --git a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
> new file mode 100644
> index 0000000..8d586f2
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
> @@ -0,0 +1,216 @@
> +From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001
> +From: Mathieu Olivari <mathieu at codeaurora.org>
> +Date: Mon, 11 May 2015 12:29:18 -0700
> +Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms
> +
> +Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
> +---
> + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++
> + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++
> + arch/arm/boot/dts/qcom-ipq8064.dtsi      | 86 ++++++++++++++++++++++++++++++++
> + 3 files changed, 160 insertions(+)
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
> +@@ -75,6 +75,16 @@
> +                                       bias-disable;
> +                               };
> +                       };
> ++
> ++                      rgmii2_pins: rgmii2_pins {
> ++                              mux {
> ++                                      pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
> ++                                             "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
> ++                                      function = "rgmii2";
> ++                                      drive-strength = <8>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> +               };
> +
> +               gsbi at 16300000 {
> +@@ -198,6 +208,31 @@
> +                               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>;
> ++
> ++                      fixed-link {
> ++                              speed = <1000>;
> ++                              full-duplex;
> ++                      };
> ++              };
> +       };
> + };
> +
> +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
> +@@ -48,6 +48,14 @@
> +                                       bias-disable;
> +                               };
> +                       };
> ++
> ++                      rgmii0_pins: rgmii0_pins {
> ++                              mux {
> ++                                      pins = "gpio2", "gpio66";
> ++                                      drive-strength = <8>;
> ++                                      bias-disable;
> ++                              };
> ++                      };
> +               };
> +
> +               gsbi2: gsbi at 12480000 {
> +@@ -189,5 +197,40 @@
> +                               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
> +@@ -753,6 +753,92 @@
> +
> +                       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 {
> diff --git a/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch
> new file mode 100644
> index 0000000..8c4718e
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch
> @@ -0,0 +1,134 @@
> +From 16d2871830ff3fe12a6bff582549a9264adff278 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangi at codeaurora.org>
> +Date: Tue, 10 May 2016 20:19:31 +0530
> +Subject: [PATCH] spi: qup: Fix fifo and dma support for IPQ806x
> +
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/spi/spi-qup.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++--
> + 1 file changed, 52 insertions(+), 2 deletions(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index 810a7fa..0808017 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -24,6 +24,7 @@
> + #include <linux/spi/spi.h>
> + #include <linux/dmaengine.h>
> + #include <linux/dma-mapping.h>
> ++#include <linux/gpio.h>
> +
> + #define QUP_CONFIG                    0x0000
> + #define QUP_STATE                     0x0004
> +@@ -152,6 +153,7 @@ struct spi_qup {
> +       int                     use_dma;
> +       struct dma_slave_config rx_conf;
> +       struct dma_slave_config tx_conf;
> ++      int mode;
> + };
> +
> +
> +@@ -370,7 +372,8 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
> +               return ret;
> +       }
> +
> +-      spi_qup_fifo_write(qup, xfer);
> ++      if (qup->mode == QUP_IO_M_MODE_FIFO)
> ++              spi_qup_fifo_write(qup, xfer);
> +
> +       return 0;
> + }
> +@@ -448,6 +451,7 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
> + {
> +       struct spi_qup *qup = spi_master_get_devdata(master);
> +       u32 mode;
> ++      size_t dma_align = dma_get_cache_alignment();
> +
> +       qup->w_size = 4;
> +
> +@@ -458,6 +462,14 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
> +
> +       qup->n_words = xfer->len / qup->w_size;
> +
> ++      if (!IS_ERR_OR_NULL(master->dma_rx) &&
> ++                      IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
> ++                      IS_ALIGNED((size_t)xfer->rx_buf, dma_align) &&
> ++                      !is_vmalloc_addr(xfer->tx_buf) &&
> ++                      !is_vmalloc_addr(xfer->rx_buf) &&
> ++                      (xfer->len > 3*qup->in_blk_sz))
> ++              qup->use_dma = 1;
> ++
> +       if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
> +               mode = QUP_IO_M_MODE_FIFO;
> +       else
> +@@ -491,7 +503,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +               return -EIO;
> +       }
> +
> +-      mode = spi_qup_get_mode(spi->master, xfer);
> ++      controller->mode = mode = spi_qup_get_mode(spi->master, xfer);
> +       n_words = controller->n_words;
> +
> +       if (mode == QUP_IO_M_MODE_FIFO) {
> +@@ -500,6 +512,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +               /* must be zero for FIFO */
> +               writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
> +               writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
> ++              controller->use_dma = 0;
> +       } else if (!controller->use_dma) {
> +               writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
> +               writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
> +@@ -750,6 +763,38 @@ err_tx:
> +       return ret;
> + }
> +
> ++static void spi_qup_set_cs(struct spi_device *spi, bool val)
> ++{
> ++      struct spi_qup *controller;
> ++      u32 spi_ioc;
> ++      u32 spi_ioc_orig;
> ++
> ++      controller = spi_master_get_devdata(spi->master);
> ++      spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
> ++      spi_ioc_orig = spi_ioc;
> ++      if (!val)
> ++              spi_ioc |= SPI_IO_C_FORCE_CS;
> ++      else
> ++              spi_ioc &= ~SPI_IO_C_FORCE_CS;
> ++
> ++      if (spi_ioc != spi_ioc_orig)
> ++              writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
> ++}
> ++
> ++static int spi_qup_setup(struct spi_device *spi)
> ++{
> ++      if (spi->cs_gpio >= 0) {
> ++              if (spi->mode & SPI_CS_HIGH)
> ++                      gpio_set_value(spi->cs_gpio, 0);
> ++              else
> ++                      gpio_set_value(spi->cs_gpio, 1);
> ++
> ++              udelay(10);
> ++      }
> ++
> ++      return 0;
> ++}
> ++
> + static int spi_qup_probe(struct platform_device *pdev)
> + {
> +       struct spi_master *master;
> +@@ -846,6 +891,11 @@ static int spi_qup_probe(struct platform_device *pdev)
> +       if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
> +               controller->qup_v1 = 1;
> +
> ++      if (!controller->qup_v1)
> ++              master->set_cs = spi_qup_set_cs;
> ++      else
> ++              master->setup = spi_qup_setup;
> ++
> +       spin_lock_init(&controller->lock);
> +       init_completion(&controller->done);
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch
> new file mode 100644
> index 0000000..e5dafd7
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch
> @@ -0,0 +1,225 @@
> +From 93f99afbc534e00d72d58336061823055ee820f1 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] 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>
> +
> +Change-Id: If3cdd924355e037d77dc8201a72895fac0461aa5
> +---
> + drivers/spi/spi-qup.c | 96 +++++++++++++++++++--------------------------------
> + 1 file changed, 36 insertions(+), 60 deletions(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index eb2cb8c..714fd4e 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -150,13 +150,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;
> +-      int mode;
> + };
> +
> +
> ++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);
> +@@ -427,7 +434,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +               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);
> +
> +@@ -446,43 +453,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +       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;
> +-      size_t dma_align = dma_get_cache_alignment();
> +-
> +-      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 (!IS_ERR_OR_NULL(master->dma_rx) &&
> +-                      IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
> +-                      IS_ALIGNED((size_t)xfer->rx_buf, dma_align) &&
> +-                      !is_vmalloc_addr(xfer->tx_buf) &&
> +-                      !is_vmalloc_addr(xfer->rx_buf) &&
> +-                      (xfer->len > 3*qup->in_blk_sz))
> +-              qup->use_dma = 1;
> +-
> +-      if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
> +-              mode = QUP_IO_M_MODE_FIFO;
> +-      else
> +-              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) {
> +@@ -503,24 +478,22 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +               return -EIO;
> +       }
> +
> +-      controller->mode = 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);
> +               controller->use_dma = 0;
> +-      } else if (!controller->use_dma) {
> +-              writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
> +-              writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
> +-              /* 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);
> +
> +@@ -541,19 +514,26 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +
> +                       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);
> +
> +@@ -594,7 +574,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +       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)
> +@@ -612,7 +592,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +                * 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);
> +@@ -646,7 +626,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
> +       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);
> +@@ -670,7 +650,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;
> +@@ -681,9 +661,7 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
> + {
> +       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) ||
> +@@ -695,12 +673,10 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
> +           !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;
> + }
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
> new file mode 100644
> index 0000000..7573c96
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
> @@ -0,0 +1,43 @@
> +From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangi at codeaurora.org>
> +Date: Thu, 4 Feb 2016 12:41:56 +0530
> +Subject: [PATCH 2/2] watchdog: qcom: set WDT_BARK_TIME register offset to one
> + second less of bite time
> +
> +Currently WDT_BARK_TIME register offset is not configured with bark
> +timeout during wdt_start,and it is taking bark timeout's default value.
> +For some versions of TZ (secure mode) will consider a BARK the same
> +as BITE and reset the board.
> +
> +So instead let's just configure the BARK time to be less than a second
> +of the bite timeout so the board does not reset in this scenario
> +
> +Change-Id: Ie09850ad7e0470ed721e6924911ca2a81fd9ff8a
> +Signed-off-by: Ram Chandra Jangir <rjangi at codeaurora.org>
> +---
> + drivers/watchdog/qcom-wdt.c | 2 ++
> + 1 file changed, 2 insertions(+)
> +
> +diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
> +index 773dcfa..002274a 100644
> +--- a/drivers/watchdog/qcom-wdt.c
> ++++ b/drivers/watchdog/qcom-wdt.c
> +@@ -22,6 +22,7 @@
> +
> + #define WDT_RST               0x38
> + #define WDT_EN                0x40
> ++#define WDT_BARK_TIME 0x4C
> + #define WDT_BITE_TIME 0x5C
> +
> + struct qcom_wdt {
> +@@ -44,6 +45,7 @@ static int qcom_wdt_start(struct watchdog_device *wdd)
> +
> +       writel(0, wdt->base + WDT_EN);
> +       writel(1, wdt->base + WDT_RST);
> ++      writel((wdd->timeout - 1) * wdt->rate, wdt->base + WDT_BARK_TIME);
> +       writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
> +       writel(1, wdt->base + WDT_EN);
> +       return 0;
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch
> new file mode 100644
> index 0000000..dd4bad3
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch
> @@ -0,0 +1,35 @@
> +From 8e830bd17e945e74964a5b61353d74e34c0791cd 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] 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.
> +
> +Change-Id: I08d78376e71590663158d6434a3fb7c0623264c9
> +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(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index 714fd4e..fe629f2 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -447,7 +447,8 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +       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;
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch
> new file mode 100644
> index 0000000..d9abc65
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch
> @@ -0,0 +1,226 @@
> +From ed56e6322b067a898a25bda1774eb1180a832246 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] 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.
> +
> +Change-Id: Ib1f876eaa05d079e0bca4358d2b25b2940986089
> +Signed-off-by: Andy Gross <andy.gross at linaro.org>
> +---
> + drivers/spi/spi-qup.c | 95 ++++++++++++++++++++++++++++++++++-----------------
> + 1 file changed, 63 insertions(+), 32 deletions(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index fe629f2..089c5e8 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -143,6 +143,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;
> +@@ -285,16 +286,16 @@ static void spi_qup_fifo_write(struct spi_qup *controller,
> +
> + 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;
> +@@ -313,11 +314,11 @@ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
> +       }
> +
> +       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);
> +
> +@@ -333,18 +334,29 @@ static void spi_qup_dma_terminate(struct spi_master *master,
> +               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;
> +
> +@@ -352,17 +364,26 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
> +       }
> +
> +       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;
> +@@ -382,6 +403,15 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
> +       if (qup->mode == QUP_IO_M_MODE_FIFO)
> +               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;
> + }
> +
> +@@ -430,7 +460,6 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +                       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;
> +       }
> +
> +@@ -619,6 +648,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
> +       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;
> +@@ -628,21 +658,13 @@ static int spi_qup_transfer_one(struct spi_master *master,
> +       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);
> +@@ -664,15 +686,23 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
> +       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)))
> +@@ -875,6 +905,7 @@ static int spi_qup_probe(struct platform_device *pdev)
> +
> +       spin_lock_init(&controller->lock);
> +       init_completion(&controller->done);
> ++      init_completion(&controller->dma_tx_done);
> +
> +       iomode = readl_relaxed(base + QUP_IO_M_MODES);
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
> new file mode 100644
> index 0000000..7a05ecd
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
> @@ -0,0 +1,317 @@
> +From 148f77310a9ddf4db5036066458d7aed92cea9ae 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] 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>
> +
> +Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf
> +---
> + drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++-----------
> + 1 file changed, 141 insertions(+), 40 deletions(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index 089c5e8..e487416 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -83,6 +83,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)
> +@@ -156,6 +158,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)
> + {
> +@@ -217,29 +225,26 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state)
> +       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;
> +-
> +-      while (controller->rx_bytes < xfer->len) {
> ++      int i, shift, num_bytes;
> ++      u32 word;
> +
> +-              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
> +@@ -247,38 +252,80 @@ static void spi_qup_fifo_read(struct spi_qup *controller,
> +                        *  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;
> ++
> ++                      num_words = 1;
> ++              }
> +
> +-      w_size = controller->w_size;
> ++              /* 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);
> +       }
> +@@ -291,6 +338,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,
> +@@ -348,11 +433,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,
> +@@ -401,7 +488,7 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
> +       }
> +
> +       if (qup->mode == QUP_IO_M_MODE_FIFO)
> +-              spi_qup_fifo_write(qup, xfer);
> ++              spi_qup_write(qup, xfer);
> +
> +       ret = spi_qup_set_state(qup, QUP_STATE_RUN);
> +       if (ret) {
> +@@ -434,10 +521,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +
> +       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;
> +       }
> +@@ -463,12 +551,20 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +               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);
> +@@ -476,6 +572,9 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +       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);
> +@@ -519,11 +618,13 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
> +               /* must be zero for FIFO */
> +               writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
> +               writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
> +-              controller->use_dma = 0;
> +       } else if (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);
> +
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch
> new file mode 100644
> index 0000000..d8a9b31
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch
> @@ -0,0 +1,67 @@
> +From b69e5e855aaae2dd9f7fc6f4a40af8e6e0cf98ee 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] 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.
> +
> +Change-Id: I0e70ed59fb50e5c48a72a38f74bd178b17c9f24d
> +Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
> +---
> + drivers/spi/spi-qup.c | 15 +++++++++------
> + 1 file changed, 9 insertions(+), 6 deletions(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index e487416..45e30c7 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -509,6 +509,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +       u32 opflags, qup_err, spi_err;
> +       unsigned long flags;
> +       int error = 0;
> ++      bool done = 0;
> +
> +       spin_lock_irqsave(&controller->lock, flags);
> +       xfer = controller->xfer;
> +@@ -567,16 +568,19 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +                       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;
> +@@ -769,7 +773,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
> + 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);
> +--
> +2.7.2
> +
> diff --git a/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
> new file mode 100644
> index 0000000..54711a1
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
> @@ -0,0 +1,32 @@
> +From f57ff801665b868d8607c9e872466b54982564bc 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] 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.
> +
> +Change-Id: I4fa95ae7bb9d78f8ba54c613b981b37d4ea81d7e
> +Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
> +---
> + drivers/spi/spi-qup.c | 3 ++-
> + 1 file changed, 2 insertions(+), 1 deletion(-)
> +
> +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
> +index 45e30c7..59bc37c 100644
> +--- a/drivers/spi/spi-qup.c
> ++++ b/drivers/spi/spi-qup.c
> +@@ -569,7 +569,8 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
> +       }
> +
> +       /* 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)
> +--
> +2.7.2
> +
> --
> 2.7.2
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel at lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list