[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 = ®s->cmd;
> ++ flow_control = true;
> ++ break;
> ++ case NAND_EXEC_CMD:
> ++ vaddr = ®s->exec;
> ++ break;
> ++ case NAND_FLASH_STATUS:
> ++ vaddr = ®s->clrflashstatus;
> ++ break;
> ++ case NAND_DEV0_CFG0:
> ++ vaddr = ®s->cfg0;
> ++ break;
> ++ case NAND_READ_STATUS:
> ++ vaddr = ®s->clrreadstatus;
> ++ break;
> ++ case NAND_DEV_CMD1:
> ++ vaddr = ®s->cmd1;
> ++ break;
> ++ case NAND_DEV_CMD1_RESTORE:
> ++ first = NAND_DEV_CMD1;
> ++ vaddr = ®s->orig_cmd1;
> ++ break;
> ++ case NAND_DEV_CMD_VLD:
> ++ vaddr = ®s->vld;
> ++ break;
> ++ case NAND_DEV_CMD_VLD_RESTORE:
> ++ first = NAND_DEV_CMD_VLD;
> ++ vaddr = ®s->orig_vld;
> ++ break;
> ++ case NAND_EBI2_ECC_BUF_CFG:
> ++ vaddr = ®s->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