summaryrefslogtreecommitdiff
path: root/target/linux/ipq806x
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x')
-rw-r--r--target/linux/ipq806x/Makefile17
-rw-r--r--target/linux/ipq806x/base-files.mk3
-rw-r--r--target/linux/ipq806x/base-files/etc/inittab4
-rw-r--r--target/linux/ipq806x/base-files/lib/ipq806x.sh40
-rw-r--r--target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh12
-rw-r--r--target/linux/ipq806x/config-3.14356
-rw-r--r--target/linux/ipq806x/image/Makefile32
-rw-r--r--target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch313
-rw-r--r--target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch223
-rw-r--r--target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch120
-rw-r--r--target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch788
-rw-r--r--target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch1482
-rw-r--r--target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch78
-rw-r--r--target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch105
-rw-r--r--target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch159
-rw-r--r--target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch249
-rw-r--r--target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch70
-rw-r--r--target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch55
-rw-r--r--target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch60
-rw-r--r--target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch181
-rw-r--r--target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch172
-rw-r--r--target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch32
-rw-r--r--target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch33
-rw-r--r--target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch32
-rw-r--r--target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch38
-rw-r--r--target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch32
-rw-r--r--target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch54
-rw-r--r--target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch214
-rw-r--r--target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch35
-rw-r--r--target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch34
-rw-r--r--target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch53
-rw-r--r--target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch71
-rw-r--r--target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch73
-rw-r--r--target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch39
-rw-r--r--target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch60
-rw-r--r--target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch31
-rw-r--r--target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch66
-rw-r--r--target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch95
-rw-r--r--target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch115
-rw-r--r--target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch69
-rw-r--r--target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch63
-rw-r--r--target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch38
-rw-r--r--target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch55
-rw-r--r--target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch74
-rw-r--r--target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch624
-rw-r--r--target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch36
-rw-r--r--target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch711
-rw-r--r--target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch120
-rw-r--r--target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch29
-rw-r--r--target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch28
-rw-r--r--target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch65
-rw-r--r--target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch1174
-rw-r--r--target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch81
-rw-r--r--target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch275
-rw-r--r--target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch472
-rw-r--r--target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch216
-rw-r--r--target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch331
-rw-r--r--target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch156
-rw-r--r--target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch43
-rw-r--r--target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch164
-rw-r--r--target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch35
-rw-r--r--target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch943
-rw-r--r--target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch477
-rw-r--r--target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch494
-rw-r--r--target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch34
-rw-r--r--target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch74
-rw-r--r--target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch835
-rw-r--r--target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch71
-rw-r--r--target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch36
-rw-r--r--target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch30
-rw-r--r--target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch31
-rw-r--r--target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch907
-rw-r--r--target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch111
-rw-r--r--target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch30
-rw-r--r--target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch69
-rw-r--r--target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch48
-rw-r--r--target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch30
-rw-r--r--target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch40
-rw-r--r--target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch28
-rw-r--r--target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch167
-rw-r--r--target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch36
-rw-r--r--target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch39
-rw-r--r--target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch755
-rw-r--r--target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch123
-rw-r--r--target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch101
-rw-r--r--target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch190
-rw-r--r--target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch25
-rw-r--r--target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch68
-rw-r--r--target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch162
-rw-r--r--target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch28
-rw-r--r--target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch246
-rw-r--r--target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch151
-rw-r--r--target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch135
-rw-r--r--target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch57
-rw-r--r--target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch75
-rw-r--r--target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch186
-rw-r--r--target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch268
-rw-r--r--target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch191
-rw-r--r--target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch265
-rw-r--r--target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch43
-rw-r--r--target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch216
-rw-r--r--target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch267
-rw-r--r--target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch30
-rw-r--r--target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch31
-rw-r--r--target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch42
-rw-r--r--target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch30
-rw-r--r--target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch36
-rw-r--r--target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch378
-rw-r--r--target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch259
-rw-r--r--target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch245
-rw-r--r--target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch37
-rw-r--r--target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch223
-rw-r--r--target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch253
-rw-r--r--target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch142
-rw-r--r--target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch208
-rw-r--r--target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch904
-rw-r--r--target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch341
-rw-r--r--target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch180
-rw-r--r--target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch32
-rw-r--r--target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch141
-rw-r--r--target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch86
-rw-r--r--target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch54
-rw-r--r--target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch110
-rw-r--r--target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch62
-rw-r--r--target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch46
-rw-r--r--target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch50
-rw-r--r--target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch1157
-rw-r--r--target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch2939
-rw-r--r--target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch155
-rw-r--r--target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch155
-rw-r--r--target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch317
-rw-r--r--target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch869
-rw-r--r--target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch269
-rw-r--r--target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch25
-rw-r--r--target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch55
-rw-r--r--target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch90
-rw-r--r--target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch46
-rw-r--r--target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch132
-rw-r--r--target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch93
-rw-r--r--target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch131
-rw-r--r--target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch726
-rw-r--r--target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch179
-rw-r--r--target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch33
-rw-r--r--target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch28
-rw-r--r--target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch146
-rw-r--r--target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch63
-rw-r--r--target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch261
-rw-r--r--target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch46
-rw-r--r--target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch72
-rw-r--r--target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch42
-rw-r--r--target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch26
-rw-r--r--target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch55
-rw-r--r--target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch8803
-rw-r--r--target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch115
-rw-r--r--target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch27
-rw-r--r--target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch169
-rw-r--r--target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch29
-rw-r--r--target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch63
-rw-r--r--target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch212
-rw-r--r--target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch874
-rw-r--r--target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch131
-rw-r--r--target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch162
-rw-r--r--target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch42
-rw-r--r--target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch259
-rw-r--r--target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch134
-rw-r--r--target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch49
-rw-r--r--target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch146
-rw-r--r--target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch663
-rw-r--r--target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch359
-rw-r--r--target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch162
-rw-r--r--target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch122
-rw-r--r--target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch203
-rw-r--r--target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch171
-rw-r--r--target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch420
-rw-r--r--target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch255
-rw-r--r--target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch104
-rw-r--r--target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch121
-rw-r--r--target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch96
-rw-r--r--target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch44
-rw-r--r--target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch931
-rw-r--r--target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch82
-rw-r--r--target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch495
-rw-r--r--target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch90
-rw-r--r--target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch46
-rw-r--r--target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch54
-rw-r--r--target/linux/ipq806x/profiles/default.mk18
186 files changed, 46248 insertions, 0 deletions
diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile
new file mode 100644
index 0000000..7521e97
--- /dev/null
+++ b/target/linux/ipq806x/Makefile
@@ -0,0 +1,17 @@
+# Copyright (c) 2013 The Linux Foundation. All rights reserved.
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=arm
+BOARD:=ipq806x
+BOARDNAME:=Qualcomm Atheros IPQ806X
+FEATURES:=squashfs
+CPU_TYPE:=cortex-a7
+
+LINUX_VERSION:=3.14.12
+
+KERNELNAME="Image dtbs"
+
+include $(INCLUDE_DIR)/target.mk
+
+$(eval $(call BuildTarget))
diff --git a/target/linux/ipq806x/base-files.mk b/target/linux/ipq806x/base-files.mk
new file mode 100644
index 0000000..fdd2c71
--- /dev/null
+++ b/target/linux/ipq806x/base-files.mk
@@ -0,0 +1,3 @@
+define Package/base-files/install-target
+ rm -f $(1)/etc/config/network
+endef
diff --git a/target/linux/ipq806x/base-files/etc/inittab b/target/linux/ipq806x/base-files/etc/inittab
new file mode 100644
index 0000000..19a6e11
--- /dev/null
+++ b/target/linux/ipq806x/base-files/etc/inittab
@@ -0,0 +1,4 @@
+# Copyright (c) 2013 The Linux Foundation. All rights reserved.
+::sysinit:/etc/init.d/rcS S boot
+::shutdown:/etc/init.d/rcS K shutdown
+ttyMSM0::askfirst:/bin/ash --login
diff --git a/target/linux/ipq806x/base-files/lib/ipq806x.sh b/target/linux/ipq806x/base-files/lib/ipq806x.sh
new file mode 100644
index 0000000..7c47fa2
--- /dev/null
+++ b/target/linux/ipq806x/base-files/lib/ipq806x.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+# Copyright (C) 2011 OpenWrt.org
+#
+
+IPQ806X_BOARD_NAME=
+IPQ806X_MODEL=
+
+ipq806x_board_detect() {
+ local machine
+ local name
+
+ machine=$(cat /proc/device-tree/model)
+
+ case "$machine" in
+ *"AP148")
+ name="ap148"
+ ;;
+ esac
+
+ [ -z "$name" ] && name="unknown"
+
+ [ -z "$IPQ806X_BOARD_NAME" ] && IPQ806X_BOARD_NAME="$name"
+ [ -z "$IPQ806X_MODEL" ] && IPQ806X_MODEL="$machine"
+
+ [ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/"
+
+ echo "$IPQ806X_BOARD_NAME" > /tmp/sysinfo/board_name
+ echo "$IPQ806X_MODEL" > /tmp/sysinfo/model
+}
+
+ipq806x_board_name() {
+ local name
+
+ [ -f /tmp/sysinfo/board_name ] && name=$(cat /tmp/sysinfo/board_name)
+ [ -z "$name" ] && name="unknown"
+
+ echo "$name"
+}
diff --git a/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh
new file mode 100644
index 0000000..785f1eb
--- /dev/null
+++ b/target/linux/ipq806x/base-files/lib/preinit/03_preinit_do_ipq806x.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+#
+
+do_ipq806x() {
+ . /lib/ipq806x.sh
+
+ ipq806x_board_detect
+}
+
+boot_hook_add preinit_main do_ipq806x
diff --git a/target/linux/ipq806x/config-3.14 b/target/linux/ipq806x/config-3.14
new file mode 100644
index 0000000..876e56d
--- /dev/null
+++ b/target/linux/ipq806x/config-3.14
@@ -0,0 +1,356 @@
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_APM_EMULATION is not set
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+# CONFIG_ARCH_MSM is not set
+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_NEEDS_CPU_IDLE_COUPLED is not set
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARM=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_NR_BANKS=8
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+# CONFIG_ARM_SP805_WATCHDOG is not set
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_AUTO_ZRELADDR=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_BOUNCE=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_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+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_XZ=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_UART_8250 is not set
+# CONFIG_DEBUG_UART_PL01X is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DTC=y
+# CONFIG_DW_DMAC_CORE is not set
+# CONFIG_DW_DMAC_PCI is not set
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_FREEZER=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIO_MSM_V2 is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SMP=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_UID16=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_HWMON=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_HZ_FIXED=0
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_QUP=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_API=y
+CONFIG_IOMMU_HELPER=y
+CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_IPQ_GCC_806X=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_KPSS_XCC=y
+CONFIG_KRAITCC=y
+CONFIG_KRAIT_CLOCKS=y
+CONFIG_KRAIT_L2_ACCESSORS=y
+CONFIG_KTIME_SCALAR=y
+# CONFIG_LEDS_REGULATOR is not set
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MDIO_GPIO=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MIGRATION=y
+# CONFIG_MLX5_CORE is not set
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MSM_GCC_8660=y
+CONFIG_MSM_GCC_8960=y
+CONFIG_MSM_GCC_8974=y
+CONFIG_MSM_IOMMU=y
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
+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_RX_BUSY_POLL=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_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IOMMU=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_MSI=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PHYLIB=y
+CONFIG_PHY_QCOM_IPQ806X_SATA=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_APQ8064=y
+CONFIG_PINCTRL_IPQ8064=y
+CONFIG_PINCTRL_MSM=y
+CONFIG_PINCTRL_MSM8X74=y
+# CONFIG_PL330_DMA is not set
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_GPIO is not set
+CONFIG_POWER_RESET_MSM=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_RCU=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_PROC_PAGE_MONITOR=y
+# CONFIG_QCOM_ADM is not set
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_TCSR=y
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_RCU_CPU_STALL_VERBOSE=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RD_GZIP=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+CONFIG_RESET_CONTROLLER=y
+# CONFIG_RFKILL_REGULATOR is not set
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_CMOS is not set
+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_STOP_MACHINE=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_STATS=y
+CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_UID16=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_USE_OF=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_VFIO is not set
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_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_XEN 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_ZBUD is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
new file mode 100644
index 0000000..977e674
--- /dev/null
+++ b/target/linux/ipq806x/image/Makefile
@@ -0,0 +1,32 @@
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+UBIFS_OPTS = -m 2048 -e 124KiB -c 4096 -U -F
+UBINIZE_OPTS = -m 2048 -p 128KiB
+
+E2SIZE=$(shell echo $$(($(CONFIG_TARGET_ROOTFS_PARTSIZE)*1024)))
+
+define Image/BuildKernel/FIT
+ gzip -9 -c $(KDIR)/Image > $(KDIR)/Image.gz
+ $(call CompressLzma,$(KDIR)/Image,$(KDIR)/Image.gz)
+ $(call Image/BuildKernel/MkFIT,$(1), $(KDIR)/Image.gz, $(LINUX_DIR)/arch/arm/boot/dts/$(1).dtb,gzip,0x42208000,0x42208000)
+ $(CP) $(KDIR)/fit-$(1).itb $(BIN_DIR)/$(IMG_PREFIX)-$(1)-fit-uImage.itb
+ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
+ $(call Image/BuildKernel/MkFIT,$(1), $(KDIR)/Image-initramfs, $(LINUX_DIR)/arch/arm/boot/dts/$(1).dtb, none,0x42208000,0x42208000)
+ $(CP) $(KDIR)/fit-$(1).itb $(BIN_DIR)/$(IMG_PREFIX)-$(1)-fit-uImage-initramfs.itb
+endif
+endef
+
+define Image/BuildKernel
+ $(CP) $(LINUX_DIR)/vmlinux $(BIN_DIR)/$(IMG_PREFIX)-vmlinux.elf
+ $(call Image/BuildKernel/FIT,qcom-ipq8064-ap148)
+endef
+
+define Image/Build
+ $(call Image/Build/$(1),$(1))
+ dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-$(1)-root.img bs=2k conv=sync
+endef
+
+$(eval $(call BuildImage))
diff --git a/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch
new file mode 100644
index 0000000..52d4cf3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch
@@ -0,0 +1,313 @@
+From 3cdba35369b404875849008ea97cf1705e6060ed Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 23 Jan 2014 14:09:54 -0600
+Subject: [PATCH 001/182] ARM: dts: msm: split out msm8660 and msm8960 soc
+ into dts include
+
+Pull the SoC device tree bits into their own files so other boards based
+on these SoCs can include them and reduce duplication across a number of
+boards.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8660-surf.dts | 59 +-------------------------
+ arch/arm/boot/dts/qcom-msm8660.dtsi | 63 ++++++++++++++++++++++++++++
+ arch/arm/boot/dts/qcom-msm8960-cdp.dts | 66 +----------------------------
+ arch/arm/boot/dts/qcom-msm8960.dtsi | 70 +++++++++++++++++++++++++++++++
+ 4 files changed, 135 insertions(+), 123 deletions(-)
+ create mode 100644 arch/arm/boot/dts/qcom-msm8660.dtsi
+ create mode 100644 arch/arm/boot/dts/qcom-msm8960.dtsi
+
+diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts
+index 68a72f5..169bad9 100644
+--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts
++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts
+@@ -1,63 +1,6 @@
+-/dts-v1/;
+-
+-/include/ "skeleton.dtsi"
+-
+-#include <dt-bindings/clock/qcom,gcc-msm8660.h>
++#include "qcom-msm8660.dtsi"
+
+ / {
+ model = "Qualcomm MSM8660 SURF";
+ compatible = "qcom,msm8660-surf", "qcom,msm8660";
+- interrupt-parent = <&intc>;
+-
+- intc: interrupt-controller@2080000 {
+- compatible = "qcom,msm-8660-qgic";
+- interrupt-controller;
+- #interrupt-cells = <3>;
+- reg = < 0x02080000 0x1000 >,
+- < 0x02081000 0x1000 >;
+- };
+-
+- timer@2000000 {
+- compatible = "qcom,scss-timer", "qcom,msm-timer";
+- interrupts = <1 0 0x301>,
+- <1 1 0x301>,
+- <1 2 0x301>;
+- reg = <0x02000000 0x100>;
+- clock-frequency = <27000000>,
+- <32768>;
+- cpu-offset = <0x40000>;
+- };
+-
+- msmgpio: gpio@800000 {
+- compatible = "qcom,msm-gpio";
+- reg = <0x00800000 0x4000>;
+- gpio-controller;
+- #gpio-cells = <2>;
+- ngpio = <173>;
+- interrupts = <0 16 0x4>;
+- interrupt-controller;
+- #interrupt-cells = <2>;
+- };
+-
+- gcc: clock-controller@900000 {
+- compatible = "qcom,gcc-msm8660";
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- reg = <0x900000 0x4000>;
+- };
+-
+- serial@19c40000 {
+- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+- reg = <0x19c40000 0x1000>,
+- <0x19c00000 0x1000>;
+- interrupts = <0 195 0x0>;
+- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
+- clock-names = "core", "iface";
+- };
+-
+- qcom,ssbi@500000 {
+- compatible = "qcom,ssbi";
+- reg = <0x500000 0x1000>;
+- qcom,controller-type = "pmic-arbiter";
+- };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi
+new file mode 100644
+index 0000000..69d6c4e
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi
+@@ -0,0 +1,63 @@
++/dts-v1/;
++
++/include/ "skeleton.dtsi"
++
++#include <dt-bindings/clock/qcom,gcc-msm8660.h>
++
++/ {
++ model = "Qualcomm MSM8660";
++ compatible = "qcom,msm8660";
++ interrupt-parent = <&intc>;
++
++ intc: interrupt-controller@2080000 {
++ compatible = "qcom,msm-8660-qgic";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = < 0x02080000 0x1000 >,
++ < 0x02081000 0x1000 >;
++ };
++
++ timer@2000000 {
++ compatible = "qcom,scss-timer", "qcom,msm-timer";
++ interrupts = <1 0 0x301>,
++ <1 1 0x301>,
++ <1 2 0x301>;
++ reg = <0x02000000 0x100>;
++ clock-frequency = <27000000>,
++ <32768>;
++ cpu-offset = <0x40000>;
++ };
++
++ msmgpio: gpio@800000 {
++ compatible = "qcom,msm-gpio";
++ reg = <0x00800000 0x4000>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ ngpio = <173>;
++ interrupts = <0 16 0x4>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ };
++
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-msm8660";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ reg = <0x900000 0x4000>;
++ };
++
++ serial@19c40000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x19c40000 0x1000>,
++ <0x19c00000 0x1000>;
++ interrupts = <0 195 0x0>;
++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
++ clock-names = "core", "iface";
++ };
++
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
++};
+diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
+index 7c30de4..a58fb88 100644
+--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts
++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
+@@ -1,70 +1,6 @@
+-/dts-v1/;
+-
+-/include/ "skeleton.dtsi"
+-
+-#include <dt-bindings/clock/qcom,gcc-msm8960.h>
++#include "qcom-msm8960.dtsi"
+
+ / {
+ model = "Qualcomm MSM8960 CDP";
+ compatible = "qcom,msm8960-cdp", "qcom,msm8960";
+- interrupt-parent = <&intc>;
+-
+- intc: interrupt-controller@2000000 {
+- compatible = "qcom,msm-qgic2";
+- interrupt-controller;
+- #interrupt-cells = <3>;
+- reg = < 0x02000000 0x1000 >,
+- < 0x02002000 0x1000 >;
+- };
+-
+- timer@200a000 {
+- compatible = "qcom,kpss-timer", "qcom,msm-timer";
+- interrupts = <1 1 0x301>,
+- <1 2 0x301>,
+- <1 3 0x301>;
+- reg = <0x0200a000 0x100>;
+- clock-frequency = <27000000>,
+- <32768>;
+- cpu-offset = <0x80000>;
+- };
+-
+- msmgpio: gpio@800000 {
+- compatible = "qcom,msm-gpio";
+- gpio-controller;
+- #gpio-cells = <2>;
+- ngpio = <150>;
+- interrupts = <0 16 0x4>;
+- interrupt-controller;
+- #interrupt-cells = <2>;
+- reg = <0x800000 0x4000>;
+- };
+-
+- gcc: clock-controller@900000 {
+- compatible = "qcom,gcc-msm8960";
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- reg = <0x900000 0x4000>;
+- };
+-
+- clock-controller@4000000 {
+- compatible = "qcom,mmcc-msm8960";
+- reg = <0x4000000 0x1000>;
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- };
+-
+- serial@16440000 {
+- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+- reg = <0x16440000 0x1000>,
+- <0x16400000 0x1000>;
+- interrupts = <0 154 0x0>;
+- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
+- clock-names = "core", "iface";
+- };
+-
+- qcom,ssbi@500000 {
+- compatible = "qcom,ssbi";
+- reg = <0x500000 0x1000>;
+- qcom,controller-type = "pmic-arbiter";
+- };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
+new file mode 100644
+index 0000000..ff00282
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
+@@ -0,0 +1,70 @@
++/dts-v1/;
++
++/include/ "skeleton.dtsi"
++
++#include <dt-bindings/clock/qcom,gcc-msm8960.h>
++
++/ {
++ model = "Qualcomm MSM8960";
++ compatible = "qcom,msm8960";
++ interrupt-parent = <&intc>;
++
++ intc: interrupt-controller@2000000 {
++ compatible = "qcom,msm-qgic2";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = < 0x02000000 0x1000 >,
++ < 0x02002000 0x1000 >;
++ };
++
++ timer@200a000 {
++ compatible = "qcom,kpss-timer", "qcom,msm-timer";
++ interrupts = <1 1 0x301>,
++ <1 2 0x301>,
++ <1 3 0x301>;
++ reg = <0x0200a000 0x100>;
++ clock-frequency = <27000000>,
++ <32768>;
++ cpu-offset = <0x80000>;
++ };
++
++ msmgpio: gpio@800000 {
++ compatible = "qcom,msm-gpio";
++ gpio-controller;
++ #gpio-cells = <2>;
++ ngpio = <150>;
++ interrupts = <0 16 0x4>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ reg = <0x800000 0x4000>;
++ };
++
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-msm8960";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ reg = <0x900000 0x4000>;
++ };
++
++ clock-controller@4000000 {
++ compatible = "qcom,mmcc-msm8960";
++ reg = <0x4000000 0x1000>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ serial@16440000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x16440000 0x1000>,
++ <0x16400000 0x1000>;
++ interrupts = <0 154 0x0>;
++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
++ clock-names = "core", "iface";
++ };
++
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
++};
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch
new file mode 100644
index 0000000..72b6618
--- /dev/null
+++ b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch
@@ -0,0 +1,223 @@
+From 18d53dfa103e63154fb8e548d55016d6ad210d28 Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Fri, 21 Jun 2013 12:17:37 -0700
+Subject: [PATCH 002/182] ARM: msm: Remove pen_release usage
+
+pen_release is no longer required as the synchronization
+is now managed by generic arm code.
+This is done as suggested in https://lkml.org/lkml/2013/6/4/184
+
+Cc: Russell King <linux@arm.linux.org.uk>
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-msm/Makefile | 2 +-
+ arch/arm/mach-msm/headsmp.S | 39 ---------------------------------------
+ arch/arm/mach-msm/hotplug.c | 31 ++++---------------------------
+ arch/arm/mach-msm/platsmp.c | 37 +++----------------------------------
+ 4 files changed, 8 insertions(+), 101 deletions(-)
+ delete mode 100644 arch/arm/mach-msm/headsmp.S
+
+diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
+index 8e307a1..721f27f 100644
+--- a/arch/arm/mach-msm/Makefile
++++ b/arch/arm/mach-msm/Makefile
+@@ -19,7 +19,7 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
+ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+
+ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+-obj-$(CONFIG_SMP) += headsmp.o platsmp.o
++obj-$(CONFIG_SMP) += platsmp.o
+
+ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
+ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
+diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S
+deleted file mode 100644
+index 6c62c3f..0000000
+--- a/arch/arm/mach-msm/headsmp.S
++++ /dev/null
+@@ -1,39 +0,0 @@
+-/*
+- * linux/arch/arm/mach-realview/headsmp.S
+- *
+- * Copyright (c) 2003 ARM Limited
+- * 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 as
+- * published by the Free Software Foundation.
+- */
+-#include <linux/linkage.h>
+-#include <linux/init.h>
+-
+-/*
+- * MSM specific entry point for secondary CPUs. This provides
+- * a "holding pen" into which all secondary cores are held until we're
+- * ready for them to initialise.
+- */
+-ENTRY(msm_secondary_startup)
+- mrc p15, 0, r0, c0, c0, 5
+- and r0, r0, #15
+- adr r4, 1f
+- ldmia r4, {r5, r6}
+- sub r4, r4, r5
+- add r6, r6, r4
+-pen: ldr r7, [r6]
+- cmp r7, r0
+- bne pen
+-
+- /*
+- * we've been released from the holding pen: secondary_stack
+- * should now contain the SVC stack for this core
+- */
+- b secondary_startup
+-ENDPROC(msm_secondary_startup)
+-
+- .align
+-1: .long .
+- .long pen_release
+diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
+index 326a872..cea80fc 100644
+--- a/arch/arm/mach-msm/hotplug.c
++++ b/arch/arm/mach-msm/hotplug.c
+@@ -24,33 +24,10 @@ static inline void cpu_leave_lowpower(void)
+
+ static inline void platform_do_lowpower(unsigned int cpu)
+ {
+- /* Just enter wfi for now. TODO: Properly shut off the cpu. */
+- for (;;) {
+- /*
+- * here's the WFI
+- */
+- asm("wfi"
+- :
+- :
+- : "memory", "cc");
+-
+- if (pen_release == cpu_logical_map(cpu)) {
+- /*
+- * OK, proper wakeup, we're done
+- */
+- break;
+- }
+-
+- /*
+- * getting here, means that we have come out of WFI without
+- * having been woken up - this shouldn't happen
+- *
+- * The trouble is, letting people know about this is not really
+- * possible, since we are currently running incoherently, and
+- * therefore cannot safely call printk() or anything else
+- */
+- pr_debug("CPU%u: spurious wakeup call\n", cpu);
+- }
++ asm("wfi"
++ :
++ :
++ : "memory", "cc");
+ }
+
+ /*
+diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
+index f10a1f5..3721b31 100644
+--- a/arch/arm/mach-msm/platsmp.c
++++ b/arch/arm/mach-msm/platsmp.c
+@@ -12,13 +12,10 @@
+ #include <linux/errno.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+-#include <linux/jiffies.h>
+ #include <linux/smp.h>
+ #include <linux/io.h>
+
+-#include <asm/cacheflush.h>
+ #include <asm/cputype.h>
+-#include <asm/mach-types.h>
+ #include <asm/smp_plat.h>
+
+ #include "scm-boot.h"
+@@ -28,7 +25,7 @@
+ #define SCSS_CPU1CORE_RESET 0xD80
+ #define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
+
+-extern void msm_secondary_startup(void);
++extern void secondary_startup(void);
+
+ static DEFINE_SPINLOCK(boot_lock);
+
+@@ -41,13 +38,6 @@ static inline int get_core_count(void)
+ static void msm_secondary_init(unsigned int cpu)
+ {
+ /*
+- * let the primary processor know we're out of the
+- * pen, then head off into the C entry point
+- */
+- pen_release = -1;
+- smp_wmb();
+-
+- /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+@@ -57,7 +47,7 @@ static void msm_secondary_init(unsigned int cpu)
+ static void prepare_cold_cpu(unsigned int cpu)
+ {
+ int ret;
+- ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup),
+ SCM_FLAG_COLDBOOT_CPU1);
+ if (ret == 0) {
+ void __iomem *sc1_base_ptr;
+@@ -75,7 +65,6 @@ static void prepare_cold_cpu(unsigned int cpu)
+
+ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ {
+- unsigned long timeout;
+ static int cold_boot_done;
+
+ /* Only need to bring cpu out of reset this way once */
+@@ -91,39 +80,19 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ spin_lock(&boot_lock);
+
+ /*
+- * The secondary processor is waiting to be released from
+- * the holding pen - release it, then wait for it to flag
+- * that it has been released by resetting pen_release.
+- *
+- * Note that "pen_release" is the hardware CPU ID, whereas
+- * "cpu" is Linux's internal ID.
+- */
+- pen_release = cpu_logical_map(cpu);
+- sync_cache_w(&pen_release);
+-
+- /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+
+- timeout = jiffies + (1 * HZ);
+- while (time_before(jiffies, timeout)) {
+- smp_rmb();
+- if (pen_release == -1)
+- break;
+-
+- udelay(10);
+- }
+-
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+- return pen_release != -1 ? -ENOSYS : 0;
++ return 0;
+ }
+
+ /*
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch
new file mode 100644
index 0000000..b6e9bba
--- /dev/null
+++ b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch
@@ -0,0 +1,120 @@
+From b5a3a19e3efa6238c6a00a8f36a8ab2c25eeebc3 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 31 Jan 2014 13:48:29 -0600
+Subject: [PATCH 003/182] ARM: msm: kill off hotplug.c
+
+Right now hotplug.c only really implements msm_cpu_die as a wfi. Just
+move that implementation into platsmp.c. At the same time we use the
+existing wfi() instead of inline asm.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-msm/Makefile | 1 -
+ arch/arm/mach-msm/common.h | 1 -
+ arch/arm/mach-msm/hotplug.c | 51 -------------------------------------------
+ arch/arm/mach-msm/platsmp.c | 7 ++++++
+ 4 files changed, 7 insertions(+), 53 deletions(-)
+ delete mode 100644 arch/arm/mach-msm/hotplug.c
+
+diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
+index 721f27f..8327f60 100644
+--- a/arch/arm/mach-msm/Makefile
++++ b/arch/arm/mach-msm/Makefile
+@@ -18,7 +18,6 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
+
+ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+
+-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+ obj-$(CONFIG_SMP) += platsmp.o
+
+ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
+diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h
+index 33c7725..0a4899b 100644
+--- a/arch/arm/mach-msm/common.h
++++ b/arch/arm/mach-msm/common.h
+@@ -24,7 +24,6 @@ extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size,
+ unsigned int mtype, void *caller);
+
+ extern struct smp_operations msm_smp_ops;
+-extern void msm_cpu_die(unsigned int cpu);
+
+ struct msm_mmc_platform_data;
+
+diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
+deleted file mode 100644
+index cea80fc..0000000
+--- a/arch/arm/mach-msm/hotplug.c
++++ /dev/null
+@@ -1,51 +0,0 @@
+-/*
+- * Copyright (C) 2002 ARM Ltd.
+- * 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 as
+- * published by the Free Software Foundation.
+- */
+-#include <linux/kernel.h>
+-#include <linux/errno.h>
+-#include <linux/smp.h>
+-
+-#include <asm/smp_plat.h>
+-
+-#include "common.h"
+-
+-static inline void cpu_enter_lowpower(void)
+-{
+-}
+-
+-static inline void cpu_leave_lowpower(void)
+-{
+-}
+-
+-static inline void platform_do_lowpower(unsigned int cpu)
+-{
+- asm("wfi"
+- :
+- :
+- : "memory", "cc");
+-}
+-
+-/*
+- * platform-specific code to shutdown a CPU
+- *
+- * Called with IRQs disabled
+- */
+-void __ref msm_cpu_die(unsigned int cpu)
+-{
+- /*
+- * we're ready for shutdown now, so do it
+- */
+- cpu_enter_lowpower();
+- platform_do_lowpower(cpu);
+-
+- /*
+- * bring this CPU back into the world of cache
+- * coherency, and then restore interrupts
+- */
+- cpu_leave_lowpower();
+-}
+diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
+index 3721b31..251a91e 100644
+--- a/arch/arm/mach-msm/platsmp.c
++++ b/arch/arm/mach-msm/platsmp.c
+@@ -29,6 +29,13 @@ extern void secondary_startup(void);
+
+ static DEFINE_SPINLOCK(boot_lock);
+
++#ifdef CONFIG_HOTPLUG_CPU
++static void __ref msm_cpu_die(unsigned int cpu)
++{
++ wfi();
++}
++#endif
++
+ static inline int get_core_count(void)
+ {
+ /* 1 + the PART[1:0] field of MIDR */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch
new file mode 100644
index 0000000..fd730c1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch
@@ -0,0 +1,788 @@
+From 00009eabeb2074bef5c89e576a7a6d827c12c3d9 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 29 Jan 2014 16:17:30 -0600
+Subject: [PATCH 004/182] clocksource: qcom: Move clocksource code out of
+ mach-msm
+
+We intend to share the clocksource code for MSM platforms between legacy
+and multiplatform supported qcom SoCs.
+
+Acked-by: Olof Johansson <olof@lixom.net>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-msm/Kconfig | 13 +-
+ arch/arm/mach-msm/Makefile | 1 -
+ arch/arm/mach-msm/timer.c | 333 --------------------------------------
+ drivers/clocksource/Kconfig | 3 +
+ drivers/clocksource/Makefile | 1 +
+ drivers/clocksource/qcom-timer.c | 329 +++++++++++++++++++++++++++++++++++++
+ 6 files changed, 338 insertions(+), 342 deletions(-)
+ delete mode 100644 arch/arm/mach-msm/timer.c
+ create mode 100644 drivers/clocksource/qcom-timer.c
+
+diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
+index 9625cf3..3c4eca7 100644
+--- a/arch/arm/mach-msm/Kconfig
++++ b/arch/arm/mach-msm/Kconfig
+@@ -21,7 +21,7 @@ config ARCH_MSM8X60
+ select CPU_V7
+ select HAVE_SMP
+ select MSM_SCM if SMP
+- select MSM_TIMER
++ select CLKSRC_QCOM
+
+ config ARCH_MSM8960
+ bool "Enable support for MSM8960"
+@@ -29,7 +29,7 @@ config ARCH_MSM8960
+ select CPU_V7
+ select HAVE_SMP
+ select MSM_SCM if SMP
+- select MSM_TIMER
++ select CLKSRC_QCOM
+
+ config ARCH_MSM8974
+ bool "Enable support for MSM8974"
+@@ -54,7 +54,7 @@ config ARCH_MSM7X00A
+ select MACH_TROUT if !MACH_HALIBUT
+ select MSM_PROC_COMM
+ select MSM_SMD
+- select MSM_TIMER
++ select CLKSRC_QCOM
+ select MSM_SMD_PKG3
+
+ config ARCH_MSM7X30
+@@ -66,7 +66,7 @@ config ARCH_MSM7X30
+ select MSM_GPIOMUX
+ select MSM_PROC_COMM
+ select MSM_SMD
+- select MSM_TIMER
++ select CLKSRC_QCOM
+ select MSM_VIC
+
+ config ARCH_QSD8X50
+@@ -78,7 +78,7 @@ config ARCH_QSD8X50
+ select MSM_GPIOMUX
+ select MSM_PROC_COMM
+ select MSM_SMD
+- select MSM_TIMER
++ select CLKSRC_QCOM
+ select MSM_VIC
+
+ endchoice
+@@ -153,7 +153,4 @@ config MSM_GPIOMUX
+ config MSM_SCM
+ bool
+
+-config MSM_TIMER
+- bool
+-
+ endif
+diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
+index 8327f60..04b1bee 100644
+--- a/arch/arm/mach-msm/Makefile
++++ b/arch/arm/mach-msm/Makefile
+@@ -1,4 +1,3 @@
+-obj-$(CONFIG_MSM_TIMER) += timer.o
+ obj-$(CONFIG_MSM_PROC_COMM) += clock.o
+
+ obj-$(CONFIG_MSM_VIC) += irq-vic.o
+diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
+deleted file mode 100644
+index fd16449..0000000
+--- a/arch/arm/mach-msm/timer.c
++++ /dev/null
+@@ -1,333 +0,0 @@
+-/*
+- *
+- * Copyright (C) 2007 Google, Inc.
+- * Copyright (c) 2009-2012, 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/clocksource.h>
+-#include <linux/clockchips.h>
+-#include <linux/cpu.h>
+-#include <linux/init.h>
+-#include <linux/interrupt.h>
+-#include <linux/irq.h>
+-#include <linux/io.h>
+-#include <linux/of.h>
+-#include <linux/of_address.h>
+-#include <linux/of_irq.h>
+-#include <linux/sched_clock.h>
+-
+-#include <asm/mach/time.h>
+-
+-#include "common.h"
+-
+-#define TIMER_MATCH_VAL 0x0000
+-#define TIMER_COUNT_VAL 0x0004
+-#define TIMER_ENABLE 0x0008
+-#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
+-#define TIMER_ENABLE_EN BIT(0)
+-#define TIMER_CLEAR 0x000C
+-#define DGT_CLK_CTL 0x10
+-#define DGT_CLK_CTL_DIV_4 0x3
+-#define TIMER_STS_GPT0_CLR_PEND BIT(10)
+-
+-#define GPT_HZ 32768
+-
+-#define MSM_DGT_SHIFT 5
+-
+-static void __iomem *event_base;
+-static void __iomem *sts_base;
+-
+-static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
+-{
+- struct clock_event_device *evt = dev_id;
+- /* Stop the timer tick */
+- if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
+- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+- ctrl &= ~TIMER_ENABLE_EN;
+- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+- }
+- evt->event_handler(evt);
+- return IRQ_HANDLED;
+-}
+-
+-static int msm_timer_set_next_event(unsigned long cycles,
+- struct clock_event_device *evt)
+-{
+- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+-
+- ctrl &= ~TIMER_ENABLE_EN;
+- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+-
+- writel_relaxed(ctrl, event_base + TIMER_CLEAR);
+- writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
+-
+- if (sts_base)
+- while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
+- cpu_relax();
+-
+- writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
+- return 0;
+-}
+-
+-static void msm_timer_set_mode(enum clock_event_mode mode,
+- struct clock_event_device *evt)
+-{
+- u32 ctrl;
+-
+- ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+- ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
+-
+- switch (mode) {
+- case CLOCK_EVT_MODE_RESUME:
+- case CLOCK_EVT_MODE_PERIODIC:
+- break;
+- case CLOCK_EVT_MODE_ONESHOT:
+- /* Timer is enabled in set_next_event */
+- break;
+- case CLOCK_EVT_MODE_UNUSED:
+- case CLOCK_EVT_MODE_SHUTDOWN:
+- break;
+- }
+- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+-}
+-
+-static struct clock_event_device __percpu *msm_evt;
+-
+-static void __iomem *source_base;
+-
+-static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
+-{
+- return readl_relaxed(source_base + TIMER_COUNT_VAL);
+-}
+-
+-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
+-{
+- /*
+- * Shift timer count down by a constant due to unreliable lower bits
+- * on some targets.
+- */
+- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
+-}
+-
+-static struct clocksource msm_clocksource = {
+- .name = "dg_timer",
+- .rating = 300,
+- .read = msm_read_timer_count,
+- .mask = CLOCKSOURCE_MASK(32),
+- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+-};
+-
+-static int msm_timer_irq;
+-static int msm_timer_has_ppi;
+-
+-static int msm_local_timer_setup(struct clock_event_device *evt)
+-{
+- int cpu = smp_processor_id();
+- int err;
+-
+- evt->irq = msm_timer_irq;
+- evt->name = "msm_timer";
+- evt->features = CLOCK_EVT_FEAT_ONESHOT;
+- evt->rating = 200;
+- evt->set_mode = msm_timer_set_mode;
+- evt->set_next_event = msm_timer_set_next_event;
+- evt->cpumask = cpumask_of(cpu);
+-
+- clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
+-
+- if (msm_timer_has_ppi) {
+- enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
+- } else {
+- err = request_irq(evt->irq, msm_timer_interrupt,
+- IRQF_TIMER | IRQF_NOBALANCING |
+- IRQF_TRIGGER_RISING, "gp_timer", evt);
+- if (err)
+- pr_err("request_irq failed\n");
+- }
+-
+- return 0;
+-}
+-
+-static void msm_local_timer_stop(struct clock_event_device *evt)
+-{
+- evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
+- disable_percpu_irq(evt->irq);
+-}
+-
+-static int msm_timer_cpu_notify(struct notifier_block *self,
+- unsigned long action, void *hcpu)
+-{
+- /*
+- * Grab cpu pointer in each case to avoid spurious
+- * preemptible warnings
+- */
+- switch (action & ~CPU_TASKS_FROZEN) {
+- case CPU_STARTING:
+- msm_local_timer_setup(this_cpu_ptr(msm_evt));
+- break;
+- case CPU_DYING:
+- msm_local_timer_stop(this_cpu_ptr(msm_evt));
+- break;
+- }
+-
+- return NOTIFY_OK;
+-}
+-
+-static struct notifier_block msm_timer_cpu_nb = {
+- .notifier_call = msm_timer_cpu_notify,
+-};
+-
+-static u64 notrace msm_sched_clock_read(void)
+-{
+- return msm_clocksource.read(&msm_clocksource);
+-}
+-
+-static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
+- bool percpu)
+-{
+- struct clocksource *cs = &msm_clocksource;
+- int res = 0;
+-
+- msm_timer_irq = irq;
+- msm_timer_has_ppi = percpu;
+-
+- msm_evt = alloc_percpu(struct clock_event_device);
+- if (!msm_evt) {
+- pr_err("memory allocation failed for clockevents\n");
+- goto err;
+- }
+-
+- if (percpu)
+- res = request_percpu_irq(irq, msm_timer_interrupt,
+- "gp_timer", msm_evt);
+-
+- if (res) {
+- pr_err("request_percpu_irq failed\n");
+- } else {
+- res = register_cpu_notifier(&msm_timer_cpu_nb);
+- if (res) {
+- free_percpu_irq(irq, msm_evt);
+- goto err;
+- }
+-
+- /* Immediately configure the timer on the boot CPU */
+- msm_local_timer_setup(__this_cpu_ptr(msm_evt));
+- }
+-
+-err:
+- writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
+- res = clocksource_register_hz(cs, dgt_hz);
+- if (res)
+- pr_err("clocksource_register failed\n");
+- sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
+-}
+-
+-#ifdef CONFIG_OF
+-static void __init msm_dt_timer_init(struct device_node *np)
+-{
+- u32 freq;
+- int irq;
+- struct resource res;
+- u32 percpu_offset;
+- void __iomem *base;
+- void __iomem *cpu0_base;
+-
+- base = of_iomap(np, 0);
+- if (!base) {
+- pr_err("Failed to map event base\n");
+- return;
+- }
+-
+- /* We use GPT0 for the clockevent */
+- irq = irq_of_parse_and_map(np, 1);
+- if (irq <= 0) {
+- pr_err("Can't get irq\n");
+- return;
+- }
+-
+- /* We use CPU0's DGT for the clocksource */
+- if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
+- percpu_offset = 0;
+-
+- if (of_address_to_resource(np, 0, &res)) {
+- pr_err("Failed to parse DGT resource\n");
+- return;
+- }
+-
+- cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
+- if (!cpu0_base) {
+- pr_err("Failed to map source base\n");
+- return;
+- }
+-
+- if (of_property_read_u32(np, "clock-frequency", &freq)) {
+- pr_err("Unknown frequency\n");
+- return;
+- }
+-
+- event_base = base + 0x4;
+- sts_base = base + 0x88;
+- source_base = cpu0_base + 0x24;
+- freq /= 4;
+- writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
+-
+- msm_timer_init(freq, 32, irq, !!percpu_offset);
+-}
+-CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+-CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+-#endif
+-
+-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
+- u32 sts)
+-{
+- void __iomem *base;
+-
+- base = ioremap(addr, SZ_256);
+- if (!base) {
+- pr_err("Failed to map timer base\n");
+- return -ENOMEM;
+- }
+- event_base = base + event;
+- source_base = base + source;
+- if (sts)
+- sts_base = base + sts;
+-
+- return 0;
+-}
+-
+-void __init msm7x01_timer_init(void)
+-{
+- struct clocksource *cs = &msm_clocksource;
+-
+- if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
+- return;
+- cs->read = msm_read_timer_count_shift;
+- cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
+- /* 600 KHz */
+- msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
+- false);
+-}
+-
+-void __init msm7x30_timer_init(void)
+-{
+- if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
+- return;
+- msm_timer_init(24576000 / 4, 32, 1, false);
+-}
+-
+-void __init qsd8x50_timer_init(void)
+-{
+- if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
+- return;
+- msm_timer_init(19200000 / 4, 32, 7, false);
+-}
+diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
+index cd6950f..6510ec4 100644
+--- a/drivers/clocksource/Kconfig
++++ b/drivers/clocksource/Kconfig
+@@ -140,3 +140,6 @@ config VF_PIT_TIMER
+ bool
+ help
+ Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
++
++config CLKSRC_QCOM
++ bool
+diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
+index c7ca50a..2e0c0cc 100644
+--- a/drivers/clocksource/Makefile
++++ b/drivers/clocksource/Makefile
+@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
+ obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
+ obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
+ obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
++obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
+
+ obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
+ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
+diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
+new file mode 100644
+index 0000000..dca829e
+--- /dev/null
++++ b/drivers/clocksource/qcom-timer.c
+@@ -0,0 +1,329 @@
++/*
++ *
++ * Copyright (C) 2007 Google, Inc.
++ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/clocksource.h>
++#include <linux/clockchips.h>
++#include <linux/cpu.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/sched_clock.h>
++
++#define TIMER_MATCH_VAL 0x0000
++#define TIMER_COUNT_VAL 0x0004
++#define TIMER_ENABLE 0x0008
++#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
++#define TIMER_ENABLE_EN BIT(0)
++#define TIMER_CLEAR 0x000C
++#define DGT_CLK_CTL 0x10
++#define DGT_CLK_CTL_DIV_4 0x3
++#define TIMER_STS_GPT0_CLR_PEND BIT(10)
++
++#define GPT_HZ 32768
++
++#define MSM_DGT_SHIFT 5
++
++static void __iomem *event_base;
++static void __iomem *sts_base;
++
++static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
++{
++ struct clock_event_device *evt = dev_id;
++ /* Stop the timer tick */
++ if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
++ ctrl &= ~TIMER_ENABLE_EN;
++ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
++ }
++ evt->event_handler(evt);
++ return IRQ_HANDLED;
++}
++
++static int msm_timer_set_next_event(unsigned long cycles,
++ struct clock_event_device *evt)
++{
++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
++
++ ctrl &= ~TIMER_ENABLE_EN;
++ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
++
++ writel_relaxed(ctrl, event_base + TIMER_CLEAR);
++ writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
++
++ if (sts_base)
++ while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
++ cpu_relax();
++
++ writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
++ return 0;
++}
++
++static void msm_timer_set_mode(enum clock_event_mode mode,
++ struct clock_event_device *evt)
++{
++ u32 ctrl;
++
++ ctrl = readl_relaxed(event_base + TIMER_ENABLE);
++ ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
++
++ switch (mode) {
++ case CLOCK_EVT_MODE_RESUME:
++ case CLOCK_EVT_MODE_PERIODIC:
++ break;
++ case CLOCK_EVT_MODE_ONESHOT:
++ /* Timer is enabled in set_next_event */
++ break;
++ case CLOCK_EVT_MODE_UNUSED:
++ case CLOCK_EVT_MODE_SHUTDOWN:
++ break;
++ }
++ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
++}
++
++static struct clock_event_device __percpu *msm_evt;
++
++static void __iomem *source_base;
++
++static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
++{
++ return readl_relaxed(source_base + TIMER_COUNT_VAL);
++}
++
++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
++{
++ /*
++ * Shift timer count down by a constant due to unreliable lower bits
++ * on some targets.
++ */
++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
++}
++
++static struct clocksource msm_clocksource = {
++ .name = "dg_timer",
++ .rating = 300,
++ .read = msm_read_timer_count,
++ .mask = CLOCKSOURCE_MASK(32),
++ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
++};
++
++static int msm_timer_irq;
++static int msm_timer_has_ppi;
++
++static int msm_local_timer_setup(struct clock_event_device *evt)
++{
++ int cpu = smp_processor_id();
++ int err;
++
++ evt->irq = msm_timer_irq;
++ evt->name = "msm_timer";
++ evt->features = CLOCK_EVT_FEAT_ONESHOT;
++ evt->rating = 200;
++ evt->set_mode = msm_timer_set_mode;
++ evt->set_next_event = msm_timer_set_next_event;
++ evt->cpumask = cpumask_of(cpu);
++
++ clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
++
++ if (msm_timer_has_ppi) {
++ enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
++ } else {
++ err = request_irq(evt->irq, msm_timer_interrupt,
++ IRQF_TIMER | IRQF_NOBALANCING |
++ IRQF_TRIGGER_RISING, "gp_timer", evt);
++ if (err)
++ pr_err("request_irq failed\n");
++ }
++
++ return 0;
++}
++
++static void msm_local_timer_stop(struct clock_event_device *evt)
++{
++ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
++ disable_percpu_irq(evt->irq);
++}
++
++static int msm_timer_cpu_notify(struct notifier_block *self,
++ unsigned long action, void *hcpu)
++{
++ /*
++ * Grab cpu pointer in each case to avoid spurious
++ * preemptible warnings
++ */
++ switch (action & ~CPU_TASKS_FROZEN) {
++ case CPU_STARTING:
++ msm_local_timer_setup(this_cpu_ptr(msm_evt));
++ break;
++ case CPU_DYING:
++ msm_local_timer_stop(this_cpu_ptr(msm_evt));
++ break;
++ }
++
++ return NOTIFY_OK;
++}
++
++static struct notifier_block msm_timer_cpu_nb = {
++ .notifier_call = msm_timer_cpu_notify,
++};
++
++static u64 notrace msm_sched_clock_read(void)
++{
++ return msm_clocksource.read(&msm_clocksource);
++}
++
++static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
++ bool percpu)
++{
++ struct clocksource *cs = &msm_clocksource;
++ int res = 0;
++
++ msm_timer_irq = irq;
++ msm_timer_has_ppi = percpu;
++
++ msm_evt = alloc_percpu(struct clock_event_device);
++ if (!msm_evt) {
++ pr_err("memory allocation failed for clockevents\n");
++ goto err;
++ }
++
++ if (percpu)
++ res = request_percpu_irq(irq, msm_timer_interrupt,
++ "gp_timer", msm_evt);
++
++ if (res) {
++ pr_err("request_percpu_irq failed\n");
++ } else {
++ res = register_cpu_notifier(&msm_timer_cpu_nb);
++ if (res) {
++ free_percpu_irq(irq, msm_evt);
++ goto err;
++ }
++
++ /* Immediately configure the timer on the boot CPU */
++ msm_local_timer_setup(__this_cpu_ptr(msm_evt));
++ }
++
++err:
++ writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
++ res = clocksource_register_hz(cs, dgt_hz);
++ if (res)
++ pr_err("clocksource_register failed\n");
++ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
++}
++
++#ifdef CONFIG_OF
++static void __init msm_dt_timer_init(struct device_node *np)
++{
++ u32 freq;
++ int irq;
++ struct resource res;
++ u32 percpu_offset;
++ void __iomem *base;
++ void __iomem *cpu0_base;
++
++ base = of_iomap(np, 0);
++ if (!base) {
++ pr_err("Failed to map event base\n");
++ return;
++ }
++
++ /* We use GPT0 for the clockevent */
++ irq = irq_of_parse_and_map(np, 1);
++ if (irq <= 0) {
++ pr_err("Can't get irq\n");
++ return;
++ }
++
++ /* We use CPU0's DGT for the clocksource */
++ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
++ percpu_offset = 0;
++
++ if (of_address_to_resource(np, 0, &res)) {
++ pr_err("Failed to parse DGT resource\n");
++ return;
++ }
++
++ cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
++ if (!cpu0_base) {
++ pr_err("Failed to map source base\n");
++ return;
++ }
++
++ if (of_property_read_u32(np, "clock-frequency", &freq)) {
++ pr_err("Unknown frequency\n");
++ return;
++ }
++
++ event_base = base + 0x4;
++ sts_base = base + 0x88;
++ source_base = cpu0_base + 0x24;
++ freq /= 4;
++ writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
++
++ msm_timer_init(freq, 32, irq, !!percpu_offset);
++}
++CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
++CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
++#endif
++
++static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
++ u32 sts)
++{
++ void __iomem *base;
++
++ base = ioremap(addr, SZ_256);
++ if (!base) {
++ pr_err("Failed to map timer base\n");
++ return -ENOMEM;
++ }
++ event_base = base + event;
++ source_base = base + source;
++ if (sts)
++ sts_base = base + sts;
++
++ return 0;
++}
++
++void __init msm7x01_timer_init(void)
++{
++ struct clocksource *cs = &msm_clocksource;
++
++ if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
++ return;
++ cs->read = msm_read_timer_count_shift;
++ cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
++ /* 600 KHz */
++ msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
++ false);
++}
++
++void __init msm7x30_timer_init(void)
++{
++ if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
++ return;
++ msm_timer_init(24576000 / 4, 32, 1, false);
++}
++
++void __init qsd8x50_timer_init(void)
++{
++ if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
++ return;
++ msm_timer_init(19200000 / 4, 32, 7, false);
++}
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch
new file mode 100644
index 0000000..c23faf0
--- /dev/null
+++ b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch
@@ -0,0 +1,1482 @@
+From 8c2a00c0129d6f718245f7a613c2bb28976b7973 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 21 Jan 2014 17:14:10 -0600
+Subject: [PATCH 005/182] ARM: qcom: Split Qualcomm support into legacy and
+ multiplatform
+
+Introduce a new mach-qcom that will support SoCs that intend to be
+multiplatform compatible while keeping mach-msm to legacy SoC/board
+support that will not transition over to multiplatform.
+
+As part of this, we move support for MSM8X60, MSM8960 and MSM8974 over
+to mach-qcom.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ MAINTAINERS | 8 ++
+ arch/arm/Kconfig | 7 +-
+ arch/arm/Kconfig.debug | 2 +-
+ arch/arm/Makefile | 1 +
+ arch/arm/boot/dts/Makefile | 6 +-
+ arch/arm/mach-msm/Kconfig | 45 +------
+ arch/arm/mach-msm/Makefile | 6 -
+ arch/arm/mach-msm/board-dt.c | 41 ------
+ arch/arm/mach-msm/platsmp.c | 137 -------------------
+ arch/arm/mach-msm/scm-boot.c | 39 ------
+ arch/arm/mach-msm/scm-boot.h | 22 ---
+ arch/arm/mach-msm/scm.c | 299 -----------------------------------------
+ arch/arm/mach-msm/scm.h | 25 ----
+ arch/arm/mach-qcom/Kconfig | 33 +++++
+ arch/arm/mach-qcom/Makefile | 5 +
+ arch/arm/mach-qcom/board.c | 40 ++++++
+ arch/arm/mach-qcom/platsmp.c | 137 +++++++++++++++++++
+ arch/arm/mach-qcom/scm-boot.c | 39 ++++++
+ arch/arm/mach-qcom/scm-boot.h | 22 +++
+ arch/arm/mach-qcom/scm.c | 299 +++++++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-qcom/scm.h | 25 ++++
+ 21 files changed, 619 insertions(+), 619 deletions(-)
+ delete mode 100644 arch/arm/mach-msm/board-dt.c
+ delete mode 100644 arch/arm/mach-msm/platsmp.c
+ delete mode 100644 arch/arm/mach-msm/scm-boot.c
+ delete mode 100644 arch/arm/mach-msm/scm-boot.h
+ delete mode 100644 arch/arm/mach-msm/scm.c
+ delete mode 100644 arch/arm/mach-msm/scm.h
+ create mode 100644 arch/arm/mach-qcom/Kconfig
+ create mode 100644 arch/arm/mach-qcom/Makefile
+ create mode 100644 arch/arm/mach-qcom/board.c
+ create mode 100644 arch/arm/mach-qcom/platsmp.c
+ create mode 100644 arch/arm/mach-qcom/scm-boot.c
+ create mode 100644 arch/arm/mach-qcom/scm-boot.h
+ create mode 100644 arch/arm/mach-qcom/scm.c
+ create mode 100644 arch/arm/mach-qcom/scm.h
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 900d98e..7d23402 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1168,6 +1168,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+ W: http://www.arm.linux.org.uk/
+ S: Maintained
+
++ARM/QUALCOMM SUPPORT
++M: Kumar Gala <galak@codeaurora.org>
++M: David Brown <davidb@codeaurora.org>
++L: linux-arm-msm@vger.kernel.org
++S: Maintained
++F: arch/arm/mach-qcom/
++T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git
++
+ ARM/RADISYS ENP2611 MACHINE SUPPORT
+ M: Lennert Buytenhek <kernel@wantstofly.org>
+ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 1594945..d02ce70 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -657,9 +657,8 @@ config ARCH_PXA
+ help
+ Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+
+-config ARCH_MSM_NODT
+- bool "Qualcomm MSM"
+- select ARCH_MSM
++config ARCH_MSM
++ bool "Qualcomm MSM (non-multiplatform)"
+ select ARCH_REQUIRE_GPIOLIB
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+@@ -1005,6 +1004,8 @@ source "arch/arm/plat-pxa/Kconfig"
+
+ source "arch/arm/mach-mmp/Kconfig"
+
++source "arch/arm/mach-qcom/Kconfig"
++
+ source "arch/arm/mach-realview/Kconfig"
+
+ source "arch/arm/mach-rockchip/Kconfig"
+diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
+index 0531da8..4491c7b 100644
+--- a/arch/arm/Kconfig.debug
++++ b/arch/arm/Kconfig.debug
+@@ -956,7 +956,7 @@ config DEBUG_STI_UART
+
+ config DEBUG_MSM_UART
+ bool
+- depends on ARCH_MSM
++ depends on ARCH_MSM || ARCH_QCOM
+
+ config DEBUG_LL_INCLUDE
+ string
+diff --git a/arch/arm/Makefile b/arch/arm/Makefile
+index 08a9ef5..51e5bed 100644
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
+ machine-$(CONFIG_ARCH_ORION5X) += orion5x
+ machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
+ machine-$(CONFIG_ARCH_PXA) += pxa
++machine-$(CONFIG_ARCH_QCOM) += qcom
+ machine-$(CONFIG_ARCH_REALVIEW) += realview
+ machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip
+ machine-$(CONFIG_ARCH_RPC) += rpc
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index 0320303..4a89023 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -119,9 +119,6 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-cloudbox.dtb \
+ kirkwood-ts219-6282.dtb
+ dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
+ dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
+-dtb-$(CONFIG_ARCH_MSM) += qcom-msm8660-surf.dtb \
+- qcom-msm8960-cdp.dtb \
+- qcom-apq8074-dragonboard.dtb
+ dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \
+ armada-370-mirabox.dtb \
+ armada-370-netgear-rn102.dtb \
+@@ -234,6 +231,9 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
+ dra7-evm.dtb
+ dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb
+ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
++dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \
++ qcom-msm8960-cdp.dtb \
++ qcom-apq8074-dragonboard.dtb
+ dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \
+ ste-hrefprev60-stuib.dtb \
+ ste-hrefprev60-tvk.dtb \
+diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
+index 3c4eca7..a7f959e 100644
+--- a/arch/arm/mach-msm/Kconfig
++++ b/arch/arm/mach-msm/Kconfig
+@@ -1,50 +1,9 @@
+-config ARCH_MSM
+- bool
+-
+-config ARCH_MSM_DT
+- bool "Qualcomm MSM DT Support" if ARCH_MULTI_V7
+- select ARCH_MSM
+- select ARCH_REQUIRE_GPIOLIB
+- select CLKSRC_OF
+- select GENERIC_CLOCKEVENTS
+- help
+- Support for Qualcomm's devicetree based MSM systems.
+-
+ if ARCH_MSM
+
+-menu "Qualcomm MSM SoC Selection"
+- depends on ARCH_MSM_DT
+-
+-config ARCH_MSM8X60
+- bool "Enable support for MSM8X60"
+- select ARM_GIC
+- select CPU_V7
+- select HAVE_SMP
+- select MSM_SCM if SMP
+- select CLKSRC_QCOM
+-
+-config ARCH_MSM8960
+- bool "Enable support for MSM8960"
+- select ARM_GIC
+- select CPU_V7
+- select HAVE_SMP
+- select MSM_SCM if SMP
+- select CLKSRC_QCOM
+-
+-config ARCH_MSM8974
+- bool "Enable support for MSM8974"
+- select ARM_GIC
+- select CPU_V7
+- select HAVE_ARM_ARCH_TIMER
+- select HAVE_SMP
+- select MSM_SCM if SMP
+-
+-endmenu
+-
+ choice
+ prompt "Qualcomm MSM SoC Type"
+ default ARCH_MSM7X00A
+- depends on ARCH_MSM_NODT
++ depends on ARCH_MSM
+
+ config ARCH_MSM7X00A
+ bool "MSM7x00A / MSM7x01A"
+@@ -99,7 +58,7 @@ config MSM_VIC
+ bool
+
+ menu "Qualcomm MSM Board Type"
+- depends on ARCH_MSM_NODT
++ depends on ARCH_MSM
+
+ config MACH_HALIBUT
+ depends on ARCH_MSM
+diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
+index 04b1bee..27c078a 100644
+--- a/arch/arm/mach-msm/Makefile
++++ b/arch/arm/mach-msm/Makefile
+@@ -13,17 +13,11 @@ obj-$(CONFIG_ARCH_QSD8X50) += dma.o io.o
+
+ obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
+ obj-$(CONFIG_MSM_SMD) += last_radio_log.o
+-obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
+-
+-CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+-
+-obj-$(CONFIG_SMP) += platsmp.o
+
+ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
+ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
+ obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
+ obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
+ obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o
+-obj-$(CONFIG_ARCH_MSM_DT) += board-dt.o
+ obj-$(CONFIG_MSM_GPIOMUX) += gpiomux.o
+ obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o
+diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
+deleted file mode 100644
+index 1f11d93..0000000
+--- a/arch/arm/mach-msm/board-dt.c
++++ /dev/null
+@@ -1,41 +0,0 @@
+-/* Copyright (c) 2010-2012,2013 The Linux Foundation. All rights reserved.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 and
+- * only version 2 as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- */
+-
+-#include <linux/init.h>
+-#include <linux/of.h>
+-#include <linux/of_platform.h>
+-
+-#include <asm/mach/arch.h>
+-#include <asm/mach/map.h>
+-
+-#include "common.h"
+-
+-static const char * const msm_dt_match[] __initconst = {
+- "qcom,msm8660-fluid",
+- "qcom,msm8660-surf",
+- "qcom,msm8960-cdp",
+- NULL
+-};
+-
+-static const char * const apq8074_dt_match[] __initconst = {
+- "qcom,apq8074-dragonboard",
+- NULL
+-};
+-
+-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+- .smp = smp_ops(msm_smp_ops),
+- .dt_compat = msm_dt_match,
+-MACHINE_END
+-
+-DT_MACHINE_START(APQ_DT, "Qualcomm MSM (Flattened Device Tree)")
+- .dt_compat = apq8074_dt_match,
+-MACHINE_END
+diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
+deleted file mode 100644
+index 251a91e..0000000
+--- a/arch/arm/mach-msm/platsmp.c
++++ /dev/null
+@@ -1,137 +0,0 @@
+-/*
+- * Copyright (C) 2002 ARM Ltd.
+- * All Rights Reserved
+- * Copyright (c) 2010, 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 as
+- * published by the Free Software Foundation.
+- */
+-
+-#include <linux/init.h>
+-#include <linux/errno.h>
+-#include <linux/delay.h>
+-#include <linux/device.h>
+-#include <linux/smp.h>
+-#include <linux/io.h>
+-
+-#include <asm/cputype.h>
+-#include <asm/smp_plat.h>
+-
+-#include "scm-boot.h"
+-#include "common.h"
+-
+-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
+-#define SCSS_CPU1CORE_RESET 0xD80
+-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
+-
+-extern void secondary_startup(void);
+-
+-static DEFINE_SPINLOCK(boot_lock);
+-
+-#ifdef CONFIG_HOTPLUG_CPU
+-static void __ref msm_cpu_die(unsigned int cpu)
+-{
+- wfi();
+-}
+-#endif
+-
+-static inline int get_core_count(void)
+-{
+- /* 1 + the PART[1:0] field of MIDR */
+- return ((read_cpuid_id() >> 4) & 3) + 1;
+-}
+-
+-static void msm_secondary_init(unsigned int cpu)
+-{
+- /*
+- * Synchronise with the boot thread.
+- */
+- spin_lock(&boot_lock);
+- spin_unlock(&boot_lock);
+-}
+-
+-static void prepare_cold_cpu(unsigned int cpu)
+-{
+- int ret;
+- ret = scm_set_boot_addr(virt_to_phys(secondary_startup),
+- SCM_FLAG_COLDBOOT_CPU1);
+- if (ret == 0) {
+- void __iomem *sc1_base_ptr;
+- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
+- if (sc1_base_ptr) {
+- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
+- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
+- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
+- iounmap(sc1_base_ptr);
+- }
+- } else
+- printk(KERN_DEBUG "Failed to set secondary core boot "
+- "address\n");
+-}
+-
+-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
+-{
+- static int cold_boot_done;
+-
+- /* Only need to bring cpu out of reset this way once */
+- if (cold_boot_done == false) {
+- prepare_cold_cpu(cpu);
+- cold_boot_done = true;
+- }
+-
+- /*
+- * set synchronisation state between this boot processor
+- * and the secondary one
+- */
+- spin_lock(&boot_lock);
+-
+- /*
+- * Send the secondary CPU a soft interrupt, thereby causing
+- * the boot monitor to read the system wide flags register,
+- * and branch to the address found there.
+- */
+- arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+-
+- /*
+- * now the secondary core is starting up let it run its
+- * calibrations, then wait for it to finish
+- */
+- spin_unlock(&boot_lock);
+-
+- return 0;
+-}
+-
+-/*
+- * Initialise the CPU possible map early - this describes the CPUs
+- * which may be present or become present in the system. The msm8x60
+- * does not support the ARM SCU, so just set the possible cpu mask to
+- * NR_CPUS.
+- */
+-static void __init msm_smp_init_cpus(void)
+-{
+- unsigned int i, ncores = get_core_count();
+-
+- if (ncores > nr_cpu_ids) {
+- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+- ncores, nr_cpu_ids);
+- ncores = nr_cpu_ids;
+- }
+-
+- for (i = 0; i < ncores; i++)
+- set_cpu_possible(i, true);
+-}
+-
+-static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
+-{
+-}
+-
+-struct smp_operations msm_smp_ops __initdata = {
+- .smp_init_cpus = msm_smp_init_cpus,
+- .smp_prepare_cpus = msm_smp_prepare_cpus,
+- .smp_secondary_init = msm_secondary_init,
+- .smp_boot_secondary = msm_boot_secondary,
+-#ifdef CONFIG_HOTPLUG_CPU
+- .cpu_die = msm_cpu_die,
+-#endif
+-};
+diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c
+deleted file mode 100644
+index 45cee3e..0000000
+--- a/arch/arm/mach-msm/scm-boot.c
++++ /dev/null
+@@ -1,39 +0,0 @@
+-/* Copyright (c) 2010, 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.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+- * 02110-1301, USA.
+- */
+-
+-#include <linux/module.h>
+-#include <linux/slab.h>
+-
+-#include "scm.h"
+-#include "scm-boot.h"
+-
+-/*
+- * Set the cold/warm boot address for one of the CPU cores.
+- */
+-int scm_set_boot_addr(phys_addr_t addr, int flags)
+-{
+- struct {
+- unsigned int flags;
+- phys_addr_t addr;
+- } cmd;
+-
+- cmd.addr = addr;
+- cmd.flags = flags;
+- return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
+- &cmd, sizeof(cmd), NULL, 0);
+-}
+-EXPORT_SYMBOL(scm_set_boot_addr);
+diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h
+deleted file mode 100644
+index 7be32ff..0000000
+--- a/arch/arm/mach-msm/scm-boot.h
++++ /dev/null
+@@ -1,22 +0,0 @@
+-/* Copyright (c) 2010, 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.
+- */
+-#ifndef __MACH_SCM_BOOT_H
+-#define __MACH_SCM_BOOT_H
+-
+-#define SCM_BOOT_ADDR 0x1
+-#define SCM_FLAG_COLDBOOT_CPU1 0x1
+-#define SCM_FLAG_WARMBOOT_CPU1 0x2
+-#define SCM_FLAG_WARMBOOT_CPU0 0x4
+-
+-int scm_set_boot_addr(phys_addr_t addr, int flags);
+-
+-#endif
+diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
+deleted file mode 100644
+index c536fd6..0000000
+--- a/arch/arm/mach-msm/scm.c
++++ /dev/null
+@@ -1,299 +0,0 @@
+-/* Copyright (c) 2010, 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.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+- * 02110-1301, USA.
+- */
+-
+-#include <linux/slab.h>
+-#include <linux/io.h>
+-#include <linux/module.h>
+-#include <linux/mutex.h>
+-#include <linux/errno.h>
+-#include <linux/err.h>
+-
+-#include <asm/cacheflush.h>
+-
+-#include "scm.h"
+-
+-/* Cache line size for msm8x60 */
+-#define CACHELINESIZE 32
+-
+-#define SCM_ENOMEM -5
+-#define SCM_EOPNOTSUPP -4
+-#define SCM_EINVAL_ADDR -3
+-#define SCM_EINVAL_ARG -2
+-#define SCM_ERROR -1
+-#define SCM_INTERRUPTED 1
+-
+-static DEFINE_MUTEX(scm_lock);
+-
+-/**
+- * struct scm_command - one SCM command buffer
+- * @len: total available memory for command and response
+- * @buf_offset: start of command buffer
+- * @resp_hdr_offset: start of response buffer
+- * @id: command to be executed
+- * @buf: buffer returned from scm_get_command_buffer()
+- *
+- * An SCM command is laid out in memory as follows:
+- *
+- * ------------------- <--- struct scm_command
+- * | command header |
+- * ------------------- <--- scm_get_command_buffer()
+- * | command buffer |
+- * ------------------- <--- struct scm_response and
+- * | response header | scm_command_to_response()
+- * ------------------- <--- scm_get_response_buffer()
+- * | response buffer |
+- * -------------------
+- *
+- * There can be arbitrary padding between the headers and buffers so
+- * you should always use the appropriate scm_get_*_buffer() routines
+- * to access the buffers in a safe manner.
+- */
+-struct scm_command {
+- u32 len;
+- u32 buf_offset;
+- u32 resp_hdr_offset;
+- u32 id;
+- u32 buf[0];
+-};
+-
+-/**
+- * struct scm_response - one SCM response buffer
+- * @len: total available memory for response
+- * @buf_offset: start of response data relative to start of scm_response
+- * @is_complete: indicates if the command has finished processing
+- */
+-struct scm_response {
+- u32 len;
+- u32 buf_offset;
+- u32 is_complete;
+-};
+-
+-/**
+- * alloc_scm_command() - Allocate an SCM command
+- * @cmd_size: size of the command buffer
+- * @resp_size: size of the response buffer
+- *
+- * Allocate an SCM command, including enough room for the command
+- * and response headers as well as the command and response buffers.
+- *
+- * Returns a valid &scm_command on success or %NULL if the allocation fails.
+- */
+-static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
+-{
+- struct scm_command *cmd;
+- size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
+- resp_size;
+-
+- cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
+- if (cmd) {
+- cmd->len = len;
+- cmd->buf_offset = offsetof(struct scm_command, buf);
+- cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
+- }
+- return cmd;
+-}
+-
+-/**
+- * free_scm_command() - Free an SCM command
+- * @cmd: command to free
+- *
+- * Free an SCM command.
+- */
+-static inline void free_scm_command(struct scm_command *cmd)
+-{
+- kfree(cmd);
+-}
+-
+-/**
+- * scm_command_to_response() - Get a pointer to a scm_response
+- * @cmd: command
+- *
+- * Returns a pointer to a response for a command.
+- */
+-static inline struct scm_response *scm_command_to_response(
+- const struct scm_command *cmd)
+-{
+- return (void *)cmd + cmd->resp_hdr_offset;
+-}
+-
+-/**
+- * scm_get_command_buffer() - Get a pointer to a command buffer
+- * @cmd: command
+- *
+- * Returns a pointer to the command buffer of a command.
+- */
+-static inline void *scm_get_command_buffer(const struct scm_command *cmd)
+-{
+- return (void *)cmd->buf;
+-}
+-
+-/**
+- * scm_get_response_buffer() - Get a pointer to a response buffer
+- * @rsp: response
+- *
+- * Returns a pointer to a response buffer of a response.
+- */
+-static inline void *scm_get_response_buffer(const struct scm_response *rsp)
+-{
+- return (void *)rsp + rsp->buf_offset;
+-}
+-
+-static int scm_remap_error(int err)
+-{
+- switch (err) {
+- case SCM_ERROR:
+- return -EIO;
+- case SCM_EINVAL_ADDR:
+- case SCM_EINVAL_ARG:
+- return -EINVAL;
+- case SCM_EOPNOTSUPP:
+- return -EOPNOTSUPP;
+- case SCM_ENOMEM:
+- return -ENOMEM;
+- }
+- return -EINVAL;
+-}
+-
+-static u32 smc(u32 cmd_addr)
+-{
+- int context_id;
+- register u32 r0 asm("r0") = 1;
+- register u32 r1 asm("r1") = (u32)&context_id;
+- register u32 r2 asm("r2") = cmd_addr;
+- do {
+- asm volatile(
+- __asmeq("%0", "r0")
+- __asmeq("%1", "r0")
+- __asmeq("%2", "r1")
+- __asmeq("%3", "r2")
+-#ifdef REQUIRES_SEC
+- ".arch_extension sec\n"
+-#endif
+- "smc #0 @ switch to secure world\n"
+- : "=r" (r0)
+- : "r" (r0), "r" (r1), "r" (r2)
+- : "r3");
+- } while (r0 == SCM_INTERRUPTED);
+-
+- return r0;
+-}
+-
+-static int __scm_call(const struct scm_command *cmd)
+-{
+- int ret;
+- u32 cmd_addr = virt_to_phys(cmd);
+-
+- /*
+- * Flush the entire cache here so callers don't have to remember
+- * to flush the cache when passing physical addresses to the secure
+- * side in the buffer.
+- */
+- flush_cache_all();
+- ret = smc(cmd_addr);
+- if (ret < 0)
+- ret = scm_remap_error(ret);
+-
+- return ret;
+-}
+-
+-/**
+- * scm_call() - Send an SCM command
+- * @svc_id: service identifier
+- * @cmd_id: command identifier
+- * @cmd_buf: command buffer
+- * @cmd_len: length of the command buffer
+- * @resp_buf: response buffer
+- * @resp_len: length of the response buffer
+- *
+- * Sends a command to the SCM and waits for the command to finish processing.
+- */
+-int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+- void *resp_buf, size_t resp_len)
+-{
+- int ret;
+- struct scm_command *cmd;
+- struct scm_response *rsp;
+-
+- cmd = alloc_scm_command(cmd_len, resp_len);
+- if (!cmd)
+- return -ENOMEM;
+-
+- cmd->id = (svc_id << 10) | cmd_id;
+- if (cmd_buf)
+- memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
+-
+- mutex_lock(&scm_lock);
+- ret = __scm_call(cmd);
+- mutex_unlock(&scm_lock);
+- if (ret)
+- goto out;
+-
+- rsp = scm_command_to_response(cmd);
+- do {
+- u32 start = (u32)rsp;
+- u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
+- start &= ~(CACHELINESIZE - 1);
+- while (start < end) {
+- asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
+- : "memory");
+- start += CACHELINESIZE;
+- }
+- } while (!rsp->is_complete);
+-
+- if (resp_buf)
+- memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
+-out:
+- free_scm_command(cmd);
+- return ret;
+-}
+-EXPORT_SYMBOL(scm_call);
+-
+-u32 scm_get_version(void)
+-{
+- int context_id;
+- static u32 version = -1;
+- register u32 r0 asm("r0");
+- register u32 r1 asm("r1");
+-
+- if (version != -1)
+- return version;
+-
+- mutex_lock(&scm_lock);
+-
+- r0 = 0x1 << 8;
+- r1 = (u32)&context_id;
+- do {
+- asm volatile(
+- __asmeq("%0", "r0")
+- __asmeq("%1", "r1")
+- __asmeq("%2", "r0")
+- __asmeq("%3", "r1")
+-#ifdef REQUIRES_SEC
+- ".arch_extension sec\n"
+-#endif
+- "smc #0 @ switch to secure world\n"
+- : "=r" (r0), "=r" (r1)
+- : "r" (r0), "r" (r1)
+- : "r2", "r3");
+- } while (r0 == SCM_INTERRUPTED);
+-
+- version = r1;
+- mutex_unlock(&scm_lock);
+-
+- return version;
+-}
+-EXPORT_SYMBOL(scm_get_version);
+diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-msm/scm.h
+deleted file mode 100644
+index 00b31ea..0000000
+--- a/arch/arm/mach-msm/scm.h
++++ /dev/null
+@@ -1,25 +0,0 @@
+-/* Copyright (c) 2010, 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.
+- */
+-#ifndef __MACH_SCM_H
+-#define __MACH_SCM_H
+-
+-#define SCM_SVC_BOOT 0x1
+-#define SCM_SVC_PIL 0x2
+-
+-extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+- void *resp_buf, size_t resp_len);
+-
+-#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
+-
+-extern u32 scm_get_version(void);
+-
+-#endif
+diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
+new file mode 100644
+index 0000000..a028be2
+--- /dev/null
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -0,0 +1,33 @@
++config ARCH_QCOM
++ bool "Qualcomm Support" if ARCH_MULTI_V7
++ select ARCH_REQUIRE_GPIOLIB
++ select ARM_GIC
++ select CLKSRC_OF
++ select GENERIC_CLOCKEVENTS
++ select HAVE_SMP
++ select QCOM_SCM if SMP
++ help
++ Support for Qualcomm's devicetree based systems.
++
++if ARCH_QCOM
++
++menu "Qualcomm SoC Selection"
++
++config ARCH_MSM8X60
++ bool "Enable support for MSM8X60"
++ select CLKSRC_QCOM
++
++config ARCH_MSM8960
++ bool "Enable support for MSM8960"
++ select CLKSRC_QCOM
++
++config ARCH_MSM8974
++ bool "Enable support for MSM8974"
++ select HAVE_ARM_ARCH_TIMER
++
++endmenu
++
++config QCOM_SCM
++ bool
++
++endif
+diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
+new file mode 100644
+index 0000000..8f756ae
+--- /dev/null
++++ b/arch/arm/mach-qcom/Makefile
+@@ -0,0 +1,5 @@
++obj-y := board.o
++obj-$(CONFIG_SMP) += platsmp.o
++obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
++
++CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+new file mode 100644
+index 0000000..4529f6b
+--- /dev/null
++++ b/arch/arm/mach-qcom/board.c
+@@ -0,0 +1,40 @@
++/* Copyright (c) 2010-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/init.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++
++extern struct smp_operations msm_smp_ops;
++
++static const char * const qcom_dt_match[] __initconst = {
++ "qcom,msm8660-surf",
++ "qcom,msm8960-cdp",
++ NULL
++};
++
++static const char * const apq8074_dt_match[] __initconst = {
++ "qcom,apq8074-dragonboard",
++ NULL
++};
++
++DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)")
++ .smp = smp_ops(msm_smp_ops),
++ .dt_compat = qcom_dt_match,
++MACHINE_END
++
++DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)")
++ .dt_compat = apq8074_dt_match,
++MACHINE_END
+diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
+new file mode 100644
+index 0000000..67823a7
+--- /dev/null
++++ b/arch/arm/mach-qcom/platsmp.c
+@@ -0,0 +1,137 @@
++/*
++ * Copyright (C) 2002 ARM Ltd.
++ * All Rights Reserved
++ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
++ * 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 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/smp.h>
++#include <linux/io.h>
++
++#include <asm/cputype.h>
++#include <asm/smp_plat.h>
++
++#include "scm-boot.h"
++
++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
++#define SCSS_CPU1CORE_RESET 0xD80
++#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
++
++extern void secondary_startup(void);
++
++static DEFINE_SPINLOCK(boot_lock);
++
++#ifdef CONFIG_HOTPLUG_CPU
++static void __ref msm_cpu_die(unsigned int cpu)
++{
++ wfi();
++}
++#endif
++
++static inline int get_core_count(void)
++{
++ /* 1 + the PART[1:0] field of MIDR */
++ return ((read_cpuid_id() >> 4) & 3) + 1;
++}
++
++static void msm_secondary_init(unsigned int cpu)
++{
++ /*
++ * Synchronise with the boot thread.
++ */
++ spin_lock(&boot_lock);
++ spin_unlock(&boot_lock);
++}
++
++static void prepare_cold_cpu(unsigned int cpu)
++{
++ int ret;
++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup),
++ SCM_FLAG_COLDBOOT_CPU1);
++ if (ret == 0) {
++ void __iomem *sc1_base_ptr;
++ sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
++ if (sc1_base_ptr) {
++ writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
++ writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
++ writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
++ iounmap(sc1_base_ptr);
++ }
++ } else
++ printk(KERN_DEBUG "Failed to set secondary core boot "
++ "address\n");
++}
++
++static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
++{
++ static int cold_boot_done;
++
++ /* Only need to bring cpu out of reset this way once */
++ if (cold_boot_done == false) {
++ prepare_cold_cpu(cpu);
++ cold_boot_done = true;
++ }
++
++ /*
++ * set synchronisation state between this boot processor
++ * and the secondary one
++ */
++ spin_lock(&boot_lock);
++
++ /*
++ * Send the secondary CPU a soft interrupt, thereby causing
++ * the boot monitor to read the system wide flags register,
++ * and branch to the address found there.
++ */
++ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
++
++ /*
++ * now the secondary core is starting up let it run its
++ * calibrations, then wait for it to finish
++ */
++ spin_unlock(&boot_lock);
++
++ return 0;
++}
++
++/*
++ * Initialise the CPU possible map early - this describes the CPUs
++ * which may be present or become present in the system. The msm8x60
++ * does not support the ARM SCU, so just set the possible cpu mask to
++ * NR_CPUS.
++ */
++static void __init msm_smp_init_cpus(void)
++{
++ unsigned int i, ncores = get_core_count();
++
++ if (ncores > nr_cpu_ids) {
++ pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
++ ncores, nr_cpu_ids);
++ ncores = nr_cpu_ids;
++ }
++
++ for (i = 0; i < ncores; i++)
++ set_cpu_possible(i, true);
++}
++
++static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
++{
++}
++
++struct smp_operations msm_smp_ops __initdata = {
++ .smp_init_cpus = msm_smp_init_cpus,
++ .smp_prepare_cpus = msm_smp_prepare_cpus,
++ .smp_secondary_init = msm_secondary_init,
++ .smp_boot_secondary = msm_boot_secondary,
++#ifdef CONFIG_HOTPLUG_CPU
++ .cpu_die = msm_cpu_die,
++#endif
++};
+diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c
+new file mode 100644
+index 0000000..45cee3e
+--- /dev/null
++++ b/arch/arm/mach-qcom/scm-boot.c
+@@ -0,0 +1,39 @@
++/* Copyright (c) 2010, 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA.
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include "scm.h"
++#include "scm-boot.h"
++
++/*
++ * Set the cold/warm boot address for one of the CPU cores.
++ */
++int scm_set_boot_addr(phys_addr_t addr, int flags)
++{
++ struct {
++ unsigned int flags;
++ phys_addr_t addr;
++ } cmd;
++
++ cmd.addr = addr;
++ cmd.flags = flags;
++ return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
++ &cmd, sizeof(cmd), NULL, 0);
++}
++EXPORT_SYMBOL(scm_set_boot_addr);
+diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h
+new file mode 100644
+index 0000000..7be32ff
+--- /dev/null
++++ b/arch/arm/mach-qcom/scm-boot.h
+@@ -0,0 +1,22 @@
++/* Copyright (c) 2010, 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.
++ */
++#ifndef __MACH_SCM_BOOT_H
++#define __MACH_SCM_BOOT_H
++
++#define SCM_BOOT_ADDR 0x1
++#define SCM_FLAG_COLDBOOT_CPU1 0x1
++#define SCM_FLAG_WARMBOOT_CPU1 0x2
++#define SCM_FLAG_WARMBOOT_CPU0 0x4
++
++int scm_set_boot_addr(phys_addr_t addr, int flags);
++
++#endif
+diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c
+new file mode 100644
+index 0000000..c536fd6
+--- /dev/null
++++ b/arch/arm/mach-qcom/scm.c
+@@ -0,0 +1,299 @@
++/* Copyright (c) 2010, 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA.
++ */
++
++#include <linux/slab.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/errno.h>
++#include <linux/err.h>
++
++#include <asm/cacheflush.h>
++
++#include "scm.h"
++
++/* Cache line size for msm8x60 */
++#define CACHELINESIZE 32
++
++#define SCM_ENOMEM -5
++#define SCM_EOPNOTSUPP -4
++#define SCM_EINVAL_ADDR -3
++#define SCM_EINVAL_ARG -2
++#define SCM_ERROR -1
++#define SCM_INTERRUPTED 1
++
++static DEFINE_MUTEX(scm_lock);
++
++/**
++ * struct scm_command - one SCM command buffer
++ * @len: total available memory for command and response
++ * @buf_offset: start of command buffer
++ * @resp_hdr_offset: start of response buffer
++ * @id: command to be executed
++ * @buf: buffer returned from scm_get_command_buffer()
++ *
++ * An SCM command is laid out in memory as follows:
++ *
++ * ------------------- <--- struct scm_command
++ * | command header |
++ * ------------------- <--- scm_get_command_buffer()
++ * | command buffer |
++ * ------------------- <--- struct scm_response and
++ * | response header | scm_command_to_response()
++ * ------------------- <--- scm_get_response_buffer()
++ * | response buffer |
++ * -------------------
++ *
++ * There can be arbitrary padding between the headers and buffers so
++ * you should always use the appropriate scm_get_*_buffer() routines
++ * to access the buffers in a safe manner.
++ */
++struct scm_command {
++ u32 len;
++ u32 buf_offset;
++ u32 resp_hdr_offset;
++ u32 id;
++ u32 buf[0];
++};
++
++/**
++ * struct scm_response - one SCM response buffer
++ * @len: total available memory for response
++ * @buf_offset: start of response data relative to start of scm_response
++ * @is_complete: indicates if the command has finished processing
++ */
++struct scm_response {
++ u32 len;
++ u32 buf_offset;
++ u32 is_complete;
++};
++
++/**
++ * alloc_scm_command() - Allocate an SCM command
++ * @cmd_size: size of the command buffer
++ * @resp_size: size of the response buffer
++ *
++ * Allocate an SCM command, including enough room for the command
++ * and response headers as well as the command and response buffers.
++ *
++ * Returns a valid &scm_command on success or %NULL if the allocation fails.
++ */
++static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
++{
++ struct scm_command *cmd;
++ size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
++ resp_size;
++
++ cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
++ if (cmd) {
++ cmd->len = len;
++ cmd->buf_offset = offsetof(struct scm_command, buf);
++ cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
++ }
++ return cmd;
++}
++
++/**
++ * free_scm_command() - Free an SCM command
++ * @cmd: command to free
++ *
++ * Free an SCM command.
++ */
++static inline void free_scm_command(struct scm_command *cmd)
++{
++ kfree(cmd);
++}
++
++/**
++ * scm_command_to_response() - Get a pointer to a scm_response
++ * @cmd: command
++ *
++ * Returns a pointer to a response for a command.
++ */
++static inline struct scm_response *scm_command_to_response(
++ const struct scm_command *cmd)
++{
++ return (void *)cmd + cmd->resp_hdr_offset;
++}
++
++/**
++ * scm_get_command_buffer() - Get a pointer to a command buffer
++ * @cmd: command
++ *
++ * Returns a pointer to the command buffer of a command.
++ */
++static inline void *scm_get_command_buffer(const struct scm_command *cmd)
++{
++ return (void *)cmd->buf;
++}
++
++/**
++ * scm_get_response_buffer() - Get a pointer to a response buffer
++ * @rsp: response
++ *
++ * Returns a pointer to a response buffer of a response.
++ */
++static inline void *scm_get_response_buffer(const struct scm_response *rsp)
++{
++ return (void *)rsp + rsp->buf_offset;
++}
++
++static int scm_remap_error(int err)
++{
++ switch (err) {
++ case SCM_ERROR:
++ return -EIO;
++ case SCM_EINVAL_ADDR:
++ case SCM_EINVAL_ARG:
++ return -EINVAL;
++ case SCM_EOPNOTSUPP:
++ return -EOPNOTSUPP;
++ case SCM_ENOMEM:
++ return -ENOMEM;
++ }
++ return -EINVAL;
++}
++
++static u32 smc(u32 cmd_addr)
++{
++ int context_id;
++ register u32 r0 asm("r0") = 1;
++ register u32 r1 asm("r1") = (u32)&context_id;
++ register u32 r2 asm("r2") = cmd_addr;
++ do {
++ asm volatile(
++ __asmeq("%0", "r0")
++ __asmeq("%1", "r0")
++ __asmeq("%2", "r1")
++ __asmeq("%3", "r2")
++#ifdef REQUIRES_SEC
++ ".arch_extension sec\n"
++#endif
++ "smc #0 @ switch to secure world\n"
++ : "=r" (r0)
++ : "r" (r0), "r" (r1), "r" (r2)
++ : "r3");
++ } while (r0 == SCM_INTERRUPTED);
++
++ return r0;
++}
++
++static int __scm_call(const struct scm_command *cmd)
++{
++ int ret;
++ u32 cmd_addr = virt_to_phys(cmd);
++
++ /*
++ * Flush the entire cache here so callers don't have to remember
++ * to flush the cache when passing physical addresses to the secure
++ * side in the buffer.
++ */
++ flush_cache_all();
++ ret = smc(cmd_addr);
++ if (ret < 0)
++ ret = scm_remap_error(ret);
++
++ return ret;
++}
++
++/**
++ * scm_call() - Send an SCM command
++ * @svc_id: service identifier
++ * @cmd_id: command identifier
++ * @cmd_buf: command buffer
++ * @cmd_len: length of the command buffer
++ * @resp_buf: response buffer
++ * @resp_len: length of the response buffer
++ *
++ * Sends a command to the SCM and waits for the command to finish processing.
++ */
++int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
++ void *resp_buf, size_t resp_len)
++{
++ int ret;
++ struct scm_command *cmd;
++ struct scm_response *rsp;
++
++ cmd = alloc_scm_command(cmd_len, resp_len);
++ if (!cmd)
++ return -ENOMEM;
++
++ cmd->id = (svc_id << 10) | cmd_id;
++ if (cmd_buf)
++ memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
++
++ mutex_lock(&scm_lock);
++ ret = __scm_call(cmd);
++ mutex_unlock(&scm_lock);
++ if (ret)
++ goto out;
++
++ rsp = scm_command_to_response(cmd);
++ do {
++ u32 start = (u32)rsp;
++ u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
++ start &= ~(CACHELINESIZE - 1);
++ while (start < end) {
++ asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
++ : "memory");
++ start += CACHELINESIZE;
++ }
++ } while (!rsp->is_complete);
++
++ if (resp_buf)
++ memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
++out:
++ free_scm_command(cmd);
++ return ret;
++}
++EXPORT_SYMBOL(scm_call);
++
++u32 scm_get_version(void)
++{
++ int context_id;
++ static u32 version = -1;
++ register u32 r0 asm("r0");
++ register u32 r1 asm("r1");
++
++ if (version != -1)
++ return version;
++
++ mutex_lock(&scm_lock);
++
++ r0 = 0x1 << 8;
++ r1 = (u32)&context_id;
++ do {
++ asm volatile(
++ __asmeq("%0", "r0")
++ __asmeq("%1", "r1")
++ __asmeq("%2", "r0")
++ __asmeq("%3", "r1")
++#ifdef REQUIRES_SEC
++ ".arch_extension sec\n"
++#endif
++ "smc #0 @ switch to secure world\n"
++ : "=r" (r0), "=r" (r1)
++ : "r" (r0), "r" (r1)
++ : "r2", "r3");
++ } while (r0 == SCM_INTERRUPTED);
++
++ version = r1;
++ mutex_unlock(&scm_lock);
++
++ return version;
++}
++EXPORT_SYMBOL(scm_get_version);
+diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h
+new file mode 100644
+index 0000000..00b31ea
+--- /dev/null
++++ b/arch/arm/mach-qcom/scm.h
+@@ -0,0 +1,25 @@
++/* Copyright (c) 2010, 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.
++ */
++#ifndef __MACH_SCM_H
++#define __MACH_SCM_H
++
++#define SCM_SVC_BOOT 0x1
++#define SCM_SVC_PIL 0x2
++
++extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
++ void *resp_buf, size_t resp_len);
++
++#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
++
++extern u32 scm_get_version(void);
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch
new file mode 100644
index 0000000..5cb6bf3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch
@@ -0,0 +1,78 @@
+From 085d4b834dfced8580aab74707e30699b63e7c36 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 29 Jan 2014 17:01:37 -0600
+Subject: [PATCH 006/182] clocksource: qcom: split building of legacy vs
+ multiplatform support
+
+The majority of the clocksource code for the Qualcomm platform is shared
+between newer (multiplatform) and older platforms. However there is a bit
+of code that isn't, so only build it for the appropriate config.
+
+Acked-by: Olof Johansson <olof@lixom.net>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/clocksource/qcom-timer.c | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
+index dca829e..e807acf 100644
+--- a/drivers/clocksource/qcom-timer.c
++++ b/drivers/clocksource/qcom-timer.c
+@@ -106,15 +106,6 @@ static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
+ return readl_relaxed(source_base + TIMER_COUNT_VAL);
+ }
+
+-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
+-{
+- /*
+- * Shift timer count down by a constant due to unreliable lower bits
+- * on some targets.
+- */
+- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
+-}
+-
+ static struct clocksource msm_clocksource = {
+ .name = "dg_timer",
+ .rating = 300,
+@@ -228,7 +219,7 @@ err:
+ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
+ }
+
+-#ifdef CONFIG_OF
++#ifdef CONFIG_ARCH_QCOM
+ static void __init msm_dt_timer_init(struct device_node *np)
+ {
+ u32 freq;
+@@ -281,7 +272,7 @@ static void __init msm_dt_timer_init(struct device_node *np)
+ }
+ CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+ CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+-#endif
++#else
+
+ static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
+ u32 sts)
+@@ -301,6 +292,15 @@ static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
+ return 0;
+ }
+
++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
++{
++ /*
++ * Shift timer count down by a constant due to unreliable lower bits
++ * on some targets.
++ */
++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
++}
++
+ void __init msm7x01_timer_init(void)
+ {
+ struct clocksource *cs = &msm_clocksource;
+@@ -327,3 +327,4 @@ void __init qsd8x50_timer_init(void)
+ return;
+ msm_timer_init(19200000 / 4, 32, 7, false);
+ }
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch
new file mode 100644
index 0000000..fc064b2
--- /dev/null
+++ b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch
@@ -0,0 +1,105 @@
+From 35eb6f73546d3b9475652a38fa641bd1a05a1ea1 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 4 Feb 2014 15:38:45 -0600
+Subject: [PATCH 007/182] ARM: qcom: Rename various msm prefixed functions to
+ qcom
+
+As mach-qcom will support a number of different Qualcomm SoC platforms
+we replace the msm prefix on function names with qcom to be a bit more
+generic.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-qcom/board.c | 4 ++--
+ arch/arm/mach-qcom/platsmp.c | 22 +++++++++++-----------
+ 2 files changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+index 4529f6b..830f69c 100644
+--- a/arch/arm/mach-qcom/board.c
++++ b/arch/arm/mach-qcom/board.c
+@@ -17,7 +17,7 @@
+ #include <asm/mach/arch.h>
+ #include <asm/mach/map.h>
+
+-extern struct smp_operations msm_smp_ops;
++extern struct smp_operations qcom_smp_ops;
+
+ static const char * const qcom_dt_match[] __initconst = {
+ "qcom,msm8660-surf",
+@@ -31,7 +31,7 @@ static const char * const apq8074_dt_match[] __initconst = {
+ };
+
+ DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)")
+- .smp = smp_ops(msm_smp_ops),
++ .smp = smp_ops(qcom_smp_ops),
+ .dt_compat = qcom_dt_match,
+ MACHINE_END
+
+diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
+index 67823a7..9c53ea7 100644
+--- a/arch/arm/mach-qcom/platsmp.c
++++ b/arch/arm/mach-qcom/platsmp.c
+@@ -30,7 +30,7 @@ extern void secondary_startup(void);
+ static DEFINE_SPINLOCK(boot_lock);
+
+ #ifdef CONFIG_HOTPLUG_CPU
+-static void __ref msm_cpu_die(unsigned int cpu)
++static void __ref qcom_cpu_die(unsigned int cpu)
+ {
+ wfi();
+ }
+@@ -42,7 +42,7 @@ static inline int get_core_count(void)
+ return ((read_cpuid_id() >> 4) & 3) + 1;
+ }
+
+-static void msm_secondary_init(unsigned int cpu)
++static void qcom_secondary_init(unsigned int cpu)
+ {
+ /*
+ * Synchronise with the boot thread.
+@@ -70,7 +70,7 @@ static void prepare_cold_cpu(unsigned int cpu)
+ "address\n");
+ }
+
+-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
++static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ {
+ static int cold_boot_done;
+
+@@ -108,7 +108,7 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ * does not support the ARM SCU, so just set the possible cpu mask to
+ * NR_CPUS.
+ */
+-static void __init msm_smp_init_cpus(void)
++static void __init qcom_smp_init_cpus(void)
+ {
+ unsigned int i, ncores = get_core_count();
+
+@@ -122,16 +122,16 @@ static void __init msm_smp_init_cpus(void)
+ set_cpu_possible(i, true);
+ }
+
+-static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
++static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+ {
+ }
+
+-struct smp_operations msm_smp_ops __initdata = {
+- .smp_init_cpus = msm_smp_init_cpus,
+- .smp_prepare_cpus = msm_smp_prepare_cpus,
+- .smp_secondary_init = msm_secondary_init,
+- .smp_boot_secondary = msm_boot_secondary,
++struct smp_operations qcom_smp_ops __initdata = {
++ .smp_init_cpus = qcom_smp_init_cpus,
++ .smp_prepare_cpus = qcom_smp_prepare_cpus,
++ .smp_secondary_init = qcom_secondary_init,
++ .smp_boot_secondary = qcom_boot_secondary,
+ #ifdef CONFIG_HOTPLUG_CPU
+- .cpu_die = msm_cpu_die,
++ .cpu_die = qcom_cpu_die,
+ #endif
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch
new file mode 100644
index 0000000..9579ac2
--- /dev/null
+++ b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch
@@ -0,0 +1,159 @@
+From 48f17325fef9cbebc5cb39aa78f4c1caff5d7b16 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 30 Oct 2013 18:21:09 -0700
+Subject: [PATCH 008/182] ARM: Introduce CPU_METHOD_OF_DECLARE() for cpu
+ hotplug/smp
+
+The goal of multi-platform kernels is to remove the need for mach
+directories and machine descriptors. To further that goal,
+introduce CPU_METHOD_OF_DECLARE() to allow cpu hotplug/smp
+support to be separated from the machine descriptors.
+Implementers should specify an enable-method property in their
+cpus node and then implement a matching set of smp_ops in their
+hotplug/smp code, wiring it up with the CPU_METHOD_OF_DECLARE()
+macro. When the kernel is compiled we'll collect all the
+enable-method smp_ops into one section for use at boot.
+
+At boot time we'll look for an enable-method in each cpu node and
+try to match that against all known CPU enable methods in the
+kernel. If there are no enable-methods in the cpu nodes we
+fallback to the cpus node and try to use any enable-method found
+there. If that doesn't work we fall back to the old way of using
+the machine descriptor.
+
+Acked-by: Mark Rutland <mark.rutland@arm.com>
+Cc: Russell King <linux@arm.linux.org.uk>
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/include/asm/smp.h | 9 +++++++++
+ arch/arm/kernel/devtree.c | 40 +++++++++++++++++++++++++++++++++++++
+ include/asm-generic/vmlinux.lds.h | 10 ++++++++++
+ 3 files changed, 59 insertions(+)
+
+diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
+index 22a3b9b..772435b 100644
+--- a/arch/arm/include/asm/smp.h
++++ b/arch/arm/include/asm/smp.h
+@@ -114,6 +114,15 @@ struct smp_operations {
+ #endif
+ };
+
++struct of_cpu_method {
++ const char *method;
++ struct smp_operations *ops;
++};
++
++#define CPU_METHOD_OF_DECLARE(name, _method, _ops) \
++ static const struct of_cpu_method __cpu_method_of_table_##name \
++ __used __section(__cpu_method_of_table) \
++ = { .method = _method, .ops = _ops }
+ /*
+ * set platform specific SMP operations
+ */
+diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
+index f751714..c7419a5 100644
+--- a/arch/arm/kernel/devtree.c
++++ b/arch/arm/kernel/devtree.c
+@@ -18,6 +18,7 @@
+ #include <linux/of_fdt.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_platform.h>
++#include <linux/smp.h>
+
+ #include <asm/cputype.h>
+ #include <asm/setup.h>
+@@ -63,6 +64,34 @@ void __init arm_dt_memblock_reserve(void)
+ }
+ }
+
++#ifdef CONFIG_SMP
++extern struct of_cpu_method __cpu_method_of_table_begin[];
++extern struct of_cpu_method __cpu_method_of_table_end[];
++
++static int __init set_smp_ops_by_method(struct device_node *node)
++{
++ const char *method;
++ struct of_cpu_method *m = __cpu_method_of_table_begin;
++
++ if (of_property_read_string(node, "enable-method", &method))
++ return 0;
++
++ for (; m < __cpu_method_of_table_end; m++)
++ if (!strcmp(m->method, method)) {
++ smp_set_ops(m->ops);
++ return 1;
++ }
++
++ return 0;
++}
++#else
++static inline int set_smp_ops_by_method(struct device_node *node)
++{
++ return 1;
++}
++#endif
++
++
+ /*
+ * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
+ * and builds the cpu logical map array containing MPIDR values related to
+@@ -79,6 +108,7 @@ void __init arm_dt_init_cpu_maps(void)
+ * read as 0.
+ */
+ struct device_node *cpu, *cpus;
++ int found_method = 0;
+ u32 i, j, cpuidx = 1;
+ u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
+
+@@ -150,8 +180,18 @@ void __init arm_dt_init_cpu_maps(void)
+ }
+
+ tmp_map[i] = hwid;
++
++ if (!found_method)
++ found_method = set_smp_ops_by_method(cpu);
+ }
+
++ /*
++ * Fallback to an enable-method in the cpus node if nothing found in
++ * a cpu node.
++ */
++ if (!found_method)
++ set_smp_ops_by_method(cpus);
++
+ if (!bootcpu_valid) {
+ pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n");
+ return;
+diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
+index bc2121f..bd02ca7 100644
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -167,6 +167,15 @@
+ #define CLK_OF_TABLES()
+ #endif
+
++#ifdef CONFIG_SMP
++#define CPU_METHOD_OF_TABLES() . = ALIGN(8); \
++ VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \
++ *(__cpu_method_of_table) \
++ VMLINUX_SYMBOL(__cpu_method_of_table_end) = .;
++#else
++#define CPU_METHOD_OF_TABLES()
++#endif
++
+ #define KERNEL_DTB() \
+ STRUCT_ALIGN(); \
+ VMLINUX_SYMBOL(__dtb_start) = .; \
+@@ -491,6 +500,7 @@
+ MEM_DISCARD(init.rodata) \
+ CLK_OF_TABLES() \
+ CLKSRC_OF_TABLES() \
++ CPU_METHOD_OF_TABLES() \
+ KERNEL_DTB() \
+ IRQCHIP_OF_MATCH_TABLE()
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch
new file mode 100644
index 0000000..6601ad4
--- /dev/null
+++ b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch
@@ -0,0 +1,249 @@
+From 391848e75f8b0ba14816da1ac8f2d838fd0d5744 Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Tue, 21 May 2013 19:13:29 -0700
+Subject: [PATCH 009/182] ARM: qcom: Re-organize platsmp to make it extensible
+
+This makes it easy to add SMP support for new devices by keying
+on a device node for the release sequence. We add the
+enable-method property for the cpus property to specify that we
+want to use the gcc-msm8660 release sequence (which is going to
+look for the global clock controller device node to map some
+Scorpion specific power and control registers). We also remove
+the nr_cpus detection code as that is done generically in the DT
+CPU detection code.
+
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+[sboyd: Port to CPU_METHOD_OF_DECLARE]
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-msm/common.h | 2 -
+ arch/arm/mach-qcom/board.c | 14 -----
+ arch/arm/mach-qcom/platsmp.c | 118 +++++++++++++++++++++++-------------------
+ 3 files changed, 65 insertions(+), 69 deletions(-)
+
+diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h
+index 0a4899b..572479a 100644
+--- a/arch/arm/mach-msm/common.h
++++ b/arch/arm/mach-msm/common.h
+@@ -23,8 +23,6 @@ extern void msm_map_qsd8x50_io(void);
+ extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size,
+ unsigned int mtype, void *caller);
+
+-extern struct smp_operations msm_smp_ops;
+-
+ struct msm_mmc_platform_data;
+
+ extern void msm_add_devices(void);
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+index 830f69c..bae617e 100644
+--- a/arch/arm/mach-qcom/board.c
++++ b/arch/arm/mach-qcom/board.c
+@@ -11,30 +11,16 @@
+ */
+
+ #include <linux/init.h>
+-#include <linux/of.h>
+-#include <linux/of_platform.h>
+
+ #include <asm/mach/arch.h>
+-#include <asm/mach/map.h>
+-
+-extern struct smp_operations qcom_smp_ops;
+
+ static const char * const qcom_dt_match[] __initconst = {
+ "qcom,msm8660-surf",
+ "qcom,msm8960-cdp",
+- NULL
+-};
+-
+-static const char * const apq8074_dt_match[] __initconst = {
+ "qcom,apq8074-dragonboard",
+ NULL
+ };
+
+ DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)")
+- .smp = smp_ops(qcom_smp_ops),
+ .dt_compat = qcom_dt_match,
+ MACHINE_END
+-
+-DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)")
+- .dt_compat = apq8074_dt_match,
+-MACHINE_END
+diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
+index 9c53ea7..ec8604d 100644
+--- a/arch/arm/mach-qcom/platsmp.c
++++ b/arch/arm/mach-qcom/platsmp.c
+@@ -13,17 +13,18 @@
+ #include <linux/errno.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
+ #include <linux/smp.h>
+ #include <linux/io.h>
+
+-#include <asm/cputype.h>
+ #include <asm/smp_plat.h>
+
+ #include "scm-boot.h"
+
+-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
+-#define SCSS_CPU1CORE_RESET 0xD80
+-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0
++#define SCSS_CPU1CORE_RESET 0x2d80
++#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64
+
+ extern void secondary_startup(void);
+
+@@ -36,12 +37,6 @@ static void __ref qcom_cpu_die(unsigned int cpu)
+ }
+ #endif
+
+-static inline int get_core_count(void)
+-{
+- /* 1 + the PART[1:0] field of MIDR */
+- return ((read_cpuid_id() >> 4) & 3) + 1;
+-}
+-
+ static void qcom_secondary_init(unsigned int cpu)
+ {
+ /*
+@@ -51,33 +46,41 @@ static void qcom_secondary_init(unsigned int cpu)
+ spin_unlock(&boot_lock);
+ }
+
+-static void prepare_cold_cpu(unsigned int cpu)
++static int scss_release_secondary(unsigned int cpu)
+ {
+- int ret;
+- ret = scm_set_boot_addr(virt_to_phys(secondary_startup),
+- SCM_FLAG_COLDBOOT_CPU1);
+- if (ret == 0) {
+- void __iomem *sc1_base_ptr;
+- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
+- if (sc1_base_ptr) {
+- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
+- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
+- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
+- iounmap(sc1_base_ptr);
+- }
+- } else
+- printk(KERN_DEBUG "Failed to set secondary core boot "
+- "address\n");
++ struct device_node *node;
++ void __iomem *base;
++
++ node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660");
++ if (!node) {
++ pr_err("%s: can't find node\n", __func__);
++ return -ENXIO;
++ }
++
++ base = of_iomap(node, 0);
++ of_node_put(node);
++ if (!base)
++ return -ENOMEM;
++
++ writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
++ writel_relaxed(0, base + SCSS_CPU1CORE_RESET);
++ writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP);
++ mb();
++ iounmap(base);
++
++ return 0;
+ }
+
+-static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle)
++static DEFINE_PER_CPU(int, cold_boot_done);
++
++static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
+ {
+- static int cold_boot_done;
++ int ret = 0;
+
+- /* Only need to bring cpu out of reset this way once */
+- if (cold_boot_done == false) {
+- prepare_cold_cpu(cpu);
+- cold_boot_done = true;
++ if (!per_cpu(cold_boot_done, cpu)) {
++ ret = func(cpu);
++ if (!ret)
++ per_cpu(cold_boot_done, cpu) = true;
+ }
+
+ /*
+@@ -99,39 +102,48 @@ static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ */
+ spin_unlock(&boot_lock);
+
+- return 0;
++ return ret;
+ }
+
+-/*
+- * Initialise the CPU possible map early - this describes the CPUs
+- * which may be present or become present in the system. The msm8x60
+- * does not support the ARM SCU, so just set the possible cpu mask to
+- * NR_CPUS.
+- */
+-static void __init qcom_smp_init_cpus(void)
++static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ {
+- unsigned int i, ncores = get_core_count();
+-
+- if (ncores > nr_cpu_ids) {
+- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+- ncores, nr_cpu_ids);
+- ncores = nr_cpu_ids;
+- }
+-
+- for (i = 0; i < ncores; i++)
+- set_cpu_possible(i, true);
++ return qcom_boot_secondary(cpu, scss_release_secondary);
+ }
+
+ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+ {
++ int cpu, map;
++ unsigned int flags = 0;
++ static const int cold_boot_flags[] = {
++ 0,
++ SCM_FLAG_COLDBOOT_CPU1,
++ };
++
++ for_each_present_cpu(cpu) {
++ map = cpu_logical_map(cpu);
++ if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) {
++ set_cpu_present(cpu, false);
++ continue;
++ }
++ flags |= cold_boot_flags[map];
++ }
++
++ if (scm_set_boot_addr(virt_to_phys(secondary_startup), flags)) {
++ for_each_present_cpu(cpu) {
++ if (cpu == smp_processor_id())
++ continue;
++ set_cpu_present(cpu, false);
++ }
++ pr_warn("Failed to set CPU boot address, disabling SMP\n");
++ }
+ }
+
+-struct smp_operations qcom_smp_ops __initdata = {
+- .smp_init_cpus = qcom_smp_init_cpus,
++static struct smp_operations smp_msm8660_ops __initdata = {
+ .smp_prepare_cpus = qcom_smp_prepare_cpus,
+ .smp_secondary_init = qcom_secondary_init,
+- .smp_boot_secondary = qcom_boot_secondary,
++ .smp_boot_secondary = msm8660_boot_secondary,
+ #ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = qcom_cpu_die,
+ #endif
+ };
++CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch
new file mode 100644
index 0000000..0bcd3e8
--- /dev/null
+++ b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch
@@ -0,0 +1,70 @@
+From 236d07c7bb0c758ea40ea0110d37306d2e7d9a4b Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Thu, 31 Oct 2013 17:26:33 -0700
+Subject: [PATCH 010/182] devicetree: bindings: Document Krait/Scorpion cpus
+ and enable-method
+
+Scorpion and Krait don't use the spin-table enable-method.
+Instead they rely on mmio register accesses to enable power and
+clocks to bring CPUs out of reset. Document their enable-methods.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+[sboyd: Split off into separate patch, renamed methods to
+match compatible nodes]
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ Documentation/devicetree/bindings/arm/cpus.txt | 25 +++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
+index 9130435..333f4ae 100644
+--- a/Documentation/devicetree/bindings/arm/cpus.txt
++++ b/Documentation/devicetree/bindings/arm/cpus.txt
+@@ -180,7 +180,11 @@ nodes to be present and contain the properties described below.
+ be one of:
+ "spin-table"
+ "psci"
+- # On ARM 32-bit systems this property is optional.
++ # On ARM 32-bit systems this property is optional and
++ can be one of:
++ "qcom,gcc-msm8660"
++ "qcom,kpss-acc-v1"
++ "qcom,kpss-acc-v2"
+
+ - cpu-release-addr
+ Usage: required for systems that have an "enable-method"
+@@ -191,6 +195,21 @@ nodes to be present and contain the properties described below.
+ property identifying a 64-bit zero-initialised
+ memory location.
+
++ - qcom,saw
++ Usage: required for systems that have an "enable-method"
++ property value of "qcom,kpss-acc-v1" or
++ "qcom,kpss-acc-v2"
++ Value type: <phandle>
++ Definition: Specifies the SAW[1] node associated with this CPU.
++
++ - qcom,acc
++ Usage: required for systems that have an "enable-method"
++ property value of "qcom,kpss-acc-v1" or
++ "qcom,kpss-acc-v2"
++ Value type: <phandle>
++ Definition: Specifies the ACC[2] node associated with this CPU.
++
++
+ Example 1 (dual-cluster big.LITTLE system 32-bit):
+
+ cpus {
+@@ -382,3 +401,7 @@ cpus {
+ cpu-release-addr = <0 0x20000000>;
+ };
+ };
++
++--
++[1] arm/msm/qcom,saw2.txt
++[2] arm/msm/qcom,kpss-acc.txt
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch
new file mode 100644
index 0000000..fc57727
--- /dev/null
+++ b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch
@@ -0,0 +1,55 @@
+From 3a4fe9a3a4aff8b1f1c3685bc9b6adbe739d7367 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 31 Oct 2013 18:20:30 -0700
+Subject: [PATCH 011/182] devicetree: bindings: Document qcom,kpss-acc
+
+The kpss acc binding describes the clock, reset, and power domain
+controller for a Krait CPU.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 30 ++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+
+diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+new file mode 100644
+index 0000000..1333db9
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+@@ -0,0 +1,30 @@
++Krait Processor Sub-system (KPSS) Application Clock Controller (ACC)
++
++The KPSS ACC provides clock, power domain, and reset control to a Krait CPU.
++There is one ACC register region per CPU within the KPSS remapped region as
++well as an alias register region that remaps accesses to the ACC associated
++with the CPU accessing the region.
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: should be one of:
++ "qcom,kpss-acc-v1"
++ "qcom,kpss-acc-v2"
++
++- reg:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: the first element specifies the base address and size of
++ the register region. An optional second element specifies
++ the base address and size of the alias register region.
++
++Example:
++
++ clock-controller@2088000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0x02088000 0x1000>,
++ <0x02008000 0x1000>;
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch
new file mode 100644
index 0000000..5bf18c1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch
@@ -0,0 +1,60 @@
+From 17adce4d3de1fca7761607d572f4979557f0f604 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 31 Oct 2013 18:20:30 -0700
+Subject: [PATCH 012/182] devicetree: bindings: Document qcom,saw2 node
+
+The saw2 binding describes the SPM/AVS wrapper hardware used to
+control the regulator supplying voltage to the Krait CPUs.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ .../devicetree/bindings/arm/msm/qcom,saw2.txt | 35 ++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
+
+diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
+new file mode 100644
+index 0000000..1505fb8
+--- /dev/null
++++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
+@@ -0,0 +1,35 @@
++SPM AVS Wrapper 2 (SAW2)
++
++The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
++Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
++micro-controller that transitions a piece of hardware (like a processor or
++subsystem) into and out of low power modes via a direct connection to
++the PMIC. It can also be wired up to interact with other processors in the
++system, notifying them when a low power state is entered or exited.
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: shall contain "qcom,saw2". A more specific value should be
++ one of:
++ "qcom,saw2-v1"
++ "qcom,saw2-v1.1"
++ "qcom,saw2-v2"
++ "qcom,saw2-v2.1"
++
++- reg:
++ Usage: required
++ Value type: <prop-encoded-array>
++ Definition: the first element specifies the base address and size of
++ the register region. An optional second element specifies
++ the base address and size of the alias register region.
++
++
++Example:
++
++ regulator@2099000 {
++ compatible = "qcom,saw2";
++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch
new file mode 100644
index 0000000..66f667c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch
@@ -0,0 +1,181 @@
+From 8e843640b3c4a43b963332fdc7b233948ad25a5b Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Tue, 21 May 2013 19:13:50 -0700
+Subject: [PATCH 013/182] ARM: qcom: Add SMP support for KPSSv1
+
+Implement support for the Krait CPU release sequence when the
+CPUs are part of the first version of the krait processor
+subsystem.
+
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-qcom/platsmp.c | 106 +++++++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-qcom/scm-boot.h | 8 ++--
+ 2 files changed, 111 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
+index ec8604d..cb0783f 100644
+--- a/arch/arm/mach-qcom/platsmp.c
++++ b/arch/arm/mach-qcom/platsmp.c
+@@ -26,6 +26,16 @@
+ #define SCSS_CPU1CORE_RESET 0x2d80
+ #define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64
+
++#define APCS_CPU_PWR_CTL 0x04
++#define PLL_CLAMP BIT(8)
++#define CORE_PWRD_UP BIT(7)
++#define COREPOR_RST BIT(5)
++#define CORE_RST BIT(4)
++#define L2DT_SLP BIT(3)
++#define CLAMP BIT(0)
++
++#define APCS_SAW2_VCTL 0x14
++
+ extern void secondary_startup(void);
+
+ static DEFINE_SPINLOCK(boot_lock);
+@@ -71,6 +81,85 @@ static int scss_release_secondary(unsigned int cpu)
+ return 0;
+ }
+
++static int kpssv1_release_secondary(unsigned int cpu)
++{
++ int ret = 0;
++ void __iomem *reg, *saw_reg;
++ struct device_node *cpu_node, *acc_node, *saw_node;
++ u32 val;
++
++ cpu_node = of_get_cpu_node(cpu, NULL);
++ if (!cpu_node)
++ return -ENODEV;
++
++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
++ if (!acc_node) {
++ ret = -ENODEV;
++ goto out_acc;
++ }
++
++ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
++ if (!saw_node) {
++ ret = -ENODEV;
++ goto out_saw;
++ }
++
++ reg = of_iomap(acc_node, 0);
++ if (!reg) {
++ ret = -ENOMEM;
++ goto out_acc_map;
++ }
++
++ saw_reg = of_iomap(saw_node, 0);
++ if (!saw_reg) {
++ ret = -ENOMEM;
++ goto out_saw_map;
++ }
++
++ /* Turn on CPU rail */
++ writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL);
++ mb();
++ udelay(512);
++
++ /* Krait bring-up sequence */
++ val = PLL_CLAMP | L2DT_SLP | CLAMP;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ val &= ~L2DT_SLP;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ ndelay(300);
++
++ val |= COREPOR_RST;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ udelay(2);
++
++ val &= ~CLAMP;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ udelay(2);
++
++ val &= ~COREPOR_RST;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ udelay(100);
++
++ val |= CORE_PWRD_UP;
++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
++ mb();
++
++ iounmap(saw_reg);
++out_saw_map:
++ iounmap(reg);
++out_acc_map:
++ of_node_put(saw_node);
++out_saw:
++ of_node_put(acc_node);
++out_acc:
++ of_node_put(cpu_node);
++ return ret;
++}
++
+ static DEFINE_PER_CPU(int, cold_boot_done);
+
+ static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
+@@ -110,6 +199,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ return qcom_boot_secondary(cpu, scss_release_secondary);
+ }
+
++static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
++{
++ return qcom_boot_secondary(cpu, kpssv1_release_secondary);
++}
++
+ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+ {
+ int cpu, map;
+@@ -117,6 +211,8 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+ static const int cold_boot_flags[] = {
+ 0,
+ SCM_FLAG_COLDBOOT_CPU1,
++ SCM_FLAG_COLDBOOT_CPU2,
++ SCM_FLAG_COLDBOOT_CPU3,
+ };
+
+ for_each_present_cpu(cpu) {
+@@ -147,3 +243,13 @@ static struct smp_operations smp_msm8660_ops __initdata = {
+ #endif
+ };
+ CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
++
++static struct smp_operations qcom_smp_kpssv1_ops __initdata = {
++ .smp_prepare_cpus = qcom_smp_prepare_cpus,
++ .smp_secondary_init = qcom_secondary_init,
++ .smp_boot_secondary = kpssv1_boot_secondary,
++#ifdef CONFIG_HOTPLUG_CPU
++ .cpu_die = qcom_cpu_die,
++#endif
++};
++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);
+diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h
+index 7be32ff..6aabb24 100644
+--- a/arch/arm/mach-qcom/scm-boot.h
++++ b/arch/arm/mach-qcom/scm-boot.h
+@@ -13,9 +13,11 @@
+ #define __MACH_SCM_BOOT_H
+
+ #define SCM_BOOT_ADDR 0x1
+-#define SCM_FLAG_COLDBOOT_CPU1 0x1
+-#define SCM_FLAG_WARMBOOT_CPU1 0x2
+-#define SCM_FLAG_WARMBOOT_CPU0 0x4
++#define SCM_FLAG_COLDBOOT_CPU1 0x01
++#define SCM_FLAG_COLDBOOT_CPU2 0x08
++#define SCM_FLAG_COLDBOOT_CPU3 0x20
++#define SCM_FLAG_WARMBOOT_CPU0 0x04
++#define SCM_FLAG_WARMBOOT_CPU1 0x02
+
+ int scm_set_boot_addr(phys_addr_t addr, int flags);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch
new file mode 100644
index 0000000..03fdaf3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch
@@ -0,0 +1,172 @@
+From eb07c23d45ddf10fa89296e6c6c6aed553d8bbf5 Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Fri, 21 Jun 2013 17:09:13 -0700
+Subject: [PATCH 014/182] ARM: qcom: Add SMP support for KPSSv2
+
+Implement support for the Krait CPU release sequence when the
+CPUs are part of the second version of the Krait processor
+subsystem.
+
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-qcom/platsmp.c | 123 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 123 insertions(+)
+
+diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
+index cb0783f..d690856 100644
+--- a/arch/arm/mach-qcom/platsmp.c
++++ b/arch/arm/mach-qcom/platsmp.c
+@@ -34,7 +34,15 @@
+ #define L2DT_SLP BIT(3)
+ #define CLAMP BIT(0)
+
++#define APC_PWR_GATE_CTL 0x14
++#define BHS_CNT_SHIFT 24
++#define LDO_PWR_DWN_SHIFT 16
++#define LDO_BYP_SHIFT 8
++#define BHS_SEG_SHIFT 1
++#define BHS_EN BIT(0)
++
+ #define APCS_SAW2_VCTL 0x14
++#define APCS_SAW2_2_VCTL 0x1c
+
+ extern void secondary_startup(void);
+
+@@ -160,6 +168,106 @@ out_acc:
+ return ret;
+ }
+
++static int kpssv2_release_secondary(unsigned int cpu)
++{
++ void __iomem *reg;
++ struct device_node *cpu_node, *l2_node, *acc_node, *saw_node;
++ void __iomem *l2_saw_base;
++ unsigned reg_val;
++ int ret;
++
++ cpu_node = of_get_cpu_node(cpu, NULL);
++ if (!cpu_node)
++ return -ENODEV;
++
++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
++ if (!acc_node) {
++ ret = -ENODEV;
++ goto out_acc;
++ }
++
++ l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
++ if (!l2_node) {
++ ret = -ENODEV;
++ goto out_l2;
++ }
++
++ saw_node = of_parse_phandle(l2_node, "qcom,saw", 0);
++ if (!saw_node) {
++ ret = -ENODEV;
++ goto out_saw;
++ }
++
++ reg = of_iomap(acc_node, 0);
++ if (!reg) {
++ ret = -ENOMEM;
++ goto out_map;
++ }
++
++ l2_saw_base = of_iomap(saw_node, 0);
++ if (!l2_saw_base) {
++ ret = -ENOMEM;
++ goto out_saw_map;
++ }
++
++ /* Turn on the BHS, turn off LDO Bypass and power down LDO */
++ reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN;
++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
++ mb();
++ /* wait for the BHS to settle */
++ udelay(1);
++
++ /* Turn on BHS segments */
++ reg_val |= 0x3f << BHS_SEG_SHIFT;
++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
++ mb();
++ /* wait for the BHS to settle */
++ udelay(1);
++
++ /* Finally turn on the bypass so that BHS supplies power */
++ reg_val |= 0x3f << LDO_BYP_SHIFT;
++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
++
++ /* enable max phases */
++ writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL);
++ mb();
++ udelay(50);
++
++ reg_val = COREPOR_RST | CLAMP;
++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ udelay(2);
++
++ reg_val &= ~CLAMP;
++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
++ mb();
++ udelay(2);
++
++ reg_val &= ~COREPOR_RST;
++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
++ mb();
++
++ reg_val |= CORE_PWRD_UP;
++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
++ mb();
++
++ ret = 0;
++
++ iounmap(l2_saw_base);
++out_saw_map:
++ iounmap(reg);
++out_map:
++ of_node_put(saw_node);
++out_saw:
++ of_node_put(l2_node);
++out_l2:
++ of_node_put(acc_node);
++out_acc:
++ of_node_put(cpu_node);
++
++ return ret;
++}
++
+ static DEFINE_PER_CPU(int, cold_boot_done);
+
+ static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
+@@ -204,6 +312,11 @@ static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
+ return qcom_boot_secondary(cpu, kpssv1_release_secondary);
+ }
+
++static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
++{
++ return qcom_boot_secondary(cpu, kpssv2_release_secondary);
++}
++
+ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+ {
+ int cpu, map;
+@@ -253,3 +366,13 @@ static struct smp_operations qcom_smp_kpssv1_ops __initdata = {
+ #endif
+ };
+ CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);
++
++static struct smp_operations qcom_smp_kpssv2_ops __initdata = {
++ .smp_prepare_cpus = qcom_smp_prepare_cpus,
++ .smp_secondary_init = qcom_secondary_init,
++ .smp_boot_secondary = kpssv2_boot_secondary,
++#ifdef CONFIG_HOTPLUG_CPU
++ .cpu_die = qcom_cpu_die,
++#endif
++};
++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch
new file mode 100644
index 0000000..9dbc06d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch
@@ -0,0 +1,32 @@
+From 17368813d4182894c6d58b66f9fccd339364de8f Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 29 Jan 2014 17:23:06 -0600
+Subject: [PATCH 015/182] tty: serial: msm: Enable building msm_serial for
+ ARCH_QCOM
+
+We've split Qualcomm MSM support into legacy and multiplatform. So add
+the ability to build the serial driver on the newer ARCH_QCOM
+multiplatform.
+
+Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/tty/serial/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
+index a3815ea..ce9b12d 100644
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1024,7 +1024,7 @@ config SERIAL_SGI_IOC3
+
+ config SERIAL_MSM
+ bool "MSM on-chip serial port support"
+- depends on ARCH_MSM
++ depends on ARCH_MSM || ARCH_QCOM
+ select SERIAL_CORE
+
+ config SERIAL_MSM_CONSOLE
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch
new file mode 100644
index 0000000..46e1fb7
--- /dev/null
+++ b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch
@@ -0,0 +1,33 @@
+From aa3cf89746c43172517378224277ba961c46e28c Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 30 Jan 2014 14:45:05 -0600
+Subject: [PATCH 016/182] drm/msm: drop ARCH_MSM Kconfig depend
+
+The ARCH_MSM depend is redundant with ARCH_MSM8960, so we can remove it.
+Additionally, we are splitting Qualcomm MSM support into legacy (ARCH_MSM)
+and multiplatform (ARCH_QCOM). The MSM8960 with be ARCH_QCOM going forward
+so dropping ARCH_MSM will work properly for the new ARCH_QCOM multiplatform
+build.
+
+Acked-by: Rob Clark <robdclark@gmail.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/gpu/drm/msm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
+index c69d1e0..b698497 100644
+--- a/drivers/gpu/drm/msm/Kconfig
++++ b/drivers/gpu/drm/msm/Kconfig
+@@ -3,7 +3,7 @@ config DRM_MSM
+ tristate "MSM DRM"
+ depends on DRM
+ depends on MSM_IOMMU
+- depends on (ARCH_MSM && ARCH_MSM8960) || (ARM && COMPILE_TEST)
++ depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
+ select DRM_KMS_HELPER
+ select SHMEM
+ select TMPFS
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
new file mode 100644
index 0000000..868146c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
@@ -0,0 +1,32 @@
+From 6e8707828be07397ee8ee437a6ef1b8f73f82287 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 30 Jan 2014 14:46:08 -0600
+Subject: [PATCH 017/182] power: reset: msm - switch Kconfig to ARCH_QCOM
+ depends
+
+We've split Qualcomm MSM support into legacy and multiplatform. The reset
+driver is only relevant on the multiplatform supported SoCs so switch the
+Kconfig depends to ARCH_QCOM.
+
+Acked-by: Dmitry Eremin-Solenikov
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/power/reset/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
+index 6d452a7..fa0e4e0 100644
+--- a/drivers/power/reset/Kconfig
++++ b/drivers/power/reset/Kconfig
+@@ -22,7 +22,7 @@ config POWER_RESET_GPIO
+
+ config POWER_RESET_MSM
+ bool "Qualcomm MSM power-off driver"
+- depends on POWER_RESET && ARCH_MSM
++ depends on POWER_RESET && ARCH_QCOM
+ help
+ Power off and restart support for Qualcomm boards.
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
new file mode 100644
index 0000000..b375fec
--- /dev/null
+++ b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
@@ -0,0 +1,38 @@
+From 4aa784548190c69d946b4dfbc0592a3ed7cd18da Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 30 Jan 2014 14:43:49 -0600
+Subject: [PATCH 018/182] hwrng: msm: switch Kconfig to ARCH_QCOM depends
+
+We've split Qualcomm MSM support into legacy and multiplatform. The RNG
+driver is only relevant on the multiplatform supported SoCs so switch the
+Kconfig depends to ARCH_QCOM.
+
+Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
+CC: Stanimir Varbanov <svarbanov@mm-sol.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/char/hw_random/Kconfig | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
+index 2f2b084..244759b 100644
+--- a/drivers/char/hw_random/Kconfig
++++ b/drivers/char/hw_random/Kconfig
+@@ -342,11 +342,11 @@ config HW_RANDOM_TPM
+ If unsure, say Y.
+
+ config HW_RANDOM_MSM
+- tristate "Qualcomm MSM Random Number Generator support"
+- depends on HW_RANDOM && ARCH_MSM
++ tristate "Qualcomm SoCs Random Number Generator support"
++ depends on HW_RANDOM && ARCH_QCOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+- Generator hardware found on Qualcomm MSM SoCs.
++ Generator hardware found on Qualcomm SoCs.
+
+ To compile this driver as a module, choose M here. the
+ module will be called msm-rng.
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
new file mode 100644
index 0000000..210f760
--- /dev/null
+++ b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch
@@ -0,0 +1,32 @@
+From 87d52cc8a390c5b208b6f0bddd90a2d01c906616 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 11 Feb 2014 14:08:06 -0600
+Subject: [PATCH 019/182] gpio: msm: switch Kconfig to ARCH_QCOM depends
+
+We've split Qualcomm MSM support into legacy and multiplatform. The gpio
+msm-v2 driver is only relevant on the multiplatform supported SoCs so
+switch the Kconfig depends to ARCH_QCOM.
+
+CC: Linus Walleij <linus.walleij@linaro.org>
+Acked-by: Alexandre Courbot <gnurou@gmail.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/gpio/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 903f24d..2c38d95 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -192,7 +192,7 @@ config GPIO_MSM_V1
+
+ config GPIO_MSM_V2
+ tristate "Qualcomm MSM GPIO v2"
+- depends on GPIOLIB && OF && ARCH_MSM
++ depends on GPIOLIB && OF && ARCH_QCOM
+ help
+ Say yes here to support the GPIO interface on ARM v7 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch
new file mode 100644
index 0000000..ba25b46
--- /dev/null
+++ b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch
@@ -0,0 +1,54 @@
+From a2f356a6d49f459a2dd681fe4a7c6a55aeb8893f Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 25 Feb 2014 14:34:05 -0600
+Subject: [PATCH 020/182] ARM: qcom: Enable basic support for Qualcomm
+ platforms in multi_v7_defconfig
+
+Enable support for the MSM8x60, MSM8960, and MSM8974 SoCs, clocks and
+serial console as part of the standard multi_v7_defconfig.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+[khilman: removed non-qcom changes]
+Signed-off-by: Kevin Hilman <khilman@linaro.org>
+---
+ arch/arm/configs/multi_v7_defconfig | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
+index ee69829..1a61bd8 100644
+--- a/arch/arm/configs/multi_v7_defconfig
++++ b/arch/arm/configs/multi_v7_defconfig
+@@ -31,6 +31,10 @@ CONFIG_SOC_OMAP5=y
+ CONFIG_SOC_AM33XX=y
+ CONFIG_SOC_DRA7XX=y
+ CONFIG_SOC_AM43XX=y
++CONFIG_ARCH_QCOM=y
++CONFIG_ARCH_MSM8X60=y
++CONFIG_ARCH_MSM8960=y
++CONFIG_ARCH_MSM8974=y
+ CONFIG_ARCH_ROCKCHIP=y
+ CONFIG_ARCH_SOCFPGA=y
+ CONFIG_PLAT_SPEAR=y
+@@ -146,6 +150,8 @@ CONFIG_SERIAL_SIRFSOC_CONSOLE=y
+ CONFIG_SERIAL_TEGRA=y
+ CONFIG_SERIAL_IMX=y
+ CONFIG_SERIAL_IMX_CONSOLE=y
++CONFIG_SERIAL_MSM=y
++CONFIG_SERIAL_MSM_CONSOLE=y
+ CONFIG_SERIAL_VT8500=y
+ CONFIG_SERIAL_VT8500_CONSOLE=y
+ CONFIG_SERIAL_OF_PLATFORM=y
+@@ -294,6 +300,10 @@ CONFIG_MFD_NVEC=y
+ CONFIG_KEYBOARD_NVEC=y
+ CONFIG_SERIO_NVEC_PS2=y
+ CONFIG_NVEC_POWER=y
++CONFIG_COMMON_CLK_QCOM=y
++CONFIG_MSM_GCC_8660=y
++CONFIG_MSM_MMCC_8960=y
++CONFIG_MSM_MMCC_8974=y
+ CONFIG_TEGRA_IOMMU_GART=y
+ CONFIG_TEGRA_IOMMU_SMMU=y
+ CONFIG_MEMORY=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch
new file mode 100644
index 0000000..b23ee3b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch
@@ -0,0 +1,214 @@
+From 5a054211d9380cef5a09da7c5e815c827f330a96 Mon Sep 17 00:00:00 2001
+From: Rohit Vaswani <rvaswani@codeaurora.org>
+Date: Fri, 1 Nov 2013 10:10:40 -0700
+Subject: [PATCH 021/182] ARM: dts: qcom: Add nodes necessary for SMP boot
+
+Add the necessary nodes to support SMP on MSM8660, MSM8960, and
+MSM8974/APQ8074. While we're here also add in the error
+interrupts for the Krait cache error detection.
+
+Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
+[sboyd: Split into separate patch, add error interrupts]
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8660.dtsi | 24 ++++++++++++
+ arch/arm/boot/dts/qcom-msm8960.dtsi | 52 ++++++++++++++++++++++++++
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 69 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 145 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi
+index 69d6c4e..c52a9e9 100644
+--- a/arch/arm/boot/dts/qcom-msm8660.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi
+@@ -9,6 +9,30 @@
+ compatible = "qcom,msm8660";
+ interrupt-parent = <&intc>;
+
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "qcom,scorpion";
++ enable-method = "qcom,gcc-msm8660";
++
++ cpu@0 {
++ device_type = "cpu";
++ reg = <0>;
++ next-level-cache = <&L2>;
++ };
++
++ cpu@1 {
++ device_type = "cpu";
++ reg = <1>;
++ next-level-cache = <&L2>;
++ };
++
++ L2: l2-cache {
++ compatible = "cache";
++ cache-level = <2>;
++ };
++ };
++
+ intc: interrupt-controller@2080000 {
+ compatible = "qcom,msm-8660-qgic";
+ interrupt-controller;
+diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
+index ff00282..02231a5 100644
+--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
+@@ -9,6 +9,36 @@
+ compatible = "qcom,msm8960";
+ interrupt-parent = <&intc>;
+
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ interrupts = <1 14 0x304>;
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++
++ cpu@0 {
++ device_type = "cpu";
++ reg = <0>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc0>;
++ qcom,saw = <&saw0>;
++ };
++
++ cpu@1 {
++ device_type = "cpu";
++ reg = <1>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc1>;
++ qcom,saw = <&saw1>;
++ };
++
++ L2: l2-cache {
++ compatible = "cache";
++ cache-level = <2>;
++ interrupts = <0 2 0x4>;
++ };
++ };
++
+ intc: interrupt-controller@2000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+@@ -53,6 +83,28 @@
+ #reset-cells = <1>;
+ };
+
++ acc0: clock-controller@2088000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ acc1: clock-controller@2098000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ saw0: regulator@2089000 {
++ compatible = "qcom,saw2";
++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ saw1: regulator@2099000 {
++ compatible = "qcom,saw2";
++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
+ serial@16440000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16440000 0x1000>,
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index 9e5dadb..39eebc5 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -9,6 +9,49 @@
+ compatible = "qcom,msm8974";
+ interrupt-parent = <&intc>;
+
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ interrupts = <1 9 0xf04>;
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v2";
++
++ cpu@0 {
++ device_type = "cpu";
++ reg = <0>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc0>;
++ };
++
++ cpu@1 {
++ device_type = "cpu";
++ reg = <1>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc1>;
++ };
++
++ cpu@2 {
++ device_type = "cpu";
++ reg = <2>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc2>;
++ };
++
++ cpu@3 {
++ device_type = "cpu";
++ reg = <3>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc3>;
++ };
++
++ L2: l2-cache {
++ compatible = "cache";
++ cache-level = <2>;
++ interrupts = <0 2 0x4>;
++ qcom,saw = <&saw_l2>;
++ };
++ };
++
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -91,6 +134,32 @@
+ };
+ };
+
++ saw_l2: regulator@f9012000 {
++ compatible = "qcom,saw2";
++ reg = <0xf9012000 0x1000>;
++ regulator;
++ };
++
++ acc0: clock-controller@f9088000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf9088000 0x1000>, <0xf9008000 0x1000>;
++ };
++
++ acc1: clock-controller@f9098000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf9098000 0x1000>, <0xf9008000 0x1000>;
++ };
++
++ acc2: clock-controller@f90a8000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf90a8000 0x1000>, <0xf9008000 0x1000>;
++ };
++
++ acc3: clock-controller@f90b8000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf90b8000 0x1000>, <0xf9008000 0x1000>;
++ };
++
+ restart@fc4ab000 {
+ compatible = "qcom,pshold";
+ reg = <0xfc4ab000 0x4>;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch
new file mode 100644
index 0000000..23edfe7
--- /dev/null
+++ b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch
@@ -0,0 +1,35 @@
+From b0f93ef8f790e77049b6149416ef0ba05cce7089 Mon Sep 17 00:00:00 2001
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Fri, 7 Feb 2014 11:23:07 +0200
+Subject: [PATCH 022/182] ARM: dts: qcom: Add RNG device tree node
+
+Add the necessary DT node to probe the rng driver on
+msm8974 platforms.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+Acked-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index 39eebc5..011eb09 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -186,5 +186,12 @@
+ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ };
++
++ rng@f9bff000 {
++ compatible = "qcom,prng";
++ reg = <0xf9bff000 0x200>;
++ clocks = <&gcc GCC_PRNG_AHB_CLK>;
++ clock-names = "core";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch
new file mode 100644
index 0000000..a3e69f3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch
@@ -0,0 +1,34 @@
+From 82bedcd5ad0b3ac8fe78f4be25b2ffa0691d7804 Mon Sep 17 00:00:00 2001
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Wed, 19 Feb 2014 16:33:06 +0200
+Subject: [PATCH 023/182] ARM: dts: qcom-msm8960-cdp: Add RNG device tree node
+
+Add the necessary DT node to probe the rng driver on
+msm8960-cdp platform.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+Tested-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8960.dtsi | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
+index 02231a5..ecfba72 100644
+--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
+@@ -119,4 +119,11 @@
+ reg = <0x500000 0x1000>;
+ qcom,controller-type = "pmic-arbiter";
+ };
++
++ rng@1a500000 {
++ compatible = "qcom,prng";
++ reg = <0x1a500000 0x200>;
++ clocks = <&gcc PRNG_CLK>;
++ clock-names = "core";
++ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch
new file mode 100644
index 0000000..013d45c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch
@@ -0,0 +1,53 @@
+From 02c987fb8a3607ab6e0ead0e5aaa7da753ce9537 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 21 Feb 2014 11:09:50 +0000
+Subject: [PATCH 024/182] ARM: dts: msm: Add krait-pmu to platforms with Krait
+ CPUs
+
+Allows us to probe the performance counters on Krait CPUs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Will Deacon <will.deacon@arm.com>
+[olof: Moved 8960 contents to the dtsi instead]
+Signed-off-by: Olof Johansson <olof@lixom.net>
+---
+ arch/arm/boot/dts/qcom-msm8960.dtsi | 6 ++++++
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 5 +++++
+ 2 files changed, 11 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
+index ecfba72..997b7b9 100644
+--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
+@@ -39,6 +39,12 @@
+ };
+ };
+
++ cpu-pmu {
++ compatible = "qcom,krait-pmu";
++ interrupts = <1 10 0x304>;
++ qcom,no-pc-write;
++ };
++
+ intc: interrupt-controller@2000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index 011eb09..f687239 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -52,6 +52,11 @@
+ };
+ };
+
++ cpu-pmu {
++ compatible = "qcom,krait-pmu";
++ interrupts = <1 7 0xf04>;
++ };
++
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch
new file mode 100644
index 0000000..fba5e4f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch
@@ -0,0 +1,71 @@
+From ef8eb0991f291df12c12477648235f955cc388b0 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Wed, 5 Mar 2014 13:33:08 -0600
+Subject: [PATCH 025/182] pinctrl: msm: drop wake_irqs bitmap
+
+Currently, the wake_irqs bitmap is used to track whether there are any
+gpio's which are configured as wake irqs, and uses this to determine
+whether or not to call enable_irq_wake()/disable_irq_wake() on the
+summary interrupt.
+
+However, the genirq core already handles this case, by maintaining a
+'wake_count' per irq_desc, and only calling into the controlling
+irq_chip when wake_count transitions 0 <-> 1.
+
+Drop this bitmap, and unconditionally call irq_set_irq_wake() on the
+summary interrupt.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index ef2bf31..0e43fdd 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -50,7 +50,6 @@
+ * @enabled_irqs: Bitmap of currently enabled irqs.
+ * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
+ * detection.
+- * @wake_irqs: Bitmap of irqs with requested as wakeup source.
+ * @soc; Reference to soc_data of platform specific data.
+ * @regs: Base address for the TLMM register map.
+ */
+@@ -65,7 +64,6 @@ struct msm_pinctrl {
+
+ DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
+ DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
+- DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO);
+
+ const struct msm_pinctrl_soc_data *soc;
+ void __iomem *regs;
+@@ -783,22 +781,12 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+ {
+ struct msm_pinctrl *pctrl;
+ unsigned long flags;
+- unsigned ngpio;
+
+ pctrl = irq_data_get_irq_chip_data(d);
+- ngpio = pctrl->chip.ngpio;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+- if (on) {
+- if (bitmap_empty(pctrl->wake_irqs, ngpio))
+- enable_irq_wake(pctrl->irq);
+- set_bit(d->hwirq, pctrl->wake_irqs);
+- } else {
+- clear_bit(d->hwirq, pctrl->wake_irqs);
+- if (bitmap_empty(pctrl->wake_irqs, ngpio))
+- disable_irq_wake(pctrl->irq);
+- }
++ irq_set_irq_wake(pctrl->irq, on);
+
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch
new file mode 100644
index 0000000..fd2e10f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch
@@ -0,0 +1,73 @@
+From 9588c91936434166007c3a15ad7f4e2f3729c5e7 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:40 -0800
+Subject: [PATCH 026/182] pinctrl: msm: Silence recursive lockdep warning
+
+If a driver calls enable_irq_wake() on a gpio turned interrupt
+from the msm pinctrl driver we'll get a lockdep warning like so:
+
+=============================================
+[ INFO: possible recursive locking detected ]
+3.14.0-rc3 #2 Not tainted
+---------------------------------------------
+modprobe/52 is trying to acquire lock:
+ (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88
+
+but task is already holding lock:
+ (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88
+
+other info that might help us debug this:
+ Possible unsafe locking scenario:
+
+ CPU0
+ ----
+ lock(&irq_desc_lock_class);
+ lock(&irq_desc_lock_class);
+
+ *** DEADLOCK ***
+
+ May be due to missing lock nesting notation
+
+4 locks held by modprobe/52:
+ #0: (&__lockdep_no_validate__){......}, at: [<c04f2864>] __driver_attach+0x48/0x98
+ #1: (&__lockdep_no_validate__){......}, at: [<c04f2874>] __driver_attach+0x58/0x98
+ #2: (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88
+ #3: (&(&pctrl->lock)->rlock){......}, at: [<c04bb4b8>] msm_gpio_irq_set_wake+0x20/0xa8
+
+Silence it by putting the gpios into their own lock class.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 0e43fdd..e61b30a 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -857,6 +857,12 @@ static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+ chained_irq_exit(chip, desc);
+ }
+
++/*
++ * This lock class tells lockdep that GPIO irqs are in a different
++ * category than their parents, so it won't report false recursion.
++ */
++static struct lock_class_key gpio_lock_class;
++
+ static int msm_gpio_init(struct msm_pinctrl *pctrl)
+ {
+ struct gpio_chip *chip;
+@@ -895,6 +901,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
+
+ for (i = 0; i < chip->ngpio; i++) {
+ irq = irq_create_mapping(pctrl->domain, i);
++ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, handle_edge_irq);
+ irq_set_chip_data(irq, pctrl);
+ }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch
new file mode 100644
index 0000000..c2b7487
--- /dev/null
+++ b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch
@@ -0,0 +1,39 @@
+From 8341db7b05b688e8e5a93acd0e80b40be409d037 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:41 -0800
+Subject: [PATCH 027/182] pinctrl: msm: Check for ngpios > MAX_NR_GPIO
+
+Fail the probe and print a warning if SoC specific drivers have
+more GPIOs than there can be accounted for in the static bitmaps.
+This should avoid silent corruption/failures in the future.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index e61b30a..90ac995 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -870,10 +870,14 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
+ int ret;
+ int i;
+ int r;
++ unsigned ngpio = pctrl->soc->ngpios;
++
++ if (WARN_ON(ngpio > MAX_NR_GPIO))
++ return -EINVAL;
+
+ chip = &pctrl->chip;
+ chip->base = 0;
+- chip->ngpio = pctrl->soc->ngpios;
++ chip->ngpio = ngpio;
+ chip->label = dev_name(pctrl->dev);
+ chip->dev = pctrl->dev;
+ chip->owner = THIS_MODULE;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch
new file mode 100644
index 0000000..8f864f2
--- /dev/null
+++ b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch
@@ -0,0 +1,60 @@
+From d2caa906f865d6919c511a956ae63ab62b240eba Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:42 -0800
+Subject: [PATCH 028/182] pinctrl: msm: Drop unused includes
+
+These includes are unused or can be handled via forward
+declarations. Remove them.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 1 -
+ drivers/pinctrl/pinctrl-msm.h | 5 +----
+ drivers/pinctrl/pinctrl-msm8x74.c | 1 -
+ 3 files changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 90ac995..4474e00 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -28,7 +28,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/irq.h>
+ #include <linux/irqchip/chained_irq.h>
+-#include <linux/of_irq.h>
+ #include <linux/spinlock.h>
+
+ #include "core.h"
+diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h
+index 206e782..8fbe9fb 100644
+--- a/drivers/pinctrl/pinctrl-msm.h
++++ b/drivers/pinctrl/pinctrl-msm.h
+@@ -13,10 +13,7 @@
+ #ifndef __PINCTRL_MSM_H__
+ #define __PINCTRL_MSM_H__
+
+-#include <linux/pinctrl/pinctrl.h>
+-#include <linux/pinctrl/pinmux.h>
+-#include <linux/pinctrl/pinconf.h>
+-#include <linux/pinctrl/machine.h>
++struct pinctrl_pin_desc;
+
+ /**
+ * struct msm_function - a pinmux function
+diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c
+index f944bf2..bb5ded69f 100644
+--- a/drivers/pinctrl/pinctrl-msm8x74.c
++++ b/drivers/pinctrl/pinctrl-msm8x74.c
+@@ -15,7 +15,6 @@
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/pinctrl/pinctrl.h>
+-#include <linux/pinctrl/pinmux.h>
+
+ #include "pinctrl-msm.h"
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch
new file mode 100644
index 0000000..6a33a9d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch
@@ -0,0 +1,31 @@
+From 2cfbd01bd994bd16dbee28a99553796f12848c4c Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:43 -0800
+Subject: [PATCH 029/182] pinctrl: msm: Drop OF_IRQ dependency
+
+This driver doesn't rely on any functionality living in
+drivers/of/irq.c to compile. Drop this dependency.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
+index 1e4e693..06cee01 100644
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -224,7 +224,7 @@ config PINCTRL_MSM
+
+ config PINCTRL_MSM8X74
+ tristate "Qualcomm 8x74 pin controller driver"
+- depends on GPIOLIB && OF && OF_IRQ
++ depends on GPIOLIB && OF
+ select PINCTRL_MSM
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch
new file mode 100644
index 0000000..975e11a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch
@@ -0,0 +1,66 @@
+From e34d9fdac8182f6ce8933501fea6e84664060bf0 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:44 -0800
+Subject: [PATCH 030/182] pinctrl: msm: Replace lookup tables with math
+
+We don't need to waste space with these lookup tables, just do
+the math directly.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 4474e00..87f6c3c 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -258,8 +258,10 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ #define MSM_PULL_DOWN 1
+ #define MSM_PULL_UP 3
+
+-static const unsigned msm_regval_to_drive[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
+-static const unsigned msm_drive_to_regval[] = { -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7 };
++static unsigned msm_regval_to_drive(u32 val)
++{
++ return (val + 1) * 2;
++}
+
+ static int msm_config_group_get(struct pinctrl_dev *pctldev,
+ unsigned int group,
+@@ -296,7 +298,7 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev,
+ arg = arg == MSM_PULL_UP;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+- arg = msm_regval_to_drive[arg];
++ arg = msm_regval_to_drive(arg);
+ break;
+ default:
+ dev_err(pctrl->dev, "Unsupported config parameter: %x\n",
+@@ -349,10 +351,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ /* Check for invalid values */
+- if (arg >= ARRAY_SIZE(msm_drive_to_regval))
++ if (arg > 16 || arg < 2 || (arg % 2) != 0)
+ arg = -1;
+ else
+- arg = msm_drive_to_regval[arg];
++ arg = (arg / 2) - 1;
+ break;
+ default:
+ dev_err(pctrl->dev, "Unsupported config parameter: %x\n",
+@@ -531,7 +533,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
+ pull = (ctl_reg >> g->pull_bit) & 3;
+
+ seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func);
+- seq_printf(s, " %dmA", msm_regval_to_drive[drive]);
++ seq_printf(s, " %dmA", msm_regval_to_drive(drive));
+ seq_printf(s, " %s", pulls[pull]);
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch
new file mode 100644
index 0000000..61c8ac1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch
@@ -0,0 +1,95 @@
+From 286113578287b9c7619b4104864cffb91820f49d Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:45 -0800
+Subject: [PATCH 031/182] pinctrl: msm: Remove impossible WARN_ON()s
+
+All these functions are limited in what they can pass as the gpio
+or irq number to whatever is setup during probe. Remove the
+checks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 87f6c3c..ab46e3a 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -401,8 +401,6 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+ u32 val;
+
+ g = &pctrl->soc->groups[offset];
+- if (WARN_ON(g->io_reg < 0))
+- return -EINVAL;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -423,8 +421,6 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in
+ u32 val;
+
+ g = &pctrl->soc->groups[offset];
+- if (WARN_ON(g->io_reg < 0))
+- return -EINVAL;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -451,8 +447,6 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+ u32 val;
+
+ g = &pctrl->soc->groups[offset];
+- if (WARN_ON(g->io_reg < 0))
+- return -EINVAL;
+
+ val = readl(pctrl->regs + g->io_reg);
+ return !!(val & BIT(g->in_bit));
+@@ -466,8 +460,6 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+ u32 val;
+
+ g = &pctrl->soc->groups[offset];
+- if (WARN_ON(g->io_reg < 0))
+- return;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -616,8 +608,6 @@ static void msm_gpio_irq_mask(struct irq_data *d)
+
+ pctrl = irq_data_get_irq_chip_data(d);
+ g = &pctrl->soc->groups[d->hwirq];
+- if (WARN_ON(g->intr_cfg_reg < 0))
+- return;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -639,8 +629,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d)
+
+ pctrl = irq_data_get_irq_chip_data(d);
+ g = &pctrl->soc->groups[d->hwirq];
+- if (WARN_ON(g->intr_status_reg < 0))
+- return;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -666,8 +654,6 @@ static void msm_gpio_irq_ack(struct irq_data *d)
+
+ pctrl = irq_data_get_irq_chip_data(d);
+ g = &pctrl->soc->groups[d->hwirq];
+- if (WARN_ON(g->intr_status_reg < 0))
+- return;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+@@ -692,8 +678,6 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+
+ pctrl = irq_data_get_irq_chip_data(d);
+ g = &pctrl->soc->groups[d->hwirq];
+- if (WARN_ON(g->intr_cfg_reg < 0))
+- return -EINVAL;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch
new file mode 100644
index 0000000..d656978
--- /dev/null
+++ b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch
@@ -0,0 +1,115 @@
+From 2d9ffb1a3f87396c3b792124870ef63fc27c568f Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 6 Mar 2014 22:44:46 -0800
+Subject: [PATCH 032/182] pinctrl: msm: Simplify msm_config_reg() and callers
+
+We don't need to check for a negative reg here because reg is
+always the same and is always non-negative. Also, collapse the
+switch statement down for the duplicate cases.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 29 +++++------------------------
+ 1 file changed, 5 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index ab46e3a..91de8bc 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -200,28 +200,17 @@ static const struct pinmux_ops msm_pinmux_ops = {
+ static int msm_config_reg(struct msm_pinctrl *pctrl,
+ const struct msm_pingroup *g,
+ unsigned param,
+- s16 *reg,
+ unsigned *mask,
+ unsigned *bit)
+ {
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+- *reg = g->ctl_reg;
+- *bit = g->pull_bit;
+- *mask = 3;
+- break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+- *reg = g->ctl_reg;
+- *bit = g->pull_bit;
+- *mask = 3;
+- break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+- *reg = g->ctl_reg;
+ *bit = g->pull_bit;
+ *mask = 3;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+- *reg = g->ctl_reg;
+ *bit = g->drv_bit;
+ *mask = 7;
+ break;
+@@ -230,12 +219,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl,
+ return -ENOTSUPP;
+ }
+
+- if (*reg < 0) {
+- dev_err(pctrl->dev, "Config param %04x not supported on group %s\n",
+- param, g->name);
+- return -ENOTSUPP;
+- }
+-
+ return 0;
+ }
+
+@@ -273,17 +256,16 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev,
+ unsigned mask;
+ unsigned arg;
+ unsigned bit;
+- s16 reg;
+ int ret;
+ u32 val;
+
+ g = &pctrl->soc->groups[group];
+
+- ret = msm_config_reg(pctrl, g, param, &reg, &mask, &bit);
++ ret = msm_config_reg(pctrl, g, param, &mask, &bit);
+ if (ret < 0)
+ return ret;
+
+- val = readl(pctrl->regs + reg);
++ val = readl(pctrl->regs + g->ctl_reg);
+ arg = (val >> bit) & mask;
+
+ /* Convert register value to pinconf value */
+@@ -323,7 +305,6 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ unsigned mask;
+ unsigned arg;
+ unsigned bit;
+- s16 reg;
+ int ret;
+ u32 val;
+ int i;
+@@ -334,7 +315,7 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+- ret = msm_config_reg(pctrl, g, param, &reg, &mask, &bit);
++ ret = msm_config_reg(pctrl, g, param, &mask, &bit);
+ if (ret < 0)
+ return ret;
+
+@@ -369,10 +350,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ }
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+- val = readl(pctrl->regs + reg);
++ val = readl(pctrl->regs + g->ctl_reg);
+ val &= ~(mask << bit);
+ val |= arg << bit;
+- writel(val, pctrl->regs + reg);
++ writel(val, pctrl->regs + g->ctl_reg);
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch
new file mode 100644
index 0000000..18c72cc
--- /dev/null
+++ b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch
@@ -0,0 +1,69 @@
+From 469f83e0ed374250be5fd6202ac535276a752fa8 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn@kryo.se>
+Date: Tue, 4 Feb 2014 19:55:31 -0800
+Subject: [PATCH 033/182] pinctrl-msm: Support output-{high,low} configuration
+
+Add support for configuring pins as output with value as from the
+pinconf-generic interface.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 91de8bc..19d2feb 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -214,6 +214,11 @@ static int msm_config_reg(struct msm_pinctrl *pctrl,
+ *bit = g->drv_bit;
+ *mask = 7;
+ break;
++ case PIN_CONFIG_OUTPUT:
++ *reg = g->ctl_reg;
++ *bit = g->oe_bit;
++ *mask = 1;
++ break;
+ default:
+ dev_err(pctrl->dev, "Invalid config param %04x\n", param);
+ return -ENOTSUPP;
+@@ -282,6 +287,14 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev,
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ arg = msm_regval_to_drive(arg);
+ break;
++ case PIN_CONFIG_OUTPUT:
++ /* Pin is not output */
++ if (!arg)
++ return -EINVAL;
++
++ val = readl(pctrl->regs + g->io_reg);
++ arg = !!(val & BIT(g->in_bit));
++ break;
+ default:
+ dev_err(pctrl->dev, "Unsupported config parameter: %x\n",
+ param);
+@@ -337,6 +350,20 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ else
+ arg = (arg / 2) - 1;
+ break;
++ case PIN_CONFIG_OUTPUT:
++ /* set output value */
++ spin_lock_irqsave(&pctrl->lock, flags);
++ val = readl(pctrl->regs + g->io_reg);
++ if (arg)
++ val |= BIT(g->out_bit);
++ else
++ val &= ~BIT(g->out_bit);
++ writel(val, pctrl->regs + g->io_reg);
++ spin_unlock_irqrestore(&pctrl->lock, flags);
++
++ /* enable output */
++ arg = 1;
++ break;
+ default:
+ dev_err(pctrl->dev, "Unsupported config parameter: %x\n",
+ param);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch
new file mode 100644
index 0000000..b8df704
--- /dev/null
+++ b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch
@@ -0,0 +1,63 @@
+From 77e4b572fcc4015e235f22fd93b8df35e452baf0 Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Thu, 6 Feb 2014 17:28:48 +0200
+Subject: [PATCH 034/182] pinctrl-msm: Add SPI8 pin definitions
+
+Add pin, group and function definitions for SPI#8
+controller.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm8x74.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c
+index bb5ded69f..dde5529 100644
+--- a/drivers/pinctrl/pinctrl-msm8x74.c
++++ b/drivers/pinctrl/pinctrl-msm8x74.c
+@@ -405,6 +405,7 @@ enum msm8x74_functions {
+ MSM_MUX_blsp_i2c6,
+ MSM_MUX_blsp_i2c11,
+ MSM_MUX_blsp_spi1,
++ MSM_MUX_blsp_spi8,
+ MSM_MUX_blsp_uart2,
+ MSM_MUX_blsp_uart8,
+ MSM_MUX_slimbus,
+@@ -415,6 +416,9 @@ static const char * const blsp_i2c2_groups[] = { "gpio6", "gpio7" };
+ static const char * const blsp_i2c6_groups[] = { "gpio29", "gpio30" };
+ static const char * const blsp_i2c11_groups[] = { "gpio83", "gpio84" };
+ static const char * const blsp_spi1_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3" };
++static const char * const blsp_spi8_groups[] = {
++ "gpio45", "gpio46", "gpio47", "gpio48"
++};
+ static const char * const blsp_uart2_groups[] = { "gpio4", "gpio5" };
+ static const char * const blsp_uart8_groups[] = { "gpio45", "gpio46" };
+ static const char * const slimbus_groups[] = { "gpio70", "gpio71" };
+@@ -424,6 +428,7 @@ static const struct msm_function msm8x74_functions[] = {
+ FUNCTION(blsp_i2c6),
+ FUNCTION(blsp_i2c11),
+ FUNCTION(blsp_spi1),
++ FUNCTION(blsp_spi8),
+ FUNCTION(blsp_uart2),
+ FUNCTION(blsp_uart8),
+ FUNCTION(slimbus),
+@@ -475,10 +480,10 @@ static const struct msm_pingroup msm8x74_groups[] = {
+ PINGROUP(42, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(44, NA, NA, NA, NA, NA, NA, NA),
+- PINGROUP(45, NA, blsp_uart8, NA, NA, NA, NA, NA),
+- PINGROUP(46, NA, blsp_uart8, NA, NA, NA, NA, NA),
+- PINGROUP(47, NA, NA, NA, NA, NA, NA, NA),
+- PINGROUP(48, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(45, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA),
++ PINGROUP(46, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA),
++ PINGROUP(47, blsp_spi8, NA, NA, NA, NA, NA, NA),
++ PINGROUP(48, blsp_spi8, NA, NA, NA, NA, NA, NA),
+ PINGROUP(49, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(50, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(51, NA, NA, NA, NA, NA, NA, NA),
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch
new file mode 100644
index 0000000..c141a0b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch
@@ -0,0 +1,38 @@
+From c73a138dd24049d06fe7b22518655ed9e7413cd2 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Fri, 14 Mar 2014 07:54:20 +0100
+Subject: [PATCH 035/182] pinctrl: msm: fix up out-of-order merge conflict
+
+Commit 051a58b4622f0e1b732acb750097c64bc00ddb93
+"pinctrl: msm: Simplify msm_config_reg() and callers"
+removed the local "reg" variable in the msm_config_reg()
+function, but the earlier
+commit ed118a5fd951bd2def8249ee251842c4f81fe4bd
+"pinctrl-msm: Support output-{high,low} configuration"
+introduced a new switchclause using it.
+
+Fix this up by removing the offending register assignment.
+
+Reported-by: Kbuild test robot <fengguang.wu@intel.com>
+Cc: Stephen Boyd <sboyd@codeaurora.org>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 19d2feb..343f421 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -215,7 +215,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl,
+ *mask = 7;
+ break;
+ case PIN_CONFIG_OUTPUT:
+- *reg = g->ctl_reg;
+ *bit = g->oe_bit;
+ *mask = 1;
+ break;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch
new file mode 100644
index 0000000..b44fb6a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch
@@ -0,0 +1,55 @@
+From 32787a9bba5a1ebeea891fd7aab954e6d344892a Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Mon, 31 Mar 2014 14:49:54 -0700
+Subject: [PATCH 036/182] pinctrl: msm: Correct interrupt code for TLMM v2
+
+Acking interrupts are done differently between on v2 and v3, so add an extra
+attribute to the pingroup struct to let the platform definitions control this.
+Also make sure to start dual edge detection by detecting the rising edge.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 6 +++++-
+ drivers/pinctrl/pinctrl-msm.h | 1 +
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 343f421..706809e 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -665,7 +665,10 @@ static void msm_gpio_irq_ack(struct irq_data *d)
+ spin_lock_irqsave(&pctrl->lock, flags);
+
+ val = readl(pctrl->regs + g->intr_status_reg);
+- val &= ~BIT(g->intr_status_bit);
++ if (g->intr_ack_high)
++ val |= BIT(g->intr_status_bit);
++ else
++ val &= ~BIT(g->intr_status_bit);
+ writel(val, pctrl->regs + g->intr_status_reg);
+
+ if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
+@@ -744,6 +747,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val |= BIT(g->intr_detection_bit);
++ val |= BIT(g->intr_polarity_bit);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ break;
+diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h
+index 8fbe9fb..6e26f1b 100644
+--- a/drivers/pinctrl/pinctrl-msm.h
++++ b/drivers/pinctrl/pinctrl-msm.h
+@@ -84,6 +84,7 @@ struct msm_pingroup {
+
+ unsigned intr_enable_bit:5;
+ unsigned intr_status_bit:5;
++ unsigned intr_ack_high:1;
+
+ unsigned intr_target_bit:5;
+ unsigned intr_raw_status_bit:5;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch
new file mode 100644
index 0000000..346dcd0
--- /dev/null
+++ b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch
@@ -0,0 +1,74 @@
+From 1c11b14dd6d740e997919f0bf789bf921548dc0f Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Mon, 31 Mar 2014 14:49:55 -0700
+Subject: [PATCH 037/182] pinctrl: msm: Make number of functions variable
+
+The various pins may have different number of functions defined, so make this
+number definable per pin instead of just increasing it to the largest one for
+all of the platforms.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 4 ++--
+ drivers/pinctrl/pinctrl-msm.h | 3 ++-
+ drivers/pinctrl/pinctrl-msm8x74.c | 3 ++-
+ 3 files changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 706809e..7d67d34 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -145,12 +145,12 @@ static int msm_pinmux_enable(struct pinctrl_dev *pctldev,
+ if (WARN_ON(g->mux_bit < 0))
+ return -EINVAL;
+
+- for (i = 0; i < ARRAY_SIZE(g->funcs); i++) {
++ for (i = 0; i < g->nfuncs; i++) {
+ if (g->funcs[i] == function)
+ break;
+ }
+
+- if (WARN_ON(i == ARRAY_SIZE(g->funcs)))
++ if (WARN_ON(i == g->nfuncs))
+ return -EINVAL;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h
+index 6e26f1b..7b2a227 100644
+--- a/drivers/pinctrl/pinctrl-msm.h
++++ b/drivers/pinctrl/pinctrl-msm.h
+@@ -65,7 +65,8 @@ struct msm_pingroup {
+ const unsigned *pins;
+ unsigned npins;
+
+- unsigned funcs[8];
++ unsigned *funcs;
++ unsigned nfuncs;
+
+ s16 ctl_reg;
+ s16 io_reg;
+diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c
+index dde5529..57766d5 100644
+--- a/drivers/pinctrl/pinctrl-msm8x74.c
++++ b/drivers/pinctrl/pinctrl-msm8x74.c
+@@ -341,7 +341,7 @@ static const unsigned int sdc2_data_pins[] = { 151 };
+ .name = "gpio" #id, \
+ .pins = gpio##id##_pins, \
+ .npins = ARRAY_SIZE(gpio##id##_pins), \
+- .funcs = { \
++ .funcs = (int[]){ \
+ MSM_MUX_NA, /* gpio mode */ \
+ MSM_MUX_##f1, \
+ MSM_MUX_##f2, \
+@@ -351,6 +351,7 @@ static const unsigned int sdc2_data_pins[] = { 151 };
+ MSM_MUX_##f6, \
+ MSM_MUX_##f7 \
+ }, \
++ .nfuncs = 8, \
+ .ctl_reg = 0x1000 + 0x10 * id, \
+ .io_reg = 0x1004 + 0x10 * id, \
+ .intr_cfg_reg = 0x1008 + 0x10 * id, \
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch
new file mode 100644
index 0000000..20e5a1d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch
@@ -0,0 +1,624 @@
+From 247288012122ccfe7d5d9af00a45814c6fdd94c5 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Mon, 31 Mar 2014 14:49:57 -0700
+Subject: [PATCH 038/182] pinctrl: msm: Add definitions for the APQ8064
+ platform
+
+This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the
+Qualcomm APQ8064 platform.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/Kconfig | 8 +
+ drivers/pinctrl/Makefile | 1 +
+ drivers/pinctrl/pinctrl-apq8064.c | 566 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 575 insertions(+)
+ create mode 100644 drivers/pinctrl/pinctrl-apq8064.c
+
+diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
+index 06cee01..91993a6 100644
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -222,6 +222,14 @@ config PINCTRL_MSM
+ select PINCONF
+ select GENERIC_PINCONF
+
++config PINCTRL_APQ8064
++ tristate "Qualcomm APQ8064 pin controller driver"
++ depends on GPIOLIB && OF
++ select PINCTRL_MSM
++ help
++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
++ Qualcomm TLMM block found in the Qualcomm APQ8064 platform.
++
+ config PINCTRL_MSM8X74
+ tristate "Qualcomm 8x74 pin controller driver"
+ depends on GPIOLIB && OF
+diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
+index 4b83588..9e1fb67 100644
+--- a/drivers/pinctrl/Makefile
++++ b/drivers/pinctrl/Makefile
+@@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o
+ obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o
+ obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o
+ obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o
++obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o
+ obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o
+ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
+ obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
+diff --git a/drivers/pinctrl/pinctrl-apq8064.c b/drivers/pinctrl/pinctrl-apq8064.c
+new file mode 100644
+index 0000000..7c2a8ba
+--- /dev/null
++++ b/drivers/pinctrl/pinctrl-apq8064.c
+@@ -0,0 +1,566 @@
++/*
++ * Copyright (c) 2014, Sony Mobile Communications AB.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pinctrl/pinctrl.h>
++
++#include "pinctrl-msm.h"
++
++static const struct pinctrl_pin_desc apq8064_pins[] = {
++ PINCTRL_PIN(0, "GPIO_0"),
++ PINCTRL_PIN(1, "GPIO_1"),
++ PINCTRL_PIN(2, "GPIO_2"),
++ PINCTRL_PIN(3, "GPIO_3"),
++ PINCTRL_PIN(4, "GPIO_4"),
++ PINCTRL_PIN(5, "GPIO_5"),
++ PINCTRL_PIN(6, "GPIO_6"),
++ PINCTRL_PIN(7, "GPIO_7"),
++ PINCTRL_PIN(8, "GPIO_8"),
++ PINCTRL_PIN(9, "GPIO_9"),
++ PINCTRL_PIN(10, "GPIO_10"),
++ PINCTRL_PIN(11, "GPIO_11"),
++ PINCTRL_PIN(12, "GPIO_12"),
++ PINCTRL_PIN(13, "GPIO_13"),
++ PINCTRL_PIN(14, "GPIO_14"),
++ PINCTRL_PIN(15, "GPIO_15"),
++ PINCTRL_PIN(16, "GPIO_16"),
++ PINCTRL_PIN(17, "GPIO_17"),
++ PINCTRL_PIN(18, "GPIO_18"),
++ PINCTRL_PIN(19, "GPIO_19"),
++ PINCTRL_PIN(20, "GPIO_20"),
++ PINCTRL_PIN(21, "GPIO_21"),
++ PINCTRL_PIN(22, "GPIO_22"),
++ PINCTRL_PIN(23, "GPIO_23"),
++ PINCTRL_PIN(24, "GPIO_24"),
++ PINCTRL_PIN(25, "GPIO_25"),
++ PINCTRL_PIN(26, "GPIO_26"),
++ PINCTRL_PIN(27, "GPIO_27"),
++ PINCTRL_PIN(28, "GPIO_28"),
++ PINCTRL_PIN(29, "GPIO_29"),
++ PINCTRL_PIN(30, "GPIO_30"),
++ PINCTRL_PIN(31, "GPIO_31"),
++ PINCTRL_PIN(32, "GPIO_32"),
++ PINCTRL_PIN(33, "GPIO_33"),
++ PINCTRL_PIN(34, "GPIO_34"),
++ PINCTRL_PIN(35, "GPIO_35"),
++ PINCTRL_PIN(36, "GPIO_36"),
++ PINCTRL_PIN(37, "GPIO_37"),
++ PINCTRL_PIN(38, "GPIO_38"),
++ PINCTRL_PIN(39, "GPIO_39"),
++ PINCTRL_PIN(40, "GPIO_40"),
++ PINCTRL_PIN(41, "GPIO_41"),
++ PINCTRL_PIN(42, "GPIO_42"),
++ PINCTRL_PIN(43, "GPIO_43"),
++ PINCTRL_PIN(44, "GPIO_44"),
++ PINCTRL_PIN(45, "GPIO_45"),
++ PINCTRL_PIN(46, "GPIO_46"),
++ PINCTRL_PIN(47, "GPIO_47"),
++ PINCTRL_PIN(48, "GPIO_48"),
++ PINCTRL_PIN(49, "GPIO_49"),
++ PINCTRL_PIN(50, "GPIO_50"),
++ PINCTRL_PIN(51, "GPIO_51"),
++ PINCTRL_PIN(52, "GPIO_52"),
++ PINCTRL_PIN(53, "GPIO_53"),
++ PINCTRL_PIN(54, "GPIO_54"),
++ PINCTRL_PIN(55, "GPIO_55"),
++ PINCTRL_PIN(56, "GPIO_56"),
++ PINCTRL_PIN(57, "GPIO_57"),
++ PINCTRL_PIN(58, "GPIO_58"),
++ PINCTRL_PIN(59, "GPIO_59"),
++ PINCTRL_PIN(60, "GPIO_60"),
++ PINCTRL_PIN(61, "GPIO_61"),
++ PINCTRL_PIN(62, "GPIO_62"),
++ PINCTRL_PIN(63, "GPIO_63"),
++ PINCTRL_PIN(64, "GPIO_64"),
++ PINCTRL_PIN(65, "GPIO_65"),
++ PINCTRL_PIN(66, "GPIO_66"),
++ PINCTRL_PIN(67, "GPIO_67"),
++ PINCTRL_PIN(68, "GPIO_68"),
++ PINCTRL_PIN(69, "GPIO_69"),
++ PINCTRL_PIN(70, "GPIO_70"),
++ PINCTRL_PIN(71, "GPIO_71"),
++ PINCTRL_PIN(72, "GPIO_72"),
++ PINCTRL_PIN(73, "GPIO_73"),
++ PINCTRL_PIN(74, "GPIO_74"),
++ PINCTRL_PIN(75, "GPIO_75"),
++ PINCTRL_PIN(76, "GPIO_76"),
++ PINCTRL_PIN(77, "GPIO_77"),
++ PINCTRL_PIN(78, "GPIO_78"),
++ PINCTRL_PIN(79, "GPIO_79"),
++ PINCTRL_PIN(80, "GPIO_80"),
++ PINCTRL_PIN(81, "GPIO_81"),
++ PINCTRL_PIN(82, "GPIO_82"),
++ PINCTRL_PIN(83, "GPIO_83"),
++ PINCTRL_PIN(84, "GPIO_84"),
++ PINCTRL_PIN(85, "GPIO_85"),
++ PINCTRL_PIN(86, "GPIO_86"),
++ PINCTRL_PIN(87, "GPIO_87"),
++ PINCTRL_PIN(88, "GPIO_88"),
++ PINCTRL_PIN(89, "GPIO_89"),
++};
++
++#define DECLARE_APQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin }
++DECLARE_APQ_GPIO_PINS(0);
++DECLARE_APQ_GPIO_PINS(1);
++DECLARE_APQ_GPIO_PINS(2);
++DECLARE_APQ_GPIO_PINS(3);
++DECLARE_APQ_GPIO_PINS(4);
++DECLARE_APQ_GPIO_PINS(5);
++DECLARE_APQ_GPIO_PINS(6);
++DECLARE_APQ_GPIO_PINS(7);
++DECLARE_APQ_GPIO_PINS(8);
++DECLARE_APQ_GPIO_PINS(9);
++DECLARE_APQ_GPIO_PINS(10);
++DECLARE_APQ_GPIO_PINS(11);
++DECLARE_APQ_GPIO_PINS(12);
++DECLARE_APQ_GPIO_PINS(13);
++DECLARE_APQ_GPIO_PINS(14);
++DECLARE_APQ_GPIO_PINS(15);
++DECLARE_APQ_GPIO_PINS(16);
++DECLARE_APQ_GPIO_PINS(17);
++DECLARE_APQ_GPIO_PINS(18);
++DECLARE_APQ_GPIO_PINS(19);
++DECLARE_APQ_GPIO_PINS(20);
++DECLARE_APQ_GPIO_PINS(21);
++DECLARE_APQ_GPIO_PINS(22);
++DECLARE_APQ_GPIO_PINS(23);
++DECLARE_APQ_GPIO_PINS(24);
++DECLARE_APQ_GPIO_PINS(25);
++DECLARE_APQ_GPIO_PINS(26);
++DECLARE_APQ_GPIO_PINS(27);
++DECLARE_APQ_GPIO_PINS(28);
++DECLARE_APQ_GPIO_PINS(29);
++DECLARE_APQ_GPIO_PINS(30);
++DECLARE_APQ_GPIO_PINS(31);
++DECLARE_APQ_GPIO_PINS(32);
++DECLARE_APQ_GPIO_PINS(33);
++DECLARE_APQ_GPIO_PINS(34);
++DECLARE_APQ_GPIO_PINS(35);
++DECLARE_APQ_GPIO_PINS(36);
++DECLARE_APQ_GPIO_PINS(37);
++DECLARE_APQ_GPIO_PINS(38);
++DECLARE_APQ_GPIO_PINS(39);
++DECLARE_APQ_GPIO_PINS(40);
++DECLARE_APQ_GPIO_PINS(41);
++DECLARE_APQ_GPIO_PINS(42);
++DECLARE_APQ_GPIO_PINS(43);
++DECLARE_APQ_GPIO_PINS(44);
++DECLARE_APQ_GPIO_PINS(45);
++DECLARE_APQ_GPIO_PINS(46);
++DECLARE_APQ_GPIO_PINS(47);
++DECLARE_APQ_GPIO_PINS(48);
++DECLARE_APQ_GPIO_PINS(49);
++DECLARE_APQ_GPIO_PINS(50);
++DECLARE_APQ_GPIO_PINS(51);
++DECLARE_APQ_GPIO_PINS(52);
++DECLARE_APQ_GPIO_PINS(53);
++DECLARE_APQ_GPIO_PINS(54);
++DECLARE_APQ_GPIO_PINS(55);
++DECLARE_APQ_GPIO_PINS(56);
++DECLARE_APQ_GPIO_PINS(57);
++DECLARE_APQ_GPIO_PINS(58);
++DECLARE_APQ_GPIO_PINS(59);
++DECLARE_APQ_GPIO_PINS(60);
++DECLARE_APQ_GPIO_PINS(61);
++DECLARE_APQ_GPIO_PINS(62);
++DECLARE_APQ_GPIO_PINS(63);
++DECLARE_APQ_GPIO_PINS(64);
++DECLARE_APQ_GPIO_PINS(65);
++DECLARE_APQ_GPIO_PINS(66);
++DECLARE_APQ_GPIO_PINS(67);
++DECLARE_APQ_GPIO_PINS(68);
++DECLARE_APQ_GPIO_PINS(69);
++DECLARE_APQ_GPIO_PINS(70);
++DECLARE_APQ_GPIO_PINS(71);
++DECLARE_APQ_GPIO_PINS(72);
++DECLARE_APQ_GPIO_PINS(73);
++DECLARE_APQ_GPIO_PINS(74);
++DECLARE_APQ_GPIO_PINS(75);
++DECLARE_APQ_GPIO_PINS(76);
++DECLARE_APQ_GPIO_PINS(77);
++DECLARE_APQ_GPIO_PINS(78);
++DECLARE_APQ_GPIO_PINS(79);
++DECLARE_APQ_GPIO_PINS(80);
++DECLARE_APQ_GPIO_PINS(81);
++DECLARE_APQ_GPIO_PINS(82);
++DECLARE_APQ_GPIO_PINS(83);
++DECLARE_APQ_GPIO_PINS(84);
++DECLARE_APQ_GPIO_PINS(85);
++DECLARE_APQ_GPIO_PINS(86);
++DECLARE_APQ_GPIO_PINS(87);
++DECLARE_APQ_GPIO_PINS(88);
++DECLARE_APQ_GPIO_PINS(89);
++
++#define FUNCTION(fname) \
++ [APQ_MUX_##fname] = { \
++ .name = #fname, \
++ .groups = fname##_groups, \
++ .ngroups = ARRAY_SIZE(fname##_groups), \
++ }
++
++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \
++ { \
++ .name = "gpio" #id, \
++ .pins = gpio##id##_pins, \
++ .npins = ARRAY_SIZE(gpio##id##_pins), \
++ .funcs = (int[]){ \
++ APQ_MUX_NA, /* gpio mode */ \
++ APQ_MUX_##f1, \
++ APQ_MUX_##f2, \
++ APQ_MUX_##f3, \
++ APQ_MUX_##f4, \
++ APQ_MUX_##f5, \
++ APQ_MUX_##f6, \
++ APQ_MUX_##f7, \
++ APQ_MUX_##f8, \
++ APQ_MUX_##f9, \
++ APQ_MUX_##f10, \
++ }, \
++ .nfuncs = 11, \
++ .ctl_reg = 0x1000 + 0x10 * id, \
++ .io_reg = 0x1004 + 0x10 * id, \
++ .intr_cfg_reg = 0x1008 + 0x10 * id, \
++ .intr_status_reg = 0x100c + 0x10 * id, \
++ .intr_target_reg = 0x400 + 0x4 * id, \
++ .mux_bit = 2, \
++ .pull_bit = 0, \
++ .drv_bit = 6, \
++ .oe_bit = 9, \
++ .in_bit = 0, \
++ .out_bit = 1, \
++ .intr_enable_bit = 0, \
++ .intr_status_bit = 0, \
++ .intr_ack_high = 1, \
++ .intr_target_bit = 0, \
++ .intr_raw_status_bit = 3, \
++ .intr_polarity_bit = 1, \
++ .intr_detection_bit = 2, \
++ .intr_detection_width = 1, \
++ }
++
++enum apq8064_functions {
++ APQ_MUX_cam_mclk,
++ APQ_MUX_codec_mic_i2s,
++ APQ_MUX_codec_spkr_i2s,
++ APQ_MUX_gsbi1,
++ APQ_MUX_gsbi2,
++ APQ_MUX_gsbi3,
++ APQ_MUX_gsbi4,
++ APQ_MUX_gsbi4_cam_i2c,
++ APQ_MUX_gsbi5,
++ APQ_MUX_gsbi5_spi_cs1,
++ APQ_MUX_gsbi5_spi_cs2,
++ APQ_MUX_gsbi5_spi_cs3,
++ APQ_MUX_gsbi6,
++ APQ_MUX_gsbi6_spi_cs1,
++ APQ_MUX_gsbi6_spi_cs2,
++ APQ_MUX_gsbi6_spi_cs3,
++ APQ_MUX_gsbi7,
++ APQ_MUX_gsbi7_spi_cs1,
++ APQ_MUX_gsbi7_spi_cs2,
++ APQ_MUX_gsbi7_spi_cs3,
++ APQ_MUX_gsbi_cam_i2c,
++ APQ_MUX_hdmi,
++ APQ_MUX_mi2s,
++ APQ_MUX_riva_bt,
++ APQ_MUX_riva_fm,
++ APQ_MUX_riva_wlan,
++ APQ_MUX_sdc2,
++ APQ_MUX_sdc4,
++ APQ_MUX_slimbus,
++ APQ_MUX_spkr_i2s,
++ APQ_MUX_tsif1,
++ APQ_MUX_tsif2,
++ APQ_MUX_usb2_hsic,
++ APQ_MUX_NA,
++};
++
++static const char * const cam_mclk_groups[] = {
++ "gpio4" "gpio5"
++};
++static const char * const codec_mic_i2s_groups[] = {
++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38"
++};
++static const char * const codec_spkr_i2s_groups[] = {
++ "gpio39", "gpio40", "gpio41", "gpio42"
++};
++static const char * const gsbi1_groups[] = {
++ "gpio18", "gpio19", "gpio20", "gpio21"
++};
++static const char * const gsbi2_groups[] = {
++ "gpio22", "gpio23", "gpio24", "gpio25"
++};
++static const char * const gsbi3_groups[] = {
++ "gpio6", "gpio7", "gpio8", "gpio9"
++};
++static const char * const gsbi4_groups[] = {
++ "gpio10", "gpio11", "gpio12", "gpio13"
++};
++static const char * const gsbi4_cam_i2c_groups[] = {
++ "gpio10", "gpio11", "gpio12", "gpio13"
++};
++static const char * const gsbi5_groups[] = {
++ "gpio51", "gpio52", "gpio53", "gpio54"
++};
++static const char * const gsbi5_spi_cs1_groups[] = {
++ "gpio47"
++};
++static const char * const gsbi5_spi_cs2_groups[] = {
++ "gpio31"
++};
++static const char * const gsbi5_spi_cs3_groups[] = {
++ "gpio32"
++};
++static const char * const gsbi6_groups[] = {
++ "gpio14", "gpio15", "gpio16", "gpio17"
++};
++static const char * const gsbi6_spi_cs1_groups[] = {
++ "gpio47"
++};
++static const char * const gsbi6_spi_cs2_groups[] = {
++ "gpio31"
++};
++static const char * const gsbi6_spi_cs3_groups[] = {
++ "gpio32"
++};
++static const char * const gsbi7_groups[] = {
++ "gpio82", "gpio83", "gpio84", "gpio85"
++};
++static const char * const gsbi7_spi_cs1_groups[] = {
++ "gpio47"
++};
++static const char * const gsbi7_spi_cs2_groups[] = {
++ "gpio31"
++};
++static const char * const gsbi7_spi_cs3_groups[] = {
++ "gpio32"
++};
++static const char * const gsbi_cam_i2c_groups[] = {
++ "gpio10", "gpio11", "gpio12", "gpio13"
++};
++static const char * const hdmi_groups[] = {
++ "gpio69", "gpio70", "gpio71", "gpio72"
++};
++static const char * const mi2s_groups[] = {
++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33"
++};
++static const char * const riva_bt_groups[] = {
++ "gpio16", "gpio17"
++};
++static const char * const riva_fm_groups[] = {
++ "gpio14", "gpio15"
++};
++static const char * const riva_wlan_groups[] = {
++ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68"
++};
++static const char * const sdc2_groups[] = {
++ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62"
++};
++static const char * const sdc4_groups[] = {
++ "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68"
++};
++static const char * const slimbus_groups[] = {
++ "gpio40", "gpio41"
++};
++static const char * const spkr_i2s_groups[] = {
++ "gpio47", "gpio48", "gpio49", "gpio50"
++};
++static const char * const tsif1_groups[] = {
++ "gpio55", "gpio56", "gpio57"
++};
++static const char * const tsif2_groups[] = {
++ "gpio58", "gpio59", "gpio60"
++};
++static const char * const usb2_hsic_groups[] = {
++ "gpio88", "gpio89"
++};
++
++static const struct msm_function apq8064_functions[] = {
++ FUNCTION(cam_mclk),
++ FUNCTION(codec_mic_i2s),
++ FUNCTION(codec_spkr_i2s),
++ FUNCTION(gsbi1),
++ FUNCTION(gsbi2),
++ FUNCTION(gsbi3),
++ FUNCTION(gsbi4),
++ FUNCTION(gsbi4_cam_i2c),
++ FUNCTION(gsbi5),
++ FUNCTION(gsbi5_spi_cs1),
++ FUNCTION(gsbi5_spi_cs2),
++ FUNCTION(gsbi5_spi_cs3),
++ FUNCTION(gsbi6),
++ FUNCTION(gsbi6_spi_cs1),
++ FUNCTION(gsbi6_spi_cs2),
++ FUNCTION(gsbi6_spi_cs3),
++ FUNCTION(gsbi7),
++ FUNCTION(gsbi7_spi_cs1),
++ FUNCTION(gsbi7_spi_cs2),
++ FUNCTION(gsbi7_spi_cs3),
++ FUNCTION(gsbi_cam_i2c),
++ FUNCTION(hdmi),
++ FUNCTION(mi2s),
++ FUNCTION(riva_bt),
++ FUNCTION(riva_fm),
++ FUNCTION(riva_wlan),
++ FUNCTION(sdc2),
++ FUNCTION(sdc4),
++ FUNCTION(slimbus),
++ FUNCTION(spkr_i2s),
++ FUNCTION(tsif1),
++ FUNCTION(tsif2),
++ FUNCTION(usb2_hsic),
++};
++
++static const struct msm_pingroup apq8064_groups[] = {
++ PINGROUP(0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(4, NA, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(5, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(6, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(7, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(8, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(9, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(10, gsbi4, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c, NA),
++ PINGROUP(11, gsbi4, NA, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c),
++ PINGROUP(12, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA),
++ PINGROUP(13, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA),
++ PINGROUP(14, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(15, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(16, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(17, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(18, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(19, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(20, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(21, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(22, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(27, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(28, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(29, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(30, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(31, mi2s, NA, gsbi5_spi_cs2, gsbi6_spi_cs2, gsbi7_spi_cs2, NA, NA, NA, NA, NA),
++ PINGROUP(32, mi2s, NA, NA, NA, NA, gsbi5_spi_cs3, gsbi6_spi_cs3, gsbi7_spi_cs3, NA, NA),
++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(34, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(35, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(36, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(37, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(38, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(39, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(40, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(41, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(42, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(47, spkr_i2s, gsbi5_spi_cs1, gsbi6_spi_cs1, gsbi7_spi_cs1, NA, NA, NA, NA, NA, NA),
++ PINGROUP(48, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(49, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(50, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(51, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(52, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(53, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(54, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(55, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(56, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(57, tsif1, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(58, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(59, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(60, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(61, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(62, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(63, NA, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(64, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(65, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(66, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(67, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(68, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(69, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(70, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(71, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(72, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(73, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(74, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(75, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(76, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(77, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(82, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(83, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(84, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(85, NA, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(87, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(88, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(89, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++};
++
++#define NUM_GPIO_PINGROUPS 90
++
++static const struct msm_pinctrl_soc_data apq8064_pinctrl = {
++ .pins = apq8064_pins,
++ .npins = ARRAY_SIZE(apq8064_pins),
++ .functions = apq8064_functions,
++ .nfunctions = ARRAY_SIZE(apq8064_functions),
++ .groups = apq8064_groups,
++ .ngroups = ARRAY_SIZE(apq8064_groups),
++ .ngpios = NUM_GPIO_PINGROUPS,
++};
++
++static int apq8064_pinctrl_probe(struct platform_device *pdev)
++{
++ return msm_pinctrl_probe(pdev, &apq8064_pinctrl);
++}
++
++static const struct of_device_id apq8064_pinctrl_of_match[] = {
++ { .compatible = "qcom,apq8064-pinctrl", },
++ { },
++};
++
++static struct platform_driver apq8064_pinctrl_driver = {
++ .driver = {
++ .name = "apq8064-pinctrl",
++ .owner = THIS_MODULE,
++ .of_match_table = apq8064_pinctrl_of_match,
++ },
++ .probe = apq8064_pinctrl_probe,
++ .remove = msm_pinctrl_remove,
++};
++
++static int __init apq8064_pinctrl_init(void)
++{
++ return platform_driver_register(&apq8064_pinctrl_driver);
++}
++arch_initcall(apq8064_pinctrl_init);
++
++static void __exit apq8064_pinctrl_exit(void)
++{
++ platform_driver_unregister(&apq8064_pinctrl_driver);
++}
++module_exit(apq8064_pinctrl_exit);
++
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
++MODULE_DESCRIPTION("Qualcomm APQ8064 pinctrl driver");
++MODULE_LICENSE("GPL v2");
++MODULE_DEVICE_TABLE(of, apq8064_pinctrl_of_match);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch
new file mode 100644
index 0000000..51ed6d6
--- /dev/null
+++ b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch
@@ -0,0 +1,36 @@
+From 8605197c200786888415bf2e30d1fbde6ba8ba03 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Tue, 1 Apr 2014 22:25:59 +0200
+Subject: [PATCH 039/182] pinctrl: msm8x74: make Kconfig dependency more
+ strict
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This driver is only useful on MSM8x74, so let the driver depend on
+ARCH_QCOM but allow compile coverage testing.
+The main benefit is that the driver isn't available to be selected for
+machines that don't have the matching hardware.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
+index 91993a6..d34639d 100644
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -232,7 +232,7 @@ config PINCTRL_APQ8064
+
+ config PINCTRL_MSM8X74
+ tristate "Qualcomm 8x74 pin controller driver"
+- depends on GPIOLIB && OF
++ depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST)
+ select PINCTRL_MSM
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch
new file mode 100644
index 0000000..6b48085
--- /dev/null
+++ b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch
@@ -0,0 +1,711 @@
+From 9bbd9d7e40944ca95e07f363b68700225beb9bef Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 14 Apr 2014 22:10:35 -0500
+Subject: [PATCH 040/182] pinctrl: qcom: Add definitions for IPQ8064
+
+This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the
+Qualcomm IPQ8064 platform.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/Kconfig | 8 +
+ drivers/pinctrl/Makefile | 1 +
+ drivers/pinctrl/pinctrl-ipq8064.c | 653 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 662 insertions(+)
+ create mode 100644 drivers/pinctrl/pinctrl-ipq8064.c
+
+diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
+index d34639d..232e6bc 100644
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -230,6 +230,14 @@ config PINCTRL_APQ8064
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm TLMM block found in the Qualcomm APQ8064 platform.
+
++config PINCTRL_IPQ8064
++ tristate "Qualcomm IPQ8064 pin controller driver"
++ depends on GPIOLIB && OF
++ select PINCTRL_MSM
++ help
++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
++ Qualcomm TLMM block found in the Qualcomm IPQ8064 platform.
++
+ config PINCTRL_MSM8X74
+ tristate "Qualcomm 8x74 pin controller driver"
+ depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST)
+diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
+index 9e1fb67..6b8474a 100644
+--- a/drivers/pinctrl/Makefile
++++ b/drivers/pinctrl/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o
+ obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o
+ obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o
+ obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o
++obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o
+ obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o
+ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
+ obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
+diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c
+new file mode 100644
+index 0000000..1700b49
+--- /dev/null
++++ b/drivers/pinctrl/pinctrl-ipq8064.c
+@@ -0,0 +1,653 @@
++/*
++ * 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/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pinctrl/pinctrl.h>
++
++#include "pinctrl-msm.h"
++
++static const struct pinctrl_pin_desc ipq8064_pins[] = {
++ PINCTRL_PIN(0, "GPIO_1"),
++ PINCTRL_PIN(1, "GPIO_1"),
++ PINCTRL_PIN(2, "GPIO_2"),
++ PINCTRL_PIN(3, "GPIO_3"),
++ PINCTRL_PIN(4, "GPIO_4"),
++ PINCTRL_PIN(5, "GPIO_5"),
++ PINCTRL_PIN(6, "GPIO_6"),
++ PINCTRL_PIN(7, "GPIO_7"),
++ PINCTRL_PIN(8, "GPIO_8"),
++ PINCTRL_PIN(9, "GPIO_9"),
++ PINCTRL_PIN(10, "GPIO_10"),
++ PINCTRL_PIN(11, "GPIO_11"),
++ PINCTRL_PIN(12, "GPIO_12"),
++ PINCTRL_PIN(13, "GPIO_13"),
++ PINCTRL_PIN(14, "GPIO_14"),
++ PINCTRL_PIN(15, "GPIO_15"),
++ PINCTRL_PIN(16, "GPIO_16"),
++ PINCTRL_PIN(17, "GPIO_17"),
++ PINCTRL_PIN(18, "GPIO_18"),
++ PINCTRL_PIN(19, "GPIO_19"),
++ PINCTRL_PIN(20, "GPIO_20"),
++ PINCTRL_PIN(21, "GPIO_21"),
++ PINCTRL_PIN(22, "GPIO_22"),
++ PINCTRL_PIN(23, "GPIO_23"),
++ PINCTRL_PIN(24, "GPIO_24"),
++ PINCTRL_PIN(25, "GPIO_25"),
++ PINCTRL_PIN(26, "GPIO_26"),
++ PINCTRL_PIN(27, "GPIO_27"),
++ PINCTRL_PIN(28, "GPIO_28"),
++ PINCTRL_PIN(29, "GPIO_29"),
++ PINCTRL_PIN(30, "GPIO_30"),
++ PINCTRL_PIN(31, "GPIO_31"),
++ PINCTRL_PIN(32, "GPIO_32"),
++ PINCTRL_PIN(33, "GPIO_33"),
++ PINCTRL_PIN(34, "GPIO_34"),
++ PINCTRL_PIN(35, "GPIO_35"),
++ PINCTRL_PIN(36, "GPIO_36"),
++ PINCTRL_PIN(37, "GPIO_37"),
++ PINCTRL_PIN(38, "GPIO_38"),
++ PINCTRL_PIN(39, "GPIO_39"),
++ PINCTRL_PIN(40, "GPIO_40"),
++ PINCTRL_PIN(41, "GPIO_41"),
++ PINCTRL_PIN(42, "GPIO_42"),
++ PINCTRL_PIN(43, "GPIO_43"),
++ PINCTRL_PIN(44, "GPIO_44"),
++ PINCTRL_PIN(45, "GPIO_45"),
++ PINCTRL_PIN(46, "GPIO_46"),
++ PINCTRL_PIN(47, "GPIO_47"),
++ PINCTRL_PIN(48, "GPIO_48"),
++ PINCTRL_PIN(49, "GPIO_49"),
++ PINCTRL_PIN(50, "GPIO_50"),
++ PINCTRL_PIN(51, "GPIO_51"),
++ PINCTRL_PIN(52, "GPIO_52"),
++ PINCTRL_PIN(53, "GPIO_53"),
++ PINCTRL_PIN(54, "GPIO_54"),
++ PINCTRL_PIN(55, "GPIO_55"),
++ PINCTRL_PIN(56, "GPIO_56"),
++ PINCTRL_PIN(57, "GPIO_57"),
++ PINCTRL_PIN(58, "GPIO_58"),
++ PINCTRL_PIN(59, "GPIO_59"),
++ PINCTRL_PIN(60, "GPIO_60"),
++ PINCTRL_PIN(61, "GPIO_61"),
++ PINCTRL_PIN(62, "GPIO_62"),
++ PINCTRL_PIN(63, "GPIO_63"),
++ PINCTRL_PIN(64, "GPIO_64"),
++ PINCTRL_PIN(65, "GPIO_65"),
++ PINCTRL_PIN(66, "GPIO_66"),
++ PINCTRL_PIN(67, "GPIO_67"),
++ PINCTRL_PIN(68, "GPIO_68"),
++
++ PINCTRL_PIN(69, "SDC3_CLK"),
++ PINCTRL_PIN(70, "SDC3_CMD"),
++ PINCTRL_PIN(71, "SDC3_DATA"),
++};
++
++#define DECLARE_IPQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin }
++DECLARE_IPQ_GPIO_PINS(0);
++DECLARE_IPQ_GPIO_PINS(1);
++DECLARE_IPQ_GPIO_PINS(2);
++DECLARE_IPQ_GPIO_PINS(3);
++DECLARE_IPQ_GPIO_PINS(4);
++DECLARE_IPQ_GPIO_PINS(5);
++DECLARE_IPQ_GPIO_PINS(6);
++DECLARE_IPQ_GPIO_PINS(7);
++DECLARE_IPQ_GPIO_PINS(8);
++DECLARE_IPQ_GPIO_PINS(9);
++DECLARE_IPQ_GPIO_PINS(10);
++DECLARE_IPQ_GPIO_PINS(11);
++DECLARE_IPQ_GPIO_PINS(12);
++DECLARE_IPQ_GPIO_PINS(13);
++DECLARE_IPQ_GPIO_PINS(14);
++DECLARE_IPQ_GPIO_PINS(15);
++DECLARE_IPQ_GPIO_PINS(16);
++DECLARE_IPQ_GPIO_PINS(17);
++DECLARE_IPQ_GPIO_PINS(18);
++DECLARE_IPQ_GPIO_PINS(19);
++DECLARE_IPQ_GPIO_PINS(20);
++DECLARE_IPQ_GPIO_PINS(21);
++DECLARE_IPQ_GPIO_PINS(22);
++DECLARE_IPQ_GPIO_PINS(23);
++DECLARE_IPQ_GPIO_PINS(24);
++DECLARE_IPQ_GPIO_PINS(25);
++DECLARE_IPQ_GPIO_PINS(26);
++DECLARE_IPQ_GPIO_PINS(27);
++DECLARE_IPQ_GPIO_PINS(28);
++DECLARE_IPQ_GPIO_PINS(29);
++DECLARE_IPQ_GPIO_PINS(30);
++DECLARE_IPQ_GPIO_PINS(31);
++DECLARE_IPQ_GPIO_PINS(32);
++DECLARE_IPQ_GPIO_PINS(33);
++DECLARE_IPQ_GPIO_PINS(34);
++DECLARE_IPQ_GPIO_PINS(35);
++DECLARE_IPQ_GPIO_PINS(36);
++DECLARE_IPQ_GPIO_PINS(37);
++DECLARE_IPQ_GPIO_PINS(38);
++DECLARE_IPQ_GPIO_PINS(39);
++DECLARE_IPQ_GPIO_PINS(40);
++DECLARE_IPQ_GPIO_PINS(41);
++DECLARE_IPQ_GPIO_PINS(42);
++DECLARE_IPQ_GPIO_PINS(43);
++DECLARE_IPQ_GPIO_PINS(44);
++DECLARE_IPQ_GPIO_PINS(45);
++DECLARE_IPQ_GPIO_PINS(46);
++DECLARE_IPQ_GPIO_PINS(47);
++DECLARE_IPQ_GPIO_PINS(48);
++DECLARE_IPQ_GPIO_PINS(49);
++DECLARE_IPQ_GPIO_PINS(50);
++DECLARE_IPQ_GPIO_PINS(51);
++DECLARE_IPQ_GPIO_PINS(52);
++DECLARE_IPQ_GPIO_PINS(53);
++DECLARE_IPQ_GPIO_PINS(54);
++DECLARE_IPQ_GPIO_PINS(55);
++DECLARE_IPQ_GPIO_PINS(56);
++DECLARE_IPQ_GPIO_PINS(57);
++DECLARE_IPQ_GPIO_PINS(58);
++DECLARE_IPQ_GPIO_PINS(59);
++DECLARE_IPQ_GPIO_PINS(60);
++DECLARE_IPQ_GPIO_PINS(61);
++DECLARE_IPQ_GPIO_PINS(62);
++DECLARE_IPQ_GPIO_PINS(63);
++DECLARE_IPQ_GPIO_PINS(64);
++DECLARE_IPQ_GPIO_PINS(65);
++DECLARE_IPQ_GPIO_PINS(66);
++DECLARE_IPQ_GPIO_PINS(67);
++DECLARE_IPQ_GPIO_PINS(68);
++
++static const unsigned int sdc3_clk_pins[] = { 69 };
++static const unsigned int sdc3_cmd_pins[] = { 70 };
++static const unsigned int sdc3_data_pins[] = { 71 };
++
++#define FUNCTION(fname) \
++ [IPQ_MUX_##fname] = { \
++ .name = #fname, \
++ .groups = fname##_groups, \
++ .ngroups = ARRAY_SIZE(fname##_groups), \
++ }
++
++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \
++ { \
++ .name = "gpio" #id, \
++ .pins = gpio##id##_pins, \
++ .npins = ARRAY_SIZE(gpio##id##_pins), \
++ .funcs = (int[]){ \
++ IPQ_MUX_NA, /* gpio mode */ \
++ IPQ_MUX_##f1, \
++ IPQ_MUX_##f2, \
++ IPQ_MUX_##f3, \
++ IPQ_MUX_##f4, \
++ IPQ_MUX_##f5, \
++ IPQ_MUX_##f6, \
++ IPQ_MUX_##f7, \
++ IPQ_MUX_##f8, \
++ IPQ_MUX_##f9, \
++ IPQ_MUX_##f10, \
++ }, \
++ .nfuncs = 11, \
++ .ctl_reg = 0x1000 + 0x10 * id, \
++ .io_reg = 0x1004 + 0x10 * id, \
++ .intr_cfg_reg = 0x1008 + 0x10 * id, \
++ .intr_status_reg = 0x100c + 0x10 * id, \
++ .intr_target_reg = 0x400 + 0x4 * id, \
++ .mux_bit = 2, \
++ .pull_bit = 0, \
++ .drv_bit = 6, \
++ .oe_bit = 9, \
++ .in_bit = 0, \
++ .out_bit = 1, \
++ .intr_enable_bit = 0, \
++ .intr_status_bit = 0, \
++ .intr_ack_high = 1, \
++ .intr_target_bit = 0, \
++ .intr_raw_status_bit = 3, \
++ .intr_polarity_bit = 1, \
++ .intr_detection_bit = 2, \
++ .intr_detection_width = 1, \
++ }
++
++#define SDC_PINGROUP(pg_name, ctl, pull, drv) \
++ { \
++ .name = #pg_name, \
++ .pins = pg_name##_pins, \
++ .npins = ARRAY_SIZE(pg_name##_pins), \
++ .ctl_reg = ctl, \
++ .io_reg = 0, \
++ .intr_cfg_reg = 0, \
++ .intr_status_reg = 0, \
++ .intr_target_reg = 0, \
++ .mux_bit = -1, \
++ .pull_bit = pull, \
++ .drv_bit = drv, \
++ .oe_bit = -1, \
++ .in_bit = -1, \
++ .out_bit = -1, \
++ .intr_enable_bit = -1, \
++ .intr_status_bit = -1, \
++ .intr_target_bit = -1, \
++ .intr_raw_status_bit = -1, \
++ .intr_polarity_bit = -1, \
++ .intr_detection_bit = -1, \
++ .intr_detection_width = -1, \
++ }
++
++enum ipq8064_functions {
++ IPQ_MUX_mdio,
++ IPQ_MUX_mi2s,
++ IPQ_MUX_pdm,
++ IPQ_MUX_ssbi,
++ IPQ_MUX_spmi,
++ IPQ_MUX_audio_pcm,
++ IPQ_MUX_gsbi1,
++ IPQ_MUX_gsbi2,
++ IPQ_MUX_gsbi4,
++ IPQ_MUX_gsbi5,
++ IPQ_MUX_gsbi5_spi_cs1,
++ IPQ_MUX_gsbi5_spi_cs2,
++ IPQ_MUX_gsbi5_spi_cs3,
++ IPQ_MUX_gsbi6,
++ IPQ_MUX_gsbi7,
++ IPQ_MUX_nss_spi,
++ IPQ_MUX_sdc1,
++ IPQ_MUX_spdif,
++ IPQ_MUX_nand,
++ IPQ_MUX_tsif1,
++ IPQ_MUX_tsif2,
++ IPQ_MUX_usb_fs_n,
++ IPQ_MUX_usb_fs,
++ IPQ_MUX_usb2_hsic,
++ IPQ_MUX_rgmii2,
++ IPQ_MUX_sata,
++ IPQ_MUX_pcie1_rst,
++ IPQ_MUX_pcie1_prsnt,
++ IPQ_MUX_pcie1_pwrflt,
++ IPQ_MUX_pcie1_pwren_n,
++ IPQ_MUX_pcie1_pwren,
++ IPQ_MUX_pcie1_clk_req,
++ IPQ_MUX_pcie2_rst,
++ IPQ_MUX_pcie2_prsnt,
++ IPQ_MUX_pcie2_pwrflt,
++ IPQ_MUX_pcie2_pwren_n,
++ IPQ_MUX_pcie2_pwren,
++ IPQ_MUX_pcie2_clk_req,
++ IPQ_MUX_pcie3_rst,
++ IPQ_MUX_pcie3_prsnt,
++ IPQ_MUX_pcie3_pwrflt,
++ IPQ_MUX_pcie3_pwren_n,
++ IPQ_MUX_pcie3_pwren,
++ IPQ_MUX_pcie3_clk_req,
++ IPQ_MUX_ps_hold,
++ IPQ_MUX_NA,
++};
++
++static const char * const mdio_groups[] = {
++ "gpio0", "gpio1", "gpio10", "gpio11",
++};
++
++static const char * const mi2s_groups[] = {
++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio33", "gpio55", "gpio56", "gpio57", "gpio58",
++};
++
++static const char * const pdm_groups[] = {
++ "gpio3", "gpio16", "gpio17", "gpio22", "gpio30", "gpio31",
++ "gpio34", "gpio35", "gpio52", "gpio55", "gpio56", "gpio58",
++ "gpio59",
++};
++
++static const char * const ssbi_groups[] = {
++ "gpio10", "gpio11",
++};
++
++static const char * const spmi_groups[] = {
++ "gpio10", "gpio11",
++};
++
++static const char * const audio_pcm_groups[] = {
++ "gpio14", "gpio15", "gpio16", "gpio17",
++};
++
++static const char * const gsbi1_groups[] = {
++ "gpio51", "gpio52", "gpio53", "gpio54",
++};
++
++static const char * const gsbi2_groups[] = {
++ "gpio22", "gpio23", "gpio24", "gpio25",
++};
++
++static const char * const gsbi4_groups[] = {
++ "gpio10", "gpio11", "gpio12", "gpio13",
++};
++
++static const char * const gsbi5_groups[] = {
++ "gpio18", "gpio19", "gpio20", "gpio21",
++};
++
++static const char * const gsbi5_spi_cs1_groups[] = {
++ "gpio6", "gpio61",
++};
++
++static const char * const gsbi5_spi_cs2_groups[] = {
++ "gpio7", "gpio62",
++};
++
++static const char * const gsbi5_spi_cs3_groups[] = {
++ "gpio2",
++};
++
++static const char * const gsbi6_groups[] = {
++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio55", "gpio56",
++ "gpio57", "gpio58",
++};
++
++static const char * const gsbi7_groups[] = {
++ "gpio6", "gpio7", "gpio8", "gpio9",
++};
++
++static const char * const nss_spi_groups[] = {
++ "gpio14", "gpio15", "gpio16", "gpio17", "gpio55", "gpio56",
++ "gpio57", "gpio58",
++};
++
++static const char * const sdc1_groups[] = {
++ "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
++ "gpio44", "gpio45", "gpio46", "gpio47",
++};
++
++static const char * const spdif_groups[] = {
++ "gpio_10", "gpio_48",
++};
++
++static const char * const nand_groups[] = {
++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47",
++};
++
++static const char * const tsif1_groups[] = {
++ "gpio55", "gpio56", "gpio57", "gpio58",
++};
++
++static const char * const tsif2_groups[] = {
++ "gpio59", "gpio60", "gpio61", "gpio62",
++};
++
++static const char * const usb_fs_n_groups[] = {
++ "gpio6",
++};
++
++static const char * const usb_fs_groups[] = {
++ "gpio6", "gpio7", "gpio8",
++};
++
++static const char * const usb2_hsic_groups[] = {
++ "gpio67", "gpio68",
++};
++
++static const char * const rgmii2_groups[] = {
++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62",
++};
++
++static const char * const sata_groups[] = {
++ "gpio10",
++};
++
++static const char * const pcie1_rst_groups[] = {
++ "gpio3",
++};
++
++static const char * const pcie1_prsnt_groups[] = {
++ "gpio3", "gpio11",
++};
++
++static const char * const pcie1_pwren_n_groups[] = {
++ "gpio4", "gpio12",
++};
++
++static const char * const pcie1_pwren_groups[] = {
++ "gpio4", "gpio12",
++};
++
++static const char * const pcie1_pwrflt_groups[] = {
++ "gpio5", "gpio13",
++};
++
++static const char * const pcie1_clk_req_groups[] = {
++ "gpio5",
++};
++
++static const char * const pcie2_rst_groups[] = {
++ "gpio48",
++};
++
++static const char * const pcie2_prsnt_groups[] = {
++ "gpio11", "gpio48",
++};
++
++static const char * const pcie2_pwren_n_groups[] = {
++ "gpio12", "gpio49",
++};
++
++static const char * const pcie2_pwren_groups[] = {
++ "gpio12", "gpio49",
++};
++
++static const char * const pcie2_pwrflt_groups[] = {
++ "gpio13", "gpio50",
++};
++
++static const char * const pcie2_clk_req_groups[] = {
++ "gpio50",
++};
++
++static const char * const pcie3_rst_groups[] = {
++ "gpio63",
++};
++
++static const char * const pcie3_prsnt_groups[] = {
++ "gpio11",
++};
++
++static const char * const pcie3_pwren_n_groups[] = {
++ "gpio12",
++};
++
++static const char * const pcie3_pwren_groups[] = {
++ "gpio12",
++};
++
++static const char * const pcie3_pwrflt_groups[] = {
++ "gpio13",
++};
++
++static const char * const pcie3_clk_req_groups[] = {
++ "gpio65",
++};
++
++static const char * const ps_hold_groups[] = {
++ "gpio26",
++};
++
++static const struct msm_function ipq8064_functions[] = {
++ FUNCTION(mdio),
++ FUNCTION(ssbi),
++ FUNCTION(spmi),
++ FUNCTION(mi2s),
++ FUNCTION(pdm),
++ FUNCTION(audio_pcm),
++ FUNCTION(gsbi1),
++ FUNCTION(gsbi2),
++ FUNCTION(gsbi4),
++ FUNCTION(gsbi5),
++ FUNCTION(gsbi5_spi_cs1),
++ FUNCTION(gsbi5_spi_cs2),
++ FUNCTION(gsbi5_spi_cs3),
++ FUNCTION(gsbi6),
++ FUNCTION(gsbi7),
++ FUNCTION(nss_spi),
++ FUNCTION(sdc1),
++ FUNCTION(spdif),
++ FUNCTION(nand),
++ FUNCTION(tsif1),
++ FUNCTION(tsif2),
++ FUNCTION(usb_fs_n),
++ FUNCTION(usb_fs),
++ FUNCTION(usb2_hsic),
++ FUNCTION(rgmii2),
++ FUNCTION(sata),
++ FUNCTION(pcie1_rst),
++ FUNCTION(pcie1_prsnt),
++ FUNCTION(pcie1_pwren_n),
++ FUNCTION(pcie1_pwren),
++ FUNCTION(pcie1_pwrflt),
++ FUNCTION(pcie1_clk_req),
++ FUNCTION(pcie2_rst),
++ FUNCTION(pcie2_prsnt),
++ FUNCTION(pcie2_pwren_n),
++ FUNCTION(pcie2_pwren),
++ FUNCTION(pcie2_pwrflt),
++ FUNCTION(pcie2_clk_req),
++ FUNCTION(pcie3_rst),
++ FUNCTION(pcie3_prsnt),
++ FUNCTION(pcie3_pwren_n),
++ FUNCTION(pcie3_pwren),
++ FUNCTION(pcie3_pwrflt),
++ FUNCTION(pcie3_clk_req),
++ FUNCTION(ps_hold),
++};
++
++static const struct msm_pingroup ipq8064_groups[] = {
++ PINGROUP(0, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(1, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(2, gsbi5_spi_cs3, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(3, pcie1_rst, pcie1_prsnt, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(4, pcie1_pwren_n, pcie1_pwren, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(5, pcie1_clk_req, pcie1_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(6, gsbi7, usb_fs, gsbi5_spi_cs1, usb_fs_n, NA, NA, NA, NA, NA, NA),
++ PINGROUP(7, gsbi7, usb_fs, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(8, gsbi7, usb_fs, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(9, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(10, gsbi4, spdif, sata, ssbi, mdio, spmi, NA, NA, NA, NA),
++ PINGROUP(11, gsbi4, pcie2_prsnt, pcie1_prsnt, pcie3_prsnt, ssbi, mdio, spmi, NA, NA, NA),
++ PINGROUP(12, gsbi4, pcie2_pwren_n, pcie1_pwren_n, pcie3_pwren_n, pcie2_pwren, pcie1_pwren, pcie3_pwren, NA, NA, NA),
++ PINGROUP(13, gsbi4, pcie2_pwrflt, pcie1_pwrflt, pcie3_pwrflt, NA, NA, NA, NA, NA, NA),
++ PINGROUP(14, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(15, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(16, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(17, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(18, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(19, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(20, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(21, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(22, gsbi2, pdm, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(26, ps_hold, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(27, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(28, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(29, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(30, mi2s, rgmii2, gsbi6, pdm, NA, NA, NA, NA, NA, NA),
++ PINGROUP(31, mi2s, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(32, mi2s, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(34, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(35, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(36, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(37, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(38, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(39, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(40, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(41, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(42, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(43, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(44, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(45, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(46, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(47, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(48, pcie2_rst, spdif, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(49, pcie2_pwren_n, pcie2_pwren, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(50, pcie2_clk_req, pcie2_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(51, gsbi1, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(52, gsbi1, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(53, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(54, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(55, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA),
++ PINGROUP(56, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA),
++ PINGROUP(57, tsif1, mi2s, gsbi6, nss_spi, NA, NA, NA, NA, NA, NA),
++ PINGROUP(58, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA),
++ PINGROUP(59, tsif2, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(60, tsif2, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(61, tsif2, rgmii2, gsbi5_spi_cs1, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(62, tsif2, rgmii2, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(63, pcie3_rst, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(65, pcie3_clk_req, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(66, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(67, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ PINGROUP(68, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++ SDC_PINGROUP(sdc3_clk, 0x204a, 14, 6),
++ SDC_PINGROUP(sdc3_cmd, 0x204a, 11, 3),
++ SDC_PINGROUP(sdc3_data, 0x204a, 9, 0),
++};
++
++#define NUM_GPIO_PINGROUPS 69
++
++static const struct msm_pinctrl_soc_data ipq8064_pinctrl = {
++ .pins = ipq8064_pins,
++ .npins = ARRAY_SIZE(ipq8064_pins),
++ .functions = ipq8064_functions,
++ .nfunctions = ARRAY_SIZE(ipq8064_functions),
++ .groups = ipq8064_groups,
++ .ngroups = ARRAY_SIZE(ipq8064_groups),
++ .ngpios = NUM_GPIO_PINGROUPS,
++};
++
++static int ipq8064_pinctrl_probe(struct platform_device *pdev)
++{
++ return msm_pinctrl_probe(pdev, &ipq8064_pinctrl);
++}
++
++static const struct of_device_id ipq8064_pinctrl_of_match[] = {
++ { .compatible = "qcom,ipq8064-pinctrl", },
++ { },
++};
++
++static struct platform_driver ipq8064_pinctrl_driver = {
++ .driver = {
++ .name = "ipq8064-pinctrl",
++ .owner = THIS_MODULE,
++ .of_match_table = ipq8064_pinctrl_of_match,
++ },
++ .probe = ipq8064_pinctrl_probe,
++ .remove = msm_pinctrl_remove,
++};
++
++static int __init ipq8064_pinctrl_init(void)
++{
++ return platform_driver_register(&ipq8064_pinctrl_driver);
++}
++arch_initcall(ipq8064_pinctrl_init);
++
++static void __exit ipq8064_pinctrl_exit(void)
++{
++ platform_driver_unregister(&ipq8064_pinctrl_driver);
++}
++module_exit(ipq8064_pinctrl_exit);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("Qualcomm IPQ8064 pinctrl driver");
++MODULE_LICENSE("GPL v2");
++MODULE_DEVICE_TABLE(of, ipq8064_pinctrl_of_match);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch
new file mode 100644
index 0000000..2a7c870
--- /dev/null
+++ b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch
@@ -0,0 +1,120 @@
+From 425015979d3b1600d14403be7d6d64ba1238e58d Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 14 Apr 2014 22:10:36 -0500
+Subject: [PATCH 041/182] dt: Document Qualcomm IPQ8064 pinctrl binding
+
+Define a new binding for the Qualcomm TLMMv2 based pin controller inside the
+IPQ8064.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ .../bindings/pinctrl/qcom,ipq8064-pinctrl.txt | 95 ++++++++++++++++++++
+ 1 file changed, 95 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt
+
+diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt
+new file mode 100644
+index 0000000..e0d35a4
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt
+@@ -0,0 +1,95 @@
++Qualcomm IPQ8064 TLMM block
++
++Required properties:
++- compatible: "qcom,ipq8064-pinctrl"
++- reg: Should be the base address and length of the TLMM block.
++- interrupts: Should be the parent IRQ of the TLMM block.
++- interrupt-controller: Marks the device node as an interrupt controller.
++- #interrupt-cells: Should be two.
++- gpio-controller: Marks the device node as a GPIO controller.
++- #gpio-cells : Should be two.
++ The first cell is the gpio pin number and the
++ second cell is used for optional parameters.
++
++Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
++a general description of GPIO and interrupt bindings.
++
++Please refer to pinctrl-bindings.txt in this directory for details of the
++common pinctrl bindings used by client devices, including the meaning of the
++phrase "pin configuration node".
++
++Qualcomm's pin configuration nodes act as a container for an abitrary number of
++subnodes. Each of these subnodes represents some desired configuration for a
++pin, a group, or a list of pins or groups. This configuration can include the
++mux function to select on those pin(s)/group(s), and various pin configuration
++parameters, such as pull-up, drive strength, etc.
++
++The name of each subnode is not important; all subnodes should be enumerated
++and processed purely based on their content.
++
++Each subnode only affects those parameters that are explicitly listed. In
++other words, a subnode that lists a mux function but no pin configuration
++parameters implies no information about any pin configuration parameters.
++Similarly, a pin subnode that describes a pullup parameter implies no
++information about e.g. the mux function.
++
++
++The following generic properties as defined in pinctrl-bindings.txt are valid
++to specify in a pin configuration subnode:
++
++ pins, function, bias-disable, bias-pull-down, bias-pull,up, drive-strength,
++ output-low, output-high.
++
++Non-empty subnodes must specify the 'pins' property.
++
++Valid values for qcom,pins are:
++ gpio0-gpio68
++ Supports mux, bias, and drive-strength
++
++ sdc3_clk, sdc3_cmd, sdc3_data
++ Supports bias and drive-strength
++
++
++Valid values for function are:
++ mdio, mi2s, pdm, ssbi, spmi, audio_pcm, gsbi1, gsbi2, gsbi4, gsbi5,
++ gsbi5_spi_cs1, gsbi5_spi_cs2, gsbi5_spi_cs3, gsbi6, gsbi7, nss_spi, sdc1,
++ spdif, nand, tsif1, tsif2, usb_fs_n, usb_fs, usb2_hsic, rgmii2, sata,
++ pcie1_rst, pcie1_prsnt, pcie1_pwren_n, pcie1_pwren, pcie1_pwrflt,
++ pcie1_clk_req, pcie2_rst, pcie2_prsnt, pcie2_pwren_n, pcie2_pwren,
++ pcie2_pwrflt, pcie2_clk_req, pcie3_rst, pcie3_prsnt, pcie3_pwren_n,
++ pcie3_pwren, pcie3_pwrflt, pcie3_clk_req, ps_hold
++
++Example:
++
++ pinmux: pinctrl@800000 {
++ compatible = "qcom,ipq8064-pinctrl";
++ reg = <0x800000 0x4000>;
++
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ interrupts = <0 32 0x4>;
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&gsbi5_uart_default>;
++
++ gsbi5_uart_default: gsbi5_uart_default {
++ mux {
++ pins = "gpio18", "gpio19";
++ function = "gsbi5";
++ };
++
++ tx {
++ pins = "gpio18";
++ drive-strength = <4>;
++ bias-disable;
++ };
++
++ rx {
++ pins = "gpio19";
++ drive-strength = <2>;
++ bias-pull-up;
++ };
++ };
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch
new file mode 100644
index 0000000..c1c116f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch
@@ -0,0 +1,29 @@
+From add2c1451495ccc4e67ced3dd12d4286500f1672 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 14 Apr 2014 22:10:37 -0500
+Subject: [PATCH 042/182] ARM: qcom: Select PINCTRL by default for ARCH_QCOM
+
+Add missing PINCTRL selection. This enables selection of pinctrollers for
+Qualcomm processors.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Acked-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
+index a028be2..6440c11 100644
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -5,6 +5,7 @@ config ARCH_QCOM
+ select CLKSRC_OF
+ select GENERIC_CLOCKEVENTS
+ select HAVE_SMP
++ select PINCTRL
+ select QCOM_SCM if SMP
+ help
+ Support for Qualcomm's devicetree based systems.
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch
new file mode 100644
index 0000000..71e5572
--- /dev/null
+++ b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch
@@ -0,0 +1,28 @@
+From 4cf6d61dc3441f50d1d56bfd72280dbcd2b584a6 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Fri, 25 Apr 2014 15:41:55 -0500
+Subject: [PATCH 043/182] pinctrl: qcom: Correct name for pin 0
+
+Fix copy/paste error in pinctrl_pin_desc for pin 0.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/pinctrl/pinctrl-ipq8064.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c
+index 1700b49..54aba9f 100644
+--- a/drivers/pinctrl/pinctrl-ipq8064.c
++++ b/drivers/pinctrl/pinctrl-ipq8064.c
+@@ -20,7 +20,7 @@
+ #include "pinctrl-msm.h"
+
+ static const struct pinctrl_pin_desc ipq8064_pins[] = {
+- PINCTRL_PIN(0, "GPIO_1"),
++ PINCTRL_PIN(0, "GPIO_0"),
+ PINCTRL_PIN(1, "GPIO_1"),
+ PINCTRL_PIN(2, "GPIO_2"),
+ PINCTRL_PIN(3, "GPIO_3"),
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch
new file mode 100644
index 0000000..148959a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch
@@ -0,0 +1,65 @@
+From b5e19b657e352d565c5ddeae5f6dfd542de9d7e5 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 10 Mar 2014 16:40:19 -0500
+Subject: [PATCH 044/182] dmaengine: qcom_bam_dma: Add device tree binding
+
+Add device tree binding support for the QCOM BAM DMA driver.
+
+Acked-by: Kumar Gala <galak@codeaurora.org>
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ .../devicetree/bindings/dma/qcom_bam_dma.txt | 41 ++++++++++++++++++++
+ 1 file changed, 41 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
+
+diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
+new file mode 100644
+index 0000000..d75a9d7
+--- /dev/null
++++ b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
+@@ -0,0 +1,41 @@
++QCOM BAM DMA controller
++
++Required properties:
++- compatible: must contain "qcom,bam-v1.4.0" for MSM8974
++- reg: Address range for DMA registers
++- interrupts: Should contain the one interrupt shared by all channels
++- #dma-cells: must be <1>, the cell in the dmas property of the client device
++ represents the channel number
++- clocks: required clock
++- clock-names: must contain "bam_clk" entry
++- qcom,ee : indicates the active Execution Environment identifier (0-7) used in
++ the secure world.
++
++Example:
++
++ uart-bam: dma@f9984000 = {
++ compatible = "qcom,bam-v1.4.0";
++ reg = <0xf9984000 0x15000>;
++ interrupts = <0 94 0>;
++ clocks = <&gcc GCC_BAM_DMA_AHB_CLK>;
++ clock-names = "bam_clk";
++ #dma-cells = <1>;
++ qcom,ee = <0>;
++ };
++
++DMA clients must use the format described in the dma.txt file, using a two cell
++specifier for each channel.
++
++Example:
++ serial@f991e000 {
++ compatible = "qcom,msm-uart";
++ reg = <0xf991e000 0x1000>
++ <0xf9944000 0x19000>;
++ interrupts = <0 108 0>;
++ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>,
++ <&gcc GCC_BLSP1_AHB_CLK>;
++ clock-names = "core", "iface";
++
++ dmas = <&uart-bam 0>, <&uart-bam 1>;
++ dma-names = "rx", "tx";
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch
new file mode 100644
index 0000000..6dcba78
--- /dev/null
+++ b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch
@@ -0,0 +1,1174 @@
+From 23dd43da9c885789b3d5aceed3e401345f8e8106 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Sat, 29 Mar 2014 18:53:16 +0530
+Subject: [PATCH 045/182] dmaengine: add Qualcomm BAM dma driver
+
+Add the DMA engine driver for the QCOM Bus Access Manager (BAM) DMA controller
+found in the MSM 8x74 platforms.
+
+Each BAM DMA device is associated with a specific on-chip peripheral. Each
+channel provides a uni-directional data transfer engine that is capable of
+transferring data between the peripheral and system memory (System mode), or
+between two peripherals (BAM2BAM).
+
+The initial release of this driver only supports slave transfers between
+peripherals and system memory.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Tested-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/Kconfig | 9 +
+ drivers/dma/Makefile | 2 +
+ drivers/dma/qcom_bam_dma.c | 1111 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1122 insertions(+)
+ create mode 100644 drivers/dma/qcom_bam_dma.c
+
+diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
+index 605b016..f87cef9 100644
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -401,4 +401,13 @@ config DMATEST
+ config DMA_ENGINE_RAID
+ bool
+
++config QCOM_BAM_DMA
++ tristate "QCOM BAM DMA support"
++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ ---help---
++ Enable support for the QCOM BAM DMA controller. This controller
++ provides DMA capabilities for a variety of on-chip devices.
++
+ endif
+diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
+index a029d0f4..5150c82 100644
+--- a/drivers/dma/Makefile
++++ b/drivers/dma/Makefile
+@@ -44,3 +44,5 @@ obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
+ obj-$(CONFIG_TI_CPPI41) += cppi41.o
+ obj-$(CONFIG_K3_DMA) += k3dma.o
+ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
++obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
++obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
+diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c
+new file mode 100644
+index 0000000..82c9231
+--- /dev/null
++++ b/drivers/dma/qcom_bam_dma.c
+@@ -0,0 +1,1111 @@
++/*
++ * 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.
++ *
++ */
++/*
++ * QCOM BAM DMA engine driver
++ *
++ * QCOM BAM DMA blocks are distributed amongst a number of the on-chip
++ * peripherals on the MSM 8x74. The configuration of the channels are dependent
++ * on the way they are hard wired to that specific peripheral. The peripheral
++ * device tree entries specify the configuration of each channel.
++ *
++ * The DMA controller requires the use of external memory for storage of the
++ * hardware descriptors for each channel. The descriptor FIFO is accessed as a
++ * circular buffer and operations are managed according to the offset within the
++ * FIFO. After pipe/channel reset, all of the pipe registers and internal state
++ * are back to defaults.
++ *
++ * During DMA operations, we write descriptors to the FIFO, being careful to
++ * handle wrapping and then write the last FIFO offset to that channel's
++ * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register
++ * indicates the current FIFO offset that is being processed, so there is some
++ * indication of where the hardware is currently working.
++ */
++
++#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/clk.h>
++#include <linux/dmaengine.h>
++
++#include "dmaengine.h"
++#include "virt-dma.h"
++
++struct bam_desc_hw {
++ u32 addr; /* Buffer physical address */
++ u16 size; /* Buffer size in bytes */
++ u16 flags;
++};
++
++#define DESC_FLAG_INT BIT(15)
++#define DESC_FLAG_EOT BIT(14)
++#define DESC_FLAG_EOB BIT(13)
++
++struct bam_async_desc {
++ struct virt_dma_desc vd;
++
++ u32 num_desc;
++ u32 xfer_len;
++ struct bam_desc_hw *curr_desc;
++
++ enum dma_transfer_direction dir;
++ size_t length;
++ struct bam_desc_hw desc[0];
++};
++
++#define BAM_CTRL 0x0000
++#define BAM_REVISION 0x0004
++#define BAM_SW_REVISION 0x0080
++#define BAM_NUM_PIPES 0x003C
++#define BAM_TIMER 0x0040
++#define BAM_TIMER_CTRL 0x0044
++#define BAM_DESC_CNT_TRSHLD 0x0008
++#define BAM_IRQ_SRCS 0x000C
++#define BAM_IRQ_SRCS_MSK 0x0010
++#define BAM_IRQ_SRCS_UNMASKED 0x0030
++#define BAM_IRQ_STTS 0x0014
++#define BAM_IRQ_CLR 0x0018
++#define BAM_IRQ_EN 0x001C
++#define BAM_CNFG_BITS 0x007C
++#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80))
++#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80))
++#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000))
++#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000))
++#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000))
++#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000))
++#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000))
++#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000))
++#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000))
++#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000))
++#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000))
++#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000))
++#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000))
++#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000))
++#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000))
++
++/* BAM CTRL */
++#define BAM_SW_RST BIT(0)
++#define BAM_EN BIT(1)
++#define BAM_EN_ACCUM BIT(4)
++#define BAM_TESTBUS_SEL_SHIFT 5
++#define BAM_TESTBUS_SEL_MASK 0x3F
++#define BAM_DESC_CACHE_SEL_SHIFT 13
++#define BAM_DESC_CACHE_SEL_MASK 0x3
++#define BAM_CACHED_DESC_STORE BIT(15)
++#define IBC_DISABLE BIT(16)
++
++/* BAM REVISION */
++#define REVISION_SHIFT 0
++#define REVISION_MASK 0xFF
++#define NUM_EES_SHIFT 8
++#define NUM_EES_MASK 0xF
++#define CE_BUFFER_SIZE BIT(13)
++#define AXI_ACTIVE BIT(14)
++#define USE_VMIDMT BIT(15)
++#define SECURED BIT(16)
++#define BAM_HAS_NO_BYPASS BIT(17)
++#define HIGH_FREQUENCY_BAM BIT(18)
++#define INACTIV_TMRS_EXST BIT(19)
++#define NUM_INACTIV_TMRS BIT(20)
++#define DESC_CACHE_DEPTH_SHIFT 21
++#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT)
++#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT)
++#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT)
++#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT)
++#define CMD_DESC_EN BIT(23)
++#define INACTIV_TMR_BASE_SHIFT 24
++#define INACTIV_TMR_BASE_MASK 0xFF
++
++/* BAM NUM PIPES */
++#define BAM_NUM_PIPES_SHIFT 0
++#define BAM_NUM_PIPES_MASK 0xFF
++#define PERIPH_NON_PIPE_GRP_SHIFT 16
++#define PERIPH_NON_PIP_GRP_MASK 0xFF
++#define BAM_NON_PIPE_GRP_SHIFT 24
++#define BAM_NON_PIPE_GRP_MASK 0xFF
++
++/* BAM CNFG BITS */
++#define BAM_PIPE_CNFG BIT(2)
++#define BAM_FULL_PIPE BIT(11)
++#define BAM_NO_EXT_P_RST BIT(12)
++#define BAM_IBC_DISABLE BIT(13)
++#define BAM_SB_CLK_REQ BIT(14)
++#define BAM_PSM_CSW_REQ BIT(15)
++#define BAM_PSM_P_RES BIT(16)
++#define BAM_AU_P_RES BIT(17)
++#define BAM_SI_P_RES BIT(18)
++#define BAM_WB_P_RES BIT(19)
++#define BAM_WB_BLK_CSW BIT(20)
++#define BAM_WB_CSW_ACK_IDL BIT(21)
++#define BAM_WB_RETR_SVPNT BIT(22)
++#define BAM_WB_DSC_AVL_P_RST BIT(23)
++#define BAM_REG_P_EN BIT(24)
++#define BAM_PSM_P_HD_DATA BIT(25)
++#define BAM_AU_ACCUMED BIT(26)
++#define BAM_CMD_ENABLE BIT(27)
++
++#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \
++ BAM_NO_EXT_P_RST | \
++ BAM_IBC_DISABLE | \
++ BAM_SB_CLK_REQ | \
++ BAM_PSM_CSW_REQ | \
++ BAM_PSM_P_RES | \
++ BAM_AU_P_RES | \
++ BAM_SI_P_RES | \
++ BAM_WB_P_RES | \
++ BAM_WB_BLK_CSW | \
++ BAM_WB_CSW_ACK_IDL | \
++ BAM_WB_RETR_SVPNT | \
++ BAM_WB_DSC_AVL_P_RST | \
++ BAM_REG_P_EN | \
++ BAM_PSM_P_HD_DATA | \
++ BAM_AU_ACCUMED | \
++ BAM_CMD_ENABLE)
++
++/* PIPE CTRL */
++#define P_EN BIT(1)
++#define P_DIRECTION BIT(3)
++#define P_SYS_STRM BIT(4)
++#define P_SYS_MODE BIT(5)
++#define P_AUTO_EOB BIT(6)
++#define P_AUTO_EOB_SEL_SHIFT 7
++#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT)
++#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT)
++#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT)
++#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT)
++#define P_PREFETCH_LIMIT_SHIFT 9
++#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT)
++#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT)
++#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT)
++#define P_WRITE_NWD BIT(11)
++#define P_LOCK_GROUP_SHIFT 16
++#define P_LOCK_GROUP_MASK 0x1F
++
++/* BAM_DESC_CNT_TRSHLD */
++#define CNT_TRSHLD 0xffff
++#define DEFAULT_CNT_THRSHLD 0x4
++
++/* BAM_IRQ_SRCS */
++#define BAM_IRQ BIT(31)
++#define P_IRQ 0x7fffffff
++
++/* BAM_IRQ_SRCS_MSK */
++#define BAM_IRQ_MSK BAM_IRQ
++#define P_IRQ_MSK P_IRQ
++
++/* BAM_IRQ_STTS */
++#define BAM_TIMER_IRQ BIT(4)
++#define BAM_EMPTY_IRQ BIT(3)
++#define BAM_ERROR_IRQ BIT(2)
++#define BAM_HRESP_ERR_IRQ BIT(1)
++
++/* BAM_IRQ_CLR */
++#define BAM_TIMER_CLR BIT(4)
++#define BAM_EMPTY_CLR BIT(3)
++#define BAM_ERROR_CLR BIT(2)
++#define BAM_HRESP_ERR_CLR BIT(1)
++
++/* BAM_IRQ_EN */
++#define BAM_TIMER_EN BIT(4)
++#define BAM_EMPTY_EN BIT(3)
++#define BAM_ERROR_EN BIT(2)
++#define BAM_HRESP_ERR_EN BIT(1)
++
++/* BAM_P_IRQ_EN */
++#define P_PRCSD_DESC_EN BIT(0)
++#define P_TIMER_EN BIT(1)
++#define P_WAKE_EN BIT(2)
++#define P_OUT_OF_DESC_EN BIT(3)
++#define P_ERR_EN BIT(4)
++#define P_TRNSFR_END_EN BIT(5)
++#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN)
++
++/* BAM_P_SW_OFSTS */
++#define P_SW_OFSTS_MASK 0xffff
++
++#define BAM_DESC_FIFO_SIZE SZ_32K
++#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1)
++#define BAM_MAX_DATA_SIZE (SZ_32K - 8)
++
++struct bam_chan {
++ struct virt_dma_chan vc;
++
++ struct bam_device *bdev;
++
++ /* configuration from device tree */
++ u32 id;
++
++ struct bam_async_desc *curr_txd; /* current running dma */
++
++ /* runtime configuration */
++ struct dma_slave_config slave;
++
++ /* fifo storage */
++ struct bam_desc_hw *fifo_virt;
++ dma_addr_t fifo_phys;
++
++ /* fifo markers */
++ unsigned short head; /* start of active descriptor entries */
++ unsigned short tail; /* end of active descriptor entries */
++
++ unsigned int initialized; /* is the channel hw initialized? */
++ unsigned int paused; /* is the channel paused? */
++ unsigned int reconfigure; /* new slave config? */
++
++ struct list_head node;
++};
++
++static inline struct bam_chan *to_bam_chan(struct dma_chan *common)
++{
++ return container_of(common, struct bam_chan, vc.chan);
++}
++
++struct bam_device {
++ void __iomem *regs;
++ struct device *dev;
++ struct dma_device common;
++ struct device_dma_parameters dma_parms;
++ struct bam_chan *channels;
++ u32 num_channels;
++
++ /* execution environment ID, from DT */
++ u32 ee;
++
++ struct clk *bamclk;
++ int irq;
++
++ /* dma start transaction tasklet */
++ struct tasklet_struct task;
++};
++
++/**
++ * bam_reset_channel - Reset individual BAM DMA channel
++ * @bchan: bam channel
++ *
++ * This function resets a specific BAM channel
++ */
++static void bam_reset_channel(struct bam_chan *bchan)
++{
++ struct bam_device *bdev = bchan->bdev;
++
++ lockdep_assert_held(&bchan->vc.lock);
++
++ /* reset channel */
++ writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id));
++ writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id));
++
++ /* don't allow cpu to reorder BAM register accesses done after this */
++ wmb();
++
++ /* make sure hw is initialized when channel is used the first time */
++ bchan->initialized = 0;
++}
++
++/**
++ * bam_chan_init_hw - Initialize channel hardware
++ * @bchan: bam channel
++ *
++ * This function resets and initializes the BAM channel
++ */
++static void bam_chan_init_hw(struct bam_chan *bchan,
++ enum dma_transfer_direction dir)
++{
++ struct bam_device *bdev = bchan->bdev;
++ u32 val;
++
++ /* Reset the channel to clear internal state of the FIFO */
++ bam_reset_channel(bchan);
++
++ /*
++ * write out 8 byte aligned address. We have enough space for this
++ * because we allocated 1 more descriptor (8 bytes) than we can use
++ */
++ writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)),
++ bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id));
++ writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs +
++ BAM_P_FIFO_SIZES(bchan->id));
++
++ /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */
++ writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id));
++
++ /* unmask the specific pipe and EE combo */
++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++ val |= BIT(bchan->id);
++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++
++ /* don't allow cpu to reorder the channel enable done below */
++ wmb();
++
++ /* set fixed direction and mode, then enable channel */
++ val = P_EN | P_SYS_MODE;
++ if (dir == DMA_DEV_TO_MEM)
++ val |= P_DIRECTION;
++
++ writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id));
++
++ bchan->initialized = 1;
++
++ /* init FIFO pointers */
++ bchan->head = 0;
++ bchan->tail = 0;
++}
++
++/**
++ * bam_alloc_chan - Allocate channel resources for DMA channel.
++ * @chan: specified channel
++ *
++ * This function allocates the FIFO descriptor memory
++ */
++static int bam_alloc_chan(struct dma_chan *chan)
++{
++ struct bam_chan *bchan = to_bam_chan(chan);
++ struct bam_device *bdev = bchan->bdev;
++
++ if (bchan->fifo_virt)
++ return 0;
++
++ /* allocate FIFO descriptor space, but only if necessary */
++ bchan->fifo_virt = dma_alloc_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE,
++ &bchan->fifo_phys, GFP_KERNEL);
++
++ if (!bchan->fifo_virt) {
++ dev_err(bdev->dev, "Failed to allocate desc fifo\n");
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++/**
++ * bam_free_chan - Frees dma resources associated with specific channel
++ * @chan: specified channel
++ *
++ * Free the allocated fifo descriptor memory and channel resources
++ *
++ */
++static void bam_free_chan(struct dma_chan *chan)
++{
++ struct bam_chan *bchan = to_bam_chan(chan);
++ struct bam_device *bdev = bchan->bdev;
++ u32 val;
++ unsigned long flags;
++
++ vchan_free_chan_resources(to_virt_chan(chan));
++
++ if (bchan->curr_txd) {
++ dev_err(bchan->bdev->dev, "Cannot free busy channel\n");
++ return;
++ }
++
++ spin_lock_irqsave(&bchan->vc.lock, flags);
++ bam_reset_channel(bchan);
++ spin_unlock_irqrestore(&bchan->vc.lock, flags);
++
++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt,
++ bchan->fifo_phys);
++ bchan->fifo_virt = NULL;
++
++ /* mask irq for pipe/channel */
++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++ val &= ~BIT(bchan->id);
++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++
++ /* disable irq */
++ writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id));
++}
++
++/**
++ * bam_slave_config - set slave configuration for channel
++ * @chan: dma channel
++ * @cfg: slave configuration
++ *
++ * Sets slave configuration for channel
++ *
++ */
++static void bam_slave_config(struct bam_chan *bchan,
++ struct dma_slave_config *cfg)
++{
++ memcpy(&bchan->slave, cfg, sizeof(*cfg));
++ bchan->reconfigure = 1;
++}
++
++/**
++ * bam_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 *bam_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 bam_chan *bchan = to_bam_chan(chan);
++ struct bam_device *bdev = bchan->bdev;
++ struct bam_async_desc *async_desc;
++ struct scatterlist *sg;
++ u32 i;
++ struct bam_desc_hw *desc;
++ unsigned int num_alloc = 0;
++
++
++ if (!is_slave_direction(direction)) {
++ dev_err(bdev->dev, "invalid dma direction\n");
++ return NULL;
++ }
++
++ /* calculate number of required entries */
++ for_each_sg(sgl, sg, sg_len, i)
++ num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_MAX_DATA_SIZE);
++
++ /* allocate enough room to accomodate the number of entries */
++ async_desc = kzalloc(sizeof(*async_desc) +
++ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT);
++
++ if (!async_desc)
++ goto err_out;
++
++ async_desc->num_desc = num_alloc;
++ async_desc->curr_desc = async_desc->desc;
++ async_desc->dir = direction;
++
++ /* fill in temporary descriptors */
++ desc = async_desc->desc;
++ for_each_sg(sgl, sg, sg_len, i) {
++ unsigned int remainder = sg_dma_len(sg);
++ unsigned int curr_offset = 0;
++
++ do {
++ desc->addr = sg_dma_address(sg) + curr_offset;
++
++ if (remainder > BAM_MAX_DATA_SIZE) {
++ desc->size = BAM_MAX_DATA_SIZE;
++ remainder -= BAM_MAX_DATA_SIZE;
++ curr_offset += BAM_MAX_DATA_SIZE;
++ } else {
++ desc->size = remainder;
++ remainder = 0;
++ }
++
++ async_desc->length += desc->size;
++ desc++;
++ } while (remainder > 0);
++ }
++
++ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
++
++err_out:
++ kfree(async_desc);
++ return NULL;
++}
++
++/**
++ * bam_dma_terminate_all - terminate all transactions on a channel
++ * @bchan: bam dma channel
++ *
++ * Dequeues and frees all transactions
++ * No callbacks are done
++ *
++ */
++static void bam_dma_terminate_all(struct bam_chan *bchan)
++{
++ unsigned long flag;
++ LIST_HEAD(head);
++
++ /* remove all transactions, including active transaction */
++ spin_lock_irqsave(&bchan->vc.lock, flag);
++ if (bchan->curr_txd) {
++ list_add(&bchan->curr_txd->vd.node, &bchan->vc.desc_issued);
++ bchan->curr_txd = NULL;
++ }
++
++ vchan_get_all_descriptors(&bchan->vc, &head);
++ spin_unlock_irqrestore(&bchan->vc.lock, flag);
++
++ vchan_dma_desc_free_list(&bchan->vc, &head);
++}
++
++/**
++ * bam_control - DMA device control
++ * @chan: dma channel
++ * @cmd: control cmd
++ * @arg: cmd argument
++ *
++ * Perform DMA control command
++ *
++ */
++static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
++ unsigned long arg)
++{
++ struct bam_chan *bchan = to_bam_chan(chan);
++ struct bam_device *bdev = bchan->bdev;
++ int ret = 0;
++ unsigned long flag;
++
++ switch (cmd) {
++ case DMA_PAUSE:
++ spin_lock_irqsave(&bchan->vc.lock, flag);
++ writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id));
++ bchan->paused = 1;
++ spin_unlock_irqrestore(&bchan->vc.lock, flag);
++ break;
++
++ case DMA_RESUME:
++ spin_lock_irqsave(&bchan->vc.lock, flag);
++ writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id));
++ bchan->paused = 0;
++ spin_unlock_irqrestore(&bchan->vc.lock, flag);
++ break;
++
++ case DMA_TERMINATE_ALL:
++ bam_dma_terminate_all(bchan);
++ break;
++
++ case DMA_SLAVE_CONFIG:
++ spin_lock_irqsave(&bchan->vc.lock, flag);
++ bam_slave_config(bchan, (struct dma_slave_config *)arg);
++ spin_unlock_irqrestore(&bchan->vc.lock, flag);
++ break;
++
++ default:
++ ret = -ENXIO;
++ break;
++ }
++
++ return ret;
++}
++
++/**
++ * process_channel_irqs - processes the channel interrupts
++ * @bdev: bam controller
++ *
++ * This function processes the channel interrupts
++ *
++ */
++static u32 process_channel_irqs(struct bam_device *bdev)
++{
++ u32 i, srcs, pipe_stts;
++ unsigned long flags;
++ struct bam_async_desc *async_desc;
++
++ srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee));
++
++ /* return early if no pipe/channel interrupts are present */
++ if (!(srcs & P_IRQ))
++ return srcs;
++
++ for (i = 0; i < bdev->num_channels; i++) {
++ struct bam_chan *bchan = &bdev->channels[i];
++
++ if (!(srcs & BIT(i)))
++ continue;
++
++ /* clear pipe irq */
++ pipe_stts = readl_relaxed(bdev->regs +
++ BAM_P_IRQ_STTS(i));
++
++ writel_relaxed(pipe_stts, bdev->regs +
++ BAM_P_IRQ_CLR(i));
++
++ spin_lock_irqsave(&bchan->vc.lock, flags);
++ async_desc = bchan->curr_txd;
++
++ if (async_desc) {
++ async_desc->num_desc -= async_desc->xfer_len;
++ async_desc->curr_desc += async_desc->xfer_len;
++ bchan->curr_txd = NULL;
++
++ /* manage FIFO */
++ bchan->head += async_desc->xfer_len;
++ bchan->head %= MAX_DESCRIPTORS;
++
++ /*
++ * if complete, process cookie. Otherwise
++ * push back to front of desc_issued so that
++ * it gets restarted by the tasklet
++ */
++ if (!async_desc->num_desc)
++ vchan_cookie_complete(&async_desc->vd);
++ else
++ list_add(&async_desc->vd.node,
++ &bchan->vc.desc_issued);
++ }
++
++ spin_unlock_irqrestore(&bchan->vc.lock, flags);
++ }
++
++ return srcs;
++}
++
++/**
++ * bam_dma_irq - irq handler for bam controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t bam_dma_irq(int irq, void *data)
++{
++ struct bam_device *bdev = data;
++ u32 clr_mask = 0, srcs = 0;
++
++ srcs |= process_channel_irqs(bdev);
++
++ /* kick off tasklet to start next dma transfer */
++ if (srcs & P_IRQ)
++ tasklet_schedule(&bdev->task);
++
++ if (srcs & BAM_IRQ)
++ clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS);
++
++ /* don't allow reorder of the various accesses to the BAM registers */
++ mb();
++
++ writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR);
++
++ return IRQ_HANDLED;
++}
++
++/**
++ * bam_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 bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++ struct dma_tx_state *txstate)
++{
++ struct bam_chan *bchan = to_bam_chan(chan);
++ struct virt_dma_desc *vd;
++ int ret;
++ size_t residue = 0;
++ unsigned int i;
++ unsigned long flags;
++
++ ret = dma_cookie_status(chan, cookie, txstate);
++ if (ret == DMA_COMPLETE)
++ return ret;
++
++ if (!txstate)
++ return bchan->paused ? DMA_PAUSED : ret;
++
++ spin_lock_irqsave(&bchan->vc.lock, flags);
++ vd = vchan_find_desc(&bchan->vc, cookie);
++ if (vd)
++ residue = container_of(vd, struct bam_async_desc, vd)->length;
++ else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie)
++ for (i = 0; i < bchan->curr_txd->num_desc; i++)
++ residue += bchan->curr_txd->curr_desc[i].size;
++
++ spin_unlock_irqrestore(&bchan->vc.lock, flags);
++
++ dma_set_residue(txstate, residue);
++
++ if (ret == DMA_IN_PROGRESS && bchan->paused)
++ ret = DMA_PAUSED;
++
++ return ret;
++}
++
++/**
++ * bam_apply_new_config
++ * @bchan: bam dma channel
++ * @dir: DMA direction
++ */
++static void bam_apply_new_config(struct bam_chan *bchan,
++ enum dma_transfer_direction dir)
++{
++ struct bam_device *bdev = bchan->bdev;
++ u32 maxburst;
++
++ if (dir == DMA_DEV_TO_MEM)
++ maxburst = bchan->slave.src_maxburst;
++ else
++ maxburst = bchan->slave.dst_maxburst;
++
++ writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD);
++
++ bchan->reconfigure = 0;
++}
++
++/**
++ * bam_start_dma - start next transaction
++ * @bchan - bam dma channel
++ */
++static void bam_start_dma(struct bam_chan *bchan)
++{
++ struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc);
++ struct bam_device *bdev = bchan->bdev;
++ struct bam_async_desc *async_desc;
++ struct bam_desc_hw *desc;
++ struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
++ sizeof(struct bam_desc_hw));
++
++ lockdep_assert_held(&bchan->vc.lock);
++
++ if (!vd)
++ return;
++
++ list_del(&vd->node);
++
++ async_desc = container_of(vd, struct bam_async_desc, vd);
++ bchan->curr_txd = async_desc;
++
++ /* on first use, initialize the channel hardware */
++ if (!bchan->initialized)
++ bam_chan_init_hw(bchan, async_desc->dir);
++
++ /* apply new slave config changes, if necessary */
++ if (bchan->reconfigure)
++ bam_apply_new_config(bchan, async_desc->dir);
++
++ desc = bchan->curr_txd->curr_desc;
++
++ if (async_desc->num_desc > MAX_DESCRIPTORS)
++ async_desc->xfer_len = MAX_DESCRIPTORS;
++ else
++ async_desc->xfer_len = async_desc->num_desc;
++
++ /* set INT on last descriptor */
++ desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT;
++
++ if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) {
++ u32 partial = MAX_DESCRIPTORS - bchan->tail;
++
++ memcpy(&fifo[bchan->tail], desc,
++ partial * sizeof(struct bam_desc_hw));
++ memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) *
++ sizeof(struct bam_desc_hw));
++ } else {
++ memcpy(&fifo[bchan->tail], desc,
++ async_desc->xfer_len * sizeof(struct bam_desc_hw));
++ }
++
++ bchan->tail += async_desc->xfer_len;
++ bchan->tail %= MAX_DESCRIPTORS;
++
++ /* ensure descriptor writes and dma start not reordered */
++ wmb();
++ writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),
++ bdev->regs + BAM_P_EVNT_REG(bchan->id));
++}
++
++/**
++ * dma_tasklet - DMA IRQ tasklet
++ * @data: tasklet argument (bam controller structure)
++ *
++ * Sets up next DMA operation and then processes all completed transactions
++ */
++static void dma_tasklet(unsigned long data)
++{
++ struct bam_device *bdev = (struct bam_device *)data;
++ struct bam_chan *bchan;
++ unsigned long flags;
++ unsigned int i;
++
++ /* go through the channels and kick off transactions */
++ for (i = 0; i < bdev->num_channels; i++) {
++ bchan = &bdev->channels[i];
++ spin_lock_irqsave(&bchan->vc.lock, flags);
++
++ if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd)
++ bam_start_dma(bchan);
++ spin_unlock_irqrestore(&bchan->vc.lock, flags);
++ }
++}
++
++/**
++ * bam_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Calls tasklet directly which in turn starts any pending transactions
++ */
++static void bam_issue_pending(struct dma_chan *chan)
++{
++ struct bam_chan *bchan = to_bam_chan(chan);
++ unsigned long flags;
++
++ spin_lock_irqsave(&bchan->vc.lock, flags);
++
++ /* if work pending and idle, start a transaction */
++ if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd)
++ bam_start_dma(bchan);
++
++ spin_unlock_irqrestore(&bchan->vc.lock, flags);
++}
++
++/**
++ * bam_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void bam_dma_free_desc(struct virt_dma_desc *vd)
++{
++ struct bam_async_desc *async_desc = container_of(vd,
++ struct bam_async_desc, vd);
++
++ kfree(async_desc);
++}
++
++static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec,
++ struct of_dma *of)
++{
++ struct bam_device *bdev = container_of(of->of_dma_data,
++ struct bam_device, common);
++ unsigned int request;
++
++ if (dma_spec->args_count != 1)
++ return NULL;
++
++ request = dma_spec->args[0];
++ if (request >= bdev->num_channels)
++ return NULL;
++
++ return dma_get_slave_channel(&(bdev->channels[request].vc.chan));
++}
++
++/**
++ * bam_init
++ * @bdev: bam device
++ *
++ * Initialization helper for global bam registers
++ */
++static int bam_init(struct bam_device *bdev)
++{
++ u32 val;
++
++ /* read revision and configuration information */
++ val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT;
++ val &= NUM_EES_MASK;
++
++ /* check that configured EE is within range */
++ if (bdev->ee >= val)
++ return -EINVAL;
++
++ val = readl_relaxed(bdev->regs + BAM_NUM_PIPES);
++ bdev->num_channels = val & BAM_NUM_PIPES_MASK;
++
++ /* s/w reset bam */
++ /* after reset all pipes are disabled and idle */
++ val = readl_relaxed(bdev->regs + BAM_CTRL);
++ val |= BAM_SW_RST;
++ writel_relaxed(val, bdev->regs + BAM_CTRL);
++ val &= ~BAM_SW_RST;
++ writel_relaxed(val, bdev->regs + BAM_CTRL);
++
++ /* make sure previous stores are visible before enabling BAM */
++ wmb();
++
++ /* enable bam */
++ val |= BAM_EN;
++ writel_relaxed(val, bdev->regs + BAM_CTRL);
++
++ /* set descriptor threshhold, start with 4 bytes */
++ writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD);
++
++ /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */
++ writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS);
++
++ /* enable irqs for errors */
++ writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN,
++ bdev->regs + BAM_IRQ_EN);
++
++ /* unmask global bam interrupt */
++ writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++
++ return 0;
++}
++
++static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan,
++ u32 index)
++{
++ bchan->id = index;
++ bchan->bdev = bdev;
++
++ vchan_init(&bchan->vc, &bdev->common);
++ bchan->vc.desc_free = bam_dma_free_desc;
++}
++
++static int bam_dma_probe(struct platform_device *pdev)
++{
++ struct bam_device *bdev;
++ struct resource *iores;
++ int ret, i;
++
++ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
++ if (!bdev)
++ return -ENOMEM;
++
++ bdev->dev = &pdev->dev;
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ bdev->regs = devm_ioremap_resource(&pdev->dev, iores);
++ if (IS_ERR(bdev->regs))
++ return PTR_ERR(bdev->regs);
++
++ bdev->irq = platform_get_irq(pdev, 0);
++ if (bdev->irq < 0)
++ return bdev->irq;
++
++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee);
++ if (ret) {
++ dev_err(bdev->dev, "Execution environment unspecified\n");
++ return ret;
++ }
++
++ bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
++ if (IS_ERR(bdev->bamclk))
++ return PTR_ERR(bdev->bamclk);
++
++ ret = clk_prepare_enable(bdev->bamclk);
++ if (ret) {
++ dev_err(bdev->dev, "failed to prepare/enable clock\n");
++ return ret;
++ }
++
++ ret = bam_init(bdev);
++ if (ret)
++ goto err_disable_clk;
++
++ tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev);
++
++ bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels,
++ sizeof(*bdev->channels), GFP_KERNEL);
++
++ if (!bdev->channels) {
++ ret = -ENOMEM;
++ goto err_disable_clk;
++ }
++
++ /* allocate and initialize channels */
++ INIT_LIST_HEAD(&bdev->common.channels);
++
++ for (i = 0; i < bdev->num_channels; i++)
++ bam_channel_init(bdev, &bdev->channels[i], i);
++
++ ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq,
++ IRQF_TRIGGER_HIGH, "bam_dma", bdev);
++ if (ret)
++ goto err_disable_clk;
++
++ /* set max dma segment size */
++ bdev->common.dev = bdev->dev;
++ bdev->common.dev->dma_parms = &bdev->dma_parms;
++ ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE);
++ if (ret) {
++ dev_err(bdev->dev, "cannot set maximum segment size\n");
++ goto err_disable_clk;
++ }
++
++ platform_set_drvdata(pdev, bdev);
++
++ /* set capabilities */
++ dma_cap_zero(bdev->common.cap_mask);
++ dma_cap_set(DMA_SLAVE, bdev->common.cap_mask);
++
++ /* initialize dmaengine apis */
++ bdev->common.device_alloc_chan_resources = bam_alloc_chan;
++ bdev->common.device_free_chan_resources = bam_free_chan;
++ bdev->common.device_prep_slave_sg = bam_prep_slave_sg;
++ bdev->common.device_control = bam_control;
++ bdev->common.device_issue_pending = bam_issue_pending;
++ bdev->common.device_tx_status = bam_tx_status;
++ bdev->common.dev = bdev->dev;
++
++ ret = dma_async_device_register(&bdev->common);
++ if (ret) {
++ dev_err(bdev->dev, "failed to register dma async device\n");
++ goto err_disable_clk;
++ }
++
++ ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate,
++ &bdev->common);
++ if (ret)
++ goto err_unregister_dma;
++
++ return 0;
++
++err_unregister_dma:
++ dma_async_device_unregister(&bdev->common);
++err_disable_clk:
++ clk_disable_unprepare(bdev->bamclk);
++ return ret;
++}
++
++static int bam_dma_remove(struct platform_device *pdev)
++{
++ struct bam_device *bdev = platform_get_drvdata(pdev);
++ u32 i;
++
++ of_dma_controller_free(pdev->dev.of_node);
++ dma_async_device_unregister(&bdev->common);
++
++ /* mask all interrupts for this execution environment */
++ writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
++
++ devm_free_irq(bdev->dev, bdev->irq, bdev);
++
++ for (i = 0; i < bdev->num_channels; i++) {
++ bam_dma_terminate_all(&bdev->channels[i]);
++ tasklet_kill(&bdev->channels[i].vc.task);
++
++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE,
++ bdev->channels[i].fifo_virt,
++ bdev->channels[i].fifo_phys);
++ }
++
++ tasklet_kill(&bdev->task);
++
++ clk_disable_unprepare(bdev->bamclk);
++
++ return 0;
++}
++
++static const struct of_device_id bam_of_match[] = {
++ { .compatible = "qcom,bam-v1.4.0", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, bam_of_match);
++
++static struct platform_driver bam_dma_driver = {
++ .probe = bam_dma_probe,
++ .remove = bam_dma_remove,
++ .driver = {
++ .name = "bam-dma-engine",
++ .owner = THIS_MODULE,
++ .of_match_table = bam_of_match,
++ },
++};
++
++module_platform_driver(bam_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM BAM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch
new file mode 100644
index 0000000..b173fcf
--- /dev/null
+++ b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch
@@ -0,0 +1,81 @@
+From 8a70c89b2fbb635a8d4fec302165343827aeed9f Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Mon, 10 Mar 2014 17:37:11 +0200
+Subject: [PATCH 046/182] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
+
+This patch adds the device-tree binding documentation for Qualcomm
+SDHCI driver. It contains the differences between the core properties
+in mmc.txt and the properties used by the sdhci-msm driver.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
+Signed-off-by: Chris Ball <chris@printf.net>
+---
+ .../devicetree/bindings/mmc/sdhci-msm.txt | 55 ++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+
+diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+new file mode 100644
+index 0000000..81b33b5
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+@@ -0,0 +1,55 @@
++* Qualcomm SDHCI controller (sdhci-msm)
++
++This file documents differences between the core properties in mmc.txt
++and the properties used by the sdhci-msm driver.
++
++Required properties:
++- compatible: Should contain "qcom,sdhci-msm-v4".
++- reg: Base address and length of the register in the following order:
++ - Host controller register map (required)
++ - SD Core register map (required)
++- interrupts: Should contain an interrupt-specifiers for the interrupts:
++ - Host controller interrupt (required)
++- pinctrl-names: Should contain only one value - "default".
++- pinctrl-0: Should specify pin control groups used for this controller.
++- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
++- clock-names: Should contain the following:
++ "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
++ "core" - SDC MMC clock (MCLK) (required)
++ "bus" - SDCC bus voter clock (optional)
++
++Example:
++
++ sdhc_1: sdhci@f9824900 {
++ compatible = "qcom,sdhci-msm-v4";
++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
++ interrupts = <0 123 0>;
++ bus-width = <8>;
++ non-removable;
++
++ vmmc = <&pm8941_l20>;
++ vqmmc = <&pm8941_s3>;
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
++
++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
++ clock-names = "core", "iface";
++ };
++
++ sdhc_2: sdhci@f98a4900 {
++ compatible = "qcom,sdhci-msm-v4";
++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
++ interrupts = <0 125 0>;
++ bus-width = <4>;
++ cd-gpios = <&msmgpio 62 0x1>;
++
++ vmmc = <&pm8941_l21>;
++ vqmmc = <&pm8941_l13>;
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
++
++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
++ clock-names = "core", "iface";
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch
new file mode 100644
index 0000000..7ec97a4
--- /dev/null
+++ b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch
@@ -0,0 +1,275 @@
+From 5a8f026acb4a7a6c6d0c868cc1790363640b9b8f Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Mon, 10 Mar 2014 17:37:12 +0200
+Subject: [PATCH 047/182] mmc: sdhci-msm: Initial support for Qualcomm
+ chipsets
+
+This platform driver adds the initial support of Secure Digital Host
+Controller Interface compliant controller found in Qualcomm chipsets.
+
+Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
+Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
+Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
+Signed-off-by: Chris Ball <chris@printf.net>
+---
+ drivers/mmc/host/Kconfig | 13 +++
+ drivers/mmc/host/Makefile | 1 +
+ drivers/mmc/host/sdhci-msm.c | 208 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 222 insertions(+)
+ create mode 100644 drivers/mmc/host/sdhci-msm.c
+
+diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
+index 1384f67..c0ea72a 100644
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -334,6 +334,19 @@ config MMC_ATMELMCI
+
+ If unsure, say N.
+
++config MMC_SDHCI_MSM
++ tristate "Qualcomm SDHCI Controller Support"
++ depends on ARCH_QCOM
++ depends on MMC_SDHCI_PLTFM
++ help
++ This selects the Secure Digital Host Controller Interface (SDHCI)
++ support present in Qualcomm SOCs. The controller supports
++ SD/MMC/SDIO devices.
++
++ If you have a controller with this interface, say Y or M here.
++
++ If unsure, say N.
++
+ config MMC_MSM
+ tristate "Qualcomm SDCC Controller Support"
+ depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
+diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
+index 3483b6b..bbc8445 100644
+--- a/drivers/mmc/host/Makefile
++++ b/drivers/mmc/host/Makefile
+@@ -64,6 +64,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
+ obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
+ obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
+ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
++obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
+
+ ifeq ($(CONFIG_CB710_DEBUG),y)
+ CFLAGS-cb710-mmc += -DDEBUG
+diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
+new file mode 100644
+index 0000000..3b0606f
+--- /dev/null
++++ b/drivers/mmc/host/sdhci-msm.c
+@@ -0,0 +1,208 @@
++/*
++ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
++ *
++ * 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/module.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/delay.h>
++
++#include "sdhci-pltfm.h"
++
++#define CORE_HC_MODE 0x78
++#define HC_MODE_EN 0x1
++#define CORE_POWER 0x0
++#define CORE_SW_RST BIT(7)
++
++
++struct sdhci_msm_host {
++ struct platform_device *pdev;
++ void __iomem *core_mem; /* MSM SDCC mapped address */
++ struct clk *clk; /* main SD/MMC bus clock */
++ struct clk *pclk; /* SDHC peripheral bus clock */
++ struct clk *bus_clk; /* SDHC bus voter clock */
++ struct mmc_host *mmc;
++ struct sdhci_pltfm_data sdhci_msm_pdata;
++};
++
++/* Platform specific tuning */
++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
++{
++ /*
++ * Tuning is required for SDR104, HS200 and HS400 cards and if the clock
++ * frequency greater than 100MHz in those modes. The standard tuning
++ * procedure should not be executed, but a custom implementation will be
++ * added here instead.
++ */
++ return 0;
++}
++
++static const struct of_device_id sdhci_msm_dt_match[] = {
++ { .compatible = "qcom,sdhci-msm-v4" },
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
++
++static struct sdhci_ops sdhci_msm_ops = {
++ .platform_execute_tuning = sdhci_msm_execute_tuning,
++};
++
++static int sdhci_msm_probe(struct platform_device *pdev)
++{
++ struct sdhci_host *host;
++ struct sdhci_pltfm_host *pltfm_host;
++ struct sdhci_msm_host *msm_host;
++ struct resource *core_memres;
++ int ret;
++ u16 host_version;
++
++ msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
++ if (!msm_host)
++ return -ENOMEM;
++
++ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
++ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
++ if (IS_ERR(host))
++ return PTR_ERR(host);
++
++ pltfm_host = sdhci_priv(host);
++ pltfm_host->priv = msm_host;
++ msm_host->mmc = host->mmc;
++ msm_host->pdev = pdev;
++
++ ret = mmc_of_parse(host->mmc);
++ if (ret)
++ goto pltfm_free;
++
++ sdhci_get_of_property(pdev);
++
++ /* Setup SDCC bus voter clock. */
++ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
++ if (!IS_ERR(msm_host->bus_clk)) {
++ /* Vote for max. clk rate for max. performance */
++ ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
++ if (ret)
++ goto pltfm_free;
++ ret = clk_prepare_enable(msm_host->bus_clk);
++ if (ret)
++ goto pltfm_free;
++ }
++
++ /* Setup main peripheral bus clock */
++ msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
++ if (IS_ERR(msm_host->pclk)) {
++ ret = PTR_ERR(msm_host->pclk);
++ dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
++ goto bus_clk_disable;
++ }
++
++ ret = clk_prepare_enable(msm_host->pclk);
++ if (ret)
++ goto bus_clk_disable;
++
++ /* Setup SDC MMC clock */
++ msm_host->clk = devm_clk_get(&pdev->dev, "core");
++ if (IS_ERR(msm_host->clk)) {
++ ret = PTR_ERR(msm_host->clk);
++ dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
++ goto pclk_disable;
++ }
++
++ ret = clk_prepare_enable(msm_host->clk);
++ if (ret)
++ goto pclk_disable;
++
++ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
++
++ if (IS_ERR(msm_host->core_mem)) {
++ dev_err(&pdev->dev, "Failed to remap registers\n");
++ ret = PTR_ERR(msm_host->core_mem);
++ goto clk_disable;
++ }
++
++ /* Reset the core and Enable SDHC mode */
++ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
++ CORE_SW_RST, msm_host->core_mem + CORE_POWER);
++
++ /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
++ usleep_range(1000, 5000);
++ if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
++ dev_err(&pdev->dev, "Stuck in reset\n");
++ ret = -ETIMEDOUT;
++ goto clk_disable;
++ }
++
++ /* Set HC_MODE_EN bit in HC_MODE register */
++ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
++
++ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
++ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
++
++ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
++ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
++ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
++ SDHCI_VENDOR_VER_SHIFT));
++
++ ret = sdhci_add_host(host);
++ if (ret)
++ goto clk_disable;
++
++ return 0;
++
++clk_disable:
++ clk_disable_unprepare(msm_host->clk);
++pclk_disable:
++ clk_disable_unprepare(msm_host->pclk);
++bus_clk_disable:
++ if (!IS_ERR(msm_host->bus_clk))
++ clk_disable_unprepare(msm_host->bus_clk);
++pltfm_free:
++ sdhci_pltfm_free(pdev);
++ return ret;
++}
++
++static int sdhci_msm_remove(struct platform_device *pdev)
++{
++ struct sdhci_host *host = platform_get_drvdata(pdev);
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct sdhci_msm_host *msm_host = pltfm_host->priv;
++ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
++ 0xffffffff);
++
++ sdhci_remove_host(host, dead);
++ sdhci_pltfm_free(pdev);
++ clk_disable_unprepare(msm_host->clk);
++ clk_disable_unprepare(msm_host->pclk);
++ if (!IS_ERR(msm_host->bus_clk))
++ clk_disable_unprepare(msm_host->bus_clk);
++ return 0;
++}
++
++static struct platform_driver sdhci_msm_driver = {
++ .probe = sdhci_msm_probe,
++ .remove = sdhci_msm_remove,
++ .driver = {
++ .name = "sdhci_msm",
++ .owner = THIS_MODULE,
++ .of_match_table = sdhci_msm_dt_match,
++ },
++};
++
++module_platform_driver(sdhci_msm_driver);
++
++MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch
new file mode 100644
index 0000000..07e122f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch
@@ -0,0 +1,472 @@
+From c2a237b3e467c8bb349c4624b71ec400abaf8ad1 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Mon, 10 Mar 2014 17:37:13 +0200
+Subject: [PATCH 048/182] mmc: sdhci-msm: Add platform_execute_tuning
+ implementation
+
+This patch adds implementation for platform specific tuning in order
+to support HS200 bus speed mode on Qualcomm SDHCI controller.
+
+Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
+Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
+Signed-off-by: Chris Ball <chris@printf.net>
+---
+ drivers/mmc/host/sdhci-msm.c | 420 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 415 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
+index 3b0606f..acb0e9e 100644
+--- a/drivers/mmc/host/sdhci-msm.c
++++ b/drivers/mmc/host/sdhci-msm.c
+@@ -18,6 +18,8 @@
+ #include <linux/of_device.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/delay.h>
++#include <linux/mmc/mmc.h>
++#include <linux/slab.h>
+
+ #include "sdhci-pltfm.h"
+
+@@ -26,6 +28,42 @@
+ #define CORE_POWER 0x0
+ #define CORE_SW_RST BIT(7)
+
++#define MAX_PHASES 16
++#define CORE_DLL_LOCK BIT(7)
++#define CORE_DLL_EN BIT(16)
++#define CORE_CDR_EN BIT(17)
++#define CORE_CK_OUT_EN BIT(18)
++#define CORE_CDR_EXT_EN BIT(19)
++#define CORE_DLL_PDN BIT(29)
++#define CORE_DLL_RST BIT(30)
++#define CORE_DLL_CONFIG 0x100
++#define CORE_DLL_STATUS 0x108
++
++#define CORE_VENDOR_SPEC 0x10c
++#define CORE_CLK_PWRSAVE BIT(1)
++
++#define CDR_SELEXT_SHIFT 20
++#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
++#define CMUX_SHIFT_PHASE_SHIFT 24
++#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
++
++static const u32 tuning_block_64[] = {
++ 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
++ 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
++ 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
++ 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
++};
++
++static const u32 tuning_block_128[] = {
++ 0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
++ 0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
++ 0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
++ 0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
++ 0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
++ 0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
++ 0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
++ 0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
++};
+
+ struct sdhci_msm_host {
+ struct platform_device *pdev;
+@@ -38,17 +76,389 @@ struct sdhci_msm_host {
+ };
+
+ /* Platform specific tuning */
+-static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
++static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
++{
++ u32 wait_cnt = 50;
++ u8 ck_out_en;
++ struct mmc_host *mmc = host->mmc;
++
++ /* Poll for CK_OUT_EN bit. max. poll time = 50us */
++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
++ CORE_CK_OUT_EN);
++
++ while (ck_out_en != poll) {
++ if (--wait_cnt == 0) {
++ dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
++ mmc_hostname(mmc), poll);
++ return -ETIMEDOUT;
++ }
++ udelay(1);
++
++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
++ CORE_CK_OUT_EN);
++ }
++
++ return 0;
++}
++
++static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
++{
++ int rc;
++ static const u8 grey_coded_phase_table[] = {
++ 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
++ 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
++ };
++ unsigned long flags;
++ u32 config;
++ struct mmc_host *mmc = host->mmc;
++
++ spin_lock_irqsave(&host->lock, flags);
++
++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
++ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
++ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
++ rc = msm_dll_poll_ck_out_en(host, 0);
++ if (rc)
++ goto err_out;
++
++ /*
++ * Write the selected DLL clock output phase (0 ... 15)
++ * to CDR_SELEXT bit field of DLL_CONFIG register.
++ */
++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
++ config &= ~CDR_SELEXT_MASK;
++ config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
++ rc = msm_dll_poll_ck_out_en(host, 1);
++ if (rc)
++ goto err_out;
++
++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
++ config |= CORE_CDR_EN;
++ config &= ~CORE_CDR_EXT_EN;
++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
++ goto out;
++
++err_out:
++ dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
++ mmc_hostname(mmc), phase);
++out:
++ spin_unlock_irqrestore(&host->lock, flags);
++ return rc;
++}
++
++/*
++ * Find out the greatest range of consecuitive selected
++ * DLL clock output phases that can be used as sampling
++ * setting for SD3.0 UHS-I card read operation (in SDR104
++ * timing mode) or for eMMC4.5 card read operation (in HS200
++ * timing mode).
++ * Select the 3/4 of the range and configure the DLL with the
++ * selected DLL clock output phase.
++ */
++
++static int msm_find_most_appropriate_phase(struct sdhci_host *host,
++ u8 *phase_table, u8 total_phases)
++{
++ int ret;
++ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
++ u8 phases_per_row[MAX_PHASES] = { 0 };
++ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
++ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
++ bool phase_0_found = false, phase_15_found = false;
++ struct mmc_host *mmc = host->mmc;
++
++ if (!total_phases || (total_phases > MAX_PHASES)) {
++ dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
++ mmc_hostname(mmc), total_phases);
++ return -EINVAL;
++ }
++
++ for (cnt = 0; cnt < total_phases; cnt++) {
++ ranges[row_index][col_index] = phase_table[cnt];
++ phases_per_row[row_index] += 1;
++ col_index++;
++
++ if ((cnt + 1) == total_phases) {
++ continue;
++ /* check if next phase in phase_table is consecutive or not */
++ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
++ row_index++;
++ col_index = 0;
++ }
++ }
++
++ if (row_index >= MAX_PHASES)
++ return -EINVAL;
++
++ /* Check if phase-0 is present in first valid window? */
++ if (!ranges[0][0]) {
++ phase_0_found = true;
++ phase_0_raw_index = 0;
++ /* Check if cycle exist between 2 valid windows */
++ for (cnt = 1; cnt <= row_index; cnt++) {
++ if (phases_per_row[cnt]) {
++ for (i = 0; i < phases_per_row[cnt]; i++) {
++ if (ranges[cnt][i] == 15) {
++ phase_15_found = true;
++ phase_15_raw_index = cnt;
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ /* If 2 valid windows form cycle then merge them as single window */
++ if (phase_0_found && phase_15_found) {
++ /* number of phases in raw where phase 0 is present */
++ u8 phases_0 = phases_per_row[phase_0_raw_index];
++ /* number of phases in raw where phase 15 is present */
++ u8 phases_15 = phases_per_row[phase_15_raw_index];
++
++ if (phases_0 + phases_15 >= MAX_PHASES)
++ /*
++ * If there are more than 1 phase windows then total
++ * number of phases in both the windows should not be
++ * more than or equal to MAX_PHASES.
++ */
++ return -EINVAL;
++
++ /* Merge 2 cyclic windows */
++ i = phases_15;
++ for (cnt = 0; cnt < phases_0; cnt++) {
++ ranges[phase_15_raw_index][i] =
++ ranges[phase_0_raw_index][cnt];
++ if (++i >= MAX_PHASES)
++ break;
++ }
++
++ phases_per_row[phase_0_raw_index] = 0;
++ phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
++ }
++
++ for (cnt = 0; cnt <= row_index; cnt++) {
++ if (phases_per_row[cnt] > curr_max) {
++ curr_max = phases_per_row[cnt];
++ selected_row_index = cnt;
++ }
++ }
++
++ i = (curr_max * 3) / 4;
++ if (i)
++ i--;
++
++ ret = ranges[selected_row_index][i];
++
++ if (ret >= MAX_PHASES) {
++ ret = -EINVAL;
++ dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
++ mmc_hostname(mmc), ret);
++ }
++
++ return ret;
++}
++
++static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
++{
++ u32 mclk_freq = 0, config;
++
++ /* Program the MCLK value to MCLK_FREQ bit field */
++ if (host->clock <= 112000000)
++ mclk_freq = 0;
++ else if (host->clock <= 125000000)
++ mclk_freq = 1;
++ else if (host->clock <= 137000000)
++ mclk_freq = 2;
++ else if (host->clock <= 150000000)
++ mclk_freq = 3;
++ else if (host->clock <= 162000000)
++ mclk_freq = 4;
++ else if (host->clock <= 175000000)
++ mclk_freq = 5;
++ else if (host->clock <= 187000000)
++ mclk_freq = 6;
++ else if (host->clock <= 200000000)
++ mclk_freq = 7;
++
++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
++ config &= ~CMUX_SHIFT_PHASE_MASK;
++ config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
++}
++
++/* Initialize the DLL (Programmable Delay Line) */
++static int msm_init_cm_dll(struct sdhci_host *host)
+ {
++ struct mmc_host *mmc = host->mmc;
++ int wait_cnt = 50;
++ unsigned long flags;
++
++ spin_lock_irqsave(&host->lock, flags);
++
+ /*
+- * Tuning is required for SDR104, HS200 and HS400 cards and if the clock
+- * frequency greater than 100MHz in those modes. The standard tuning
+- * procedure should not be executed, but a custom implementation will be
+- * added here instead.
++ * Make sure that clock is always enabled when DLL
++ * tuning is in progress. Keeping PWRSAVE ON may
++ * turn off the clock.
+ */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
++ & ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
++
++ /* Write 1 to DLL_RST bit of DLL_CONFIG register */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
++ msm_cm_dll_set_freq(host);
++
++ /* Write 0 to DLL_RST bit of DLL_CONFIG register */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Set DLL_EN bit to 1. */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Set CK_OUT_EN bit to 1. */
++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
++
++ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
++ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
++ CORE_DLL_LOCK)) {
++ /* max. wait for 50us sec for LOCK bit to be set */
++ if (--wait_cnt == 0) {
++ dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
++ mmc_hostname(mmc));
++ spin_unlock_irqrestore(&host->lock, flags);
++ return -ETIMEDOUT;
++ }
++ udelay(1);
++ }
++
++ spin_unlock_irqrestore(&host->lock, flags);
+ return 0;
+ }
+
++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
++{
++ int tuning_seq_cnt = 3;
++ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
++ const u32 *tuning_block_pattern = tuning_block_64;
++ int size = sizeof(tuning_block_64); /* Pattern size in bytes */
++ int rc;
++ struct mmc_host *mmc = host->mmc;
++ struct mmc_ios ios = host->mmc->ios;
++
++ /*
++ * Tuning is required for SDR104, HS200 and HS400 cards and
++ * if clock frequency is greater than 100MHz in these modes.
++ */
++ if (host->clock <= 100 * 1000 * 1000 ||
++ !((ios.timing == MMC_TIMING_MMC_HS200) ||
++ (ios.timing == MMC_TIMING_UHS_SDR104)))
++ return 0;
++
++ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
++ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
++ tuning_block_pattern = tuning_block_128;
++ size = sizeof(tuning_block_128);
++ }
++
++ data_buf = kmalloc(size, GFP_KERNEL);
++ if (!data_buf)
++ return -ENOMEM;
++
++retry:
++ /* First of all reset the tuning block */
++ rc = msm_init_cm_dll(host);
++ if (rc)
++ goto out;
++
++ phase = 0;
++ do {
++ struct mmc_command cmd = { 0 };
++ struct mmc_data data = { 0 };
++ struct mmc_request mrq = {
++ .cmd = &cmd,
++ .data = &data
++ };
++ struct scatterlist sg;
++
++ /* Set the phase in delay line hw block */
++ rc = msm_config_cm_dll_phase(host, phase);
++ if (rc)
++ goto out;
++
++ cmd.opcode = opcode;
++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
++
++ data.blksz = size;
++ data.blocks = 1;
++ data.flags = MMC_DATA_READ;
++ data.timeout_ns = NSEC_PER_SEC; /* 1 second */
++
++ data.sg = &sg;
++ data.sg_len = 1;
++ sg_init_one(&sg, data_buf, size);
++ memset(data_buf, 0, size);
++ mmc_wait_for_req(mmc, &mrq);
++
++ if (!cmd.error && !data.error &&
++ !memcmp(data_buf, tuning_block_pattern, size)) {
++ /* Tuning is successful at this tuning point */
++ tuned_phases[tuned_phase_cnt++] = phase;
++ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
++ mmc_hostname(mmc), phase);
++ }
++ } while (++phase < ARRAY_SIZE(tuned_phases));
++
++ if (tuned_phase_cnt) {
++ rc = msm_find_most_appropriate_phase(host, tuned_phases,
++ tuned_phase_cnt);
++ if (rc < 0)
++ goto out;
++ else
++ phase = rc;
++
++ /*
++ * Finally set the selected phase in delay
++ * line hw block.
++ */
++ rc = msm_config_cm_dll_phase(host, phase);
++ if (rc)
++ goto out;
++ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
++ mmc_hostname(mmc), phase);
++ } else {
++ if (--tuning_seq_cnt)
++ goto retry;
++ /* Tuning failed */
++ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
++ mmc_hostname(mmc));
++ rc = -EIO;
++ }
++
++out:
++ kfree(data_buf);
++ return rc;
++}
++
+ static const struct of_device_id sdhci_msm_dt_match[] = {
+ { .compatible = "qcom,sdhci-msm-v4" },
+ {},
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch
new file mode 100644
index 0000000..f520e6c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch
@@ -0,0 +1,216 @@
+From 60ee5b21ce14a7d5c269115b0751b904d0e005ed Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Fri, 28 Feb 2014 14:42:47 +0100
+Subject: [PATCH 049/182] drivers: of: add initialization code for static
+ reserved memory
+
+This patch adds support for static (defined by 'reg' property) reserved
+memory regions declared in device tree.
+
+Memory blocks can be reliably reserved only during early boot. This must
+happen before the whole memory management subsystem is initialized,
+because we need to ensure that the given contiguous blocks are not yet
+allocated by kernel. Also it must happen before kernel mappings for the
+whole low memory are created, to ensure that there will be no mappings
+(for reserved blocks). Typically, all this happens before device tree
+structures are unflattened, so we need to get reserved memory layout
+directly from fdt.
+
+Based on previous code provided by Josh Cartwright <joshc@codeaurora.org>
+
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ drivers/of/fdt.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/of_fdt.h | 4 ++
+ 2 files changed, 135 insertions(+)
+
+diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
+index 758b4f8..819e112 100644
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -15,6 +15,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/of_fdt.h>
++#include <linux/sizes.h>
+ #include <linux/string.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+@@ -440,6 +441,118 @@ struct boot_param_header *initial_boot_params;
+ #ifdef CONFIG_OF_EARLY_FLATTREE
+
+ /**
++ * res_mem_reserve_reg() - reserve all memory described in 'reg' property
++ */
++static int __init __reserved_mem_reserve_reg(unsigned long node,
++ const char *uname)
++{
++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
++ phys_addr_t base, size;
++ unsigned long len;
++ __be32 *prop;
++ int nomap;
++
++ prop = of_get_flat_dt_prop(node, "reg", &len);
++ if (!prop)
++ return -ENOENT;
++
++ if (len && len % t_len != 0) {
++ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
++ uname);
++ return -EINVAL;
++ }
++
++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
++
++ while (len >= t_len) {
++ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
++ size = dt_mem_next_cell(dt_root_size_cells, &prop);
++
++ if (base && size &&
++ early_init_dt_reserve_memory_arch(base, size, nomap) == 0)
++ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n",
++ uname, &base, (unsigned long)size / SZ_1M);
++ else
++ pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n",
++ uname, &base, (unsigned long)size / SZ_1M);
++
++ len -= t_len;
++ }
++ return 0;
++}
++
++/**
++ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
++ * in /reserved-memory matches the values supported by the current implementation,
++ * also check if ranges property has been provided
++ */
++static int __reserved_mem_check_root(unsigned long node)
++{
++ __be32 *prop;
++
++ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
++ if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
++ return -EINVAL;
++
++ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
++ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
++ return -EINVAL;
++
++ prop = of_get_flat_dt_prop(node, "ranges", NULL);
++ if (!prop)
++ return -EINVAL;
++ return 0;
++}
++
++/**
++ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
++ */
++static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
++ int depth, void *data)
++{
++ static int found;
++ const char *status;
++
++ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
++ if (__reserved_mem_check_root(node) != 0) {
++ pr_err("Reserved memory: unsupported node format, ignoring\n");
++ /* break scan */
++ return 1;
++ }
++ found = 1;
++ /* scan next node */
++ return 0;
++ } else if (!found) {
++ /* scan next node */
++ return 0;
++ } else if (found && depth < 2) {
++ /* scanning of /reserved-memory has been finished */
++ return 1;
++ }
++
++ status = of_get_flat_dt_prop(node, "status", NULL);
++ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
++ return 0;
++
++ __reserved_mem_reserve_reg(node, uname);
++
++ /* scan next node */
++ return 0;
++}
++
++/**
++ * early_init_fdt_scan_reserved_mem() - create reserved memory regions
++ *
++ * This function grabs memory from early allocator for device exclusive use
++ * defined in device tree structures. It should be called by arch specific code
++ * once the early allocator (i.e. memblock) has been fully activated.
++ */
++void __init early_init_fdt_scan_reserved_mem(void)
++{
++ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
++}
++
++/**
+ * of_scan_flat_dt - scan flattened tree blob and call callback on each.
+ * @it: callback function
+ * @data: context data pointer
+@@ -856,6 +969,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
+ memblock_add(base, size);
+ }
+
++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
++ phys_addr_t size, bool nomap)
++{
++ if (memblock_is_region_reserved(base, size))
++ return -EBUSY;
++ if (nomap)
++ return memblock_remove(base, size);
++ return memblock_reserve(base, size);
++}
++
+ /*
+ * called from unflatten_device_tree() to bootstrap devicetree itself
+ * Architectures can override this definition if memblock isn't used
+@@ -864,6 +987,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
+ {
+ return __va(memblock_alloc(size, align));
+ }
++#else
++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
++ phys_addr_t size, bool nomap)
++{
++ pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
++ base, size, nomap ? " (nomap)" : "");
++ return -ENOSYS;
++}
+ #endif
+
+ bool __init early_init_dt_scan(void *params)
+diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
+index 2b77058..ddd7219 100644
+--- a/include/linux/of_fdt.h
++++ b/include/linux/of_fdt.h
+@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
+ int depth, void *data);
+ extern int early_init_dt_scan_memory(unsigned long node, const char *uname,
+ int depth, void *data);
++extern void early_init_fdt_scan_reserved_mem(void);
+ extern void early_init_dt_add_memory_arch(u64 base, u64 size);
++extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
++ bool no_map);
+ extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align);
+ extern u64 dt_mem_next_cell(int s, __be32 **cellp);
+
+@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void);
+ extern void early_init_devtree(void *);
+ extern void early_get_first_memblock_info(void *, phys_addr_t *);
+ #else /* CONFIG_OF_FLATTREE */
++static inline void early_init_fdt_scan_reserved_mem(void) {}
+ static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
+ static inline void unflatten_device_tree(void) {}
+ static inline void unflatten_and_copy_device_tree(void) {}
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch
new file mode 100644
index 0000000..b23867b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch
@@ -0,0 +1,331 @@
+From 968b497e1027ec78e986370976c76e1652aa0459 Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Fri, 28 Feb 2014 14:42:48 +0100
+Subject: [PATCH 050/182] drivers: of: add initialization code for dynamic
+ reserved memory
+
+This patch adds support for dynamically allocated reserved memory regions
+declared in device tree. Such regions are defined by 'size', 'alignment'
+and 'alloc-ranges' properties.
+
+Based on previous code provided by Josh Cartwright <joshc@codeaurora.org>
+
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ drivers/of/Kconfig | 6 ++
+ drivers/of/Makefile | 1 +
+ drivers/of/fdt.c | 13 ++-
+ drivers/of/of_reserved_mem.c | 188 +++++++++++++++++++++++++++++++++++++++
+ include/linux/of_reserved_mem.h | 21 +++++
+ 5 files changed, 227 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/of/of_reserved_mem.c
+ create mode 100644 include/linux/of_reserved_mem.h
+
+diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
+index c6973f1..30a7d87 100644
+--- a/drivers/of/Kconfig
++++ b/drivers/of/Kconfig
+@@ -75,4 +75,10 @@ config OF_MTD
+ depends on MTD
+ def_bool y
+
++config OF_RESERVED_MEM
++ depends on OF_EARLY_FLATTREE
++ bool
++ help
++ Helpers to allow for reservation of memory regions
++
+ endmenu # OF
+diff --git a/drivers/of/Makefile b/drivers/of/Makefile
+index efd0510..ed9660a 100644
+--- a/drivers/of/Makefile
++++ b/drivers/of/Makefile
+@@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
+ obj-$(CONFIG_OF_PCI) += of_pci.o
+ obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
+ obj-$(CONFIG_OF_MTD) += of_mtd.o
++obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
+index 819e112..510c0d8 100644
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -15,6 +15,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/of_fdt.h>
++#include <linux/of_reserved_mem.h>
+ #include <linux/sizes.h>
+ #include <linux/string.h>
+ #include <linux/errno.h>
+@@ -450,7 +451,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
+ phys_addr_t base, size;
+ unsigned long len;
+ __be32 *prop;
+- int nomap;
++ int nomap, first = 1;
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop)
+@@ -477,6 +478,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
+ uname, &base, (unsigned long)size / SZ_1M);
+
+ len -= t_len;
++ if (first) {
++ fdt_reserved_mem_save_node(node, uname, base, size);
++ first = 0;
++ }
+ }
+ return 0;
+ }
+@@ -512,6 +517,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ {
+ static int found;
+ const char *status;
++ int err;
+
+ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
+ if (__reserved_mem_check_root(node) != 0) {
+@@ -534,7 +540,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
+ return 0;
+
+- __reserved_mem_reserve_reg(node, uname);
++ err = __reserved_mem_reserve_reg(node, uname);
++ if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
++ fdt_reserved_mem_save_node(node, uname, 0, 0);
+
+ /* scan next node */
+ return 0;
+@@ -550,6 +558,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ void __init early_init_fdt_scan_reserved_mem(void)
+ {
+ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
++ fdt_init_reserved_mem();
+ }
+
+ /**
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+new file mode 100644
+index 0000000..69b8117
+--- /dev/null
++++ b/drivers/of/of_reserved_mem.c
+@@ -0,0 +1,188 @@
++/*
++ * Device tree based initialization code for reserved memory.
++ *
++ * Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
++ * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd.
++ * http://www.samsung.com
++ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
++ * Author: Josh Cartwright <joshc@codeaurora.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License or (at your optional) any later version of the license.
++ */
++
++#include <linux/err.h>
++#include <linux/of.h>
++#include <linux/of_fdt.h>
++#include <linux/of_platform.h>
++#include <linux/mm.h>
++#include <linux/sizes.h>
++#include <linux/of_reserved_mem.h>
++
++#define MAX_RESERVED_REGIONS 16
++static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
++static int reserved_mem_count;
++
++#if defined(CONFIG_HAVE_MEMBLOCK)
++#include <linux/memblock.h>
++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
++ phys_addr_t *res_base)
++{
++ /*
++ * We use __memblock_alloc_base() because memblock_alloc_base()
++ * panic()s on allocation failure.
++ */
++ phys_addr_t base = __memblock_alloc_base(size, align, end);
++ if (!base)
++ return -ENOMEM;
++
++ /*
++ * Check if the allocated region fits in to start..end window
++ */
++ if (base < start) {
++ memblock_free(base, size);
++ return -ENOMEM;
++ }
++
++ *res_base = base;
++ if (nomap)
++ return memblock_remove(base, size);
++ return 0;
++}
++#else
++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
++ phys_addr_t *res_base)
++{
++ pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n",
++ size, nomap ? " (nomap)" : "");
++ return -ENOSYS;
++}
++#endif
++
++/**
++ * res_mem_save_node() - save fdt node for second pass initialization
++ */
++void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
++ phys_addr_t base, phys_addr_t size)
++{
++ struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
++
++ if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
++ pr_err("Reserved memory: not enough space all defined regions.\n");
++ return;
++ }
++
++ rmem->fdt_node = node;
++ rmem->name = uname;
++ rmem->base = base;
++ rmem->size = size;
++
++ reserved_mem_count++;
++ return;
++}
++
++/**
++ * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align'
++ * and 'alloc-ranges' properties
++ */
++static int __init __reserved_mem_alloc_size(unsigned long node,
++ const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
++{
++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
++ phys_addr_t start = 0, end = 0;
++ phys_addr_t base = 0, align = 0, size;
++ unsigned long len;
++ __be32 *prop;
++ int nomap;
++ int ret;
++
++ prop = of_get_flat_dt_prop(node, "size", &len);
++ if (!prop)
++ return -EINVAL;
++
++ if (len != dt_root_size_cells * sizeof(__be32)) {
++ pr_err("Reserved memory: invalid size property in '%s' node.\n",
++ uname);
++ return -EINVAL;
++ }
++ size = dt_mem_next_cell(dt_root_size_cells, &prop);
++
++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
++
++ prop = of_get_flat_dt_prop(node, "alignment", &len);
++ if (prop) {
++ if (len != dt_root_addr_cells * sizeof(__be32)) {
++ pr_err("Reserved memory: invalid alignment property in '%s' node.\n",
++ uname);
++ return -EINVAL;
++ }
++ align = dt_mem_next_cell(dt_root_addr_cells, &prop);
++ }
++
++ prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
++ if (prop) {
++
++ if (len % t_len != 0) {
++ pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n",
++ uname);
++ return -EINVAL;
++ }
++
++ base = 0;
++
++ while (len > 0) {
++ start = dt_mem_next_cell(dt_root_addr_cells, &prop);
++ end = start + dt_mem_next_cell(dt_root_size_cells,
++ &prop);
++
++ ret = early_init_dt_alloc_reserved_memory_arch(size,
++ align, start, end, nomap, &base);
++ if (ret == 0) {
++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
++ uname, &base,
++ (unsigned long)size / SZ_1M);
++ break;
++ }
++ len -= t_len;
++ }
++
++ } else {
++ ret = early_init_dt_alloc_reserved_memory_arch(size, align,
++ 0, 0, nomap, &base);
++ if (ret == 0)
++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
++ uname, &base, (unsigned long)size / SZ_1M);
++ }
++
++ if (base == 0) {
++ pr_info("Reserved memory: failed to allocate memory for node '%s'\n",
++ uname);
++ return -ENOMEM;
++ }
++
++ *res_base = base;
++ *res_size = size;
++
++ return 0;
++}
++
++/**
++ * fdt_init_reserved_mem - allocate and init all saved reserved memory regions
++ */
++void __init fdt_init_reserved_mem(void)
++{
++ int i;
++ for (i = 0; i < reserved_mem_count; i++) {
++ struct reserved_mem *rmem = &reserved_mem[i];
++ unsigned long node = rmem->fdt_node;
++ int err = 0;
++
++ if (rmem->size == 0)
++ err = __reserved_mem_alloc_size(node, rmem->name,
++ &rmem->base, &rmem->size);
++ }
++}
+diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
+new file mode 100644
+index 0000000..89226ed
+--- /dev/null
++++ b/include/linux/of_reserved_mem.h
+@@ -0,0 +1,21 @@
++#ifndef __OF_RESERVED_MEM_H
++#define __OF_RESERVED_MEM_H
++
++struct reserved_mem {
++ const char *name;
++ unsigned long fdt_node;
++ phys_addr_t base;
++ phys_addr_t size;
++};
++
++#ifdef CONFIG_OF_RESERVED_MEM
++void fdt_init_reserved_mem(void);
++void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
++ phys_addr_t base, phys_addr_t size);
++#else
++static inline void fdt_init_reserved_mem(void) { }
++static inline void fdt_reserved_mem_save_node(unsigned long node,
++ const char *uname, phys_addr_t base, phys_addr_t size) { }
++#endif
++
++#endif /* __OF_RESERVED_MEM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch
new file mode 100644
index 0000000..ce01a8f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch
@@ -0,0 +1,156 @@
+From fc9e25ed9db2fcfbf16a9cbbf5a1f449da78f1d9 Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Fri, 28 Feb 2014 14:42:49 +0100
+Subject: [PATCH 051/182] drivers: of: add support for custom reserved memory
+ drivers
+
+Add support for custom reserved memory drivers. Call their init() function
+for each reserved region and prepare for using operations provided by them
+with by the reserved_mem->ops array.
+
+Based on previous code provided by Josh Cartwright <joshc@codeaurora.org>
+
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ drivers/of/of_reserved_mem.c | 29 +++++++++++++++++++++++++++++
+ include/asm-generic/vmlinux.lds.h | 11 +++++++++++
+ include/linux/of_reserved_mem.h | 32 ++++++++++++++++++++++++++++++++
+ 3 files changed, 72 insertions(+)
+
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+index 69b8117..daaaf93 100644
+--- a/drivers/of/of_reserved_mem.c
++++ b/drivers/of/of_reserved_mem.c
+@@ -170,6 +170,33 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
+ return 0;
+ }
+
++static const struct of_device_id __rmem_of_table_sentinel
++ __used __section(__reservedmem_of_table_end);
++
++/**
++ * res_mem_init_node() - call region specific reserved memory init code
++ */
++static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
++{
++ extern const struct of_device_id __reservedmem_of_table[];
++ const struct of_device_id *i;
++
++ for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
++ reservedmem_of_init_fn initfn = i->data;
++ const char *compat = i->compatible;
++
++ if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
++ continue;
++
++ if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) {
++ pr_info("Reserved memory: initialized node %s, compatible id %s\n",
++ rmem->name, compat);
++ return 0;
++ }
++ }
++ return -ENOENT;
++}
++
+ /**
+ * fdt_init_reserved_mem - allocate and init all saved reserved memory regions
+ */
+@@ -184,5 +211,7 @@ void __init fdt_init_reserved_mem(void)
+ if (rmem->size == 0)
+ err = __reserved_mem_alloc_size(node, rmem->name,
+ &rmem->base, &rmem->size);
++ if (err == 0)
++ __reserved_mem_init_node(rmem);
+ }
+ }
+diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
+index bd02ca7..146e4ff 100644
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -167,6 +167,16 @@
+ #define CLK_OF_TABLES()
+ #endif
+
++#ifdef CONFIG_OF_RESERVED_MEM
++#define RESERVEDMEM_OF_TABLES() \
++ . = ALIGN(8); \
++ VMLINUX_SYMBOL(__reservedmem_of_table) = .; \
++ *(__reservedmem_of_table) \
++ *(__reservedmem_of_table_end)
++#else
++#define RESERVEDMEM_OF_TABLES()
++#endif
++
+ #ifdef CONFIG_SMP
+ #define CPU_METHOD_OF_TABLES() . = ALIGN(8); \
+ VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \
+@@ -499,6 +509,7 @@
+ TRACE_SYSCALLS() \
+ MEM_DISCARD(init.rodata) \
+ CLK_OF_TABLES() \
++ RESERVEDMEM_OF_TABLES() \
+ CLKSRC_OF_TABLES() \
+ CPU_METHOD_OF_TABLES() \
+ KERNEL_DTB() \
+diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
+index 89226ed..9b1fbb7 100644
+--- a/include/linux/of_reserved_mem.h
++++ b/include/linux/of_reserved_mem.h
+@@ -1,21 +1,53 @@
+ #ifndef __OF_RESERVED_MEM_H
+ #define __OF_RESERVED_MEM_H
+
++struct device;
++struct of_phandle_args;
++struct reserved_mem_ops;
++
+ struct reserved_mem {
+ const char *name;
+ unsigned long fdt_node;
++ const struct reserved_mem_ops *ops;
+ phys_addr_t base;
+ phys_addr_t size;
++ void *priv;
++};
++
++struct reserved_mem_ops {
++ void (*device_init)(struct reserved_mem *rmem,
++ struct device *dev);
++ void (*device_release)(struct reserved_mem *rmem,
++ struct device *dev);
+ };
+
++typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem,
++ unsigned long node, const char *uname);
++
+ #ifdef CONFIG_OF_RESERVED_MEM
+ void fdt_init_reserved_mem(void);
+ void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
+ phys_addr_t base, phys_addr_t size);
++
++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
++ static const struct of_device_id __reservedmem_of_table_##name \
++ __used __section(__reservedmem_of_table) \
++ = { .compatible = compat, \
++ .data = (init == (reservedmem_of_init_fn)NULL) ? \
++ init : init }
++
+ #else
+ static inline void fdt_init_reserved_mem(void) { }
+ static inline void fdt_reserved_mem_save_node(unsigned long node,
+ const char *uname, phys_addr_t base, phys_addr_t size) { }
++
++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
++ static const struct of_device_id __reservedmem_of_table_##name \
++ __attribute__((unused)) \
++ = { .compatible = compat, \
++ .data = (init == (reservedmem_of_init_fn)NULL) ? \
++ init : init }
++
+ #endif
+
+ #endif /* __OF_RESERVED_MEM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch
new file mode 100644
index 0000000..8ff5632
--- /dev/null
+++ b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch
@@ -0,0 +1,43 @@
+From 95c831bca9181df62230171fd66286d08ea886a8 Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Fri, 28 Feb 2014 14:42:54 +0100
+Subject: [PATCH 052/182] arm: add support for reserved memory defined by
+ device tree
+
+Enable reserved memory initialization from device tree.
+
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mm/init.c | 2 ++
+ 2 files changed, 3 insertions(+)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index d02ce70..4332e8d 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1922,6 +1922,7 @@ config USE_OF
+ select IRQ_DOMAIN
+ select OF
+ select OF_EARLY_FLATTREE
++ select OF_RESERVED_MEM
+ help
+ Include support for flattened device tree machine descriptions.
+
+diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
+index 804d615..2a77ba8 100644
+--- a/arch/arm/mm/init.c
++++ b/arch/arm/mm/init.c
+@@ -323,6 +323,8 @@ void __init arm_memblock_init(struct meminfo *mi,
+ if (mdesc->reserve)
+ mdesc->reserve();
+
++ early_init_fdt_scan_reserved_mem();
++
+ /*
+ * reserve memory for DMA contigouos allocations,
+ * must come from DMA area inside low memory
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch
new file mode 100644
index 0000000..b499e17
--- /dev/null
+++ b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch
@@ -0,0 +1,164 @@
+From 060a8e3db75ef98fb296b03b8fb0cdfcbcc76c6e Mon Sep 17 00:00:00 2001
+From: Grant Likely <grant.likely@linaro.org>
+Date: Fri, 28 Feb 2014 14:42:46 +0100
+Subject: [PATCH 053/182] of: document bindings for reserved-memory nodes
+
+Reserved memory nodes allow for the reservation of static (fixed
+address) regions, or dynamically allocated regions for a specific
+purpose.
+
+[joshc: Based on binding document proposed (in non-patch form) here:
+ http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca
+ adapted to support #memory-region-cells]
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+[mszyprow: removed #memory-region-cells property]
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+[grant.likely: removed residual #memory-region-cells example]
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ .../bindings/reserved-memory/reserved-memory.txt | 133 ++++++++++++++++++++
+ 1 file changed, 133 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+
+diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+new file mode 100644
+index 0000000..3da0ebd
+--- /dev/null
++++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+@@ -0,0 +1,133 @@
++*** Reserved memory regions ***
++
++Reserved memory is specified as a node under the /reserved-memory node.
++The operating system shall exclude reserved memory from normal usage
++one can create child nodes describing particular reserved (excluded from
++normal use) memory regions. Such memory regions are usually designed for
++the special usage by various device drivers.
++
++Parameters for each memory region can be encoded into the device tree
++with the following nodes:
++
++/reserved-memory node
++---------------------
++#address-cells, #size-cells (required) - standard definition
++ - Should use the same values as the root node
++ranges (required) - standard definition
++ - Should be empty
++
++/reserved-memory/ child nodes
++-----------------------------
++Each child of the reserved-memory node specifies one or more regions of
++reserved memory. Each child node may either use a 'reg' property to
++specify a specific range of reserved memory, or a 'size' property with
++optional constraints to request a dynamically allocated block of memory.
++
++Following the generic-names recommended practice, node names should
++reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit
++address (@<address>) should be appended to the name if the node is a
++static allocation.
++
++Properties:
++Requires either a) or b) below.
++a) static allocation
++ reg (required) - standard definition
++b) dynamic allocation
++ size (required) - length based on parent's #size-cells
++ - Size in bytes of memory to reserve.
++ alignment (optional) - length based on parent's #size-cells
++ - Address boundary for alignment of allocation.
++ alloc-ranges (optional) - prop-encoded-array (address, length pairs).
++ - Specifies regions of memory that are
++ acceptable to allocate from.
++
++If both reg and size are present, then the reg property takes precedence
++and size is ignored.
++
++Additional properties:
++compatible (optional) - standard definition
++ - may contain the following strings:
++ - shared-dma-pool: This indicates a region of memory meant to be
++ used as a shared pool of DMA buffers for a set of devices. It can
++ be used by an operating system to instanciate the necessary pool
++ management subsystem if necessary.
++ - vendor specific string in the form <vendor>,[<device>-]<usage>
++no-map (optional) - empty property
++ - Indicates the operating system must not create a virtual mapping
++ of the region as part of its standard mapping of system memory,
++ nor permit speculative access to it under any circumstances other
++ than under the control of the device driver using the region.
++reusable (optional) - empty property
++ - The operating system can use the memory in this region with the
++ limitation that the device driver(s) owning the region need to be
++ able to reclaim it back. Typically that means that the operating
++ system can use that region to store volatile or cached data that
++ can be otherwise regenerated or migrated elsewhere.
++
++Linux implementation note:
++- If a "linux,cma-default" property is present, then Linux will use the
++ region for the default pool of the contiguous memory allocator.
++
++Device node references to reserved memory
++-----------------------------------------
++Regions in the /reserved-memory node may be referenced by other device
++nodes by adding a memory-region property to the device node.
++
++memory-region (optional) - phandle, specifier pairs to children of /reserved-memory
++
++Example
++-------
++This example defines 3 contiguous regions are defined for Linux kernel:
++one default of all device drivers (named linux,cma@72000000 and 64MiB in size),
++one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and
++one for multimedia processing (named multimedia-memory@77000000, 64MiB).
++
++/ {
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ memory {
++ reg = <0x40000000 0x40000000>;
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ /* global autoconfigured region for contiguous allocations */
++ linux,cma {
++ compatible = "shared-dma-pool";
++ reusable;
++ size = <0x4000000>;
++ alignment = <0x2000>;
++ linux,cma-default;
++ };
++
++ display_reserved: framebuffer@78000000 {
++ reg = <0x78000000 0x800000>;
++ };
++
++ multimedia_reserved: multimedia@77000000 {
++ compatible = "acme,multimedia-memory";
++ reg = <0x77000000 0x4000000>;
++ };
++ };
++
++ /* ... */
++
++ fb0: video@12300000 {
++ memory-region = <&display_reserved>;
++ /* ... */
++ };
++
++ scaler: scaler@12500000 {
++ memory-region = <&multimedia_reserved>;
++ /* ... */
++ };
++
++ codec: codec@12600000 {
++ memory-region = <&multimedia_reserved>;
++ /* ... */
++ };
++};
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch
new file mode 100644
index 0000000..a5423d8
--- /dev/null
+++ b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch
@@ -0,0 +1,35 @@
+From 884ab569aec3a0847702dff0dad133bfb67e234c Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Thu, 13 Mar 2014 16:36:36 -0500
+Subject: [PATCH 054/182] of: only scan for reserved mem when fdt present
+
+When the reserved memory patches hit -next, several legacy (non-DT) boot
+failures were detected and bisected down to that commit. There needs to
+be some sanity checking whether a DT is even present before parsing the
+reserved ranges.
+
+Reported-by: Kevin Hilman <khilman@linaro.org>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Tested-by: Kevin Hilman <khilman@linaro.org>
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+---
+ drivers/of/fdt.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
+index 510c0d8..501bc83 100644
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -557,6 +557,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ */
+ void __init early_init_fdt_scan_reserved_mem(void)
+ {
++ if (!initial_boot_params)
++ return;
++
+ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
+ fdt_init_reserved_mem();
+ }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch
new file mode 100644
index 0000000..8b59438
--- /dev/null
+++ b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch
@@ -0,0 +1,943 @@
+From 1b0018dfd6295cbcc87738601b84bf49f3004419 Mon Sep 17 00:00:00 2001
+From: Kenneth Heitke <kheitke@codeaurora.org>
+Date: Wed, 12 Feb 2014 13:44:22 -0600
+Subject: [PATCH 055/182] spmi: Linux driver framework for SPMI
+
+System Power Management Interface (SPMI) is a specification
+developed by the MIPI (Mobile Industry Process Interface) Alliance
+optimized for the real time control of Power Management ICs (PMIC).
+
+SPMI is a two-wire serial interface that supports up to 4 master
+devices and up to 16 logical slaves.
+
+The framework supports message APIs, multiple busses (1 controller
+per bus) and multiple clients/slave devices per controller.
+
+Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org>
+Signed-off-by: Michael Bohan <mbohan@codeaurora.org>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/Kconfig | 2 +
+ drivers/Makefile | 1 +
+ drivers/spmi/Kconfig | 9 +
+ drivers/spmi/Makefile | 4 +
+ drivers/spmi/spmi.c | 609 +++++++++++++++++++++++++++++++++++++++
+ include/dt-bindings/spmi/spmi.h | 18 ++
+ include/linux/mod_devicetable.h | 8 +
+ include/linux/spmi.h | 191 ++++++++++++
+ 8 files changed, 842 insertions(+)
+ create mode 100644 drivers/spmi/Kconfig
+ create mode 100644 drivers/spmi/Makefile
+ create mode 100644 drivers/spmi/spmi.c
+ create mode 100644 include/dt-bindings/spmi/spmi.h
+ create mode 100644 include/linux/spmi.h
+
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index b3138fb..e0a4ae6 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
+
+ source "drivers/spi/Kconfig"
+
++source "drivers/spmi/Kconfig"
++
+ source "drivers/hsi/Kconfig"
+
+ source "drivers/pps/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 8e3b8b0..3d6de8b 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/
+ obj-$(CONFIG_TARGET_CORE) += target/
+ obj-$(CONFIG_MTD) += mtd/
+ obj-$(CONFIG_SPI) += spi/
++obj-$(CONFIG_SPMI) += spmi/
+ obj-y += hsi/
+ obj-y += net/
+ obj-$(CONFIG_ATM) += atm/
+diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
+new file mode 100644
+index 0000000..1dbfee0
+--- /dev/null
++++ b/drivers/spmi/Kconfig
+@@ -0,0 +1,9 @@
++#
++# SPMI driver configuration
++#
++menuconfig SPMI
++ tristate "SPMI support"
++ help
++ SPMI (System Power Management Interface) is a two-wire
++ serial interface between baseband and application processors
++ and Power Management Integrated Circuits (PMIC).
+diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
+new file mode 100644
+index 0000000..1de1acd
+--- /dev/null
++++ b/drivers/spmi/Makefile
+@@ -0,0 +1,4 @@
++#
++# Makefile for kernel SPMI framework.
++#
++obj-$(CONFIG_SPMI) += spmi.o
+diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
+new file mode 100644
+index 0000000..6122c8f
+--- /dev/null
++++ b/drivers/spmi/spmi.c
+@@ -0,0 +1,609 @@
++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/idr.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/spmi.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++
++#include <dt-bindings/spmi/spmi.h>
++
++static DEFINE_IDA(ctrl_ida);
++
++static void spmi_dev_release(struct device *dev)
++{
++ struct spmi_device *sdev = to_spmi_device(dev);
++ kfree(sdev);
++}
++
++static const struct device_type spmi_dev_type = {
++ .release = spmi_dev_release,
++};
++
++static void spmi_ctrl_release(struct device *dev)
++{
++ struct spmi_controller *ctrl = to_spmi_controller(dev);
++ ida_simple_remove(&ctrl_ida, ctrl->nr);
++ kfree(ctrl);
++}
++
++static const struct device_type spmi_ctrl_type = {
++ .release = spmi_ctrl_release,
++};
++
++#ifdef CONFIG_PM_RUNTIME
++static int spmi_runtime_suspend(struct device *dev)
++{
++ struct spmi_device *sdev = to_spmi_device(dev);
++ int err;
++
++ err = pm_generic_runtime_suspend(dev);
++ if (err)
++ return err;
++
++ return spmi_command_sleep(sdev);
++}
++
++static int spmi_runtime_resume(struct device *dev)
++{
++ struct spmi_device *sdev = to_spmi_device(dev);
++ int err;
++
++ err = spmi_command_wakeup(sdev);
++ if (err)
++ return err;
++
++ return pm_generic_runtime_resume(dev);
++}
++#endif
++
++static const struct dev_pm_ops spmi_pm_ops = {
++ SET_RUNTIME_PM_OPS(
++ spmi_runtime_suspend,
++ spmi_runtime_resume,
++ NULL
++ )
++};
++
++static int spmi_device_match(struct device *dev, struct device_driver *drv)
++{
++ if (of_driver_match_device(dev, drv))
++ return 1;
++
++ if (drv->name)
++ return strncmp(dev_name(dev), drv->name,
++ SPMI_NAME_SIZE) == 0;
++
++ return 0;
++}
++
++/**
++ * spmi_device_add() - add a device previously constructed via spmi_device_alloc()
++ * @sdev: spmi_device to be added
++ */
++int spmi_device_add(struct spmi_device *sdev)
++{
++ struct spmi_controller *ctrl = sdev->ctrl;
++ int err;
++
++ dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid);
++
++ err = device_add(&sdev->dev);
++ if (err < 0) {
++ dev_err(&sdev->dev, "Can't add %s, status %d\n",
++ dev_name(&sdev->dev), err);
++ goto err_device_add;
++ }
++
++ dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev));
++
++err_device_add:
++ return err;
++}
++EXPORT_SYMBOL_GPL(spmi_device_add);
++
++/**
++ * spmi_device_remove(): remove an SPMI device
++ * @sdev: spmi_device to be removed
++ */
++void spmi_device_remove(struct spmi_device *sdev)
++{
++ device_unregister(&sdev->dev);
++}
++EXPORT_SYMBOL_GPL(spmi_device_remove);
++
++static inline int
++spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid)
++{
++ if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type)
++ return -EINVAL;
++
++ return ctrl->cmd(ctrl, opcode, sid);
++}
++
++static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode,
++ u8 sid, u16 addr, u8 *buf, size_t len)
++{
++ if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type)
++ return -EINVAL;
++
++ return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len);
++}
++
++static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode,
++ u8 sid, u16 addr, const u8 *buf, size_t len)
++{
++ if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type)
++ return -EINVAL;
++
++ return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len);
++}
++
++/**
++ * spmi_register_read() - register read
++ * @sdev: SPMI device.
++ * @addr: slave register address (5-bit address).
++ * @buf: buffer to be populated with data from the Slave.
++ *
++ * Reads 1 byte of data from a Slave device register.
++ */
++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf)
++{
++ /* 5-bit register address */
++ if (addr > 0x1F)
++ return -EINVAL;
++
++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr,
++ buf, 1);
++}
++EXPORT_SYMBOL_GPL(spmi_register_read);
++
++/**
++ * spmi_ext_register_read() - extended register read
++ * @sdev: SPMI device.
++ * @addr: slave register address (8-bit address).
++ * @buf: buffer to be populated with data from the Slave.
++ * @len: the request number of bytes to read (up to 16 bytes).
++ *
++ * Reads up to 16 bytes of data from the extended register space on a
++ * Slave device.
++ */
++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf,
++ size_t len)
++{
++ /* 8-bit register address, up to 16 bytes */
++ if (len == 0 || len > 16)
++ return -EINVAL;
++
++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr,
++ buf, len);
++}
++EXPORT_SYMBOL_GPL(spmi_ext_register_read);
++
++/**
++ * spmi_ext_register_readl() - extended register read long
++ * @sdev: SPMI device.
++ * @addr: slave register address (16-bit address).
++ * @buf: buffer to be populated with data from the Slave.
++ * @len: the request number of bytes to read (up to 8 bytes).
++ *
++ * Reads up to 8 bytes of data from the extended register space on a
++ * Slave device using 16-bit address.
++ */
++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf,
++ size_t len)
++{
++ /* 16-bit register address, up to 8 bytes */
++ if (len == 0 || len > 8)
++ return -EINVAL;
++
++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr,
++ buf, len);
++}
++EXPORT_SYMBOL_GPL(spmi_ext_register_readl);
++
++/**
++ * spmi_register_write() - register write
++ * @sdev: SPMI device
++ * @addr: slave register address (5-bit address).
++ * @data: buffer containing the data to be transferred to the Slave.
++ *
++ * Writes 1 byte of data to a Slave device register.
++ */
++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data)
++{
++ /* 5-bit register address */
++ if (addr > 0x1F)
++ return -EINVAL;
++
++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr,
++ &data, 1);
++}
++EXPORT_SYMBOL_GPL(spmi_register_write);
++
++/**
++ * spmi_register_zero_write() - register zero write
++ * @sdev: SPMI device.
++ * @data: the data to be written to register 0 (7-bits).
++ *
++ * Writes data to register 0 of the Slave device.
++ */
++int spmi_register_zero_write(struct spmi_device *sdev, u8 data)
++{
++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0,
++ &data, 1);
++}
++EXPORT_SYMBOL_GPL(spmi_register_zero_write);
++
++/**
++ * spmi_ext_register_write() - extended register write
++ * @sdev: SPMI device.
++ * @addr: slave register address (8-bit address).
++ * @buf: buffer containing the data to be transferred to the Slave.
++ * @len: the request number of bytes to read (up to 16 bytes).
++ *
++ * Writes up to 16 bytes of data to the extended register space of a
++ * Slave device.
++ */
++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf,
++ size_t len)
++{
++ /* 8-bit register address, up to 16 bytes */
++ if (len == 0 || len > 16)
++ return -EINVAL;
++
++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr,
++ buf, len);
++}
++EXPORT_SYMBOL_GPL(spmi_ext_register_write);
++
++/**
++ * spmi_ext_register_writel() - extended register write long
++ * @sdev: SPMI device.
++ * @addr: slave register address (16-bit address).
++ * @buf: buffer containing the data to be transferred to the Slave.
++ * @len: the request number of bytes to read (up to 8 bytes).
++ *
++ * Writes up to 8 bytes of data to the extended register space of a
++ * Slave device using 16-bit address.
++ */
++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf,
++ size_t len)
++{
++ /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
++ if (len == 0 || len > 8)
++ return -EINVAL;
++
++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid,
++ addr, buf, len);
++}
++EXPORT_SYMBOL_GPL(spmi_ext_register_writel);
++
++/**
++ * spmi_command_reset() - sends RESET command to the specified slave
++ * @sdev: SPMI device.
++ *
++ * The Reset command initializes the Slave and forces all registers to
++ * their reset values. The Slave shall enter the STARTUP state after
++ * receiving a Reset command.
++ */
++int spmi_command_reset(struct spmi_device *sdev)
++{
++ return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid);
++}
++EXPORT_SYMBOL_GPL(spmi_command_reset);
++
++/**
++ * spmi_command_sleep() - sends SLEEP command to the specified SPMI device
++ * @sdev: SPMI device.
++ *
++ * The Sleep command causes the Slave to enter the user defined SLEEP state.
++ */
++int spmi_command_sleep(struct spmi_device *sdev)
++{
++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid);
++}
++EXPORT_SYMBOL_GPL(spmi_command_sleep);
++
++/**
++ * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device
++ * @sdev: SPMI device.
++ *
++ * The Wakeup command causes the Slave to move from the SLEEP state to
++ * the ACTIVE state.
++ */
++int spmi_command_wakeup(struct spmi_device *sdev)
++{
++ return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid);
++}
++EXPORT_SYMBOL_GPL(spmi_command_wakeup);
++
++/**
++ * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device
++ * @sdev: SPMI device.
++ *
++ * The Shutdown command causes the Slave to enter the SHUTDOWN state.
++ */
++int spmi_command_shutdown(struct spmi_device *sdev)
++{
++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid);
++}
++EXPORT_SYMBOL_GPL(spmi_command_shutdown);
++
++static int spmi_drv_probe(struct device *dev)
++{
++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
++ struct spmi_device *sdev = to_spmi_device(dev);
++ int err;
++
++ /* Ensure the slave is in ACTIVE state */
++ err = spmi_command_wakeup(sdev);
++ if (err)
++ goto fail_wakeup;
++
++ pm_runtime_get_noresume(dev);
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
++ err = sdrv->probe(sdev);
++ if (err)
++ goto fail_probe;
++
++ return 0;
++
++fail_probe:
++ pm_runtime_disable(dev);
++ pm_runtime_set_suspended(dev);
++ pm_runtime_put_noidle(dev);
++fail_wakeup:
++ return err;
++}
++
++static int spmi_drv_remove(struct device *dev)
++{
++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
++
++ pm_runtime_get_sync(dev);
++ sdrv->remove(to_spmi_device(dev));
++ pm_runtime_put_noidle(dev);
++
++ pm_runtime_disable(dev);
++ pm_runtime_set_suspended(dev);
++ pm_runtime_put_noidle(dev);
++ return 0;
++}
++
++static struct bus_type spmi_bus_type = {
++ .name = "spmi",
++ .match = spmi_device_match,
++ .pm = &spmi_pm_ops,
++ .probe = spmi_drv_probe,
++ .remove = spmi_drv_remove,
++};
++
++/**
++ * spmi_controller_alloc() - Allocate a new SPMI device
++ * @ctrl: associated controller
++ *
++ * Caller is responsible for either calling spmi_device_add() to add the
++ * newly allocated controller, or calling spmi_device_put() to discard it.
++ */
++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl)
++{
++ struct spmi_device *sdev;
++
++ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
++ if (!sdev)
++ return NULL;
++
++ sdev->ctrl = ctrl;
++ device_initialize(&sdev->dev);
++ sdev->dev.parent = &ctrl->dev;
++ sdev->dev.bus = &spmi_bus_type;
++ sdev->dev.type = &spmi_dev_type;
++ return sdev;
++}
++EXPORT_SYMBOL_GPL(spmi_device_alloc);
++
++/**
++ * spmi_controller_alloc() - Allocate a new SPMI controller
++ * @parent: parent device
++ * @size: size of private data
++ *
++ * Caller is responsible for either calling spmi_controller_add() to add the
++ * newly allocated controller, or calling spmi_controller_put() to discard it.
++ * The allocated private data region may be accessed via
++ * spmi_controller_get_drvdata()
++ */
++struct spmi_controller *spmi_controller_alloc(struct device *parent,
++ size_t size)
++{
++ struct spmi_controller *ctrl;
++ int id;
++
++ if (WARN_ON(!parent))
++ return NULL;
++
++ ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
++ if (!ctrl)
++ return NULL;
++
++ device_initialize(&ctrl->dev);
++ ctrl->dev.type = &spmi_ctrl_type;
++ ctrl->dev.bus = &spmi_bus_type;
++ ctrl->dev.parent = parent;
++ ctrl->dev.of_node = parent->of_node;
++ spmi_controller_set_drvdata(ctrl, &ctrl[1]);
++
++ id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL);
++ if (id < 0) {
++ dev_err(parent,
++ "unable to allocate SPMI controller identifier.\n");
++ spmi_controller_put(ctrl);
++ return NULL;
++ }
++
++ ctrl->nr = id;
++ dev_set_name(&ctrl->dev, "spmi-%d", id);
++
++ dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
++ return ctrl;
++}
++EXPORT_SYMBOL_GPL(spmi_controller_alloc);
++
++static void of_spmi_register_devices(struct spmi_controller *ctrl)
++{
++ struct device_node *node;
++ int err;
++
++ if (!ctrl->dev.of_node)
++ return;
++
++ for_each_available_child_of_node(ctrl->dev.of_node, node) {
++ struct spmi_device *sdev;
++ u32 reg[2];
++
++ dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name);
++
++ err = of_property_read_u32_array(node, "reg", reg, 2);
++ if (err) {
++ dev_err(&ctrl->dev,
++ "node %s err (%d) does not have 'reg' property\n",
++ node->full_name, err);
++ continue;
++ }
++
++ if (reg[1] != SPMI_USID) {
++ dev_err(&ctrl->dev,
++ "node %s contains unsupported 'reg' entry\n",
++ node->full_name);
++ continue;
++ }
++
++ if (reg[0] >= SPMI_MAX_SLAVE_ID) {
++ dev_err(&ctrl->dev,
++ "invalid usid on node %s\n",
++ node->full_name);
++ continue;
++ }
++
++ dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]);
++
++ sdev = spmi_device_alloc(ctrl);
++ if (!sdev)
++ continue;
++
++ sdev->dev.of_node = node;
++ sdev->usid = (u8) reg[0];
++
++ err = spmi_device_add(sdev);
++ if (err) {
++ dev_err(&sdev->dev,
++ "failure adding device. status %d\n", err);
++ spmi_device_put(sdev);
++ }
++ }
++}
++
++/**
++ * spmi_controller_add() - Add an SPMI controller
++ * @ctrl: controller to be registered.
++ *
++ * Register a controller previously allocated via spmi_controller_alloc() with
++ * the SPMI core.
++ */
++int spmi_controller_add(struct spmi_controller *ctrl)
++{
++ int ret;
++
++ /* Can't register until after driver model init */
++ if (WARN_ON(!spmi_bus_type.p))
++ return -EAGAIN;
++
++ ret = device_add(&ctrl->dev);
++ if (ret)
++ return ret;
++
++ if (IS_ENABLED(CONFIG_OF))
++ of_spmi_register_devices(ctrl);
++
++ dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n",
++ ctrl->nr, &ctrl->dev);
++
++ return 0;
++};
++EXPORT_SYMBOL_GPL(spmi_controller_add);
++
++/* Remove a device associated with a controller */
++static int spmi_ctrl_remove_device(struct device *dev, void *data)
++{
++ struct spmi_device *spmidev = to_spmi_device(dev);
++ if (dev->type == &spmi_dev_type)
++ spmi_device_remove(spmidev);
++ return 0;
++}
++
++/**
++ * spmi_controller_remove(): remove an SPMI controller
++ * @ctrl: controller to remove
++ *
++ * Remove a SPMI controller. Caller is responsible for calling
++ * spmi_controller_put() to discard the allocated controller.
++ */
++void spmi_controller_remove(struct spmi_controller *ctrl)
++{
++ int dummy;
++
++ if (!ctrl)
++ return;
++
++ dummy = device_for_each_child(&ctrl->dev, NULL,
++ spmi_ctrl_remove_device);
++ device_del(&ctrl->dev);
++}
++EXPORT_SYMBOL_GPL(spmi_controller_remove);
++
++/**
++ * spmi_driver_register() - Register client driver with SPMI core
++ * @sdrv: client driver to be associated with client-device.
++ *
++ * This API will register the client driver with the SPMI framework.
++ * It is typically called from the driver's module-init function.
++ */
++int spmi_driver_register(struct spmi_driver *sdrv)
++{
++ sdrv->driver.bus = &spmi_bus_type;
++ return driver_register(&sdrv->driver);
++}
++EXPORT_SYMBOL_GPL(spmi_driver_register);
++
++static void __exit spmi_exit(void)
++{
++ bus_unregister(&spmi_bus_type);
++}
++module_exit(spmi_exit);
++
++static int __init spmi_init(void)
++{
++ return bus_register(&spmi_bus_type);
++}
++postcore_initcall(spmi_init);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("SPMI module");
++MODULE_ALIAS("platform:spmi");
+diff --git a/include/dt-bindings/spmi/spmi.h b/include/dt-bindings/spmi/spmi.h
+new file mode 100644
+index 0000000..d11e1e5
+--- /dev/null
++++ b/include/dt-bindings/spmi/spmi.h
+@@ -0,0 +1,18 @@
++/* 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 __DT_BINDINGS_SPMI_H
++#define __DT_BINDINGS_SPMI_H
++
++#define SPMI_USID 0
++#define SPMI_GSID 1
++
++#endif
+diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
+index 45e9214..677e474 100644
+--- a/include/linux/mod_devicetable.h
++++ b/include/linux/mod_devicetable.h
+@@ -432,6 +432,14 @@ struct spi_device_id {
+ kernel_ulong_t driver_data; /* Data private to the driver */
+ };
+
++#define SPMI_NAME_SIZE 32
++#define SPMI_MODULE_PREFIX "spmi:"
++
++struct spmi_device_id {
++ char name[SPMI_NAME_SIZE];
++ kernel_ulong_t driver_data; /* Data private to the driver */
++};
++
+ /* dmi */
+ enum dmi_field {
+ DMI_NONE,
+diff --git a/include/linux/spmi.h b/include/linux/spmi.h
+new file mode 100644
+index 0000000..91f5eab
+--- /dev/null
++++ b/include/linux/spmi.h
+@@ -0,0 +1,191 @@
++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#ifndef _LINUX_SPMI_H
++#define _LINUX_SPMI_H
++
++#include <linux/types.h>
++#include <linux/device.h>
++#include <linux/mod_devicetable.h>
++
++/* Maximum slave identifier */
++#define SPMI_MAX_SLAVE_ID 16
++
++/* SPMI Commands */
++#define SPMI_CMD_EXT_WRITE 0x00
++#define SPMI_CMD_RESET 0x10
++#define SPMI_CMD_SLEEP 0x11
++#define SPMI_CMD_SHUTDOWN 0x12
++#define SPMI_CMD_WAKEUP 0x13
++#define SPMI_CMD_AUTHENTICATE 0x14
++#define SPMI_CMD_MSTR_READ 0x15
++#define SPMI_CMD_MSTR_WRITE 0x16
++#define SPMI_CMD_TRANSFER_BUS_OWNERSHIP 0x1A
++#define SPMI_CMD_DDB_MASTER_READ 0x1B
++#define SPMI_CMD_DDB_SLAVE_READ 0x1C
++#define SPMI_CMD_EXT_READ 0x20
++#define SPMI_CMD_EXT_WRITEL 0x30
++#define SPMI_CMD_EXT_READL 0x38
++#define SPMI_CMD_WRITE 0x40
++#define SPMI_CMD_READ 0x60
++#define SPMI_CMD_ZERO_WRITE 0x80
++
++/**
++ * struct spmi_device - Basic representation of an SPMI device
++ * @dev: Driver model representation of the device.
++ * @ctrl: SPMI controller managing the bus hosting this device.
++ * @usid: This devices' Unique Slave IDentifier.
++ */
++struct spmi_device {
++ struct device dev;
++ struct spmi_controller *ctrl;
++ u8 usid;
++};
++
++static inline struct spmi_device *to_spmi_device(struct device *d)
++{
++ return container_of(d, struct spmi_device, dev);
++}
++
++static inline void *spmi_device_get_drvdata(const struct spmi_device *sdev)
++{
++ return dev_get_drvdata(&sdev->dev);
++}
++
++static inline void spmi_device_set_drvdata(struct spmi_device *sdev, void *data)
++{
++ dev_set_drvdata(&sdev->dev, data);
++}
++
++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl);
++
++static inline void spmi_device_put(struct spmi_device *sdev)
++{
++ if (sdev)
++ put_device(&sdev->dev);
++}
++
++int spmi_device_add(struct spmi_device *sdev);
++
++void spmi_device_remove(struct spmi_device *sdev);
++
++/**
++ * struct spmi_controller - interface to the SPMI master controller
++ * @dev: Driver model representation of the device.
++ * @nr: board-specific number identifier for this controller/bus
++ * @cmd: sends a non-data command sequence on the SPMI bus.
++ * @read_cmd: sends a register read command sequence on the SPMI bus.
++ * @write_cmd: sends a register write command sequence on the SPMI bus.
++ */
++struct spmi_controller {
++ struct device dev;
++ unsigned int nr;
++ int (*cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid);
++ int (*read_cmd)(struct spmi_controller *ctrl, u8 opcode,
++ u8 sid, u16 addr, u8 *buf, size_t len);
++ int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode,
++ u8 sid, u16 addr, const u8 *buf, size_t len);
++};
++
++static inline struct spmi_controller *to_spmi_controller(struct device *d)
++{
++ return container_of(d, struct spmi_controller, dev);
++}
++
++static inline
++void *spmi_controller_get_drvdata(const struct spmi_controller *ctrl)
++{
++ return dev_get_drvdata(&ctrl->dev);
++}
++
++static inline void spmi_controller_set_drvdata(struct spmi_controller *ctrl,
++ void *data)
++{
++ dev_set_drvdata(&ctrl->dev, data);
++}
++
++struct spmi_controller *spmi_controller_alloc(struct device *parent,
++ size_t size);
++
++/**
++ * spmi_controller_put() - decrement controller refcount
++ * @ctrl SPMI controller.
++ */
++static inline void spmi_controller_put(struct spmi_controller *ctrl)
++{
++ if (ctrl)
++ put_device(&ctrl->dev);
++}
++
++int spmi_controller_add(struct spmi_controller *ctrl);
++void spmi_controller_remove(struct spmi_controller *ctrl);
++
++/**
++ * struct spmi_driver - SPMI slave device driver
++ * @driver: SPMI device drivers should initialize name and owner field of
++ * this structure.
++ * @probe: binds this driver to a SPMI device.
++ * @remove: unbinds this driver from the SPMI device.
++ * @shutdown: standard shutdown callback used during powerdown/halt.
++ * @suspend: standard suspend callback used during system suspend.
++ * @resume: standard resume callback used during system resume.
++ *
++ * If PM runtime support is desired for a slave, a device driver can call
++ * pm_runtime_put() from their probe() routine (and a balancing
++ * pm_runtime_get() in remove()). PM runtime support for a slave is
++ * implemented by issuing a SLEEP command to the slave on runtime_suspend(),
++ * transitioning the slave into the SLEEP state. On runtime_resume(), a WAKEUP
++ * command is sent to the slave to bring it back to ACTIVE.
++ */
++struct spmi_driver {
++ struct device_driver driver;
++ int (*probe)(struct spmi_device *sdev);
++ void (*remove)(struct spmi_device *sdev);
++};
++
++static inline struct spmi_driver *to_spmi_driver(struct device_driver *d)
++{
++ return container_of(d, struct spmi_driver, driver);
++}
++
++int spmi_driver_register(struct spmi_driver *sdrv);
++
++/**
++ * spmi_driver_unregister() - unregister an SPMI client driver
++ * @sdrv: the driver to unregister
++ */
++static inline void spmi_driver_unregister(struct spmi_driver *sdrv)
++{
++ if (sdrv)
++ driver_unregister(&sdrv->driver);
++}
++
++#define module_spmi_driver(__spmi_driver) \
++ module_driver(__spmi_driver, spmi_driver_register, \
++ spmi_driver_unregister)
++
++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf);
++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf,
++ size_t len);
++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf,
++ size_t len);
++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data);
++int spmi_register_zero_write(struct spmi_device *sdev, u8 data);
++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr,
++ const u8 *buf, size_t len);
++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr,
++ const u8 *buf, size_t len);
++int spmi_command_reset(struct spmi_device *sdev);
++int spmi_command_sleep(struct spmi_device *sdev);
++int spmi_command_wakeup(struct spmi_device *sdev);
++int spmi_command_shutdown(struct spmi_device *sdev);
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch
new file mode 100644
index 0000000..24f841e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch
@@ -0,0 +1,477 @@
+From 1e1f53fbc2848b23af572c16d19e8004f6a7c9c1 Mon Sep 17 00:00:00 2001
+From: Kenneth Heitke <kheitke@codeaurora.org>
+Date: Wed, 12 Feb 2014 13:44:24 -0600
+Subject: [PATCH 056/182] spmi: Add MSM PMIC Arbiter SPMI controller
+
+Qualcomm's PMIC Arbiter SPMI controller functions as a bus master and
+is used to communication with one or more PMIC (slave) devices on the
+SPMI bus. The PMIC Arbiter is actually a hardware wrapper around the
+SPMI controller that provides concurrent and autonomous PMIC access
+to various entities that need to communicate with the PMIC.
+
+The SPMI controller hardware handles all of the SPMI bus activity (bus
+arbitration, sequence start condition, transmission of frames, etc).
+This software driver uses the PMIC Arbiter register interface to
+initiate command sequences on the SPMI bus. The status register is
+read to determine when the command sequence has completed and whether
+or not it completed successfully.
+
+Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spmi/Kconfig | 17 ++
+ drivers/spmi/Makefile | 2 +
+ drivers/spmi/spmi-pmic-arb.c | 405 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 424 insertions(+)
+ create mode 100644 drivers/spmi/spmi-pmic-arb.c
+
+diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
+index 1dbfee0..80b7901 100644
+--- a/drivers/spmi/Kconfig
++++ b/drivers/spmi/Kconfig
+@@ -7,3 +7,20 @@ menuconfig SPMI
+ SPMI (System Power Management Interface) is a two-wire
+ serial interface between baseband and application processors
+ and Power Management Integrated Circuits (PMIC).
++
++if SPMI
++
++config SPMI_MSM_PMIC_ARB
++ tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
++ depends on ARM
++ depends on ARCH_MSM || COMPILE_TEST
++ default ARCH_MSM
++ help
++ If you say yes to this option, support will be included for the
++ built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
++ processors.
++
++ This is required for communicating with Qualcomm PMICs and
++ other devices that have the SPMI interface.
++
++endif
+diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
+index 1de1acd..fc75104 100644
+--- a/drivers/spmi/Makefile
++++ b/drivers/spmi/Makefile
+@@ -2,3 +2,5 @@
+ # Makefile for kernel SPMI framework.
+ #
+ obj-$(CONFIG_SPMI) += spmi.o
++
++obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o
+diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
+new file mode 100644
+index 0000000..2dd27e8
+--- /dev/null
++++ b/drivers/spmi/spmi-pmic-arb.c
+@@ -0,0 +1,405 @@
++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/spmi.h>
++
++/* PMIC Arbiter configuration registers */
++#define PMIC_ARB_VERSION 0x0000
++#define PMIC_ARB_INT_EN 0x0004
++
++/* PMIC Arbiter channel registers */
++#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N)))
++#define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N)))
++#define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N)))
++#define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N)))
++#define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N)))
++#define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N)))
++#define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N)))
++
++/* Interrupt Controller */
++#define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N))))
++#define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N)))
++#define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N)))
++#define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N)))
++
++/* Mapping Table */
++#define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N)))
++#define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF)
++#define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1)
++#define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF)
++#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1)
++#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF)
++
++#define SPMI_MAPPING_TABLE_LEN 255
++#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */
++
++/* Ownership Table */
++#define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N)))
++#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7)
++
++/* Channel Status fields */
++enum pmic_arb_chnl_status {
++ PMIC_ARB_STATUS_DONE = (1 << 0),
++ PMIC_ARB_STATUS_FAILURE = (1 << 1),
++ PMIC_ARB_STATUS_DENIED = (1 << 2),
++ PMIC_ARB_STATUS_DROPPED = (1 << 3),
++};
++
++/* Command register fields */
++#define PMIC_ARB_CMD_MAX_BYTE_COUNT 8
++
++/* Command Opcodes */
++enum pmic_arb_cmd_op_code {
++ PMIC_ARB_OP_EXT_WRITEL = 0,
++ PMIC_ARB_OP_EXT_READL = 1,
++ PMIC_ARB_OP_EXT_WRITE = 2,
++ PMIC_ARB_OP_RESET = 3,
++ PMIC_ARB_OP_SLEEP = 4,
++ PMIC_ARB_OP_SHUTDOWN = 5,
++ PMIC_ARB_OP_WAKEUP = 6,
++ PMIC_ARB_OP_AUTHENTICATE = 7,
++ PMIC_ARB_OP_MSTR_READ = 8,
++ PMIC_ARB_OP_MSTR_WRITE = 9,
++ PMIC_ARB_OP_EXT_READ = 13,
++ PMIC_ARB_OP_WRITE = 14,
++ PMIC_ARB_OP_READ = 15,
++ PMIC_ARB_OP_ZERO_WRITE = 16,
++};
++
++/* Maximum number of support PMIC peripherals */
++#define PMIC_ARB_MAX_PERIPHS 256
++#define PMIC_ARB_PERIPH_ID_VALID (1 << 15)
++#define PMIC_ARB_TIMEOUT_US 100
++#define PMIC_ARB_MAX_TRANS_BYTES (8)
++
++#define PMIC_ARB_APID_MASK 0xFF
++#define PMIC_ARB_PPID_MASK 0xFFF
++
++/* interrupt enable bit */
++#define SPMI_PIC_ACC_ENABLE_BIT BIT(0)
++
++/**
++ * spmi_pmic_arb_dev - SPMI PMIC Arbiter object
++ *
++ * @base: address of the PMIC Arbiter core registers.
++ * @intr: address of the SPMI interrupt control registers.
++ * @cnfg: address of the PMIC Arbiter configuration registers.
++ * @lock: lock to synchronize accesses.
++ * @channel: which channel to use for accesses.
++ */
++struct spmi_pmic_arb_dev {
++ void __iomem *base;
++ void __iomem *intr;
++ void __iomem *cnfg;
++ raw_spinlock_t lock;
++ u8 channel;
++};
++
++static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
++{
++ return readl_relaxed(dev->base + offset);
++}
++
++static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev,
++ u32 offset, u32 val)
++{
++ writel_relaxed(val, dev->base + offset);
++}
++
++/**
++ * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
++ * @bc: byte count -1. range: 0..3
++ * @reg: register's address
++ * @buf: output parameter, length must be bc + 1
++ */
++static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
++{
++ u32 data = pmic_arb_base_read(dev, reg);
++ memcpy(buf, &data, (bc & 3) + 1);
++}
++
++/**
++ * pa_write_data: write 1..4 bytes from buf to pmic-arb's register
++ * @bc: byte-count -1. range: 0..3.
++ * @reg: register's address.
++ * @buf: buffer to write. length must be bc + 1.
++ */
++static void
++pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc)
++{
++ u32 data = 0;
++ memcpy(&data, buf, (bc & 3) + 1);
++ pmic_arb_base_write(dev, reg, data);
++}
++
++static int pmic_arb_wait_for_done(struct spmi_controller *ctrl)
++{
++ struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl);
++ u32 status = 0;
++ u32 timeout = PMIC_ARB_TIMEOUT_US;
++ u32 offset = PMIC_ARB_STATUS(dev->channel);
++
++ while (timeout--) {
++ status = pmic_arb_base_read(dev, offset);
++
++ if (status & PMIC_ARB_STATUS_DONE) {
++ if (status & PMIC_ARB_STATUS_DENIED) {
++ dev_err(&ctrl->dev,
++ "%s: transaction denied (0x%x)\n",
++ __func__, status);
++ return -EPERM;
++ }
++
++ if (status & PMIC_ARB_STATUS_FAILURE) {
++ dev_err(&ctrl->dev,
++ "%s: transaction failed (0x%x)\n",
++ __func__, status);
++ return -EIO;
++ }
++
++ if (status & PMIC_ARB_STATUS_DROPPED) {
++ dev_err(&ctrl->dev,
++ "%s: transaction dropped (0x%x)\n",
++ __func__, status);
++ return -EIO;
++ }
++
++ return 0;
++ }
++ udelay(1);
++ }
++
++ dev_err(&ctrl->dev,
++ "%s: timeout, status 0x%x\n",
++ __func__, status);
++ return -ETIMEDOUT;
++}
++
++/* Non-data command */
++static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
++{
++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
++ unsigned long flags;
++ u32 cmd;
++ int rc;
++
++ /* Check for valid non-data command */
++ if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
++ return -EINVAL;
++
++ cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
++
++ raw_spin_lock_irqsave(&pmic_arb->lock, flags);
++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
++ rc = pmic_arb_wait_for_done(ctrl);
++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
++
++ return rc;
++}
++
++static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
++ u16 addr, u8 *buf, size_t len)
++{
++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
++ unsigned long flags;
++ u8 bc = len - 1;
++ u32 cmd;
++ int rc;
++
++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
++ dev_err(&ctrl->dev,
++ "pmic-arb supports 1..%d bytes per trans, but %d requested",
++ PMIC_ARB_MAX_TRANS_BYTES, len);
++ return -EINVAL;
++ }
++
++ /* Check the opcode */
++ if (opc >= 0x60 && opc <= 0x7F)
++ opc = PMIC_ARB_OP_READ;
++ else if (opc >= 0x20 && opc <= 0x2F)
++ opc = PMIC_ARB_OP_EXT_READ;
++ else if (opc >= 0x38 && opc <= 0x3F)
++ opc = PMIC_ARB_OP_EXT_READL;
++ else
++ return -EINVAL;
++
++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
++
++ raw_spin_lock_irqsave(&pmic_arb->lock, flags);
++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
++ rc = pmic_arb_wait_for_done(ctrl);
++ if (rc)
++ goto done;
++
++ pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel),
++ min_t(u8, bc, 3));
++
++ if (bc > 3)
++ pa_read_data(pmic_arb, buf + 4,
++ PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4);
++
++done:
++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
++ return rc;
++}
++
++static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
++ u16 addr, const u8 *buf, size_t len)
++{
++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
++ unsigned long flags;
++ u8 bc = len - 1;
++ u32 cmd;
++ int rc;
++
++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
++ dev_err(&ctrl->dev,
++ "pmic-arb supports 1..%d bytes per trans, but:%d requested",
++ PMIC_ARB_MAX_TRANS_BYTES, len);
++ return -EINVAL;
++ }
++
++ /* Check the opcode */
++ if (opc >= 0x40 && opc <= 0x5F)
++ opc = PMIC_ARB_OP_WRITE;
++ else if (opc >= 0x00 && opc <= 0x0F)
++ opc = PMIC_ARB_OP_EXT_WRITE;
++ else if (opc >= 0x30 && opc <= 0x37)
++ opc = PMIC_ARB_OP_EXT_WRITEL;
++ else if (opc >= 0x80 && opc <= 0xFF)
++ opc = PMIC_ARB_OP_ZERO_WRITE;
++ else
++ return -EINVAL;
++
++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
++
++ /* Write data to FIFOs */
++ raw_spin_lock_irqsave(&pmic_arb->lock, flags);
++ pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel)
++ , min_t(u8, bc, 3));
++ if (bc > 3)
++ pa_write_data(pmic_arb, buf + 4,
++ PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4);
++
++ /* Start the transaction */
++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
++ rc = pmic_arb_wait_for_done(ctrl);
++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
++
++ return rc;
++}
++
++static int spmi_pmic_arb_probe(struct platform_device *pdev)
++{
++ struct spmi_pmic_arb_dev *pa;
++ struct spmi_controller *ctrl;
++ struct resource *res;
++ u32 channel;
++ int err, i;
++
++ ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
++ if (!ctrl)
++ return -ENOMEM;
++
++ pa = spmi_controller_get_drvdata(ctrl);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
++ pa->base = devm_ioremap_resource(&ctrl->dev, res);
++ if (IS_ERR(pa->base)) {
++ err = PTR_ERR(pa->base);
++ goto err_put_ctrl;
++ }
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
++ pa->intr = devm_ioremap_resource(&ctrl->dev, res);
++ if (IS_ERR(pa->intr)) {
++ err = PTR_ERR(pa->intr);
++ goto err_put_ctrl;
++ }
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
++ pa->cnfg = devm_ioremap_resource(&ctrl->dev, res);
++ if (IS_ERR(pa->cnfg)) {
++ err = PTR_ERR(pa->cnfg);
++ goto err_put_ctrl;
++ }
++
++ err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
++ if (err) {
++ dev_err(&pdev->dev, "channel unspecified.\n");
++ goto err_put_ctrl;
++ }
++
++ if (channel > 5) {
++ dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
++ channel);
++ goto err_put_ctrl;
++ }
++
++ pa->channel = channel;
++
++ platform_set_drvdata(pdev, ctrl);
++ raw_spin_lock_init(&pa->lock);
++
++ ctrl->cmd = pmic_arb_cmd;
++ ctrl->read_cmd = pmic_arb_read_cmd;
++ ctrl->write_cmd = pmic_arb_write_cmd;
++
++ err = spmi_controller_add(ctrl);
++ if (err)
++ goto err_put_ctrl;
++
++ dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n",
++ pmic_arb_base_read(pa, PMIC_ARB_VERSION));
++
++ return 0;
++
++err_put_ctrl:
++ spmi_controller_put(ctrl);
++ return err;
++}
++
++static int spmi_pmic_arb_remove(struct platform_device *pdev)
++{
++ struct spmi_controller *ctrl = platform_get_drvdata(pdev);
++ spmi_controller_remove(ctrl);
++ spmi_controller_put(ctrl);
++ return 0;
++}
++
++static const struct of_device_id spmi_pmic_arb_match_table[] = {
++ { .compatible = "qcom,spmi-pmic-arb", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
++
++static struct platform_driver spmi_pmic_arb_driver = {
++ .probe = spmi_pmic_arb_probe,
++ .remove = spmi_pmic_arb_remove,
++ .driver = {
++ .name = "spmi_pmic_arb",
++ .owner = THIS_MODULE,
++ .of_match_table = spmi_pmic_arb_match_table,
++ },
++};
++module_platform_driver(spmi_pmic_arb_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:spmi_pmic_arb");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch
new file mode 100644
index 0000000..cbd49b0
--- /dev/null
+++ b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch
@@ -0,0 +1,494 @@
+From b5bc51d44485c7ce0ca180a8c5de11a206f686e8 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Wed, 12 Feb 2014 13:44:25 -0600
+Subject: [PATCH 057/182] spmi: pmic_arb: add support for interrupt handling
+
+The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller,
+also implements interrupt handling for slave devices. Note, this is
+outside the scope of SPMI, as SPMI leaves interrupt handling completely
+unspecified.
+
+Extend the driver to provide a irq_chip implementation and chained irq
+handling which allows for these interrupts to be used.
+
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spmi/Kconfig | 1 +
+ drivers/spmi/spmi-pmic-arb.c | 377 +++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 376 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
+index 80b7901..075bd79 100644
+--- a/drivers/spmi/Kconfig
++++ b/drivers/spmi/Kconfig
+@@ -13,6 +13,7 @@ if SPMI
+ config SPMI_MSM_PMIC_ARB
+ tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+ depends on ARM
++ depends on IRQ_DOMAIN
+ depends on ARCH_MSM || COMPILE_TEST
+ default ARCH_MSM
+ help
+diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
+index 2dd27e8..246e03a 100644
+--- a/drivers/spmi/spmi-pmic-arb.c
++++ b/drivers/spmi/spmi-pmic-arb.c
+@@ -13,6 +13,9 @@
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/irq.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code {
+ * @cnfg: address of the PMIC Arbiter configuration registers.
+ * @lock: lock to synchronize accesses.
+ * @channel: which channel to use for accesses.
++ * @irq: PMIC ARB interrupt.
++ * @ee: the current Execution Environment
++ * @min_apid: minimum APID (used for bounding IRQ search)
++ * @max_apid: maximum APID
++ * @mapping_table: in-memory copy of PPID -> APID mapping table.
++ * @domain: irq domain object for PMIC IRQ domain
++ * @spmic: SPMI controller object
++ * @apid_to_ppid: cached mapping from APID to PPID
+ */
+ struct spmi_pmic_arb_dev {
+ void __iomem *base;
+@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev {
+ void __iomem *cnfg;
+ raw_spinlock_t lock;
+ u8 channel;
++ int irq;
++ u8 ee;
++ u8 min_apid;
++ u8 max_apid;
++ u32 mapping_table[SPMI_MAPPING_TABLE_LEN];
++ struct irq_domain *domain;
++ struct spmi_controller *spmic;
++ u16 apid_to_ppid[256];
+ };
+
+ static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
+@@ -306,12 +325,316 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+ return rc;
+ }
+
++enum qpnpint_regs {
++ QPNPINT_REG_RT_STS = 0x10,
++ QPNPINT_REG_SET_TYPE = 0x11,
++ QPNPINT_REG_POLARITY_HIGH = 0x12,
++ QPNPINT_REG_POLARITY_LOW = 0x13,
++ QPNPINT_REG_LATCHED_CLR = 0x14,
++ QPNPINT_REG_EN_SET = 0x15,
++ QPNPINT_REG_EN_CLR = 0x16,
++ QPNPINT_REG_LATCHED_STS = 0x18,
++};
++
++struct spmi_pmic_arb_qpnpint_type {
++ u8 type; /* 1 -> edge */
++ u8 polarity_high;
++ u8 polarity_low;
++} __packed;
++
++/* Simplified accessor functions for irqchip callbacks */
++static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
++ size_t len)
++{
++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
++ u8 sid = d->hwirq >> 24;
++ u8 per = d->hwirq >> 16;
++
++ if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
++ (per << 8) + reg, buf, len))
++ dev_err_ratelimited(&pa->spmic->dev,
++ "failed irqchip transaction on %x\n",
++ d->irq);
++}
++
++static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
++{
++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
++ u8 sid = d->hwirq >> 24;
++ u8 per = d->hwirq >> 16;
++
++ if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
++ (per << 8) + reg, buf, len))
++ dev_err_ratelimited(&pa->spmic->dev,
++ "failed irqchip transaction on %x\n",
++ d->irq);
++}
++
++static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
++{
++ unsigned int irq;
++ u32 status;
++ int id;
++
++ status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid));
++ while (status) {
++ id = ffs(status) - 1;
++ status &= ~(1 << id);
++ irq = irq_find_mapping(pa->domain,
++ pa->apid_to_ppid[apid] << 16
++ | id << 8
++ | apid);
++ generic_handle_irq(irq);
++ }
++}
++
++static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc)
++{
++ struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq);
++ struct irq_chip *chip = irq_get_chip(irq);
++ void __iomem *intr = pa->intr;
++ int first = pa->min_apid >> 5;
++ int last = pa->max_apid >> 5;
++ u32 status;
++ int i, id;
++
++ chained_irq_enter(chip, desc);
++
++ for (i = first; i <= last; ++i) {
++ status = readl_relaxed(intr +
++ SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i));
++ while (status) {
++ id = ffs(status) - 1;
++ status &= ~(1 << id);
++ periph_interrupt(pa, id + i * 32);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void qpnpint_irq_ack(struct irq_data *d)
++{
++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
++ u8 irq = d->hwirq >> 8;
++ u8 apid = d->hwirq;
++ unsigned long flags;
++ u8 data;
++
++ raw_spin_lock_irqsave(&pa->lock, flags);
++ writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid));
++ raw_spin_unlock_irqrestore(&pa->lock, flags);
++
++ data = 1 << irq;
++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
++}
++
++static void qpnpint_irq_mask(struct irq_data *d)
++{
++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
++ u8 irq = d->hwirq >> 8;
++ u8 apid = d->hwirq;
++ unsigned long flags;
++ u32 status;
++ u8 data;
++
++ raw_spin_lock_irqsave(&pa->lock, flags);
++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
++ if (status & SPMI_PIC_ACC_ENABLE_BIT) {
++ status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
++ writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid));
++ }
++ raw_spin_unlock_irqrestore(&pa->lock, flags);
++
++ data = 1 << irq;
++ qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
++}
++
++static void qpnpint_irq_unmask(struct irq_data *d)
++{
++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
++ u8 irq = d->hwirq >> 8;
++ u8 apid = d->hwirq;
++ unsigned long flags;
++ u32 status;
++ u8 data;
++
++ raw_spin_lock_irqsave(&pa->lock, flags);
++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
++ if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
++ writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT,
++ pa->intr + SPMI_PIC_ACC_ENABLE(apid));
++ }
++ raw_spin_unlock_irqrestore(&pa->lock, flags);
++
++ data = 1 << irq;
++ qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1);
++}
++
++static void qpnpint_irq_enable(struct irq_data *d)
++{
++ u8 irq = d->hwirq >> 8;
++ u8 data;
++
++ qpnpint_irq_unmask(d);
++
++ data = 1 << irq;
++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
++}
++
++static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
++{
++ struct spmi_pmic_arb_qpnpint_type type;
++ u8 irq = d->hwirq >> 8;
++
++ qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
++
++ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
++ type.type |= 1 << irq;
++ if (flow_type & IRQF_TRIGGER_RISING)
++ type.polarity_high |= 1 << irq;
++ if (flow_type & IRQF_TRIGGER_FALLING)
++ type.polarity_low |= 1 << irq;
++ } else {
++ if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
++ (flow_type & (IRQF_TRIGGER_LOW)))
++ return -EINVAL;
++
++ type.type &= ~(1 << irq); /* level trig */
++ if (flow_type & IRQF_TRIGGER_HIGH)
++ type.polarity_high |= 1 << irq;
++ else
++ type.polarity_low |= 1 << irq;
++ }
++
++ qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
++ return 0;
++}
++
++static struct irq_chip pmic_arb_irqchip = {
++ .name = "pmic_arb",
++ .irq_enable = qpnpint_irq_enable,
++ .irq_ack = qpnpint_irq_ack,
++ .irq_mask = qpnpint_irq_mask,
++ .irq_unmask = qpnpint_irq_unmask,
++ .irq_set_type = qpnpint_irq_set_type,
++ .flags = IRQCHIP_MASK_ON_SUSPEND
++ | IRQCHIP_SKIP_SET_WAKE,
++};
++
++struct spmi_pmic_arb_irq_spec {
++ unsigned slave:4;
++ unsigned per:8;
++ unsigned irq:3;
++};
++
++static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
++ struct spmi_pmic_arb_irq_spec *spec,
++ u8 *apid)
++{
++ u16 ppid = spec->slave << 8 | spec->per;
++ u32 *mapping_table = pa->mapping_table;
++ int index = 0, i;
++ u32 data;
++
++ for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
++ data = mapping_table[index];
++
++ if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
++ if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
++ index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
++ } else {
++ *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
++ return 0;
++ }
++ } else {
++ if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
++ index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
++ } else {
++ *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
++ return 0;
++ }
++ }
++ }
++
++ return -ENODEV;
++}
++
++static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
++ struct device_node *controller,
++ const u32 *intspec,
++ unsigned int intsize,
++ unsigned long *out_hwirq,
++ unsigned int *out_type)
++{
++ struct spmi_pmic_arb_dev *pa = d->host_data;
++ struct spmi_pmic_arb_irq_spec spec;
++ int err;
++ u8 apid;
++
++ dev_dbg(&pa->spmic->dev,
++ "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
++ intspec[0], intspec[1], intspec[2]);
++
++ if (d->of_node != controller)
++ return -EINVAL;
++ if (intsize != 4)
++ return -EINVAL;
++ if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
++ return -EINVAL;
++
++ spec.slave = intspec[0];
++ spec.per = intspec[1];
++ spec.irq = intspec[2];
++
++ err = search_mapping_table(pa, &spec, &apid);
++ if (err)
++ return err;
++
++ pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per;
++
++ /* Keep track of {max,min}_apid for bounding search during interrupt */
++ if (apid > pa->max_apid)
++ pa->max_apid = apid;
++ if (apid < pa->min_apid)
++ pa->min_apid = apid;
++
++ *out_hwirq = spec.slave << 24
++ | spec.per << 16
++ | spec.irq << 8
++ | apid;
++ *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
++
++ dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
++
++ return 0;
++}
++
++static int qpnpint_irq_domain_map(struct irq_domain *d,
++ unsigned int virq,
++ irq_hw_number_t hwirq)
++{
++ struct spmi_pmic_arb_dev *pa = d->host_data;
++
++ dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
++
++ irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
++ irq_set_chip_data(virq, d->host_data);
++ irq_set_noprobe(virq);
++ return 0;
++}
++
++static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
++ .map = qpnpint_irq_domain_map,
++ .xlate = qpnpint_irq_domain_dt_translate,
++};
++
+ static int spmi_pmic_arb_probe(struct platform_device *pdev)
+ {
+ struct spmi_pmic_arb_dev *pa;
+ struct spmi_controller *ctrl;
+ struct resource *res;
+- u32 channel;
++ u32 channel, ee;
+ int err, i;
+
+ ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
+@@ -319,6 +642,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
+ return -ENOMEM;
+
+ pa = spmi_controller_get_drvdata(ctrl);
++ pa->spmic = ctrl;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ pa->base = devm_ioremap_resource(&ctrl->dev, res);
+@@ -341,6 +665,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
+ goto err_put_ctrl;
+ }
+
++ pa->irq = platform_get_irq_byname(pdev, "periph_irq");
++ if (pa->irq < 0) {
++ err = pa->irq;
++ goto err_put_ctrl;
++ }
++
+ err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
+ if (err) {
+ dev_err(&pdev->dev, "channel unspecified.\n");
+@@ -355,6 +685,29 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
+
+ pa->channel = channel;
+
++ err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
++ if (err) {
++ dev_err(&pdev->dev, "EE unspecified.\n");
++ goto err_put_ctrl;
++ }
++
++ if (ee > 5) {
++ dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
++ err = -EINVAL;
++ goto err_put_ctrl;
++ }
++
++ pa->ee = ee;
++
++ for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i)
++ pa->mapping_table[i] = readl_relaxed(
++ pa->cnfg + SPMI_MAPPING_TABLE_REG(i));
++
++ /* Initialize max_apid/min_apid to the opposite bounds, during
++ * the irq domain translation, we are sure to update these */
++ pa->max_apid = 0;
++ pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
++
+ platform_set_drvdata(pdev, ctrl);
+ raw_spin_lock_init(&pa->lock);
+
+@@ -362,15 +715,31 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
+ ctrl->read_cmd = pmic_arb_read_cmd;
+ ctrl->write_cmd = pmic_arb_write_cmd;
+
++ dev_dbg(&pdev->dev, "adding irq domain\n");
++ pa->domain = irq_domain_add_tree(pdev->dev.of_node,
++ &pmic_arb_irq_domain_ops, pa);
++ if (!pa->domain) {
++ dev_err(&pdev->dev, "unable to create irq_domain\n");
++ err = -ENOMEM;
++ goto err_put_ctrl;
++ }
++
++ irq_set_handler_data(pa->irq, pa);
++ irq_set_chained_handler(pa->irq, pmic_arb_chained_irq);
++
+ err = spmi_controller_add(ctrl);
+ if (err)
+- goto err_put_ctrl;
++ goto err_domain_remove;
+
+ dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n",
+ pmic_arb_base_read(pa, PMIC_ARB_VERSION));
+
+ return 0;
+
++err_domain_remove:
++ irq_set_chained_handler(pa->irq, NULL);
++ irq_set_handler_data(pa->irq, NULL);
++ irq_domain_remove(pa->domain);
+ err_put_ctrl:
+ spmi_controller_put(ctrl);
+ return err;
+@@ -379,7 +748,11 @@ err_put_ctrl:
+ static int spmi_pmic_arb_remove(struct platform_device *pdev)
+ {
+ struct spmi_controller *ctrl = platform_get_drvdata(pdev);
++ struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl);
+ spmi_controller_remove(ctrl);
++ irq_set_chained_handler(pa->irq, NULL);
++ irq_set_handler_data(pa->irq, NULL);
++ irq_domain_remove(pa->domain);
+ spmi_controller_put(ctrl);
+ return 0;
+ }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch
new file mode 100644
index 0000000..271e8ad
--- /dev/null
+++ b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch
@@ -0,0 +1,34 @@
+From 840fe915072597b5ba9599c4579f014f47b9638e Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Mon, 3 Mar 2014 10:49:43 -0600
+Subject: [PATCH 058/182] spmi: pmic_arb: make selectable on ARCH_QCOM
+
+With the split of Qualcomm MSM support into legacy and multiplatform,
+the SPMI PMIC arb driver is only relevant on the multiplatform supported
+SoCs. Switch the Kconfig depends to ARCH_QCOM.
+
+Acked-by: Kumar Gala <galak@codeaurora.org>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spmi/Kconfig | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
+index 075bd79..bf1295e 100644
+--- a/drivers/spmi/Kconfig
++++ b/drivers/spmi/Kconfig
+@@ -14,8 +14,8 @@ config SPMI_MSM_PMIC_ARB
+ tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+ depends on ARM
+ depends on IRQ_DOMAIN
+- depends on ARCH_MSM || COMPILE_TEST
+- default ARCH_MSM
++ depends on ARCH_QCOM || COMPILE_TEST
++ default ARCH_QCOM
+ help
+ If you say yes to this option, support will be included for the
+ built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch
new file mode 100644
index 0000000..f252fa0
--- /dev/null
+++ b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch
@@ -0,0 +1,74 @@
+From 9d13f01e2ec45253adaae1a330cdc4eb881c7377 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Mon, 3 Mar 2014 10:49:44 -0600
+Subject: [PATCH 059/182] spmi: pm: drop bus-level PM suspend/resume routines
+
+SPMI defines the behavior of a device in the "SLEEP" state as being
+"user-defined or specified by the device manufacturer". Without
+clearly-defined bus-level semantics for low-power states, push the
+responsibility of transitioning a device into/out of "SLEEP" into SPMI
+device drivers.
+
+Cc: Felipe Balbi <balbi@ti.com>
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spmi/spmi.c | 35 -----------------------------------
+ 1 file changed, 35 deletions(-)
+
+diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
+index 6122c8f..3b57807 100644
+--- a/drivers/spmi/spmi.c
++++ b/drivers/spmi/spmi.c
+@@ -46,40 +46,6 @@ static const struct device_type spmi_ctrl_type = {
+ .release = spmi_ctrl_release,
+ };
+
+-#ifdef CONFIG_PM_RUNTIME
+-static int spmi_runtime_suspend(struct device *dev)
+-{
+- struct spmi_device *sdev = to_spmi_device(dev);
+- int err;
+-
+- err = pm_generic_runtime_suspend(dev);
+- if (err)
+- return err;
+-
+- return spmi_command_sleep(sdev);
+-}
+-
+-static int spmi_runtime_resume(struct device *dev)
+-{
+- struct spmi_device *sdev = to_spmi_device(dev);
+- int err;
+-
+- err = spmi_command_wakeup(sdev);
+- if (err)
+- return err;
+-
+- return pm_generic_runtime_resume(dev);
+-}
+-#endif
+-
+-static const struct dev_pm_ops spmi_pm_ops = {
+- SET_RUNTIME_PM_OPS(
+- spmi_runtime_suspend,
+- spmi_runtime_resume,
+- NULL
+- )
+-};
+-
+ static int spmi_device_match(struct device *dev, struct device_driver *drv)
+ {
+ if (of_driver_match_device(dev, drv))
+@@ -391,7 +357,6 @@ static int spmi_drv_remove(struct device *dev)
+ static struct bus_type spmi_bus_type = {
+ .name = "spmi",
+ .match = spmi_device_match,
+- .pm = &spmi_pm_ops,
+ .probe = spmi_drv_probe,
+ .remove = spmi_drv_remove,
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch
new file mode 100644
index 0000000..64993a3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch
@@ -0,0 +1,835 @@
+From 226eff10dc11af5d6b1bc31e5cedc079aa564fb3 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Thu, 13 Mar 2014 19:07:43 -0700
+Subject: [PATCH 060/182] i2c: qup: New bus driver for the Qualcomm QUP I2C
+ controller
+
+This bus driver supports the QUP i2c hardware controller in the Qualcomm SOCs.
+The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path
+engine with input/output FIFOs and an embedded i2c mini-core. The driver
+supports FIFO mode (for low bandwidth applications) and block mode (interrupt
+generated for each block-size data transfer).
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Tested-by: Philip Elcan <pelcan@codeaurora.org>
+[wsa: removed needless IS_ERR_VALUE]
+Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
+---
+ drivers/i2c/busses/Kconfig | 10 +
+ drivers/i2c/busses/Makefile | 1 +
+ drivers/i2c/busses/i2c-qup.c | 768 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 779 insertions(+)
+ create mode 100644 drivers/i2c/busses/i2c-qup.c
+
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index de17c55..1886315 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -648,6 +648,16 @@ config I2C_PXA_SLAVE
+ is necessary for systems where the PXA may be a target on the
+ I2C bus.
+
++config I2C_QUP
++ tristate "Qualcomm QUP based I2C controller"
++ depends on ARCH_QCOM
++ help
++ If you say yes to this option, support will be included for the
++ built-in I2C interface on the Qualcomm SoCs.
++
++ This driver can also be built as a module. If so, the module
++ will be called i2c-qup.
++
+ config I2C_RIIC
+ tristate "Renesas RIIC adapter"
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
+index a08931f..bf2257b 100644
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
+ obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
+ obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
+ obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
++obj-$(CONFIG_I2C_QUP) += i2c-qup.o
+ obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
+ obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
+ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
+diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
+new file mode 100644
+index 0000000..c9d5f78
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-qup.c
+@@ -0,0 +1,768 @@
++/*
++ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2014, Sony Mobile Communications AB.
++ *
++ *
++ * 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/err.h>
++#include <linux/i2c.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++
++/* QUP Registers */
++#define QUP_CONFIG 0x000
++#define QUP_STATE 0x004
++#define QUP_IO_MODE 0x008
++#define QUP_SW_RESET 0x00c
++#define QUP_OPERATIONAL 0x018
++#define QUP_ERROR_FLAGS 0x01c
++#define QUP_ERROR_FLAGS_EN 0x020
++#define QUP_HW_VERSION 0x030
++#define QUP_MX_OUTPUT_CNT 0x100
++#define QUP_OUT_FIFO_BASE 0x110
++#define QUP_MX_WRITE_CNT 0x150
++#define QUP_MX_INPUT_CNT 0x200
++#define QUP_MX_READ_CNT 0x208
++#define QUP_IN_FIFO_BASE 0x218
++#define QUP_I2C_CLK_CTL 0x400
++#define QUP_I2C_STATUS 0x404
++
++/* QUP States and reset values */
++#define QUP_RESET_STATE 0
++#define QUP_RUN_STATE 1
++#define QUP_PAUSE_STATE 3
++#define QUP_STATE_MASK 3
++
++#define QUP_STATE_VALID BIT(2)
++#define QUP_I2C_MAST_GEN BIT(4)
++
++#define QUP_OPERATIONAL_RESET 0x000ff0
++#define QUP_I2C_STATUS_RESET 0xfffffc
++
++/* QUP OPERATIONAL FLAGS */
++#define QUP_I2C_NACK_FLAG BIT(3)
++#define QUP_OUT_NOT_EMPTY BIT(4)
++#define QUP_IN_NOT_EMPTY BIT(5)
++#define QUP_OUT_FULL BIT(6)
++#define QUP_OUT_SVC_FLAG BIT(8)
++#define QUP_IN_SVC_FLAG BIT(9)
++#define QUP_MX_OUTPUT_DONE BIT(10)
++#define QUP_MX_INPUT_DONE BIT(11)
++
++/* I2C mini core related values */
++#define QUP_CLOCK_AUTO_GATE BIT(13)
++#define I2C_MINI_CORE (2 << 8)
++#define I2C_N_VAL 15
++/* Most significant word offset in FIFO port */
++#define QUP_MSW_SHIFT (I2C_N_VAL + 1)
++
++/* Packing/Unpacking words in FIFOs, and IO modes */
++#define QUP_OUTPUT_BLK_MODE (1 << 10)
++#define QUP_INPUT_BLK_MODE (1 << 12)
++#define QUP_UNPACK_EN BIT(14)
++#define QUP_PACK_EN BIT(15)
++
++#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN)
++
++#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03)
++#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07)
++#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03)
++#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07)
++
++/* QUP tags */
++#define QUP_TAG_START (1 << 8)
++#define QUP_TAG_DATA (2 << 8)
++#define QUP_TAG_STOP (3 << 8)
++#define QUP_TAG_REC (4 << 8)
++
++/* Status, Error flags */
++#define I2C_STATUS_WR_BUFFER_FULL BIT(0)
++#define I2C_STATUS_BUS_ACTIVE BIT(8)
++#define I2C_STATUS_ERROR_MASK 0x38000fc
++#define QUP_STATUS_ERROR_FLAGS 0x7c
++
++#define QUP_READ_LIMIT 256
++
++struct qup_i2c_dev {
++ struct device *dev;
++ void __iomem *base;
++ int irq;
++ struct clk *clk;
++ struct clk *pclk;
++ struct i2c_adapter adap;
++
++ int clk_ctl;
++ int out_fifo_sz;
++ int in_fifo_sz;
++ int out_blk_sz;
++ int in_blk_sz;
++
++ unsigned long one_byte_t;
++
++ struct i2c_msg *msg;
++ /* Current posion in user message buffer */
++ int pos;
++ /* I2C protocol errors */
++ u32 bus_err;
++ /* QUP core errors */
++ u32 qup_err;
++
++ struct completion xfer;
++};
++
++static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
++{
++ struct qup_i2c_dev *qup = dev;
++ u32 bus_err;
++ u32 qup_err;
++ u32 opflags;
++
++ bus_err = readl(qup->base + QUP_I2C_STATUS);
++ qup_err = readl(qup->base + QUP_ERROR_FLAGS);
++ opflags = readl(qup->base + QUP_OPERATIONAL);
++
++ if (!qup->msg) {
++ /* Clear Error interrupt */
++ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
++ return IRQ_HANDLED;
++ }
++
++ bus_err &= I2C_STATUS_ERROR_MASK;
++ qup_err &= QUP_STATUS_ERROR_FLAGS;
++
++ if (qup_err) {
++ /* Clear Error interrupt */
++ writel(qup_err, qup->base + QUP_ERROR_FLAGS);
++ goto done;
++ }
++
++ if (bus_err) {
++ /* Clear Error interrupt */
++ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
++ goto done;
++ }
++
++ if (opflags & QUP_IN_SVC_FLAG)
++ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
++
++ if (opflags & QUP_OUT_SVC_FLAG)
++ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
++
++done:
++ qup->qup_err = qup_err;
++ qup->bus_err = bus_err;
++ complete(&qup->xfer);
++ return IRQ_HANDLED;
++}
++
++static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup,
++ u32 req_state, u32 req_mask)
++{
++ int retries = 1;
++ u32 state;
++
++ /*
++ * State transition takes 3 AHB clocks cycles + 3 I2C master clock
++ * cycles. So retry once after a 1uS delay.
++ */
++ do {
++ state = readl(qup->base + QUP_STATE);
++
++ if (state & QUP_STATE_VALID &&
++ (state & req_mask) == req_state)
++ return 0;
++
++ udelay(1);
++ } while (retries--);
++
++ return -ETIMEDOUT;
++}
++
++static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state)
++{
++ return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
++}
++
++static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup)
++{
++ return qup_i2c_poll_state_mask(qup, 0, 0);
++}
++
++static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup)
++{
++ return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
++}
++
++static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
++{
++ if (qup_i2c_poll_state_valid(qup) != 0)
++ return -EIO;
++
++ writel(state, qup->base + QUP_STATE);
++
++ if (qup_i2c_poll_state(qup, state) != 0)
++ return -EIO;
++ return 0;
++}
++
++static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup)
++{
++ unsigned long timeout;
++ u32 opflags;
++ u32 status;
++
++ timeout = jiffies + HZ;
++
++ for (;;) {
++ opflags = readl(qup->base + QUP_OPERATIONAL);
++ status = readl(qup->base + QUP_I2C_STATUS);
++
++ if (!(opflags & QUP_OUT_NOT_EMPTY) &&
++ !(status & I2C_STATUS_BUS_ACTIVE))
++ return 0;
++
++ if (time_after(jiffies, timeout))
++ return -ETIMEDOUT;
++
++ usleep_range(qup->one_byte_t, qup->one_byte_t * 2);
++ }
++}
++
++static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ /* Number of entries to shift out, including the start */
++ int total = msg->len + 1;
++
++ if (total < qup->out_fifo_sz) {
++ /* FIFO mode */
++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
++ writel(total, qup->base + QUP_MX_WRITE_CNT);
++ } else {
++ /* BLOCK mode (transfer data on chunks) */
++ writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
++ qup->base + QUP_IO_MODE);
++ writel(total, qup->base + QUP_MX_OUTPUT_CNT);
++ }
++}
++
++static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ u32 addr = msg->addr << 1;
++ u32 qup_tag;
++ u32 opflags;
++ int idx;
++ u32 val;
++
++ if (qup->pos == 0) {
++ val = QUP_TAG_START | addr;
++ idx = 1;
++ } else {
++ val = 0;
++ idx = 0;
++ }
++
++ while (qup->pos < msg->len) {
++ /* Check that there's space in the FIFO for our pair */
++ opflags = readl(qup->base + QUP_OPERATIONAL);
++ if (opflags & QUP_OUT_FULL)
++ break;
++
++ if (qup->pos == msg->len - 1)
++ qup_tag = QUP_TAG_STOP;
++ else
++ qup_tag = QUP_TAG_DATA;
++
++ if (idx & 1)
++ val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT;
++ else
++ val = qup_tag | msg->buf[qup->pos];
++
++ /* Write out the pair and the last odd value */
++ if (idx & 1 || qup->pos == msg->len - 1)
++ writel(val, qup->base + QUP_OUT_FIFO_BASE);
++
++ qup->pos++;
++ idx++;
++ }
++}
++
++static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ unsigned long left;
++ int ret;
++
++ qup->msg = msg;
++ qup->pos = 0;
++
++ enable_irq(qup->irq);
++
++ qup_i2c_set_write_mode(qup, msg);
++
++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
++ if (ret)
++ goto err;
++
++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
++
++ do {
++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
++ if (ret)
++ goto err;
++
++ qup_i2c_issue_write(qup, msg);
++
++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
++ if (ret)
++ goto err;
++
++ left = wait_for_completion_timeout(&qup->xfer, HZ);
++ if (!left) {
++ writel(1, qup->base + QUP_SW_RESET);
++ ret = -ETIMEDOUT;
++ goto err;
++ }
++
++ if (qup->bus_err || qup->qup_err) {
++ if (qup->bus_err & QUP_I2C_NACK_FLAG)
++ dev_err(qup->dev, "NACK from %x\n", msg->addr);
++ ret = -EIO;
++ goto err;
++ }
++ } while (qup->pos < msg->len);
++
++ /* Wait for the outstanding data in the fifo to drain */
++ ret = qup_i2c_wait_writeready(qup);
++
++err:
++ disable_irq(qup->irq);
++ qup->msg = NULL;
++
++ return ret;
++}
++
++static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
++{
++ if (len < qup->in_fifo_sz) {
++ /* FIFO mode */
++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
++ writel(len, qup->base + QUP_MX_READ_CNT);
++ } else {
++ /* BLOCK mode (transfer data on chunks) */
++ writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
++ qup->base + QUP_IO_MODE);
++ writel(len, qup->base + QUP_MX_INPUT_CNT);
++ }
++}
++
++static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ u32 addr, len, val;
++
++ addr = (msg->addr << 1) | 1;
++
++ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
++ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
++
++ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
++ writel(val, qup->base + QUP_OUT_FIFO_BASE);
++}
++
++
++static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ u32 opflags;
++ u32 val = 0;
++ int idx;
++
++ for (idx = 0; qup->pos < msg->len; idx++) {
++ if ((idx & 1) == 0) {
++ /* Check that FIFO have data */
++ opflags = readl(qup->base + QUP_OPERATIONAL);
++ if (!(opflags & QUP_IN_NOT_EMPTY))
++ break;
++
++ /* Reading 2 words at time */
++ val = readl(qup->base + QUP_IN_FIFO_BASE);
++
++ msg->buf[qup->pos++] = val & 0xFF;
++ } else {
++ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
++ }
++ }
++}
++
++static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
++{
++ unsigned long left;
++ int ret;
++
++ /*
++ * The QUP block will issue a NACK and STOP on the bus when reaching
++ * the end of the read, the length of the read is specified as one byte
++ * which limits the possible read to 256 (QUP_READ_LIMIT) bytes.
++ */
++ if (msg->len > QUP_READ_LIMIT) {
++ dev_err(qup->dev, "HW not capable of reads over %d bytes\n",
++ QUP_READ_LIMIT);
++ return -EINVAL;
++ }
++
++ qup->msg = msg;
++ qup->pos = 0;
++
++ enable_irq(qup->irq);
++
++ qup_i2c_set_read_mode(qup, msg->len);
++
++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
++ if (ret)
++ goto err;
++
++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
++
++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
++ if (ret)
++ goto err;
++
++ qup_i2c_issue_read(qup, msg);
++
++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
++ if (ret)
++ goto err;
++
++ do {
++ left = wait_for_completion_timeout(&qup->xfer, HZ);
++ if (!left) {
++ writel(1, qup->base + QUP_SW_RESET);
++ ret = -ETIMEDOUT;
++ goto err;
++ }
++
++ if (qup->bus_err || qup->qup_err) {
++ if (qup->bus_err & QUP_I2C_NACK_FLAG)
++ dev_err(qup->dev, "NACK from %x\n", msg->addr);
++ ret = -EIO;
++ goto err;
++ }
++
++ qup_i2c_read_fifo(qup, msg);
++ } while (qup->pos < msg->len);
++
++err:
++ disable_irq(qup->irq);
++ qup->msg = NULL;
++
++ return ret;
++}
++
++static int qup_i2c_xfer(struct i2c_adapter *adap,
++ struct i2c_msg msgs[],
++ int num)
++{
++ struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
++ int ret, idx;
++
++ ret = pm_runtime_get_sync(qup->dev);
++ if (ret)
++ goto out;
++
++ writel(1, qup->base + QUP_SW_RESET);
++ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
++ if (ret)
++ goto out;
++
++ /* Configure QUP as I2C mini core */
++ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
++
++ for (idx = 0; idx < num; idx++) {
++ if (msgs[idx].len == 0) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (qup_i2c_poll_state_i2c_master(qup)) {
++ ret = -EIO;
++ goto out;
++ }
++
++ if (msgs[idx].flags & I2C_M_RD)
++ ret = qup_i2c_read_one(qup, &msgs[idx]);
++ else
++ ret = qup_i2c_write_one(qup, &msgs[idx]);
++
++ if (ret)
++ break;
++
++ ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
++ if (ret)
++ break;
++ }
++
++ if (ret == 0)
++ ret = num;
++out:
++
++ pm_runtime_mark_last_busy(qup->dev);
++ pm_runtime_put_autosuspend(qup->dev);
++
++ return ret;
++}
++
++static u32 qup_i2c_func(struct i2c_adapter *adap)
++{
++ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
++}
++
++static const struct i2c_algorithm qup_i2c_algo = {
++ .master_xfer = qup_i2c_xfer,
++ .functionality = qup_i2c_func,
++};
++
++static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
++{
++ clk_prepare_enable(qup->clk);
++ clk_prepare_enable(qup->pclk);
++}
++
++static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
++{
++ u32 config;
++
++ qup_i2c_change_state(qup, QUP_RESET_STATE);
++ clk_disable_unprepare(qup->clk);
++ config = readl(qup->base + QUP_CONFIG);
++ config |= QUP_CLOCK_AUTO_GATE;
++ writel(config, qup->base + QUP_CONFIG);
++ clk_disable_unprepare(qup->pclk);
++}
++
++static int qup_i2c_probe(struct platform_device *pdev)
++{
++ static const int blk_sizes[] = {4, 16, 32};
++ struct device_node *node = pdev->dev.of_node;
++ struct qup_i2c_dev *qup;
++ unsigned long one_bit_t;
++ struct resource *res;
++ u32 io_mode, hw_ver, size;
++ int ret, fs_div, hs_div;
++ int src_clk_freq;
++ int clk_freq = 100000;
++
++ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
++ if (!qup)
++ return -ENOMEM;
++
++ qup->dev = &pdev->dev;
++ init_completion(&qup->xfer);
++ platform_set_drvdata(pdev, qup);
++
++ of_property_read_u32(node, "clock-frequency", &clk_freq);
++
++ /* We support frequencies up to FAST Mode (400KHz) */
++ if (!clk_freq || clk_freq > 400000) {
++ dev_err(qup->dev, "clock frequency not supported %d\n",
++ clk_freq);
++ return -EINVAL;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ qup->base = devm_ioremap_resource(qup->dev, res);
++ if (IS_ERR(qup->base))
++ return PTR_ERR(qup->base);
++
++ qup->irq = platform_get_irq(pdev, 0);
++ if (qup->irq < 0) {
++ dev_err(qup->dev, "No IRQ defined\n");
++ return qup->irq;
++ }
++
++ qup->clk = devm_clk_get(qup->dev, "core");
++ if (IS_ERR(qup->clk)) {
++ dev_err(qup->dev, "Could not get core clock\n");
++ return PTR_ERR(qup->clk);
++ }
++
++ qup->pclk = devm_clk_get(qup->dev, "iface");
++ if (IS_ERR(qup->pclk)) {
++ dev_err(qup->dev, "Could not get iface clock\n");
++ return PTR_ERR(qup->pclk);
++ }
++
++ qup_i2c_enable_clocks(qup);
++
++ /*
++ * Bootloaders might leave a pending interrupt on certain QUP's,
++ * so we reset the core before registering for interrupts.
++ */
++ writel(1, qup->base + QUP_SW_RESET);
++ ret = qup_i2c_poll_state_valid(qup);
++ if (ret)
++ goto fail;
++
++ ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
++ IRQF_TRIGGER_HIGH, "i2c_qup", qup);
++ if (ret) {
++ dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
++ goto fail;
++ }
++ disable_irq(qup->irq);
++
++ hw_ver = readl(qup->base + QUP_HW_VERSION);
++ dev_dbg(qup->dev, "Revision %x\n", hw_ver);
++
++ io_mode = readl(qup->base + QUP_IO_MODE);
++
++ /*
++ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
++ * associated with each byte written/received
++ */
++ size = QUP_OUTPUT_BLOCK_SIZE(io_mode);
++ if (size > ARRAY_SIZE(blk_sizes))
++ return -EIO;
++ qup->out_blk_sz = blk_sizes[size] / 2;
++
++ size = QUP_INPUT_BLOCK_SIZE(io_mode);
++ if (size > ARRAY_SIZE(blk_sizes))
++ return -EIO;
++ qup->in_blk_sz = blk_sizes[size] / 2;
++
++ size = QUP_OUTPUT_FIFO_SIZE(io_mode);
++ qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
++
++ size = QUP_INPUT_FIFO_SIZE(io_mode);
++ qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
++
++ src_clk_freq = clk_get_rate(qup->clk);
++ fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
++ hs_div = 3;
++ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
++
++ /*
++ * Time it takes for a byte to be clocked out on the bus.
++ * Each byte takes 9 clock cycles (8 bits + 1 ack).
++ */
++ one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
++ qup->one_byte_t = one_bit_t * 9;
++
++ dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
++ qup->in_blk_sz, qup->in_fifo_sz,
++ qup->out_blk_sz, qup->out_fifo_sz);
++
++ i2c_set_adapdata(&qup->adap, qup);
++ qup->adap.algo = &qup_i2c_algo;
++ qup->adap.dev.parent = qup->dev;
++ qup->adap.dev.of_node = pdev->dev.of_node;
++ strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
++
++ ret = i2c_add_adapter(&qup->adap);
++ if (ret)
++ goto fail;
++
++ pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
++ pm_runtime_use_autosuspend(qup->dev);
++ pm_runtime_set_active(qup->dev);
++ pm_runtime_enable(qup->dev);
++ return 0;
++
++fail:
++ qup_i2c_disable_clocks(qup);
++ return ret;
++}
++
++static int qup_i2c_remove(struct platform_device *pdev)
++{
++ struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
++
++ disable_irq(qup->irq);
++ qup_i2c_disable_clocks(qup);
++ i2c_del_adapter(&qup->adap);
++ pm_runtime_disable(qup->dev);
++ pm_runtime_set_suspended(qup->dev);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int qup_i2c_pm_suspend_runtime(struct device *device)
++{
++ struct qup_i2c_dev *qup = dev_get_drvdata(device);
++
++ dev_dbg(device, "pm_runtime: suspending...\n");
++ qup_i2c_disable_clocks(qup);
++ return 0;
++}
++
++static int qup_i2c_pm_resume_runtime(struct device *device)
++{
++ struct qup_i2c_dev *qup = dev_get_drvdata(device);
++
++ dev_dbg(device, "pm_runtime: resuming...\n");
++ qup_i2c_enable_clocks(qup);
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_PM_SLEEP
++static int qup_i2c_suspend(struct device *device)
++{
++ qup_i2c_pm_suspend_runtime(device);
++ return 0;
++}
++
++static int qup_i2c_resume(struct device *device)
++{
++ qup_i2c_pm_resume_runtime(device);
++ pm_runtime_mark_last_busy(device);
++ pm_request_autosuspend(device);
++ return 0;
++}
++#endif
++
++static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(
++ qup_i2c_suspend,
++ qup_i2c_resume)
++ SET_RUNTIME_PM_OPS(
++ qup_i2c_pm_suspend_runtime,
++ qup_i2c_pm_resume_runtime,
++ NULL)
++};
++
++static const struct of_device_id qup_i2c_dt_match[] = {
++ { .compatible = "qcom,i2c-qup-v1.1.1" },
++ { .compatible = "qcom,i2c-qup-v2.1.1" },
++ { .compatible = "qcom,i2c-qup-v2.2.1" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
++
++static struct platform_driver qup_i2c_driver = {
++ .probe = qup_i2c_probe,
++ .remove = qup_i2c_remove,
++ .driver = {
++ .name = "i2c_qup",
++ .owner = THIS_MODULE,
++ .pm = &qup_i2c_qup_pm_ops,
++ .of_match_table = qup_i2c_dt_match,
++ },
++};
++
++module_platform_driver(qup_i2c_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:i2c_qup");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch
new file mode 100644
index 0000000..6ddae4f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch
@@ -0,0 +1,71 @@
+From 81480a89c72d811376e9e040729721705b2a984d Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Thu, 13 Mar 2014 19:07:42 -0700
+Subject: [PATCH 061/182] i2c: qup: Add device tree bindings information
+
+The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and
+provide input and output FIFO's for it. I2C controller can operate
+as master with supported bus speeds of 100Kbps and 400Kbps.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+[bjorn: reformulated part of binding description
+ added version to compatible
+ cleaned up example]
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Acked-by: Rob Herring <robh@kernel.org>
+[wsa: removed the dummy child node which was a confusing example]
+Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
+---
+ .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 40 ++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
+
+diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
+new file mode 100644
+index 0000000..dc71754
+--- /dev/null
++++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
+@@ -0,0 +1,40 @@
++Qualcomm Universal Peripheral (QUP) I2C controller
++
++Required properties:
++ - compatible: Should be:
++ * "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064.
++ * "qcom,i2c-qup-v2.1.1" for 8974 v1.
++ * "qcom,i2c-qup-v2.2.1" for 8974 v2 and later.
++ - reg: Should contain QUP register address and length.
++ - interrupts: Should contain I2C interrupt.
++
++ - clocks: A list of phandles + clock-specifiers, one for each entry in
++ clock-names.
++ - clock-names: Should contain:
++ * "core" for the core clock
++ * "iface" for the AHB clock
++
++ - #address-cells: Should be <1> Address cells for i2c device address
++ - #size-cells: Should be <0> as i2c addresses have no size component
++
++Optional properties:
++ - clock-frequency: Should specify the desired i2c bus clock frequency in Hz,
++ defaults to 100kHz if omitted.
++
++Child nodes should conform to i2c bus binding.
++
++Example:
++
++ i2c@f9924000 {
++ compatible = "qcom,i2c-qup-v2.2.1";
++ reg = <0xf9924000 0x1000>;
++ interrupts = <0 96 0>;
++
++ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
++ clock-names = "core", "iface";
++
++ clock-frequency = <355000>;
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch
new file mode 100644
index 0000000..a140997
--- /dev/null
+++ b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch
@@ -0,0 +1,36 @@
+From aa50d1b0d81e4a9509f15894ec64013aa5190b59 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Thu, 3 Apr 2014 10:22:54 +0300
+Subject: [PATCH 062/182] i2c: qup: off by ones in qup_i2c_probe()
+
+These should ">= ARRAY_SIZE()" instead of "> ARRAY_SIZE()".
+
+Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller')
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
+---
+ drivers/i2c/busses/i2c-qup.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
+index c9d5f78..ee40980 100644
+--- a/drivers/i2c/busses/i2c-qup.c
++++ b/drivers/i2c/busses/i2c-qup.c
+@@ -633,12 +633,12 @@ static int qup_i2c_probe(struct platform_device *pdev)
+ * associated with each byte written/received
+ */
+ size = QUP_OUTPUT_BLOCK_SIZE(io_mode);
+- if (size > ARRAY_SIZE(blk_sizes))
++ if (size >= ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->out_blk_sz = blk_sizes[size] / 2;
+
+ size = QUP_INPUT_BLOCK_SIZE(io_mode);
+- if (size > ARRAY_SIZE(blk_sizes))
++ if (size >= ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->in_blk_sz = blk_sizes[size] / 2;
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch
new file mode 100644
index 0000000..60411ab
--- /dev/null
+++ b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch
@@ -0,0 +1,30 @@
+From 62bdf6c11c9120543ba4831f0226bf51db1d1fc0 Mon Sep 17 00:00:00 2001
+From: Wolfram Sang <wsa@the-dreams.de>
+Date: Thu, 3 Apr 2014 11:30:33 +0200
+Subject: [PATCH 063/182] i2c: qup: use proper type fro clk_freq
+
+It is used with of_property_read_u32(), so it should be u32.
+
+Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller')
+---
+ drivers/i2c/busses/i2c-qup.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
+index ee40980..1b4cf14 100644
+--- a/drivers/i2c/busses/i2c-qup.c
++++ b/drivers/i2c/busses/i2c-qup.c
+@@ -562,7 +562,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
+ u32 io_mode, hw_ver, size;
+ int ret, fs_div, hs_div;
+ int src_clk_freq;
+- int clk_freq = 100000;
++ u32 clk_freq = 100000;
+
+ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
+ if (!qup)
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch
new file mode 100644
index 0000000..eaec950
--- /dev/null
+++ b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch
@@ -0,0 +1,31 @@
+From 82f2e53f207019c0d364d92515410be8dffd7524 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Fri, 2 May 2014 20:54:29 -0500
+Subject: [PATCH 064/182] i2c: qup: Fix pm_runtime_get_sync usage
+
+This patch corrects the error check on the call to pm_runtime_get_sync.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
+---
+ drivers/i2c/busses/i2c-qup.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
+index 1b4cf14..2a5efb5 100644
+--- a/drivers/i2c/busses/i2c-qup.c
++++ b/drivers/i2c/busses/i2c-qup.c
+@@ -479,7 +479,7 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
+ int ret, idx;
+
+ ret = pm_runtime_get_sync(qup->dev);
+- if (ret)
++ if (ret < 0)
+ goto out;
+
+ writel(1, qup->base + QUP_SW_RESET);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch
new file mode 100644
index 0000000..a392e04
--- /dev/null
+++ b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch
@@ -0,0 +1,907 @@
+From 24884115a6029995dba2561b1ee810f28a34271a Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Thu, 13 Feb 2014 18:21:38 +0200
+Subject: [PATCH 065/182] spi: Add Qualcomm QUP SPI controller support
+
+Qualcomm Universal Peripheral (QUP) core is an AHB slave that
+provides a common data path (an output FIFO and an input FIFO)
+for serial peripheral interface (SPI) mini-core. SPI in master
+mode supports up to 50MHz, up to four chip selects, programmable
+data path from 4 bits to 32 bits and numerous protocol variants.
+
+Cc: Alok Chauhan <alokc@codeaurora.org>
+Cc: Gilad Avidov <gavidov@codeaurora.org>
+Cc: Kiran Gunda <kgunda@codeaurora.org>
+Cc: Sagar Dharia <sdharia@codeaurora.org>
+Cc: dsneddon@codeaurora.org
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/Kconfig | 13 +
+ drivers/spi/Makefile | 1 +
+ drivers/spi/spi-qup.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 851 insertions(+)
+ create mode 100644 drivers/spi/spi-qup.c
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 581ee2a..9e9e3ed 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -381,6 +381,19 @@ config SPI_RSPI
+ help
+ SPI driver for Renesas RSPI and QSPI blocks.
+
++config SPI_QUP
++ tristate "Qualcomm SPI controller with QUP interface"
++ depends on ARCH_MSM_DT
++ help
++ Qualcomm Universal Peripheral (QUP) core is an AHB slave that
++ provides a common data path (an output FIFO and an input FIFO)
++ for serial peripheral interface (SPI) mini-core. SPI in master
++ mode supports up to 50MHz, up to four chip selects, programmable
++ data path from 4 bits to 32 bits and numerous protocol variants.
++
++ This driver can also be built as a module. If so, the module
++ will be called spi_qup.
++
+ config SPI_S3C24XX
+ tristate "Samsung S3C24XX series SPI"
+ depends on ARCH_S3C24XX
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index 95af48d..e598147 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
+ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
+ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
+ obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
++obj-$(CONFIG_SPI_QUP) += spi-qup.o
+ obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
+ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
+ spi-s3c24xx-hw-y := spi-s3c24xx.o
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+new file mode 100644
+index 0000000..b0bcc09
+--- /dev/null
++++ b/drivers/spi/spi-qup.c
+@@ -0,0 +1,837 @@
++/*
++ * Copyright (c) 2008-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 rev 2 and
++ * only rev 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/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/spi/spi.h>
++
++#define QUP_CONFIG 0x0000
++#define QUP_STATE 0x0004
++#define QUP_IO_M_MODES 0x0008
++#define QUP_SW_RESET 0x000c
++#define QUP_OPERATIONAL 0x0018
++#define QUP_ERROR_FLAGS 0x001c
++#define QUP_ERROR_FLAGS_EN 0x0020
++#define QUP_OPERATIONAL_MASK 0x0028
++#define QUP_HW_VERSION 0x0030
++#define QUP_MX_OUTPUT_CNT 0x0100
++#define QUP_OUTPUT_FIFO 0x0110
++#define QUP_MX_WRITE_CNT 0x0150
++#define QUP_MX_INPUT_CNT 0x0200
++#define QUP_MX_READ_CNT 0x0208
++#define QUP_INPUT_FIFO 0x0218
++
++#define SPI_CONFIG 0x0300
++#define SPI_IO_CONTROL 0x0304
++#define SPI_ERROR_FLAGS 0x0308
++#define SPI_ERROR_FLAGS_EN 0x030c
++
++/* QUP_CONFIG fields */
++#define QUP_CONFIG_SPI_MODE (1 << 8)
++#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13)
++#define QUP_CONFIG_NO_INPUT BIT(7)
++#define QUP_CONFIG_NO_OUTPUT BIT(6)
++#define QUP_CONFIG_N 0x001f
++
++/* QUP_STATE fields */
++#define QUP_STATE_VALID BIT(2)
++#define QUP_STATE_RESET 0
++#define QUP_STATE_RUN 1
++#define QUP_STATE_PAUSE 3
++#define QUP_STATE_MASK 3
++#define QUP_STATE_CLEAR 2
++
++#define QUP_HW_VERSION_2_1_1 0x20010001
++
++/* QUP_IO_M_MODES fields */
++#define QUP_IO_M_PACK_EN BIT(15)
++#define QUP_IO_M_UNPACK_EN BIT(14)
++#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12
++#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10
++#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT)
++#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT)
++
++#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0)
++#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2)
++#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5)
++#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7)
++
++#define QUP_IO_M_MODE_FIFO 0
++#define QUP_IO_M_MODE_BLOCK 1
++#define QUP_IO_M_MODE_DMOV 2
++#define QUP_IO_M_MODE_BAM 3
++
++/* QUP_OPERATIONAL fields */
++#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)
++#define QUP_OP_OUT_SERVICE_FLAG BIT(8)
++#define QUP_OP_IN_FIFO_FULL BIT(7)
++#define QUP_OP_OUT_FIFO_FULL BIT(6)
++#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5)
++#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4)
++
++/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */
++#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5)
++#define QUP_ERROR_INPUT_UNDER_RUN BIT(4)
++#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3)
++#define QUP_ERROR_INPUT_OVER_RUN BIT(2)
++
++/* SPI_CONFIG fields */
++#define SPI_CONFIG_HS_MODE BIT(10)
++#define SPI_CONFIG_INPUT_FIRST BIT(9)
++#define SPI_CONFIG_LOOPBACK BIT(8)
++
++/* SPI_IO_CONTROL fields */
++#define SPI_IO_C_FORCE_CS BIT(11)
++#define SPI_IO_C_CLK_IDLE_HIGH BIT(10)
++#define SPI_IO_C_MX_CS_MODE BIT(8)
++#define SPI_IO_C_CS_N_POLARITY_0 BIT(4)
++#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2)
++#define SPI_IO_C_CS_SELECT_MASK 0x000c
++#define SPI_IO_C_TRISTATE_CS BIT(1)
++#define SPI_IO_C_NO_TRI_STATE BIT(0)
++
++/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */
++#define SPI_ERROR_CLK_OVER_RUN BIT(1)
++#define SPI_ERROR_CLK_UNDER_RUN BIT(0)
++
++#define SPI_NUM_CHIPSELECTS 4
++
++/* high speed mode is when bus rate is greater then 26MHz */
++#define SPI_HS_MIN_RATE 26000000
++#define SPI_MAX_RATE 50000000
++
++#define SPI_DELAY_THRESHOLD 1
++#define SPI_DELAY_RETRY 10
++
++struct spi_qup_device {
++ int select;
++ u16 mode;
++};
++
++struct spi_qup {
++ void __iomem *base;
++ struct device *dev;
++ struct clk *cclk; /* core clock */
++ struct clk *iclk; /* interface clock */
++ int irq;
++ u32 max_speed_hz;
++ spinlock_t lock;
++
++ int in_fifo_sz;
++ int out_fifo_sz;
++ int in_blk_sz;
++ int out_blk_sz;
++
++ struct spi_transfer *xfer;
++ struct completion done;
++ int error;
++ int w_size; /* bytes per SPI word */
++ int tx_bytes;
++ int rx_bytes;
++};
++
++
++static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
++{
++ u32 opstate = readl_relaxed(controller->base + QUP_STATE);
++
++ return opstate & QUP_STATE_VALID;
++}
++
++static int spi_qup_set_state(struct spi_qup *controller, u32 state)
++{
++ unsigned long loop;
++ u32 cur_state;
++
++ loop = 0;
++ while (!spi_qup_is_valid_state(controller)) {
++
++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
++
++ if (++loop > SPI_DELAY_RETRY)
++ return -EIO;
++ }
++
++ if (loop)
++ dev_dbg(controller->dev, "invalid state for %ld,us %d\n",
++ loop, state);
++
++ cur_state = readl_relaxed(controller->base + QUP_STATE);
++ /*
++ * Per spec: for PAUSE_STATE to RESET_STATE, two writes
++ * of (b10) are required
++ */
++ if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) &&
++ (state == QUP_STATE_RESET)) {
++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
++ } else {
++ cur_state &= ~QUP_STATE_MASK;
++ cur_state |= state;
++ writel_relaxed(cur_state, controller->base + QUP_STATE);
++ }
++
++ loop = 0;
++ while (!spi_qup_is_valid_state(controller)) {
++
++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
++
++ if (++loop > SPI_DELAY_RETRY)
++ return -EIO;
++ }
++
++ return 0;
++}
++
++
++static void spi_qup_fifo_read(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ u8 *rx_buf = xfer->rx_buf;
++ u32 word, state;
++ int idx, shift, w_size;
++
++ w_size = controller->w_size;
++
++ while (controller->rx_bytes < xfer->len) {
++
++ state = readl_relaxed(controller->base + QUP_OPERATIONAL);
++ if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
++ break;
++
++ word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
++
++ if (!rx_buf) {
++ controller->rx_bytes += w_size;
++ continue;
++ }
++
++ for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++ /*
++ * The data format depends on bytes per SPI word:
++ * 4 bytes: 0x12345678
++ * 2 bytes: 0x00001234
++ * 1 byte : 0x00000012
++ */
++ shift = BITS_PER_BYTE;
++ shift *= (w_size - idx - 1);
++ rx_buf[controller->rx_bytes] = word >> shift;
++ }
++ }
++}
++
++static void spi_qup_fifo_write(struct spi_qup *controller,
++ struct spi_transfer *xfer)
++{
++ const u8 *tx_buf = xfer->tx_buf;
++ u32 word, state, data;
++ int idx, w_size;
++
++ w_size = controller->w_size;
++
++ while (controller->tx_bytes < xfer->len) {
++
++ state = readl_relaxed(controller->base + QUP_OPERATIONAL);
++ if (state & QUP_OP_OUT_FIFO_FULL)
++ break;
++
++ word = 0;
++ for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
++
++ if (!tx_buf) {
++ controller->tx_bytes += w_size;
++ break;
++ }
++
++ data = tx_buf[controller->tx_bytes];
++ word |= data << (BITS_PER_BYTE * (3 - idx));
++ }
++
++ writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
++ }
++}
++
++static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
++{
++ struct spi_qup *controller = dev_id;
++ struct spi_transfer *xfer;
++ u32 opflags, qup_err, spi_err;
++ unsigned long flags;
++ int error = 0;
++
++ spin_lock_irqsave(&controller->lock, flags);
++ xfer = controller->xfer;
++ controller->xfer = NULL;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++ qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
++ spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
++ 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 %x08 %x08 %x08\n",
++ qup_err, spi_err, opflags);
++ return IRQ_HANDLED;
++ }
++
++ if (qup_err) {
++ if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
++ dev_warn(controller->dev, "OUTPUT_OVER_RUN\n");
++ if (qup_err & QUP_ERROR_INPUT_UNDER_RUN)
++ dev_warn(controller->dev, "INPUT_UNDER_RUN\n");
++ if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN)
++ dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n");
++ if (qup_err & QUP_ERROR_INPUT_OVER_RUN)
++ dev_warn(controller->dev, "INPUT_OVER_RUN\n");
++
++ error = -EIO;
++ }
++
++ if (spi_err) {
++ if (spi_err & SPI_ERROR_CLK_OVER_RUN)
++ 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;
++ }
++
++ if (opflags & QUP_OP_IN_SERVICE_FLAG)
++ spi_qup_fifo_read(controller, xfer);
++
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++ spi_qup_fifo_write(controller, xfer);
++
++ spin_lock_irqsave(&controller->lock, flags);
++ controller->error = error;
++ controller->xfer = xfer;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++ if (controller->rx_bytes == xfer->len || error)
++ complete(&controller->done);
++
++ return IRQ_HANDLED;
++}
++
++
++/* set clock freq ... bits per word */
++static int spi_qup_io_config(struct spi_qup *controller,
++ struct spi_qup_device *chip,
++ struct spi_transfer *xfer)
++{
++ u32 config, iomode, mode;
++ int ret, n_words, w_size;
++
++ if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
++ dev_err(controller->dev, "too big size for loopback %d > %d\n",
++ xfer->len, controller->in_fifo_sz);
++ return -EIO;
++ }
++
++ ret = clk_set_rate(controller->cclk, xfer->speed_hz);
++ if (ret) {
++ dev_err(controller->dev, "fail to set frequency %d",
++ xfer->speed_hz);
++ return -EIO;
++ }
++
++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++ dev_err(controller->dev, "cannot set RESET state\n");
++ return -EIO;
++ }
++
++ w_size = 4;
++ if (xfer->bits_per_word <= 8)
++ w_size = 1;
++ else if (xfer->bits_per_word <= 16)
++ w_size = 2;
++
++ n_words = xfer->len / w_size;
++ controller->w_size = w_size;
++
++ if (n_words <= controller->in_fifo_sz) {
++ mode = QUP_IO_M_MODE_FIFO;
++ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
++ /* must be zero for FIFO */
++ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
++ } else {
++ 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);
++ 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);
++
++ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
++
++ config = readl_relaxed(controller->base + SPI_CONFIG);
++
++ if (chip->mode & SPI_LOOP)
++ config |= SPI_CONFIG_LOOPBACK;
++ else
++ config &= ~SPI_CONFIG_LOOPBACK;
++
++ if (chip->mode & SPI_CPHA)
++ config &= ~SPI_CONFIG_INPUT_FIRST;
++ else
++ config |= SPI_CONFIG_INPUT_FIRST;
++
++ /*
++ * HS_MODE improves signal stability for spi-clk high rates,
++ * but is invalid in loop back mode.
++ */
++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP))
++ config |= SPI_CONFIG_HS_MODE;
++ else
++ config &= ~SPI_CONFIG_HS_MODE;
++
++ writel_relaxed(config, controller->base + SPI_CONFIG);
++
++ config = readl_relaxed(controller->base + QUP_CONFIG);
++ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
++ config |= xfer->bits_per_word - 1;
++ config |= QUP_CONFIG_SPI_MODE;
++ writel_relaxed(config, controller->base + QUP_CONFIG);
++
++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
++ return 0;
++}
++
++static void spi_qup_set_cs(struct spi_device *spi, bool enable)
++{
++ struct spi_qup *controller = spi_master_get_devdata(spi->master);
++ struct spi_qup_device *chip = spi_get_ctldata(spi);
++
++ u32 iocontol, mask;
++
++ iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
++
++ /* Disable auto CS toggle and use manual */
++ iocontol &= ~SPI_IO_C_MX_CS_MODE;
++ iocontol |= SPI_IO_C_FORCE_CS;
++
++ iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
++ iocontol |= SPI_IO_C_CS_SELECT(chip->select);
++
++ mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select;
++
++ if (enable)
++ iocontol |= mask;
++ else
++ iocontol &= ~mask;
++
++ writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
++}
++
++static int spi_qup_transfer_one(struct spi_master *master,
++ struct spi_device *spi,
++ struct spi_transfer *xfer)
++{
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ struct spi_qup_device *chip = spi_get_ctldata(spi);
++ unsigned long timeout, flags;
++ int ret = -EIO;
++
++ ret = spi_qup_io_config(controller, chip, xfer);
++ if (ret)
++ return ret;
++
++ timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
++ timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
++ timeout = 100 * msecs_to_jiffies(timeout);
++
++ reinit_completion(&controller->done);
++
++ spin_lock_irqsave(&controller->lock, flags);
++ controller->xfer = xfer;
++ controller->error = 0;
++ controller->rx_bytes = 0;
++ controller->tx_bytes = 0;
++ spin_unlock_irqrestore(&controller->lock, flags);
++
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set RUN state\n");
++ goto exit;
++ }
++
++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
++ dev_warn(controller->dev, "cannot set PAUSE state\n");
++ goto exit;
++ }
++
++ spi_qup_fifo_write(controller, xfer);
++
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set EXECUTE state\n");
++ goto exit;
++ }
++
++ if (!wait_for_completion_timeout(&controller->done, timeout))
++ ret = -ETIMEDOUT;
++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);
++ return ret;
++}
++
++static int spi_qup_setup(struct spi_device *spi)
++{
++ struct spi_qup *controller = spi_master_get_devdata(spi->master);
++ struct spi_qup_device *chip = spi_get_ctldata(spi);
++
++ if (spi->chip_select >= spi->master->num_chipselect) {
++ dev_err(controller->dev, "invalid chip_select %d\n",
++ spi->chip_select);
++ return -EINVAL;
++ }
++
++ if (spi->max_speed_hz > controller->max_speed_hz) {
++ dev_err(controller->dev, "invalid max_speed_hz %d\n",
++ spi->max_speed_hz);
++ return -EINVAL;
++ }
++
++ if (!chip) {
++ /* First setup */
++ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
++ if (!chip) {
++ dev_err(controller->dev, "no memory for chip data\n");
++ return -ENOMEM;
++ }
++
++ chip->mode = spi->mode;
++ chip->select = spi->chip_select;
++ spi_set_ctldata(spi, chip);
++ }
++
++ return 0;
++}
++
++static void spi_qup_cleanup(struct spi_device *spi)
++{
++ struct spi_qup_device *chip = spi_get_ctldata(spi);
++
++ if (!chip)
++ return;
++
++ spi_set_ctldata(spi, NULL);
++ kfree(chip);
++}
++
++static int spi_qup_probe(struct platform_device *pdev)
++{
++ struct spi_master *master;
++ struct clk *iclk, *cclk;
++ struct spi_qup *controller;
++ struct resource *res;
++ struct device *dev;
++ void __iomem *base;
++ u32 data, max_freq, iomode;
++ int ret, irq, size;
++
++ dev = &pdev->dev;
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ irq = platform_get_irq(pdev, 0);
++
++ if (irq < 0)
++ return irq;
++
++ cclk = devm_clk_get(dev, "core");
++ if (IS_ERR(cclk))
++ return PTR_ERR(cclk);
++
++ iclk = devm_clk_get(dev, "iface");
++ if (IS_ERR(iclk))
++ return PTR_ERR(iclk);
++
++ /* This is optional parameter */
++ if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
++ max_freq = SPI_MAX_RATE;
++
++ if (!max_freq || max_freq > SPI_MAX_RATE) {
++ dev_err(dev, "invalid clock frequency %d\n", max_freq);
++ return -ENXIO;
++ }
++
++ ret = clk_prepare_enable(cclk);
++ if (ret) {
++ dev_err(dev, "cannot enable core clock\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(iclk);
++ if (ret) {
++ clk_disable_unprepare(cclk);
++ dev_err(dev, "cannot enable iface clock\n");
++ return ret;
++ }
++
++ data = readl_relaxed(base + QUP_HW_VERSION);
++
++ if (data < QUP_HW_VERSION_2_1_1) {
++ clk_disable_unprepare(cclk);
++ clk_disable_unprepare(iclk);
++ dev_err(dev, "v.%08x is not supported\n", data);
++ return -ENXIO;
++ }
++
++ master = spi_alloc_master(dev, sizeof(struct spi_qup));
++ if (!master) {
++ clk_disable_unprepare(cclk);
++ clk_disable_unprepare(iclk);
++ dev_err(dev, "cannot allocate master\n");
++ return -ENOMEM;
++ }
++
++ master->bus_num = pdev->id;
++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
++ master->num_chipselect = SPI_NUM_CHIPSELECTS;
++ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
++ master->setup = spi_qup_setup;
++ master->cleanup = spi_qup_cleanup;
++ master->set_cs = spi_qup_set_cs;
++ master->transfer_one = spi_qup_transfer_one;
++ master->dev.of_node = pdev->dev.of_node;
++ master->auto_runtime_pm = true;
++
++ platform_set_drvdata(pdev, master);
++
++ controller = spi_master_get_devdata(master);
++
++ controller->dev = dev;
++ controller->base = base;
++ controller->iclk = iclk;
++ controller->cclk = cclk;
++ controller->irq = irq;
++ controller->max_speed_hz = max_freq;
++
++ spin_lock_init(&controller->lock);
++ init_completion(&controller->done);
++
++ iomode = readl_relaxed(base + QUP_IO_M_MODES);
++
++ size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
++ if (size)
++ controller->out_blk_sz = size * 16;
++ else
++ controller->out_blk_sz = 4;
++
++ size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
++ if (size)
++ controller->in_blk_sz = size * 16;
++ else
++ controller->in_blk_sz = 4;
++
++ size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
++ controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
++
++ size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
++ controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
++
++ dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
++ data, controller->in_blk_sz, controller->in_fifo_sz,
++ controller->out_blk_sz, controller->out_fifo_sz);
++
++ writel_relaxed(1, base + QUP_SW_RESET);
++
++ ret = spi_qup_set_state(controller, QUP_STATE_RESET);
++ if (ret) {
++ dev_err(dev, "cannot set RESET state\n");
++ goto error;
++ }
++
++ writel_relaxed(0, base + QUP_OPERATIONAL);
++ writel_relaxed(0, base + QUP_IO_M_MODES);
++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
++ writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
++ base + SPI_ERROR_FLAGS_EN);
++
++ writel_relaxed(0, base + SPI_CONFIG);
++ writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
++
++ ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
++ IRQF_TRIGGER_HIGH, pdev->name, controller);
++ if (ret)
++ goto error;
++
++ ret = devm_spi_register_master(dev, master);
++ if (ret)
++ goto error;
++
++ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
++ pm_runtime_use_autosuspend(dev);
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++ return 0;
++
++error:
++ clk_disable_unprepare(cclk);
++ clk_disable_unprepare(iclk);
++ spi_master_put(master);
++ return ret;
++}
++
++#ifdef CONFIG_PM_RUNTIME
++static int spi_qup_pm_suspend_runtime(struct device *device)
++{
++ struct spi_master *master = dev_get_drvdata(device);
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ u32 config;
++
++ /* Enable clocks auto gaiting */
++ config = readl(controller->base + QUP_CONFIG);
++ config |= QUP_CLOCK_AUTO_GATE;
++ writel_relaxed(config, controller->base + QUP_CONFIG);
++ return 0;
++}
++
++static int spi_qup_pm_resume_runtime(struct device *device)
++{
++ struct spi_master *master = dev_get_drvdata(device);
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ u32 config;
++
++ /* Disable clocks auto gaiting */
++ config = readl_relaxed(controller->base + QUP_CONFIG);
++ config &= ~QUP_CLOCK_AUTO_GATE;
++ writel_relaxed(config, controller->base + QUP_CONFIG);
++ return 0;
++}
++#endif /* CONFIG_PM_RUNTIME */
++
++#ifdef CONFIG_PM_SLEEP
++static int spi_qup_suspend(struct device *device)
++{
++ struct spi_master *master = dev_get_drvdata(device);
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ int ret;
++
++ ret = spi_master_suspend(master);
++ if (ret)
++ return ret;
++
++ ret = spi_qup_set_state(controller, QUP_STATE_RESET);
++ if (ret)
++ return ret;
++
++ clk_disable_unprepare(controller->cclk);
++ clk_disable_unprepare(controller->iclk);
++ return 0;
++}
++
++static int spi_qup_resume(struct device *device)
++{
++ struct spi_master *master = dev_get_drvdata(device);
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ int ret;
++
++ ret = clk_prepare_enable(controller->iclk);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(controller->cclk);
++ if (ret)
++ return ret;
++
++ ret = spi_qup_set_state(controller, QUP_STATE_RESET);
++ if (ret)
++ return ret;
++
++ return spi_master_resume(master);
++}
++#endif /* CONFIG_PM_SLEEP */
++
++static int spi_qup_remove(struct platform_device *pdev)
++{
++ struct spi_master *master = dev_get_drvdata(&pdev->dev);
++ struct spi_qup *controller = spi_master_get_devdata(master);
++ int ret;
++
++ ret = pm_runtime_get_sync(&pdev->dev);
++ if (ret)
++ return ret;
++
++ ret = spi_qup_set_state(controller, QUP_STATE_RESET);
++ if (ret)
++ return ret;
++
++ clk_disable_unprepare(controller->cclk);
++ clk_disable_unprepare(controller->iclk);
++
++ pm_runtime_put_noidle(&pdev->dev);
++ pm_runtime_disable(&pdev->dev);
++ spi_master_put(master);
++ return 0;
++}
++
++static struct of_device_id spi_qup_dt_match[] = {
++ { .compatible = "qcom,spi-qup-v2.1.1", },
++ { .compatible = "qcom,spi-qup-v2.2.1", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
++
++static const struct dev_pm_ops spi_qup_dev_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
++ SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
++ spi_qup_pm_resume_runtime,
++ NULL)
++};
++
++static struct platform_driver spi_qup_driver = {
++ .driver = {
++ .name = "spi_qup",
++ .owner = THIS_MODULE,
++ .pm = &spi_qup_dev_pm_ops,
++ .of_match_table = spi_qup_dt_match,
++ },
++ .probe = spi_qup_probe,
++ .remove = spi_qup_remove,
++};
++module_platform_driver(spi_qup_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_VERSION("0.4");
++MODULE_ALIAS("platform:spi_qup");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch
new file mode 100644
index 0000000..0d8b14c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch
@@ -0,0 +1,111 @@
+From a8e8c90a3cc81c6a7a44ff7fb18ceb71978c9155 Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Thu, 13 Feb 2014 18:21:23 +0200
+Subject: [PATCH 066/182] spi: qup: Add device tree bindings information
+
+The Qualcomm Universal Peripheral (QUP) core is an
+AHB slave that provides a common data path (an output
+FIFO and an input FIFO) for serial peripheral interface
+(SPI) mini-core.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ .../devicetree/bindings/spi/qcom,spi-qup.txt | 85 ++++++++++++++++++++
+ 1 file changed, 85 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+
+diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+new file mode 100644
+index 0000000..b82a268
+--- /dev/null
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -0,0 +1,85 @@
++Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
++
++The QUP core is an AHB slave that provides a common data path (an output FIFO
++and an input FIFO) for serial peripheral interface (SPI) mini-core.
++
++SPI in master mode supports up to 50MHz, up to four chip selects, programmable
++data path from 4 bits to 32 bits and numerous protocol variants.
++
++Required properties:
++- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1"
++- reg: Should contain base register location and length
++- interrupts: Interrupt number used by this controller
++
++- clocks: Should contain the core clock and the AHB clock.
++- clock-names: Should be "core" for the core clock and "iface" for the
++ AHB clock.
++
++- #address-cells: Number of cells required to define a chip select
++ address on the SPI bus. Should be set to 1.
++- #size-cells: Should be zero.
++
++Optional properties:
++- spi-max-frequency: Specifies maximum SPI clock frequency,
++ Units - Hz. Definition as per
++ Documentation/devicetree/bindings/spi/spi-bus.txt
++
++SPI slave nodes must be children of the SPI master node and can contain
++properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
++
++Example:
++
++ spi_8: spi@f9964000 { /* BLSP2 QUP2 */
++
++ compatible = "qcom,spi-qup-v2";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0xf9964000 0x1000>;
++ interrupts = <0 102 0>;
++ spi-max-frequency = <19200000>;
++
++ clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
++ clock-names = "core", "iface";
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi8_default>;
++
++ device@0 {
++ compatible = "arm,pl022-dummy";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <0>; /* Chip select 0 */
++ spi-max-frequency = <19200000>;
++ spi-cpol;
++ };
++
++ device@1 {
++ compatible = "arm,pl022-dummy";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <1>; /* Chip select 1 */
++ spi-max-frequency = <9600000>;
++ spi-cpha;
++ };
++
++ device@2 {
++ compatible = "arm,pl022-dummy";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <2>; /* Chip select 2 */
++ spi-max-frequency = <19200000>;
++ spi-cpol;
++ spi-cpha;
++ };
++
++ device@3 {
++ compatible = "arm,pl022-dummy";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <3>; /* Chip select 3 */
++ spi-max-frequency = <19200000>;
++ spi-cpol;
++ spi-cpha;
++ spi-cs-high;
++ };
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch
new file mode 100644
index 0000000..decf585
--- /dev/null
+++ b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch
@@ -0,0 +1,30 @@
+From c6a3f7bda60cfd78c2b05e8b2ec0fbf0d39da9ea Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Fri, 21 Feb 2014 09:33:16 +0800
+Subject: [PATCH 067/182] spi: qup: Remove spi_master_put in spi_qup_remove
+
+This driver uses devm_spi_register_master() so don't explicitly call
+spi_master_put() in spi_qup_remove().
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index b0bcc09..5edc56f 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -802,7 +802,6 @@ static int spi_qup_remove(struct platform_device *pdev)
+
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+- spi_master_put(master);
+ return 0;
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch
new file mode 100644
index 0000000..6914d44
--- /dev/null
+++ b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch
@@ -0,0 +1,69 @@
+From a4b122f945c564e47a42a9665b484785e86648de Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Fri, 21 Feb 2014 09:34:16 +0800
+Subject: [PATCH 068/182] spi: qup: Convert ot let spi core handle checking
+ transfer speed
+
+Set master->max_speed_hz then spi core will handle checking transfer speed.
+So we can remove the same checking in this driver.
+
+Also remove checking spi->chip_select in spi_qup_setup(), the checking is done
+by spi core.
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 15 +--------------
+ 1 file changed, 1 insertion(+), 14 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 5edc56f..dec339d 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -134,7 +134,6 @@ struct spi_qup {
+ struct clk *cclk; /* core clock */
+ struct clk *iclk; /* interface clock */
+ int irq;
+- u32 max_speed_hz;
+ spinlock_t lock;
+
+ int in_fifo_sz;
+@@ -517,18 +516,6 @@ static int spi_qup_setup(struct spi_device *spi)
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+ struct spi_qup_device *chip = spi_get_ctldata(spi);
+
+- if (spi->chip_select >= spi->master->num_chipselect) {
+- dev_err(controller->dev, "invalid chip_select %d\n",
+- spi->chip_select);
+- return -EINVAL;
+- }
+-
+- if (spi->max_speed_hz > controller->max_speed_hz) {
+- dev_err(controller->dev, "invalid max_speed_hz %d\n",
+- spi->max_speed_hz);
+- return -EINVAL;
+- }
+-
+ if (!chip) {
+ /* First setup */
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+@@ -629,6 +616,7 @@ static int spi_qup_probe(struct platform_device *pdev)
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
++ master->max_speed_hz = max_freq;
+ master->setup = spi_qup_setup;
+ master->cleanup = spi_qup_cleanup;
+ master->set_cs = spi_qup_set_cs;
+@@ -645,7 +633,6 @@ static int spi_qup_probe(struct platform_device *pdev)
+ controller->iclk = iclk;
+ controller->cclk = cclk;
+ controller->irq = irq;
+- controller->max_speed_hz = max_freq;
+
+ spin_lock_init(&controller->lock);
+ init_completion(&controller->done);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch
new file mode 100644
index 0000000..558f860
--- /dev/null
+++ b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch
@@ -0,0 +1,48 @@
+From df7444fdd808111f7df507b00d357b44d3259376 Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Sun, 23 Feb 2014 13:27:16 +0800
+Subject: [PATCH 069/182] spi: qup: Fix build error due to a typo
+
+Fix below build error when CONFIG_PM_RUNTIME=y:
+
+C [M] drivers/spi/spi-qup.o
+drivers/spi/spi-qup.c: In function 'spi_qup_pm_suspend_runtime':
+drivers/spi/spi-qup.c:712:12: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function)
+drivers/spi/spi-qup.c:712:12: note: each undeclared identifier is reported only once for each function it appears in
+drivers/spi/spi-qup.c: In function 'spi_qup_pm_resume_runtime':
+drivers/spi/spi-qup.c:725:13: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function)
+make[2]: *** [drivers/spi/spi-qup.o] Error 1
+make[1]: *** [drivers/spi] Error 2
+make: *** [drivers] Error 2
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index dec339d..886edb4 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -709,7 +709,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device)
+
+ /* Enable clocks auto gaiting */
+ config = readl(controller->base + QUP_CONFIG);
+- config |= QUP_CLOCK_AUTO_GATE;
++ config |= QUP_CONFIG_CLOCK_AUTO_GATE;
+ writel_relaxed(config, controller->base + QUP_CONFIG);
+ return 0;
+ }
+@@ -722,7 +722,7 @@ static int spi_qup_pm_resume_runtime(struct device *device)
+
+ /* Disable clocks auto gaiting */
+ config = readl_relaxed(controller->base + QUP_CONFIG);
+- config &= ~QUP_CLOCK_AUTO_GATE;
++ config &= ~QUP_CONFIG_CLOCK_AUTO_GATE;
+ writel_relaxed(config, controller->base + QUP_CONFIG);
+ return 0;
+ }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch
new file mode 100644
index 0000000..aee0bf4
--- /dev/null
+++ b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch
@@ -0,0 +1,30 @@
+From 4706cb5844e0f1a08ab25e103f6415fde5328ddc Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Sun, 23 Feb 2014 13:28:33 +0800
+Subject: [PATCH 070/182] spi: qup: Enable driver compilation with
+ COMPILE_TEST
+
+This helps increasing build testing coverage.
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 9e9e3ed..e6a04f8 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -383,7 +383,7 @@ config SPI_RSPI
+
+ config SPI_QUP
+ tristate "Qualcomm SPI controller with QUP interface"
+- depends on ARCH_MSM_DT
++ depends on ARCH_MSM_DT || COMPILE_TEST
+ help
+ Qualcomm Universal Peripheral (QUP) core is an AHB slave that
+ provides a common data path (an output FIFO and an input FIFO)
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch
new file mode 100644
index 0000000..ce773da
--- /dev/null
+++ b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch
@@ -0,0 +1,40 @@
+From 7137376bc6415ab9b2f0c5983245a1273812e8b9 Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Mon, 24 Feb 2014 12:07:51 +0800
+Subject: [PATCH 071/182] spi: qup: Depend on ARM && COMPILE_TEST to avoid
+ build error
+
+This driver uses writel_relaxed() which does not exist in x86, ppc, etc.
+Make it depend on ARM && COMPILE_TEST to avoid below build error:
+
+ CC [M] drivers/spi/spi-qup.o
+drivers/spi/spi-qup.c: In function 'spi_qup_set_state':
+drivers/spi/spi-qup.c:180:3: error: implicit declaration of function 'writel_relaxed' [-Werror=implicit-function-declaration]
+cc1: some warnings being treated as errors
+make[2]: *** [drivers/spi/spi-qup.o] Error 1
+make[1]: *** [drivers/spi] Error 2
+make: *** [drivers] Error 2
+
+Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index e6a04f8..2d9111c 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -383,7 +383,7 @@ config SPI_RSPI
+
+ config SPI_QUP
+ tristate "Qualcomm SPI controller with QUP interface"
+- depends on ARCH_MSM_DT || COMPILE_TEST
++ depends on ARCH_MSM_DT || (ARM && COMPILE_TEST)
+ help
+ Qualcomm Universal Peripheral (QUP) core is an AHB slave that
+ provides a common data path (an output FIFO and an input FIFO)
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch
new file mode 100644
index 0000000..fcdc517
--- /dev/null
+++ b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch
@@ -0,0 +1,28 @@
+From 7b8e00404cfc652abbd538df1365a8046ed0d782 Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Mon, 24 Feb 2014 13:48:12 +0800
+Subject: [PATCH 072/182] spi: qup: Remove module version
+
+The module version is unlikely to be updated, use kernel version should be
+enough.
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 886edb4..203f0d4 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -819,5 +819,4 @@ static struct platform_driver spi_qup_driver = {
+ module_platform_driver(spi_qup_driver);
+
+ MODULE_LICENSE("GPL v2");
+-MODULE_VERSION("0.4");
+ MODULE_ALIAS("platform:spi_qup");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch
new file mode 100644
index 0000000..69b4609
--- /dev/null
+++ b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch
@@ -0,0 +1,167 @@
+From 3027505ab8c8cb17291e53bca1d7bd770539d468 Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin@ingics.com>
+Date: Mon, 24 Feb 2014 23:07:36 +0800
+Subject: [PATCH 073/182] spi: qup: Get rid of using struct spi_qup_device
+
+Current code uses struct spi_qup_device to store spi->mode and spi->chip_select
+settings. We can get these settings in spi_qup_transfer_one and spi_qup_set_cs
+without using struct spi_qup_device. Refactor the code a bit to remove
+spi_qup_setup(), spi_qup_cleanup(), and struct spi_qup_device.
+
+Signed-off-by: Axel Lin <axel.lin@ingics.com>
+Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 61 ++++++++-----------------------------------------
+ 1 file changed, 9 insertions(+), 52 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 203f0d4..b032e88 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -123,11 +123,6 @@
+ #define SPI_DELAY_THRESHOLD 1
+ #define SPI_DELAY_RETRY 10
+
+-struct spi_qup_device {
+- int select;
+- u16 mode;
+-};
+-
+ struct spi_qup {
+ void __iomem *base;
+ struct device *dev;
+@@ -338,14 +333,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+
+
+ /* set clock freq ... bits per word */
+-static int spi_qup_io_config(struct spi_qup *controller,
+- struct spi_qup_device *chip,
+- struct spi_transfer *xfer)
++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;
+ int ret, n_words, w_size;
+
+- if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
++ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+ dev_err(controller->dev, "too big size for loopback %d > %d\n",
+ xfer->len, controller->in_fifo_sz);
+ return -EIO;
+@@ -399,12 +393,12 @@ static int spi_qup_io_config(struct spi_qup *controller,
+
+ config = readl_relaxed(controller->base + SPI_CONFIG);
+
+- if (chip->mode & SPI_LOOP)
++ if (spi->mode & SPI_LOOP)
+ config |= SPI_CONFIG_LOOPBACK;
+ else
+ config &= ~SPI_CONFIG_LOOPBACK;
+
+- if (chip->mode & SPI_CPHA)
++ if (spi->mode & SPI_CPHA)
+ config &= ~SPI_CONFIG_INPUT_FIRST;
+ else
+ config |= SPI_CONFIG_INPUT_FIRST;
+@@ -413,7 +407,7 @@ static int spi_qup_io_config(struct spi_qup *controller,
+ * HS_MODE improves signal stability for spi-clk high rates,
+ * but is invalid in loop back mode.
+ */
+- if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP))
++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
+ config |= SPI_CONFIG_HS_MODE;
+ else
+ config &= ~SPI_CONFIG_HS_MODE;
+@@ -433,7 +427,6 @@ static int spi_qup_io_config(struct spi_qup *controller,
+ static void spi_qup_set_cs(struct spi_device *spi, bool enable)
+ {
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- struct spi_qup_device *chip = spi_get_ctldata(spi);
+
+ u32 iocontol, mask;
+
+@@ -444,9 +437,9 @@ static void spi_qup_set_cs(struct spi_device *spi, bool enable)
+ iocontol |= SPI_IO_C_FORCE_CS;
+
+ iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
+- iocontol |= SPI_IO_C_CS_SELECT(chip->select);
++ iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select);
+
+- mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select;
++ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+
+ if (enable)
+ iocontol |= mask;
+@@ -461,11 +454,10 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ struct spi_transfer *xfer)
+ {
+ struct spi_qup *controller = spi_master_get_devdata(master);
+- struct spi_qup_device *chip = spi_get_ctldata(spi);
+ unsigned long timeout, flags;
+ int ret = -EIO;
+
+- ret = spi_qup_io_config(controller, chip, xfer);
++ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
+@@ -511,38 +503,6 @@ exit:
+ return ret;
+ }
+
+-static int spi_qup_setup(struct spi_device *spi)
+-{
+- struct spi_qup *controller = spi_master_get_devdata(spi->master);
+- struct spi_qup_device *chip = spi_get_ctldata(spi);
+-
+- if (!chip) {
+- /* First setup */
+- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+- if (!chip) {
+- dev_err(controller->dev, "no memory for chip data\n");
+- return -ENOMEM;
+- }
+-
+- chip->mode = spi->mode;
+- chip->select = spi->chip_select;
+- spi_set_ctldata(spi, chip);
+- }
+-
+- return 0;
+-}
+-
+-static void spi_qup_cleanup(struct spi_device *spi)
+-{
+- struct spi_qup_device *chip = spi_get_ctldata(spi);
+-
+- if (!chip)
+- return;
+-
+- spi_set_ctldata(spi, NULL);
+- kfree(chip);
+-}
+-
+ static int spi_qup_probe(struct platform_device *pdev)
+ {
+ struct spi_master *master;
+@@ -561,7 +521,6 @@ static int spi_qup_probe(struct platform_device *pdev)
+ return PTR_ERR(base);
+
+ irq = platform_get_irq(pdev, 0);
+-
+ if (irq < 0)
+ return irq;
+
+@@ -617,8 +576,6 @@ static int spi_qup_probe(struct platform_device *pdev)
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+ master->max_speed_hz = max_freq;
+- master->setup = spi_qup_setup;
+- master->cleanup = spi_qup_cleanup;
+ master->set_cs = spi_qup_set_cs;
+ master->transfer_one = spi_qup_transfer_one;
+ master->dev.of_node = pdev->dev.of_node;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch
new file mode 100644
index 0000000..9f279e0
--- /dev/null
+++ b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch
@@ -0,0 +1,36 @@
+From 5148138d2b879c7b024fa3865fb3980c6dc84fcc Mon Sep 17 00:00:00 2001
+From: Paul Bolle <pebolle@tiscali.nl>
+Date: Mon, 7 Apr 2014 16:15:45 +0200
+Subject: [PATCH 074/182] spi: qup: Depend on ARCH_QCOM
+
+Commit 8fc1b0f87d9f ("ARM: qcom: Split Qualcomm support into legacy and
+multiplatform") removed Kconfig symbol ARCH_MSM_DT. But that commit
+left one (optional) dependency on ARCH_MSM_DT untouched.
+
+Three Kconfig symbols used to depend on ARCH_MSM_DT: ARCH_MSM8X60,
+ARCH_MSM8960, and ARCH_MSM8974. These three symbols now depend on
+ARCH_QCOM. So it appears this driver needs to depend on ARCH_QCOM too.
+
+Signed-off-by: Paul Bolle <pebolle@tiscali.nl>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 2d9111c..a2d3570 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -383,7 +383,7 @@ config SPI_RSPI
+
+ config SPI_QUP
+ tristate "Qualcomm SPI controller with QUP interface"
+- depends on ARCH_MSM_DT || (ARM && COMPILE_TEST)
++ depends on ARCH_QCOM || (ARM && COMPILE_TEST)
+ help
+ Qualcomm Universal Peripheral (QUP) core is an AHB slave that
+ provides a common data path (an output FIFO and an input FIFO)
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch
new file mode 100644
index 0000000..f1071db
--- /dev/null
+++ b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch
@@ -0,0 +1,39 @@
+From e8e77359524e6629bac68db4183694fccffaed8e Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Tue, 13 May 2014 16:34:42 -0500
+Subject: [PATCH 075/182] spi: qup: Correct selection of FIFO/Block mode
+
+This patch fixes the calculation for determining whether to use FIFO or BLOCK
+mode.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+---
+ drivers/spi/spi-qup.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index b032e88..65bf18e 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -287,7 +287,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+
+ if (!xfer) {
+- dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n",
++ dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
+ qup_err, spi_err, opflags);
+ return IRQ_HANDLED;
+ }
+@@ -366,7 +366,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ n_words = xfer->len / w_size;
+ controller->w_size = w_size;
+
+- if (n_words <= controller->in_fifo_sz) {
++ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+ mode = QUP_IO_M_MODE_FIFO;
+ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch
new file mode 100644
index 0000000..c801ed5
--- /dev/null
+++ b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch
@@ -0,0 +1,755 @@
+From d79cb0c583772f38a9367af106d61bcf8bfa08e4 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 21 Mar 2014 17:59:37 -0700
+Subject: [PATCH 078/182] clk: qcom: Consolidate common probe code
+
+Most of the probe code is the same between all the different
+clock controllers. Consolidate the code into a common.c file.
+This makes changes to the common probe parts easier and reduces
+chances for bugs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/common.c | 99 +++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/common.h | 34 ++++++++++++++
+ drivers/clk/qcom/gcc-msm8660.c | 87 +++++-----------------------------
+ drivers/clk/qcom/gcc-msm8960.c | 77 +++++-------------------------
+ drivers/clk/qcom/gcc-msm8974.c | 77 +++++-------------------------
+ drivers/clk/qcom/mmcc-msm8960.c | 78 +++++-------------------------
+ drivers/clk/qcom/mmcc-msm8974.c | 80 +++++++------------------------
+ 8 files changed, 196 insertions(+), 337 deletions(-)
+ create mode 100644 drivers/clk/qcom/common.c
+ create mode 100644 drivers/clk/qcom/common.h
+
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index f60db2e..689e05b 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -1,5 +1,6 @@
+ obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o
+
++clk-qcom-y += common.o
+ clk-qcom-y += clk-regmap.o
+ clk-qcom-y += clk-pll.o
+ clk-qcom-y += clk-rcg.o
+diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
+new file mode 100644
+index 0000000..86b45fb
+--- /dev/null
++++ b/drivers/clk/qcom/common.c
+@@ -0,0 +1,99 @@
++/*
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/export.h>
++#include <linux/regmap.h>
++#include <linux/platform_device.h>
++#include <linux/clk-provider.h>
++#include <linux/reset-controller.h>
++
++#include "common.h"
++#include "clk-regmap.h"
++#include "reset.h"
++
++struct qcom_cc {
++ struct qcom_reset_controller reset;
++ struct clk_onecell_data data;
++ struct clk *clks[];
++};
++
++int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
++{
++ void __iomem *base;
++ struct resource *res;
++ int i, ret;
++ struct device *dev = &pdev->dev;
++ struct clk *clk;
++ struct clk_onecell_data *data;
++ struct clk **clks;
++ struct regmap *regmap;
++ struct qcom_reset_controller *reset;
++ struct qcom_cc *cc;
++ size_t num_clks = desc->num_clks;
++ struct clk_regmap **rclks = desc->clks;
++
++ 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(dev, base, desc->config);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
++ GFP_KERNEL);
++ if (!cc)
++ return -ENOMEM;
++
++ clks = cc->clks;
++ data = &cc->data;
++ data->clks = clks;
++ data->clk_num = num_clks;
++
++ for (i = 0; i < num_clks; i++) {
++ if (!rclks[i])
++ continue;
++ clk = devm_clk_register_regmap(dev, rclks[i]);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++ clks[i] = clk;
++ }
++
++ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
++ if (ret)
++ return ret;
++
++ reset = &cc->reset;
++ reset->rcdev.of_node = dev->of_node;
++ reset->rcdev.ops = &qcom_reset_ops;
++ reset->rcdev.owner = dev->driver->owner;
++ reset->rcdev.nr_resets = desc->num_resets;
++ reset->regmap = regmap;
++ reset->reset_map = desc->resets;
++ platform_set_drvdata(pdev, &reset->rcdev);
++
++ ret = reset_controller_register(&reset->rcdev);
++ if (ret)
++ of_clk_del_provider(dev->of_node);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(qcom_cc_probe);
++
++void qcom_cc_remove(struct platform_device *pdev)
++{
++ of_clk_del_provider(pdev->dev.of_node);
++ reset_controller_unregister(platform_get_drvdata(pdev));
++}
++EXPORT_SYMBOL_GPL(qcom_cc_remove);
+diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
+new file mode 100644
+index 0000000..2c3cfc8
+--- /dev/null
++++ b/drivers/clk/qcom/common.h
+@@ -0,0 +1,34 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#ifndef __QCOM_CLK_COMMON_H__
++#define __QCOM_CLK_COMMON_H__
++
++struct platform_device;
++struct regmap_config;
++struct clk_regmap;
++struct qcom_reset_map;
++
++struct qcom_cc_desc {
++ const struct regmap_config *config;
++ struct clk_regmap **clks;
++ size_t num_clks;
++ const struct qcom_reset_map *resets;
++ size_t num_resets;
++};
++
++extern int qcom_cc_probe(struct platform_device *pdev,
++ const struct qcom_cc_desc *desc);
++
++extern void qcom_cc_remove(struct platform_device *pdev);
++
++#endif
+diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c
+index bc0b7f1..44bc6fa 100644
+--- a/drivers/clk/qcom/gcc-msm8660.c
++++ b/drivers/clk/qcom/gcc-msm8660.c
+@@ -25,6 +25,7 @@
+ #include <dt-bindings/clock/qcom,gcc-msm8660.h>
+ #include <dt-bindings/reset/qcom,gcc-msm8660.h>
+
++#include "common.h"
+ #include "clk-regmap.h"
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+@@ -2701,94 +2702,28 @@ static const struct regmap_config gcc_msm8660_regmap_config = {
+ .fast_io = true,
+ };
+
++static const struct qcom_cc_desc gcc_msm8660_desc = {
++ .config = &gcc_msm8660_regmap_config,
++ .clks = gcc_msm8660_clks,
++ .num_clks = ARRAY_SIZE(gcc_msm8660_clks),
++ .resets = gcc_msm8660_resets,
++ .num_resets = ARRAY_SIZE(gcc_msm8660_resets),
++};
++
+ static const struct of_device_id gcc_msm8660_match_table[] = {
+ { .compatible = "qcom,gcc-msm8660" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table);
+
+-struct qcom_cc {
+- struct qcom_reset_controller reset;
+- struct clk_onecell_data data;
+- struct clk *clks[];
+-};
+-
+ static int gcc_msm8660_probe(struct platform_device *pdev)
+ {
+- void __iomem *base;
+- struct resource *res;
+- int i, ret;
+- struct device *dev = &pdev->dev;
+- struct clk *clk;
+- struct clk_onecell_data *data;
+- struct clk **clks;
+- struct regmap *regmap;
+- size_t num_clks;
+- struct qcom_reset_controller *reset;
+- struct qcom_cc *cc;
+-
+- 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(dev, base, &gcc_msm8660_regmap_config);
+- if (IS_ERR(regmap))
+- return PTR_ERR(regmap);
+-
+- num_clks = ARRAY_SIZE(gcc_msm8660_clks);
+- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+- GFP_KERNEL);
+- if (!cc)
+- return -ENOMEM;
+-
+- clks = cc->clks;
+- data = &cc->data;
+- data->clks = clks;
+- data->clk_num = num_clks;
+-
+- /* Temporary until RPM clocks supported */
+- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+-
+- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+-
+- for (i = 0; i < num_clks; i++) {
+- if (!gcc_msm8660_clks[i])
+- continue;
+- clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+- clks[i] = clk;
+- }
+-
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+- if (ret)
+- return ret;
+-
+- reset = &cc->reset;
+- reset->rcdev.of_node = dev->of_node;
+- reset->rcdev.ops = &qcom_reset_ops,
+- reset->rcdev.owner = THIS_MODULE,
+- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets),
+- reset->regmap = regmap;
+- reset->reset_map = gcc_msm8660_resets,
+- platform_set_drvdata(pdev, &reset->rcdev);
+-
+- ret = reset_controller_register(&reset->rcdev);
+- if (ret)
+- of_clk_del_provider(dev->of_node);
+-
+- return ret;
++ return qcom_cc_probe(pdev, &gcc_msm8660_desc);
+ }
+
+ static int gcc_msm8660_remove(struct platform_device *pdev)
+ {
+- of_clk_del_provider(pdev->dev.of_node);
+- reset_controller_unregister(platform_get_drvdata(pdev));
++ qcom_cc_remove(pdev);
+ return 0;
+ }
+
+diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
+index fd446ab..633b019 100644
+--- a/drivers/clk/qcom/gcc-msm8960.c
++++ b/drivers/clk/qcom/gcc-msm8960.c
+@@ -25,6 +25,7 @@
+ #include <dt-bindings/clock/qcom,gcc-msm8960.h>
+ #include <dt-bindings/reset/qcom,gcc-msm8960.h>
+
++#include "common.h"
+ #include "clk-regmap.h"
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+@@ -2875,51 +2876,24 @@ static const struct regmap_config gcc_msm8960_regmap_config = {
+ .fast_io = true,
+ };
+
++static const struct qcom_cc_desc gcc_msm8960_desc = {
++ .config = &gcc_msm8960_regmap_config,
++ .clks = gcc_msm8960_clks,
++ .num_clks = ARRAY_SIZE(gcc_msm8960_clks),
++ .resets = gcc_msm8960_resets,
++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets),
++};
++
+ static const struct of_device_id gcc_msm8960_match_table[] = {
+ { .compatible = "qcom,gcc-msm8960" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table);
+
+-struct qcom_cc {
+- struct qcom_reset_controller reset;
+- struct clk_onecell_data data;
+- struct clk *clks[];
+-};
+-
+ static int gcc_msm8960_probe(struct platform_device *pdev)
+ {
+- void __iomem *base;
+- struct resource *res;
+- int i, ret;
+- struct device *dev = &pdev->dev;
+ struct clk *clk;
+- struct clk_onecell_data *data;
+- struct clk **clks;
+- struct regmap *regmap;
+- size_t num_clks;
+- struct qcom_reset_controller *reset;
+- struct qcom_cc *cc;
+-
+- 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(dev, base, &gcc_msm8960_regmap_config);
+- if (IS_ERR(regmap))
+- return PTR_ERR(regmap);
+-
+- num_clks = ARRAY_SIZE(gcc_msm8960_clks);
+- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+- GFP_KERNEL);
+- if (!cc)
+- return -ENOMEM;
+-
+- clks = cc->clks;
+- data = &cc->data;
+- data->clks = clks;
+- data->clk_num = num_clks;
++ struct device *dev = &pdev->dev;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
+@@ -2930,39 +2904,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+- for (i = 0; i < num_clks; i++) {
+- if (!gcc_msm8960_clks[i])
+- continue;
+- clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+- clks[i] = clk;
+- }
+-
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+- if (ret)
+- return ret;
+-
+- reset = &cc->reset;
+- reset->rcdev.of_node = dev->of_node;
+- reset->rcdev.ops = &qcom_reset_ops,
+- reset->rcdev.owner = THIS_MODULE,
+- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets),
+- reset->regmap = regmap;
+- reset->reset_map = gcc_msm8960_resets,
+- platform_set_drvdata(pdev, &reset->rcdev);
+-
+- ret = reset_controller_register(&reset->rcdev);
+- if (ret)
+- of_clk_del_provider(dev->of_node);
+-
+- return ret;
++ return qcom_cc_probe(pdev, &gcc_msm8960_desc);
+ }
+
+ static int gcc_msm8960_remove(struct platform_device *pdev)
+ {
+- of_clk_del_provider(pdev->dev.of_node);
+- reset_controller_unregister(platform_get_drvdata(pdev));
++ qcom_cc_remove(pdev);
+ return 0;
+ }
+
+diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c
+index 51d457e..0d1edc1 100644
+--- a/drivers/clk/qcom/gcc-msm8974.c
++++ b/drivers/clk/qcom/gcc-msm8974.c
+@@ -25,6 +25,7 @@
+ #include <dt-bindings/clock/qcom,gcc-msm8974.h>
+ #include <dt-bindings/reset/qcom,gcc-msm8974.h>
+
++#include "common.h"
+ #include "clk-regmap.h"
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+@@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = {
+ .fast_io = true,
+ };
+
++static const struct qcom_cc_desc gcc_msm8974_desc = {
++ .config = &gcc_msm8974_regmap_config,
++ .clks = gcc_msm8974_clocks,
++ .num_clks = ARRAY_SIZE(gcc_msm8974_clocks),
++ .resets = gcc_msm8974_resets,
++ .num_resets = ARRAY_SIZE(gcc_msm8974_resets),
++};
++
+ static const struct of_device_id gcc_msm8974_match_table[] = {
+ { .compatible = "qcom,gcc-msm8974" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table);
+
+-struct qcom_cc {
+- struct qcom_reset_controller reset;
+- struct clk_onecell_data data;
+- struct clk *clks[];
+-};
+-
+ static int gcc_msm8974_probe(struct platform_device *pdev)
+ {
+- void __iomem *base;
+- struct resource *res;
+- int i, ret;
+- struct device *dev = &pdev->dev;
+ struct clk *clk;
+- struct clk_onecell_data *data;
+- struct clk **clks;
+- struct regmap *regmap;
+- size_t num_clks;
+- struct qcom_reset_controller *reset;
+- struct qcom_cc *cc;
+-
+- 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(dev, base, &gcc_msm8974_regmap_config);
+- if (IS_ERR(regmap))
+- return PTR_ERR(regmap);
+-
+- num_clks = ARRAY_SIZE(gcc_msm8974_clocks);
+- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+- GFP_KERNEL);
+- if (!cc)
+- return -ENOMEM;
+-
+- clks = cc->clks;
+- data = &cc->data;
+- data->clks = clks;
+- data->clk_num = num_clks;
++ struct device *dev = &pdev->dev;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000);
+@@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev)
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+- for (i = 0; i < num_clks; i++) {
+- if (!gcc_msm8974_clocks[i])
+- continue;
+- clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+- clks[i] = clk;
+- }
+-
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+- if (ret)
+- return ret;
+-
+- reset = &cc->reset;
+- reset->rcdev.of_node = dev->of_node;
+- reset->rcdev.ops = &qcom_reset_ops,
+- reset->rcdev.owner = THIS_MODULE,
+- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets),
+- reset->regmap = regmap;
+- reset->reset_map = gcc_msm8974_resets,
+- platform_set_drvdata(pdev, &reset->rcdev);
+-
+- ret = reset_controller_register(&reset->rcdev);
+- if (ret)
+- of_clk_del_provider(dev->of_node);
+-
+- return ret;
++ return qcom_cc_probe(pdev, &gcc_msm8974_desc);
+ }
+
+ static int gcc_msm8974_remove(struct platform_device *pdev)
+ {
+- of_clk_del_provider(pdev->dev.of_node);
+- reset_controller_unregister(platform_get_drvdata(pdev));
++ qcom_cc_remove(pdev);
+ return 0;
+ }
+
+diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c
+index f9b59c7..12f3c0b 100644
+--- a/drivers/clk/qcom/mmcc-msm8960.c
++++ b/drivers/clk/qcom/mmcc-msm8960.c
+@@ -26,6 +26,7 @@
+ #include <dt-bindings/clock/qcom,mmcc-msm8960.h>
+ #include <dt-bindings/reset/qcom,mmcc-msm8960.h>
+
++#include "common.h"
+ #include "clk-regmap.h"
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+@@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = {
+ .fast_io = true,
+ };
+
++static const struct qcom_cc_desc mmcc_msm8960_desc = {
++ .config = &mmcc_msm8960_regmap_config,
++ .clks = mmcc_msm8960_clks,
++ .num_clks = ARRAY_SIZE(mmcc_msm8960_clks),
++ .resets = mmcc_msm8960_resets,
++ .num_resets = ARRAY_SIZE(mmcc_msm8960_resets),
++};
++
+ static const struct of_device_id mmcc_msm8960_match_table[] = {
+ { .compatible = "qcom,mmcc-msm8960" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table);
+
+-struct qcom_cc {
+- struct qcom_reset_controller reset;
+- struct clk_onecell_data data;
+- struct clk *clks[];
+-};
+-
+ static int mmcc_msm8960_probe(struct platform_device *pdev)
+ {
+- void __iomem *base;
+- struct resource *res;
+- int i, ret;
+- struct device *dev = &pdev->dev;
+- struct clk *clk;
+- struct clk_onecell_data *data;
+- struct clk **clks;
+- struct regmap *regmap;
+- size_t num_clks;
+- struct qcom_reset_controller *reset;
+- struct qcom_cc *cc;
+-
+- 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(dev, base, &mmcc_msm8960_regmap_config);
+- if (IS_ERR(regmap))
+- return PTR_ERR(regmap);
+-
+- num_clks = ARRAY_SIZE(mmcc_msm8960_clks);
+- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+- GFP_KERNEL);
+- if (!cc)
+- return -ENOMEM;
+-
+- clks = cc->clks;
+- data = &cc->data;
+- data->clks = clks;
+- data->clk_num = num_clks;
+-
+- for (i = 0; i < num_clks; i++) {
+- if (!mmcc_msm8960_clks[i])
+- continue;
+- clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+- clks[i] = clk;
+- }
+-
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+- if (ret)
+- return ret;
+-
+- reset = &cc->reset;
+- reset->rcdev.of_node = dev->of_node;
+- reset->rcdev.ops = &qcom_reset_ops,
+- reset->rcdev.owner = THIS_MODULE,
+- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets),
+- reset->regmap = regmap;
+- reset->reset_map = mmcc_msm8960_resets,
+- platform_set_drvdata(pdev, &reset->rcdev);
+-
+- ret = reset_controller_register(&reset->rcdev);
+- if (ret)
+- of_clk_del_provider(dev->of_node);
+-
+- return ret;
++ return qcom_cc_probe(pdev, &mmcc_msm8960_desc);
+ }
+
+ static int mmcc_msm8960_remove(struct platform_device *pdev)
+ {
+- of_clk_del_provider(pdev->dev.of_node);
+- reset_controller_unregister(platform_get_drvdata(pdev));
++ qcom_cc_remove(pdev);
+ return 0;
+ }
+
+diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c
+index c957745..60b7c24 100644
+--- a/drivers/clk/qcom/mmcc-msm8974.c
++++ b/drivers/clk/qcom/mmcc-msm8974.c
+@@ -25,6 +25,7 @@
+ #include <dt-bindings/clock/qcom,mmcc-msm8974.h>
+ #include <dt-bindings/reset/qcom,mmcc-msm8974.h>
+
++#include "common.h"
+ #include "clk-regmap.h"
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+@@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = {
+ .fast_io = true,
+ };
+
++static const struct qcom_cc_desc mmcc_msm8974_desc = {
++ .config = &mmcc_msm8974_regmap_config,
++ .clks = mmcc_msm8974_clocks,
++ .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks),
++ .resets = mmcc_msm8974_resets,
++ .num_resets = ARRAY_SIZE(mmcc_msm8974_resets),
++};
++
+ static const struct of_device_id mmcc_msm8974_match_table[] = {
+ { .compatible = "qcom,mmcc-msm8974" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table);
+
+-struct qcom_cc {
+- struct qcom_reset_controller reset;
+- struct clk_onecell_data data;
+- struct clk *clks[];
+-};
+-
+ static int mmcc_msm8974_probe(struct platform_device *pdev)
+ {
+- void __iomem *base;
+- struct resource *res;
+- int i, ret;
+- struct device *dev = &pdev->dev;
+- struct clk *clk;
+- struct clk_onecell_data *data;
+- struct clk **clks;
++ int ret;
+ struct regmap *regmap;
+- size_t num_clks;
+- struct qcom_reset_controller *reset;
+- struct qcom_cc *cc;
+-
+- 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(dev, base, &mmcc_msm8974_regmap_config);
+- if (IS_ERR(regmap))
+- return PTR_ERR(regmap);
+-
+- num_clks = ARRAY_SIZE(mmcc_msm8974_clocks);
+- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+- GFP_KERNEL);
+- if (!cc)
+- return -ENOMEM;
+-
+- clks = cc->clks;
+- data = &cc->data;
+- data->clks = clks;
+- data->clk_num = num_clks;
+-
+- clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true);
+- clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false);
+
+- for (i = 0; i < num_clks; i++) {
+- if (!mmcc_msm8974_clocks[i])
+- continue;
+- clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]);
+- if (IS_ERR(clk))
+- return PTR_ERR(clk);
+- clks[i] = clk;
+- }
+-
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
++ ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc);
+ if (ret)
+ return ret;
+
+- reset = &cc->reset;
+- reset->rcdev.of_node = dev->of_node;
+- reset->rcdev.ops = &qcom_reset_ops,
+- reset->rcdev.owner = THIS_MODULE,
+- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets),
+- reset->regmap = regmap;
+- reset->reset_map = mmcc_msm8974_resets,
+- platform_set_drvdata(pdev, &reset->rcdev);
+-
+- ret = reset_controller_register(&reset->rcdev);
+- if (ret)
+- of_clk_del_provider(dev->of_node);
++ regmap = dev_get_regmap(&pdev->dev, NULL);
++ clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true);
++ clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false);
+
+- return ret;
++ return 0;
+ }
+
+ static int mmcc_msm8974_remove(struct platform_device *pdev)
+ {
+- of_clk_del_provider(pdev->dev.of_node);
+- reset_controller_unregister(platform_get_drvdata(pdev));
++ qcom_cc_remove(pdev);
+ return 0;
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch
new file mode 100644
index 0000000..d83d5d9
--- /dev/null
+++ b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch
@@ -0,0 +1,123 @@
+From 0f171b8a6e1723f0ce6f98f8b3fba7d2583088c1 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 4 Apr 2014 11:31:29 -0500
+Subject: [PATCH 079/182] clk: qcom: Add basic support for APQ8064 global
+ clock controller clocks
+
+The APQ8064 and MSM8960 share a significant amount of clock data and
+code between the two SoCs. Rather than duplicating the data we just add
+support for a unqiue APQ8064 clock table into the MSM8960 code.
+
+For now add just enough clocks to get a basic serial port going on an
+APQ8064 device.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+[mturquette@linaro.org: trivial conflict due to missing ipq8064 support]
+---
+ .../devicetree/bindings/clock/qcom,gcc.txt | 1 +
+ drivers/clk/qcom/Kconfig | 4 +--
+ drivers/clk/qcom/gcc-msm8960.c | 30 ++++++++++++++++++--
+ 3 files changed, 30 insertions(+), 5 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+index 767401f..7b7104e 100644
+--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+@@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding
+ Required properties :
+ - compatible : shall contain only one of the following:
+
++ "qcom,gcc-apq8064"
+ "qcom,gcc-msm8660"
+ "qcom,gcc-msm8960"
+ "qcom,gcc-msm8974"
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index 995bcfa..7f696b7 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -13,10 +13,10 @@ config MSM_GCC_8660
+ i2c, USB, SD/eMMC, etc.
+
+ config MSM_GCC_8960
+- tristate "MSM8960 Global Clock Controller"
++ tristate "APQ8064/MSM8960 Global Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+- Support for the global clock controller on msm8960 devices.
++ Support for the global clock controller on apq8064/msm8960 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, SATA, PCIe, etc.
+
+diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
+index 633b019..8e2b6dd 100644
+--- a/drivers/clk/qcom/gcc-msm8960.c
++++ b/drivers/clk/qcom/gcc-msm8960.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+@@ -2868,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
+ [RIVA_RESET] = { 0x35e0 },
+ };
+
++static struct clk_regmap *gcc_apq8064_clks[] = {
++ [PLL8] = &pll8.clkr,
++ [PLL8_VOTE] = &pll8_vote,
++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr,
++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr,
++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr,
++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr,
++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr,
++};
++
+ static const struct regmap_config gcc_msm8960_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+@@ -2884,8 +2894,17 @@ static const struct qcom_cc_desc gcc_msm8960_desc = {
+ .num_resets = ARRAY_SIZE(gcc_msm8960_resets),
+ };
+
++static const struct qcom_cc_desc gcc_apq8064_desc = {
++ .config = &gcc_msm8960_regmap_config,
++ .clks = gcc_apq8064_clks,
++ .num_clks = ARRAY_SIZE(gcc_apq8064_clks),
++ .resets = gcc_msm8960_resets,
++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets),
++};
++
+ static const struct of_device_id gcc_msm8960_match_table[] = {
+- { .compatible = "qcom,gcc-msm8960" },
++ { .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc },
++ { .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table);
+@@ -2894,6 +2913,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
+ {
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
++ const struct of_device_id *match;
++
++ match = of_match_device(gcc_msm8960_match_table, &pdev->dev);
++ if (!match)
++ return -EINVAL;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
+@@ -2904,7 +2928,7 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+- return qcom_cc_probe(pdev, &gcc_msm8960_desc);
++ return qcom_cc_probe(pdev, match->data);
+ }
+
+ static int gcc_msm8960_remove(struct platform_device *pdev)
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch
new file mode 100644
index 0000000..3cc5bf8
--- /dev/null
+++ b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch
@@ -0,0 +1,101 @@
+From 7456451e9df88d4c33479e3d4ea124d8a91ceb57 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 4 Apr 2014 11:32:56 -0500
+Subject: [PATCH 080/182] clk: qcom: Various fixes for MSM8960's global clock
+ controller
+
+* Remove CE2_SLEEP_CLK, doesn't exist on 8960 family SoCs
+* Fix incorrect offset for PMIC_SSBI2_RESET
+* Fix typo:
+ SIC_TIC -> SPS_TIC_H
+ SFAB_ADM0_M2_A_CLK -> SFAB_ADM0_M2_H_CLK
+* Fix naming convention:
+ SFAB_CFPB_S_HCLK -> SFAB_CFPB_S_H_CLK
+ SATA_SRC_CLK -> SATA_CLK_SRC
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/gcc-msm8960.c | 4 ++--
+ include/dt-bindings/clock/qcom,gcc-msm8960.h | 7 +++----
+ include/dt-bindings/reset/qcom,gcc-msm8960.h | 2 +-
+ 3 files changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
+index 8e2b6dd..f4ffd91 100644
+--- a/drivers/clk/qcom/gcc-msm8960.c
++++ b/drivers/clk/qcom/gcc-msm8960.c
+@@ -2810,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
+ [PPSS_PROC_RESET] = { 0x2594, 1 },
+ [PPSS_RESET] = { 0x2594},
+ [DMA_BAM_RESET] = { 0x25c0, 7 },
+- [SIC_TIC_RESET] = { 0x2600, 7 },
++ [SPS_TIC_H_RESET] = { 0x2600, 7 },
+ [SLIMBUS_H_RESET] = { 0x2620, 7 },
+ [SFAB_CFPB_M_RESET] = { 0x2680, 7 },
+ [SFAB_CFPB_S_RESET] = { 0x26c0, 7 },
+@@ -2823,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
+ [SFAB_SFPB_M_RESET] = { 0x2780, 7 },
+ [SFAB_SFPB_S_RESET] = { 0x27a0, 7 },
+ [RPM_PROC_RESET] = { 0x27c0, 7 },
+- [PMIC_SSBI2_RESET] = { 0x270c, 12 },
++ [PMIC_SSBI2_RESET] = { 0x280c, 12 },
+ [SDC1_RESET] = { 0x2830 },
+ [SDC2_RESET] = { 0x2850 },
+ [SDC3_RESET] = { 0x2870 },
+diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h
+index 03bbf49..f9f5471 100644
+--- a/include/dt-bindings/clock/qcom,gcc-msm8960.h
++++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h
+@@ -51,7 +51,7 @@
+ #define QDSS_TSCTR_CLK 34
+ #define SFAB_ADM0_M0_A_CLK 35
+ #define SFAB_ADM0_M1_A_CLK 36
+-#define SFAB_ADM0_M2_A_CLK 37
++#define SFAB_ADM0_M2_H_CLK 37
+ #define ADM0_CLK 38
+ #define ADM0_PBUS_CLK 39
+ #define MSS_XPU_CLK 40
+@@ -99,7 +99,7 @@
+ #define CFPB2_H_CLK 82
+ #define SFAB_CFPB_M_H_CLK 83
+ #define CFPB_MASTER_H_CLK 84
+-#define SFAB_CFPB_S_HCLK 85
++#define SFAB_CFPB_S_H_CLK 85
+ #define CFPB_SPLITTER_H_CLK 86
+ #define TSIF_H_CLK 87
+ #define TSIF_INACTIVITY_TIMERS_CLK 88
+@@ -110,7 +110,6 @@
+ #define CE1_SLEEP_CLK 93
+ #define CE2_H_CLK 94
+ #define CE2_CORE_CLK 95
+-#define CE2_SLEEP_CLK 96
+ #define SFPB_H_CLK_SRC 97
+ #define SFPB_H_CLK 98
+ #define SFAB_SFPB_M_H_CLK 99
+@@ -252,7 +251,7 @@
+ #define MSS_S_H_CLK 235
+ #define MSS_CXO_SRC_CLK 236
+ #define SATA_H_CLK 237
+-#define SATA_SRC_CLK 238
++#define SATA_CLK_SRC 238
+ #define SATA_RXOOB_CLK 239
+ #define SATA_PMALIVE_CLK 240
+ #define SATA_PHY_REF_CLK 241
+diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h
+index a840e68..07edd0e 100644
+--- a/include/dt-bindings/reset/qcom,gcc-msm8960.h
++++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h
+@@ -58,7 +58,7 @@
+ #define PPSS_PROC_RESET 41
+ #define PPSS_RESET 42
+ #define DMA_BAM_RESET 43
+-#define SIC_TIC_RESET 44
++#define SPS_TIC_H_RESET 44
+ #define SLIMBUS_H_RESET 45
+ #define SFAB_CFPB_M_RESET 46
+ #define SFAB_CFPB_S_RESET 47
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch
new file mode 100644
index 0000000..f022463
--- /dev/null
+++ b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch
@@ -0,0 +1,190 @@
+From e1f51c2d41c8936b8383cf4ac53c1ff05b0e2e2f Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 27 Feb 2014 12:54:24 -0800
+Subject: [PATCH 081/182] ARM: config: Add qcom_defconfig
+
+Add a defconfig for mach-qcom platforms (copied from msm_defconfig).
+Although these platforms are part of the multi-platform kernel, it's
+useful to have a stripped down version of the defconfig that just
+selects the DT based Qualcomm platforms and drivers.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 164 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 164 insertions(+)
+ create mode 100644 arch/arm/configs/qcom_defconfig
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+new file mode 100644
+index 0000000..bfed753
+--- /dev/null
++++ b/arch/arm/configs/qcom_defconfig
+@@ -0,0 +1,164 @@
++CONFIG_SYSVIPC=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_SYSCTL_SYSCALL=y
++CONFIG_KALLSYMS_ALL=y
++CONFIG_EMBEDDED=y
++# CONFIG_SLUB_DEBUG is not set
++# CONFIG_COMPAT_BRK is not set
++CONFIG_PROFILING=y
++CONFIG_OPROFILE=y
++CONFIG_KPROBES=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_MODULE_FORCE_UNLOAD=y
++CONFIG_MODVERSIONS=y
++CONFIG_PARTITION_ADVANCED=y
++CONFIG_ARCH_QCOM=y
++CONFIG_ARCH_MSM8X60=y
++CONFIG_ARCH_MSM8960=y
++CONFIG_ARCH_MSM8974=y
++CONFIG_SMP=y
++CONFIG_PREEMPT=y
++CONFIG_AEABI=y
++CONFIG_HIGHMEM=y
++CONFIG_HIGHPTE=y
++CONFIG_CLEANCACHE=y
++CONFIG_ARM_APPENDED_DTB=y
++CONFIG_ARM_ATAG_DTB_COMPAT=y
++CONFIG_VFP=y
++CONFIG_NEON=y
++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_IP_MULTIPLE_TABLES=y
++CONFIG_IP_ROUTE_VERBOSE=y
++CONFIG_IP_PNP=y
++CONFIG_IP_PNP_DHCP=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++# CONFIG_IPV6 is not set
++CONFIG_CFG80211=y
++CONFIG_RFKILL=y
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_M25P80=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_SCSI=y
++CONFIG_SCSI_TGT=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_CHR_DEV_SG=y
++CONFIG_CHR_DEV_SCH=y
++CONFIG_SCSI_MULTI_LUN=y
++CONFIG_SCSI_CONSTANTS=y
++CONFIG_SCSI_LOGGING=y
++CONFIG_SCSI_SCAN_ASYNC=y
++CONFIG_NETDEVICES=y
++CONFIG_DUMMY=y
++CONFIG_MDIO_BITBANG=y
++CONFIG_MDIO_GPIO=y
++CONFIG_SLIP=y
++CONFIG_SLIP_COMPRESSED=y
++CONFIG_SLIP_MODE_SLIP6=y
++CONFIG_USB_USBNET=y
++# CONFIG_USB_NET_AX8817X is not set
++# CONFIG_USB_NET_ZAURUS is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_KEYBOARD_ATKBD is not set
++# CONFIG_MOUSE_PS2 is not set
++CONFIG_INPUT_JOYSTICK=y
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_INPUT_MISC=y
++CONFIG_INPUT_UINPUT=y
++CONFIG_SERIO_LIBPS2=y
++# CONFIG_LEGACY_PTYS is not set
++CONFIG_SERIAL_MSM=y
++CONFIG_SERIAL_MSM_CONSOLE=y
++CONFIG_HW_RANDOM=y
++CONFIG_HW_RANDOM_MSM=y
++CONFIG_I2C=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_QUP=y
++CONFIG_SPI=y
++CONFIG_SPI_QUP=y
++CONFIG_SPMI=y
++CONFIG_PINCTRL_APQ8064=y
++CONFIG_PINCTRL_IPQ8064=y
++CONFIG_PINCTRL_MSM8X74=y
++CONFIG_DEBUG_GPIO=y
++CONFIG_GPIO_SYSFS=y
++CONFIG_POWER_SUPPLY=y
++CONFIG_POWER_RESET=y
++CONFIG_POWER_RESET_MSM=y
++CONFIG_THERMAL=y
++CONFIG_REGULATOR=y
++CONFIG_MEDIA_SUPPORT=y
++CONFIG_FB=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_DYNAMIC_MINORS=y
++# CONFIG_SND_ARM is not set
++# CONFIG_SND_SPI is not set
++# CONFIG_SND_USB is not set
++CONFIG_SND_SOC=y
++CONFIG_HID_BATTERY_STRENGTH=y
++CONFIG_USB=y
++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
++CONFIG_USB_MON=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_ACM=y
++CONFIG_USB_SERIAL=y
++CONFIG_USB_GADGET=y
++CONFIG_USB_GADGET_DEBUG_FILES=y
++CONFIG_USB_GADGET_VBUS_DRAW=500
++CONFIG_MMC=y
++CONFIG_MMC_BLOCK_MINORS=16
++CONFIG_MMC_SDHCI=y
++CONFIG_MMC_SDHCI_PLTFM=y
++CONFIG_MMC_SDHCI_MSM=y
++CONFIG_RTC_CLASS=y
++CONFIG_DMADEVICES=y
++CONFIG_QCOM_BAM_DMA=y
++CONFIG_STAGING=y
++CONFIG_COMMON_CLK_QCOM=y
++CONFIG_MSM_GCC_8660=y
++CONFIG_MSM_MMCC_8960=y
++CONFIG_MSM_MMCC_8974=y
++CONFIG_MSM_IOMMU=y
++CONFIG_GENERIC_PHY=y
++CONFIG_EXT2_FS=y
++CONFIG_EXT2_FS_XATTR=y
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
++CONFIG_EXT4_FS=y
++CONFIG_FUSE_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_TMPFS=y
++CONFIG_JFFS2_FS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V3_ACL=y
++CONFIG_NFS_V4=y
++CONFIG_CIFS=y
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ASCII=y
++CONFIG_NLS_ISO8859_1=y
++CONFIG_NLS_UTF8=y
++CONFIG_PRINTK_TIME=y
++CONFIG_DYNAMIC_DEBUG=y
++CONFIG_DEBUG_INFO=y
++CONFIG_MAGIC_SYSRQ=y
++CONFIG_LOCKUP_DETECTOR=y
++# CONFIG_DETECT_HUNG_TASK is not set
++# CONFIG_SCHED_DEBUG is not set
++CONFIG_TIMER_STATS=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch
new file mode 100644
index 0000000..7bcd246
--- /dev/null
+++ b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch
@@ -0,0 +1,25 @@
+From d247fb8a019e4cc5a607c7fff9922e8e98c2c660 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 29 May 2014 11:25:24 -0500
+Subject: [PATCH 082/182] ARM: qcom: Enable GSBI driver in defconfig
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index bfed753..42ebd72 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -131,6 +131,7 @@ CONFIG_RTC_CLASS=y
+ CONFIG_DMADEVICES=y
+ CONFIG_QCOM_BAM_DMA=y
+ CONFIG_STAGING=y
++CONFIG_QCOM_GSBI=y
+ CONFIG_COMMON_CLK_QCOM=y
+ CONFIG_MSM_GCC_8660=y
+ CONFIG_MSM_MMCC_8960=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch
new file mode 100644
index 0000000..837ec9d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch
@@ -0,0 +1,68 @@
+From 2dac9498966c79a6abb09764419ffd42a44c78cf Mon Sep 17 00:00:00 2001
+From: Santosh Shilimkar <santosh.shilimkar@ti.com>
+Date: Wed, 23 Apr 2014 19:46:17 -0400
+Subject: [PATCH 083/182] soc: Introduce drivers/soc place-holder for SOC
+ specific drivers
+
+Based on earlier thread "https://lkml.org/lkml/2013/10/7/662" and
+discussion at Kernel Summit'2013, it was agreed to create
+'driver/soc' for drivers which are quite SOC specific.
+
+Further discussion on the subject is in response to
+the earlier version of the patch is here:
+ http://lwn.net/Articles/588942/
+
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Kumar Gala <galak@codeaurora.org>
+Cc: Paul Walmsley <paul@pwsan.com>
+Cc: Olof Johansson <olof@lixom.net>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sandeep Nair <sandeep_n@ti.com>
+Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/Kconfig | 2 ++
+ drivers/Makefile | 3 +++
+ drivers/soc/Kconfig | 3 +++
+ 3 files changed, 8 insertions(+)
+ create mode 100644 drivers/soc/Kconfig
+
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index e0a4ae6..a299cbd 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -132,6 +132,8 @@ source "drivers/staging/Kconfig"
+
+ source "drivers/platform/Kconfig"
+
++source "drivers/soc/Kconfig"
++
+ source "drivers/clk/Kconfig"
+
+ source "drivers/hwspinlock/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 3d6de8b..4c2bdc1 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -33,6 +33,9 @@ obj-y += amba/
+ # really early.
+ obj-$(CONFIG_DMADEVICES) += dma/
+
++# SOC specific infrastructure drivers.
++obj-y += soc/
++
+ obj-$(CONFIG_VIRTIO) += virtio/
+ obj-$(CONFIG_XEN) += xen/
+
+diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
+new file mode 100644
+index 0000000..339baa8
+--- /dev/null
++++ b/drivers/soc/Kconfig
+@@ -0,0 +1,3 @@
++menu "SOC (System On Chip) specific Drivers"
++
++endmenu
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch
new file mode 100644
index 0000000..f522558
--- /dev/null
+++ b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch
@@ -0,0 +1,162 @@
+From a2f0fc20ea49e5dbbdbb21444683ea760fbdd38f Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 24 Apr 2014 11:31:21 -0500
+Subject: [PATCH 084/182] soc: qcom: Add GSBI driver
+
+The GSBI (General Serial Bus Interface) driver controls the overarching
+configuration of the shared serial bus infrastructure on APQ8064, IPQ8064, and
+earlier QCOM processors. The GSBI supports UART, I2C, SPI, and UIM
+functionality in various combinations.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/soc/Kconfig | 2 +
+ drivers/soc/Makefile | 5 +++
+ drivers/soc/qcom/Kconfig | 11 ++++++
+ drivers/soc/qcom/Makefile | 1 +
+ drivers/soc/qcom/qcom_gsbi.c | 84 ++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 103 insertions(+)
+ create mode 100644 drivers/soc/Makefile
+ create mode 100644 drivers/soc/qcom/Kconfig
+ create mode 100644 drivers/soc/qcom/Makefile
+ create mode 100644 drivers/soc/qcom/qcom_gsbi.c
+
+diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
+index 339baa8..c854385 100644
+--- a/drivers/soc/Kconfig
++++ b/drivers/soc/Kconfig
+@@ -1,3 +1,5 @@
+ menu "SOC (System On Chip) specific Drivers"
+
++source "drivers/soc/qcom/Kconfig"
++
+ endmenu
+diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
+new file mode 100644
+index 0000000..0f7c447
+--- /dev/null
++++ b/drivers/soc/Makefile
+@@ -0,0 +1,5 @@
++#
++# Makefile for the Linux Kernel SOC specific device drivers.
++#
++
++obj-$(CONFIG_ARCH_QCOM) += qcom/
+diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
+new file mode 100644
+index 0000000..7bd2c94
+--- /dev/null
++++ b/drivers/soc/qcom/Kconfig
+@@ -0,0 +1,11 @@
++#
++# QCOM Soc drivers
++#
++config QCOM_GSBI
++ tristate "QCOM General Serial Bus Interface"
++ depends on ARCH_QCOM
++ help
++ Say y here to enable GSBI support. The GSBI provides control
++ functions for connecting the underlying serial UART, SPI, and I2C
++ devices to the output pins.
++
+diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
+new file mode 100644
+index 0000000..4389012
+--- /dev/null
++++ b/drivers/soc/qcom/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
+diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
+new file mode 100644
+index 0000000..061dd06
+--- /dev/null
++++ b/drivers/soc/qcom/qcom_gsbi.c
+@@ -0,0 +1,84 @@
++/*
++ * 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 rev 2 and
++ * only rev 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/of_platform.h>
++#include <linux/platform_device.h>
++
++#define GSBI_CTRL_REG 0x0000
++#define GSBI_PROTOCOL_SHIFT 4
++
++static int gsbi_probe(struct platform_device *pdev)
++{
++ struct device_node *node = pdev->dev.of_node;
++ struct resource *res;
++ void __iomem *base;
++ struct clk *hclk;
++ u32 mode, crci = 0;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ if (of_property_read_u32(node, "qcom,mode", &mode)) {
++ dev_err(&pdev->dev, "missing mode configuration\n");
++ return -EINVAL;
++ }
++
++ /* not required, so default to 0 if not present */
++ of_property_read_u32(node, "qcom,crci", &crci);
++
++ dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n", mode, crci);
++
++ hclk = devm_clk_get(&pdev->dev, "iface");
++ if (IS_ERR(hclk))
++ return PTR_ERR(hclk);
++
++ clk_prepare_enable(hclk);
++
++ writel_relaxed((mode << GSBI_PROTOCOL_SHIFT) | crci,
++ base + GSBI_CTRL_REG);
++
++ /* make sure the gsbi control write is not reordered */
++ wmb();
++
++ clk_disable_unprepare(hclk);
++
++ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
++}
++
++static const struct of_device_id gsbi_dt_match[] = {
++ { .compatible = "qcom,gsbi-v1.0.0", },
++};
++
++MODULE_DEVICE_TABLE(of, gsbi_dt_match);
++
++static struct platform_driver gsbi_driver = {
++ .driver = {
++ .name = "gsbi",
++ .owner = THIS_MODULE,
++ .of_match_table = gsbi_dt_match,
++ },
++ .probe = gsbi_probe,
++};
++
++module_platform_driver(gsbi_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM GSBI driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch
new file mode 100644
index 0000000..c619ece
--- /dev/null
+++ b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch
@@ -0,0 +1,28 @@
+From 8efaabc573e58f4b891340f4aedb34f9d0656067 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 26 May 2014 18:07:05 +0200
+Subject: [PATCH 085/182] soc: qcom: fix of_device_id table
+
+The match tables must be zero-terminated, and Kbuild now helpfully
+fails to link the kernel if that isn't the case.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/soc/qcom/qcom_gsbi.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
+index 061dd06..447458e 100644
+--- a/drivers/soc/qcom/qcom_gsbi.c
++++ b/drivers/soc/qcom/qcom_gsbi.c
+@@ -64,6 +64,7 @@ static int gsbi_probe(struct platform_device *pdev)
+
+ static const struct of_device_id gsbi_dt_match[] = {
+ { .compatible = "qcom,gsbi-v1.0.0", },
++ { },
+ };
+
+ MODULE_DEVICE_TABLE(of, gsbi_dt_match);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch
new file mode 100644
index 0000000..afc293b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch
@@ -0,0 +1,246 @@
+From 48ab619dd6e308c57dac3e5d022a3099806bf79e Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Tue, 14 Jan 2014 12:34:55 -0800
+Subject: [PATCH 086/182] msm_serial: Add support for poll_{get,put}_char()
+
+Implement the polling functionality for the MSM serial driver.
+This allows us to use KGDB on this hardware.
+
+Cc: David Brown <davidb@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/msm_serial.c | 140 ++++++++++++++++++++++++++++++++++++++-
+ drivers/tty/serial/msm_serial.h | 9 +++
+ 2 files changed, 146 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
+index b5d779c..053b98e 100644
+--- a/drivers/tty/serial/msm_serial.c
++++ b/drivers/tty/serial/msm_serial.c
+@@ -39,6 +39,13 @@
+
+ #include "msm_serial.h"
+
++enum {
++ UARTDM_1P1 = 1,
++ UARTDM_1P2,
++ UARTDM_1P3,
++ UARTDM_1P4,
++};
++
+ struct msm_port {
+ struct uart_port uart;
+ char name[16];
+@@ -309,6 +316,8 @@ static unsigned int msm_get_mctrl(struct uart_port *port)
+
+ static void msm_reset(struct uart_port *port)
+ {
++ struct msm_port *msm_port = UART_TO_MSM(port);
++
+ /* reset everything */
+ msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+ msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+@@ -316,6 +325,10 @@ static void msm_reset(struct uart_port *port)
+ msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+ msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
+ msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
++
++ /* Disable DM modes */
++ if (msm_port->is_uartdm)
++ msm_write(port, 0, UARTDM_DMEN);
+ }
+
+ static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+@@ -711,6 +724,117 @@ static void msm_power(struct uart_port *port, unsigned int state,
+ }
+ }
+
++#ifdef CONFIG_CONSOLE_POLL
++static int msm_poll_init(struct uart_port *port)
++{
++ struct msm_port *msm_port = UART_TO_MSM(port);
++
++ /* Enable single character mode on RX FIFO */
++ if (msm_port->is_uartdm >= UARTDM_1P4)
++ msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN);
++
++ return 0;
++}
++
++static int msm_poll_get_char_single(struct uart_port *port)
++{
++ struct msm_port *msm_port = UART_TO_MSM(port);
++ unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : UART_RF;
++
++ if (!(msm_read(port, UART_SR) & UART_SR_RX_READY))
++ return NO_POLL_CHAR;
++ else
++ return msm_read(port, rf_reg) & 0xff;
++}
++
++static int msm_poll_get_char_dm_1p3(struct uart_port *port)
++{
++ int c;
++ static u32 slop;
++ static int count;
++ unsigned char *sp = (unsigned char *)&slop;
++
++ /* Check if a previous read had more than one char */
++ if (count) {
++ c = sp[sizeof(slop) - count];
++ count--;
++ /* Or if FIFO is empty */
++ } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) {
++ /*
++ * If RX packing buffer has less than a word, force stale to
++ * push contents into RX FIFO
++ */
++ count = msm_read(port, UARTDM_RXFS);
++ count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK;
++ if (count) {
++ msm_write(port, UART_CR_CMD_FORCE_STALE, UART_CR);
++ slop = msm_read(port, UARTDM_RF);
++ c = sp[0];
++ count--;
++ } else {
++ c = NO_POLL_CHAR;
++ }
++ /* FIFO has a word */
++ } else {
++ slop = msm_read(port, UARTDM_RF);
++ c = sp[0];
++ count = sizeof(slop) - 1;
++ }
++
++ return c;
++}
++
++static int msm_poll_get_char(struct uart_port *port)
++{
++ u32 imr;
++ int c;
++ struct msm_port *msm_port = UART_TO_MSM(port);
++
++ /* Disable all interrupts */
++ imr = msm_read(port, UART_IMR);
++ msm_write(port, 0, UART_IMR);
++
++ if (msm_port->is_uartdm == UARTDM_1P3)
++ c = msm_poll_get_char_dm_1p3(port);
++ else
++ c = msm_poll_get_char_single(port);
++
++ /* Enable interrupts */
++ msm_write(port, imr, UART_IMR);
++
++ return c;
++}
++
++static void msm_poll_put_char(struct uart_port *port, unsigned char c)
++{
++ u32 imr;
++ struct msm_port *msm_port = UART_TO_MSM(port);
++
++ /* Disable all interrupts */
++ imr = msm_read(port, UART_IMR);
++ msm_write(port, 0, UART_IMR);
++
++ if (msm_port->is_uartdm)
++ reset_dm_count(port, 1);
++
++ /* Wait until FIFO is empty */
++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
++ cpu_relax();
++
++ /* Write a character */
++ msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
++
++ /* Wait until FIFO is empty */
++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
++ cpu_relax();
++
++ /* Enable interrupts */
++ msm_write(port, imr, UART_IMR);
++
++ return;
++}
++#endif
++
+ static struct uart_ops msm_uart_pops = {
+ .tx_empty = msm_tx_empty,
+ .set_mctrl = msm_set_mctrl,
+@@ -729,6 +853,11 @@ static struct uart_ops msm_uart_pops = {
+ .config_port = msm_config_port,
+ .verify_port = msm_verify_port,
+ .pm = msm_power,
++#ifdef CONFIG_CONSOLE_POLL
++ .poll_init = msm_poll_init,
++ .poll_get_char = msm_poll_get_char,
++ .poll_put_char = msm_poll_put_char,
++#endif
+ };
+
+ static struct msm_port msm_uart_ports[] = {
+@@ -900,7 +1029,10 @@ static struct uart_driver msm_uart_driver = {
+ static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
+
+ static const struct of_device_id msm_uartdm_table[] = {
+- { .compatible = "qcom,msm-uartdm" },
++ { .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 },
++ { .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 },
++ { .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 },
++ { .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 },
+ { }
+ };
+
+@@ -909,6 +1041,7 @@ static int __init msm_serial_probe(struct platform_device *pdev)
+ struct msm_port *msm_port;
+ struct resource *resource;
+ struct uart_port *port;
++ const struct of_device_id *id;
+ int irq;
+
+ if (pdev->id == -1)
+@@ -923,8 +1056,9 @@ static int __init msm_serial_probe(struct platform_device *pdev)
+ port->dev = &pdev->dev;
+ msm_port = UART_TO_MSM(port);
+
+- if (of_match_device(msm_uartdm_table, &pdev->dev))
+- msm_port->is_uartdm = 1;
++ id = of_match_device(msm_uartdm_table, &pdev->dev);
++ if (id)
++ msm_port->is_uartdm = (unsigned long)id->data;
+ else
+ msm_port->is_uartdm = 0;
+
+diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h
+index 469fda5..1e9b68b 100644
+--- a/drivers/tty/serial/msm_serial.h
++++ b/drivers/tty/serial/msm_serial.h
+@@ -59,6 +59,7 @@
+ #define UART_CR_CMD_RESET_RFR (14 << 4)
+ #define UART_CR_CMD_PROTECTION_EN (16 << 4)
+ #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4)
++#define UART_CR_CMD_FORCE_STALE (4 << 8)
+ #define UART_CR_CMD_RESET_TX_READY (3 << 8)
+ #define UART_CR_TX_DISABLE (1 << 3)
+ #define UART_CR_TX_ENABLE (1 << 2)
+@@ -113,6 +114,14 @@
+ #define GSBI_PROTOCOL_UART 0x40
+ #define GSBI_PROTOCOL_IDLE 0x0
+
++#define UARTDM_RXFS 0x50
++#define UARTDM_RXFS_BUF_SHIFT 0x7
++#define UARTDM_RXFS_BUF_MASK 0x7
++
++#define UARTDM_DMEN 0x3C
++#define UARTDM_DMEN_RX_SC_ENABLE BIT(5)
++#define UARTDM_DMEN_TX_SC_ENABLE BIT(4)
++
+ #define UARTDM_DMRX 0x34
+ #define UARTDM_NCF_TX 0x40
+ #define UARTDM_RX_TOTAL_SNAP 0x38
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch
new file mode 100644
index 0000000..8cbb52a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch
@@ -0,0 +1,151 @@
+From 025909cbf933cc20c2ff5ea9f87de8e17a739a08 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 24 Apr 2014 11:31:22 -0500
+Subject: [PATCH 087/182] tty: serial: msm: Remove direct access to GSBI
+
+This patch removes direct access of the GSBI registers. GSBI configuration
+should be done through the GSBI driver directly.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/tty/serial/msm_serial.c | 48 ++-------------------------------------
+ drivers/tty/serial/msm_serial.h | 5 ----
+ 2 files changed, 2 insertions(+), 51 deletions(-)
+
+diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
+index 053b98e..778e376 100644
+--- a/drivers/tty/serial/msm_serial.c
++++ b/drivers/tty/serial/msm_serial.c
+@@ -52,7 +52,6 @@ struct msm_port {
+ struct clk *clk;
+ struct clk *pclk;
+ unsigned int imr;
+- void __iomem *gsbi_base;
+ int is_uartdm;
+ unsigned int old_snap_state;
+ };
+@@ -599,9 +598,7 @@ static const char *msm_type(struct uart_port *port)
+ static void msm_release_port(struct uart_port *port)
+ {
+ struct platform_device *pdev = to_platform_device(port->dev);
+- struct msm_port *msm_port = UART_TO_MSM(port);
+ struct resource *uart_resource;
+- struct resource *gsbi_resource;
+ resource_size_t size;
+
+ uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+@@ -612,28 +609,12 @@ static void msm_release_port(struct uart_port *port)
+ release_mem_region(port->mapbase, size);
+ iounmap(port->membase);
+ port->membase = NULL;
+-
+- if (msm_port->gsbi_base) {
+- writel_relaxed(GSBI_PROTOCOL_IDLE,
+- msm_port->gsbi_base + GSBI_CONTROL);
+-
+- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+- if (unlikely(!gsbi_resource))
+- return;
+-
+- size = resource_size(gsbi_resource);
+- release_mem_region(gsbi_resource->start, size);
+- iounmap(msm_port->gsbi_base);
+- msm_port->gsbi_base = NULL;
+- }
+ }
+
+ static int msm_request_port(struct uart_port *port)
+ {
+- struct msm_port *msm_port = UART_TO_MSM(port);
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *uart_resource;
+- struct resource *gsbi_resource;
+ resource_size_t size;
+ int ret;
+
+@@ -652,30 +633,8 @@ static int msm_request_port(struct uart_port *port)
+ goto fail_release_port;
+ }
+
+- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+- /* Is this a GSBI-based port? */
+- if (gsbi_resource) {
+- size = resource_size(gsbi_resource);
+-
+- if (!request_mem_region(gsbi_resource->start, size,
+- "msm_serial")) {
+- ret = -EBUSY;
+- goto fail_release_port_membase;
+- }
+-
+- msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
+- if (!msm_port->gsbi_base) {
+- ret = -EBUSY;
+- goto fail_release_gsbi;
+- }
+- }
+-
+ return 0;
+
+-fail_release_gsbi:
+- release_mem_region(gsbi_resource->start, size);
+-fail_release_port_membase:
+- iounmap(port->membase);
+ fail_release_port:
+ release_mem_region(port->mapbase, size);
+ return ret;
+@@ -683,7 +642,6 @@ fail_release_port:
+
+ static void msm_config_port(struct uart_port *port, int flags)
+ {
+- struct msm_port *msm_port = UART_TO_MSM(port);
+ int ret;
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_MSM;
+@@ -691,9 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags)
+ if (ret)
+ return;
+ }
+- if (msm_port->gsbi_base)
+- writel_relaxed(GSBI_PROTOCOL_UART,
+- msm_port->gsbi_base + GSBI_CONTROL);
+ }
+
+ static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
+@@ -1110,6 +1065,7 @@ static struct of_device_id msm_match_table[] = {
+
+ static struct platform_driver msm_platform_driver = {
+ .remove = msm_serial_remove,
++ .probe = msm_serial_probe,
+ .driver = {
+ .name = "msm_serial",
+ .owner = THIS_MODULE,
+@@ -1125,7 +1081,7 @@ static int __init msm_serial_init(void)
+ if (unlikely(ret))
+ return ret;
+
+- ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe);
++ ret = platform_driver_register(&msm_platform_driver);
+ if (unlikely(ret))
+ uart_unregister_driver(&msm_uart_driver);
+
+diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h
+index 1e9b68b..d98d45e 100644
+--- a/drivers/tty/serial/msm_serial.h
++++ b/drivers/tty/serial/msm_serial.h
+@@ -109,11 +109,6 @@
+ #define UART_ISR 0x0014
+ #define UART_ISR_TX_READY (1 << 7)
+
+-#define GSBI_CONTROL 0x0
+-#define GSBI_PROTOCOL_CODE 0x30
+-#define GSBI_PROTOCOL_UART 0x40
+-#define GSBI_PROTOCOL_IDLE 0x0
+-
+ #define UARTDM_RXFS 0x50
+ #define UARTDM_RXFS_BUF_SHIFT 0x7
+ #define UARTDM_RXFS_BUF_MASK 0x7
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch
new file mode 100644
index 0000000..39762c1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch
@@ -0,0 +1,135 @@
+From 5a58dbf4d82c29f7e6d89abc3520bed1aa2af05c Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 24 Apr 2014 11:31:20 -0500
+Subject: [PATCH 088/182] soc: qcom: Add device tree binding for GSBI
+
+Add device tree binding support for the QCOM GSBI driver.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ .../devicetree/bindings/soc/qcom/qcom,gsbi.txt | 78 ++++++++++++++++++++
+ include/dt-bindings/soc/qcom,gsbi.h | 26 +++++++
+ 2 files changed, 104 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+ create mode 100644 include/dt-bindings/soc/qcom,gsbi.h
+
+diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+new file mode 100644
+index 0000000..4ce24d4
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+@@ -0,0 +1,78 @@
++QCOM GSBI (General Serial Bus Interface) Driver
++
++The GSBI controller is modeled as a node with zero or more child nodes, each
++representing a serial sub-node device that is mux'd as part of the GSBI
++configuration settings. The mode setting will govern the input/output mode of
++the 4 GSBI IOs.
++
++Required properties:
++- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
++- reg: Address range for GSBI registers
++- clocks: required clock
++- clock-names: must contain "iface" entry
++- qcom,mode : indicates MUX value for configuration of the serial interface.
++ Please reference dt-bindings/soc/qcom,gsbi.h for valid mux values.
++
++Optional properties:
++- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference
++ dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
++
++Required properties if child node exists:
++- #address-cells: Must be 1
++- #size-cells: Must be 1
++- ranges: Must be present
++
++Properties for children:
++
++A GSBI controller node can contain 0 or more child nodes representing serial
++devices. These serial devices can be a QCOM UART, I2C controller, spi
++controller, or some combination of aforementioned devices.
++
++See the following for child node definitions:
++Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
++Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt
++
++Example for APQ8064:
++
++#include <dt-bindings/soc/qcom,gsbi.h>
++
++ gsbi4@16300000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x16300000 0x100>;
++ clocks = <&gcc GSBI4_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ qcom,crci = <GSBI_CRCI_QUP>;
++
++ /* child nodes go under here */
++
++ i2c_qup4: i2c@16380000 {
++ compatible = "qcom,i2c-qup-v1.1.1";
++ reg = <0x16380000 0x1000>;
++ interrupts = <0 153 0>;
++
++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>;
++ clock-names = "core", "iface";
++
++ clock-frequency = <200000>;
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ };
++
++ uart4: serial@16340000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x16340000 0x1000>,
++ <0x16300000 0x1000>;
++ interrupts = <0 152 0x0>;
++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>;
++ clock-names = "core", "iface";
++ status = "ok";
++ };
++ };
++
+diff --git a/include/dt-bindings/soc/qcom,gsbi.h b/include/dt-bindings/soc/qcom,gsbi.h
+new file mode 100644
+index 0000000..7ac4292
+--- /dev/null
++++ b/include/dt-bindings/soc/qcom,gsbi.h
+@@ -0,0 +1,26 @@
++/* 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 __DT_BINDINGS_QCOM_GSBI_H
++#define __DT_BINDINGS_QCOM_GSBI_H
++
++#define GSBI_PROT_IDLE 0
++#define GSBI_PROT_I2C_UIM 1
++#define GSBI_PROT_I2C 2
++#define GSBI_PROT_SPI 3
++#define GSBI_PROT_UART_W_FC 4
++#define GSBI_PROT_UIM 5
++#define GSBI_PROT_I2C_UART 6
++
++#define GSBI_CRCI_QUP 0
++#define GSBI_CRCI_UART 1
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch
new file mode 100644
index 0000000..1663fbe
--- /dev/null
+++ b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch
@@ -0,0 +1,57 @@
+From b3a77c7cab10272988231482e2c654c5f10a910c Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Thu, 6 Feb 2014 17:28:49 +0200
+Subject: [PATCH 089/182] ARM: dts: MSM8974: Add pinctrl node
+
+Add the pin control node and pin definitions of SPI8.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Acked-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index f687239..23aa387 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -198,5 +198,34 @@
+ clocks = <&gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "core";
+ };
++
++ msmgpio: pinctrl@fd510000 {
++ compatible = "qcom,msm8974-pinctrl";
++ reg = <0xfd510000 0x4000>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ interrupts = <0 208 0>;
++
++ spi8_default: spi8_default {
++ mosi {
++ pins = "gpio45";
++ function = "blsp_spi8";
++ };
++ miso {
++ pins = "gpio46";
++ function = "blsp_spi8";
++ };
++ cs {
++ pins = "gpio47";
++ function = "blsp_spi8";
++ };
++ clk {
++ pins = "gpio48";
++ function = "blsp_spi8";
++ };
++ };
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch
new file mode 100644
index 0000000..c16258c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch
@@ -0,0 +1,75 @@
+From 6632619d49f0f90c4d74caad67749864f154cae4 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Fri, 31 Jan 2014 16:21:56 +0200
+Subject: [PATCH 090/182] ARM: dts: msm: Add SDHC controller nodes for MSM8974
+ and DB8074 board
+
+Add support for the 2 SDHC controllers on the DB8074 board. The first
+controller (at 0xf9824900) is connected to an on board soldered eMMC.
+The second controller (at 0xf98a4900) is connected to a uSD card slot.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 13 +++++++++++++
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 22 ++++++++++++++++++++++
+ 2 files changed, 35 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
+index 13ac3e2..92320c4 100644
+--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
+@@ -3,4 +3,17 @@
+ / {
+ model = "Qualcomm APQ8074 Dragonboard";
+ compatible = "qcom,apq8074-dragonboard", "qcom,apq8074";
++
++ soc: soc {
++ sdhci@f9824900 {
++ bus-width = <8>;
++ non-removable;
++ status = "ok";
++ };
++
++ sdhci@f98a4900 {
++ cd-gpios = <&msmgpio 62 0x1>;
++ bus-width = <4>;
++ };
++ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index 23aa387..c530a33 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -192,6 +192,28 @@
+ clock-names = "core", "iface";
+ };
+
++ sdhci@f9824900 {
++ compatible = "qcom,sdhci-msm-v4";
++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
++ reg-names = "hc_mem", "core_mem";
++ interrupts = <0 123 0>, <0 138 0>;
++ interrupt-names = "hc_irq", "pwr_irq";
++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++
++ sdhci@f98a4900 {
++ compatible = "qcom,sdhci-msm-v4";
++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
++ reg-names = "hc_mem", "core_mem";
++ interrupts = <0 125 0>, <0 221 0>;
++ interrupt-names = "hc_irq", "pwr_irq";
++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++
+ rng@f9bff000 {
+ compatible = "qcom,prng";
+ reg = <0xf9bff000 0x200>;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch
new file mode 100644
index 0000000..dcaaa3d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch
@@ -0,0 +1,186 @@
+From 63495b04141e60ceb40d4632a41b7cd4a3d23dd2 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 28 May 2014 12:01:29 -0500
+Subject: [PATCH 091/182] ARM: dts: qcom: Update msm8974/apq8074 device trees
+
+* Move SoC peripherals into an SoC container node
+* Move serial enabling into board file (qcom-apq8074-dragonboard.dts)
+* Move spi pinctrl into board file
+* Cleanup cpu node to match binding spec, enable-method and compatible
+ should be per cpu, not part of the container
+* Drop interrupts property from l2-cache node as its not part of the
+ binding spec
+* Move timer node out of SoC container
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 28 +++++++++++++-
+ arch/arm/boot/dts/qcom-msm8974.dtsi | 49 +++++++++---------------
+ 2 files changed, 45 insertions(+), 32 deletions(-)
+
+diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
+index 92320c4..b4dfb01 100644
+--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
+@@ -4,7 +4,11 @@
+ model = "Qualcomm APQ8074 Dragonboard";
+ compatible = "qcom,apq8074-dragonboard", "qcom,apq8074";
+
+- soc: soc {
++ soc {
++ serial@f991e000 {
++ status = "ok";
++ };
++
+ sdhci@f9824900 {
+ bus-width = <8>;
+ non-removable;
+@@ -15,5 +19,27 @@
+ cd-gpios = <&msmgpio 62 0x1>;
+ bus-width = <4>;
+ };
++
++
++ pinctrl@fd510000 {
++ spi8_default: spi8_default {
++ mosi {
++ pins = "gpio45";
++ function = "blsp_spi8";
++ };
++ miso {
++ pins = "gpio46";
++ function = "blsp_spi8";
++ };
++ cs {
++ pins = "gpio47";
++ function = "blsp_spi8";
++ };
++ clk {
++ pins = "gpio48";
++ function = "blsp_spi8";
++ };
++ };
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
+index c530a33..69dca2a 100644
+--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
+@@ -13,10 +13,10 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <1 9 0xf04>;
+- compatible = "qcom,krait";
+- enable-method = "qcom,kpss-acc-v2";
+
+ cpu@0 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v2";
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+@@ -24,6 +24,8 @@
+ };
+
+ cpu@1 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v2";
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+@@ -31,6 +33,8 @@
+ };
+
+ cpu@2 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v2";
+ device_type = "cpu";
+ reg = <2>;
+ next-level-cache = <&L2>;
+@@ -38,6 +42,8 @@
+ };
+
+ cpu@3 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v2";
+ device_type = "cpu";
+ reg = <3>;
+ next-level-cache = <&L2>;
+@@ -47,7 +53,6 @@
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+- interrupts = <0 2 0x4>;
+ qcom,saw = <&saw_l2>;
+ };
+ };
+@@ -57,6 +62,15 @@
+ interrupts = <1 7 0xf04>;
+ };
+
++ timer {
++ compatible = "arm,armv7-timer";
++ interrupts = <1 2 0xf08>,
++ <1 3 0xf08>,
++ <1 4 0xf08>,
++ <1 1 0xf08>;
++ clock-frequency = <19200000>;
++ };
++
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -71,15 +85,6 @@
+ <0xf9002000 0x1000>;
+ };
+
+- timer {
+- compatible = "arm,armv7-timer";
+- interrupts = <1 2 0xf08>,
+- <1 3 0xf08>,
+- <1 4 0xf08>,
+- <1 1 0xf08>;
+- clock-frequency = <19200000>;
+- };
+-
+ timer@f9020000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -190,6 +195,7 @@
+ interrupts = <0 108 0x0>;
+ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
++ status = "disabled";
+ };
+
+ sdhci@f9824900 {
+@@ -229,25 +235,6 @@
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 208 0>;
+-
+- spi8_default: spi8_default {
+- mosi {
+- pins = "gpio45";
+- function = "blsp_spi8";
+- };
+- miso {
+- pins = "gpio46";
+- function = "blsp_spi8";
+- };
+- cs {
+- pins = "gpio47";
+- function = "blsp_spi8";
+- };
+- clk {
+- pins = "gpio48";
+- function = "blsp_spi8";
+- };
+- };
+ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch
new file mode 100644
index 0000000..5c0c7a5
--- /dev/null
+++ b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch
@@ -0,0 +1,268 @@
+From 881200420e6ece87d9abbb13c0653d26455cdbdd Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 28 May 2014 12:09:53 -0500
+Subject: [PATCH 092/182] ARM: dts: qcom: Update msm8960 device trees
+
+* Move SoC peripherals into an SoC container node
+* Move serial enabling into board file (qcom-msm8960-cdp.dts)
+* Cleanup cpu node to match binding spec, enable-method and compatible
+ should be per cpu, not part of the container
+* Drop interrupts property from l2-cache node as its not part of the
+ binding spec
+* Add GSBI node and configuration of GSBI controller
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8960-cdp.dts | 10 ++
+ arch/arm/boot/dts/qcom-msm8960.dtsi | 176 ++++++++++++++++++--------------
+ 2 files changed, 108 insertions(+), 78 deletions(-)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
+index a58fb88..8f75cc4 100644
+--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts
++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
+@@ -3,4 +3,14 @@
+ / {
+ model = "Qualcomm MSM8960 CDP";
+ compatible = "qcom,msm8960-cdp", "qcom,msm8960";
++
++ soc {
++ gsbi@16400000 {
++ status = "ok";
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ serial@16440000 {
++ status = "ok";
++ };
++ };
++ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
+index 997b7b9..5303e53 100644
+--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
+@@ -3,6 +3,7 @@
+ /include/ "skeleton.dtsi"
+
+ #include <dt-bindings/clock/qcom,gcc-msm8960.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
+
+ / {
+ model = "Qualcomm MSM8960";
+@@ -13,10 +14,10 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <1 14 0x304>;
+- compatible = "qcom,krait";
+- enable-method = "qcom,kpss-acc-v1";
+
+ cpu@0 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+@@ -25,6 +26,8 @@
+ };
+
+ cpu@1 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+@@ -35,7 +38,6 @@
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+- interrupts = <0 2 0x4>;
+ };
+ };
+
+@@ -45,91 +47,109 @@
+ qcom,no-pc-write;
+ };
+
+- intc: interrupt-controller@2000000 {
+- compatible = "qcom,msm-qgic2";
+- interrupt-controller;
+- #interrupt-cells = <3>;
+- reg = < 0x02000000 0x1000 >,
+- < 0x02002000 0x1000 >;
+- };
++ soc: soc {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "simple-bus";
++
++ intc: interrupt-controller@2000000 {
++ compatible = "qcom,msm-qgic2";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = <0x02000000 0x1000>,
++ <0x02002000 0x1000>;
++ };
+
+- timer@200a000 {
+- compatible = "qcom,kpss-timer", "qcom,msm-timer";
+- interrupts = <1 1 0x301>,
+- <1 2 0x301>,
+- <1 3 0x301>;
+- reg = <0x0200a000 0x100>;
+- clock-frequency = <27000000>,
+- <32768>;
+- cpu-offset = <0x80000>;
+- };
++ timer@200a000 {
++ compatible = "qcom,kpss-timer", "qcom,msm-timer";
++ interrupts = <1 1 0x301>,
++ <1 2 0x301>,
++ <1 3 0x301>;
++ reg = <0x0200a000 0x100>;
++ clock-frequency = <27000000>,
++ <32768>;
++ cpu-offset = <0x80000>;
++ };
+
+- msmgpio: gpio@800000 {
+- compatible = "qcom,msm-gpio";
+- gpio-controller;
+- #gpio-cells = <2>;
+- ngpio = <150>;
+- interrupts = <0 16 0x4>;
+- interrupt-controller;
+- #interrupt-cells = <2>;
+- reg = <0x800000 0x4000>;
+- };
++ msmgpio: gpio@800000 {
++ compatible = "qcom,msm-gpio";
++ gpio-controller;
++ #gpio-cells = <2>;
++ ngpio = <150>;
++ interrupts = <0 16 0x4>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ reg = <0x800000 0x4000>;
++ };
+
+- gcc: clock-controller@900000 {
+- compatible = "qcom,gcc-msm8960";
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- reg = <0x900000 0x4000>;
+- };
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-msm8960";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ reg = <0x900000 0x4000>;
++ };
+
+- clock-controller@4000000 {
+- compatible = "qcom,mmcc-msm8960";
+- reg = <0x4000000 0x1000>;
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- };
++ clock-controller@4000000 {
++ compatible = "qcom,mmcc-msm8960";
++ reg = <0x4000000 0x1000>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
+
+- acc0: clock-controller@2088000 {
+- compatible = "qcom,kpss-acc-v1";
+- reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+- };
++ acc0: clock-controller@2088000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ };
+
+- acc1: clock-controller@2098000 {
+- compatible = "qcom,kpss-acc-v1";
+- reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+- };
++ acc1: clock-controller@2098000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ };
+
+- saw0: regulator@2089000 {
+- compatible = "qcom,saw2";
+- reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
+- regulator;
+- };
++ saw0: regulator@2089000 {
++ compatible = "qcom,saw2";
++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
+
+- saw1: regulator@2099000 {
+- compatible = "qcom,saw2";
+- reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
+- regulator;
+- };
++ saw1: regulator@2099000 {
++ compatible = "qcom,saw2";
++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
+
+- serial@16440000 {
+- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+- reg = <0x16440000 0x1000>,
+- <0x16400000 0x1000>;
+- interrupts = <0 154 0x0>;
+- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
+- clock-names = "core", "iface";
+- };
++ gsbi5: gsbi@16400000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x16400000 0x100>;
++ clocks = <&gcc GSBI5_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ serial@16440000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x16440000 0x1000>,
++ <0x16400000 0x1000>;
++ interrupts = <0 154 0x0>;
++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++ };
+
+- qcom,ssbi@500000 {
+- compatible = "qcom,ssbi";
+- reg = <0x500000 0x1000>;
+- qcom,controller-type = "pmic-arbiter";
+- };
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
+
+- rng@1a500000 {
+- compatible = "qcom,prng";
+- reg = <0x1a500000 0x200>;
+- clocks = <&gcc PRNG_CLK>;
+- clock-names = "core";
++ rng@1a500000 {
++ compatible = "qcom,prng";
++ reg = <0x1a500000 0x200>;
++ clocks = <&gcc PRNG_CLK>;
++ clock-names = "core";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch
new file mode 100644
index 0000000..72f9939
--- /dev/null
+++ b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch
@@ -0,0 +1,191 @@
+From 355bf7c6410f5b6e37b5c2b28ebe59bb701c42d6 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Wed, 28 May 2014 12:12:40 -0500
+Subject: [PATCH 093/182] ARM: dts: qcom: Update msm8660 device trees
+
+* Move SoC peripherals into an SoC container node
+* Move serial enabling into board file (qcom-msm8660-surf.dts)
+* Cleanup cpu node to match binding spec, enable-method and compatible
+ should be per cpu, not part of the container
+* Add GSBI node and configuration of GSBI controller
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-msm8660-surf.dts | 10 +++
+ arch/arm/boot/dts/qcom-msm8660.dtsi | 115 ++++++++++++++++++-------------
+ 2 files changed, 78 insertions(+), 47 deletions(-)
+
+diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts
+index 169bad9..45180ad 100644
+--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts
++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts
+@@ -3,4 +3,14 @@
+ / {
+ model = "Qualcomm MSM8660 SURF";
+ compatible = "qcom,msm8660-surf", "qcom,msm8660";
++
++ soc {
++ gsbi@19c00000 {
++ status = "ok";
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ serial@19c40000 {
++ status = "ok";
++ };
++ };
++ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi
+index c52a9e9..53837aaa2f 100644
+--- a/arch/arm/boot/dts/qcom-msm8660.dtsi
++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi
+@@ -3,6 +3,7 @@
+ /include/ "skeleton.dtsi"
+
+ #include <dt-bindings/clock/qcom,gcc-msm8660.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
+
+ / {
+ model = "Qualcomm MSM8660";
+@@ -12,16 +13,18 @@
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+- compatible = "qcom,scorpion";
+- enable-method = "qcom,gcc-msm8660";
+
+ cpu@0 {
++ compatible = "qcom,scorpion";
++ enable-method = "qcom,gcc-msm8660";
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu@1 {
++ compatible = "qcom,scorpion";
++ enable-method = "qcom,gcc-msm8660";
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+@@ -33,55 +36,73 @@
+ };
+ };
+
+- intc: interrupt-controller@2080000 {
+- compatible = "qcom,msm-8660-qgic";
+- interrupt-controller;
+- #interrupt-cells = <3>;
+- reg = < 0x02080000 0x1000 >,
+- < 0x02081000 0x1000 >;
+- };
++ soc: soc {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "simple-bus";
+
+- timer@2000000 {
+- compatible = "qcom,scss-timer", "qcom,msm-timer";
+- interrupts = <1 0 0x301>,
+- <1 1 0x301>,
+- <1 2 0x301>;
+- reg = <0x02000000 0x100>;
+- clock-frequency = <27000000>,
+- <32768>;
+- cpu-offset = <0x40000>;
+- };
++ intc: interrupt-controller@2080000 {
++ compatible = "qcom,msm-8660-qgic";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = < 0x02080000 0x1000 >,
++ < 0x02081000 0x1000 >;
++ };
+
+- msmgpio: gpio@800000 {
+- compatible = "qcom,msm-gpio";
+- reg = <0x00800000 0x4000>;
+- gpio-controller;
+- #gpio-cells = <2>;
+- ngpio = <173>;
+- interrupts = <0 16 0x4>;
+- interrupt-controller;
+- #interrupt-cells = <2>;
+- };
++ timer@2000000 {
++ compatible = "qcom,scss-timer", "qcom,msm-timer";
++ interrupts = <1 0 0x301>,
++ <1 1 0x301>,
++ <1 2 0x301>;
++ reg = <0x02000000 0x100>;
++ clock-frequency = <27000000>,
++ <32768>;
++ cpu-offset = <0x40000>;
++ };
+
+- gcc: clock-controller@900000 {
+- compatible = "qcom,gcc-msm8660";
+- #clock-cells = <1>;
+- #reset-cells = <1>;
+- reg = <0x900000 0x4000>;
+- };
++ msmgpio: gpio@800000 {
++ compatible = "qcom,msm-gpio";
++ reg = <0x00800000 0x4000>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ ngpio = <173>;
++ interrupts = <0 16 0x4>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ };
+
+- serial@19c40000 {
+- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+- reg = <0x19c40000 0x1000>,
+- <0x19c00000 0x1000>;
+- interrupts = <0 195 0x0>;
+- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
+- clock-names = "core", "iface";
+- };
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-msm8660";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ reg = <0x900000 0x4000>;
++ };
++
++ gsbi12: gsbi@19c00000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x19c00000 0x100>;
++ clocks = <&gcc GSBI12_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
+
+- qcom,ssbi@500000 {
+- compatible = "qcom,ssbi";
+- reg = <0x500000 0x1000>;
+- qcom,controller-type = "pmic-arbiter";
++ serial@19c40000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x19c40000 0x1000>,
++ <0x19c00000 0x1000>;
++ interrupts = <0 195 0x0>;
++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++ };
++
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch
new file mode 100644
index 0000000..848cbc7
--- /dev/null
+++ b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch
@@ -0,0 +1,265 @@
+From 07d7d95706c1bf373bd6b30c42f95c7b8dc8b9ce Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 3 Apr 2014 14:48:22 -0500
+Subject: [PATCH 094/182] ARM: dts: qcom: Add initial APQ8064 SoC and IFC6410
+ board device trees
+
+Add basic APQ8064 SoC include device tree and support for basic booting on
+the IFC6410 board. Also, keep dtb build list and qcom_dt_match in sorted
+order.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile | 8 +-
+ arch/arm/boot/dts/qcom-apq8064-ifc6410.dts | 16 +++
+ arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi | 1 +
+ arch/arm/boot/dts/qcom-apq8064.dtsi | 170 ++++++++++++++++++++++++++++
+ arch/arm/mach-qcom/board.c | 3 +-
+ 5 files changed, 194 insertions(+), 4 deletions(-)
+ create mode 100644 arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+ create mode 100644 arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi
+ create mode 100644 arch/arm/boot/dts/qcom-apq8064.dtsi
+
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index 4a89023..ee3dfea 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -231,9 +231,11 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
+ dra7-evm.dtb
+ dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb
+ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
+-dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \
+- qcom-msm8960-cdp.dtb \
+- qcom-apq8074-dragonboard.dtb
++dtb-$(CONFIG_ARCH_QCOM) += \
++ qcom-apq8064-ifc6410.dtb \
++ qcom-apq8074-dragonboard.dtb \
++ qcom-msm8660-surf.dtb \
++ qcom-msm8960-cdp.dtb
+ dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \
+ ste-hrefprev60-stuib.dtb \
+ ste-hrefprev60-tvk.dtb \
+diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+new file mode 100644
+index 0000000..7c2441d
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+@@ -0,0 +1,16 @@
++#include "qcom-apq8064-v2.0.dtsi"
++
++/ {
++ model = "Qualcomm APQ8064/IFC6410";
++ compatible = "qcom,apq8064-ifc6410", "qcom,apq8064";
++
++ soc {
++ gsbi@16600000 {
++ status = "ok";
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ serial@16640000 {
++ status = "ok";
++ };
++ };
++ };
++};
+diff --git a/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi
+new file mode 100644
+index 0000000..935c394
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi
+@@ -0,0 +1 @@
++#include "qcom-apq8064.dtsi"
+diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
+new file mode 100644
+index 0000000..92bf793
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
+@@ -0,0 +1,170 @@
++/dts-v1/;
++
++#include "skeleton.dtsi"
++#include <dt-bindings/clock/qcom,gcc-msm8960.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
++
++/ {
++ model = "Qualcomm APQ8064";
++ compatible = "qcom,apq8064";
++ interrupt-parent = <&intc>;
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cpu@0 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <0>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc0>;
++ qcom,saw = <&saw0>;
++ };
++
++ cpu@1 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <1>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc1>;
++ qcom,saw = <&saw1>;
++ };
++
++ cpu@2 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <2>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc2>;
++ qcom,saw = <&saw2>;
++ };
++
++ cpu@3 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <3>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc3>;
++ qcom,saw = <&saw3>;
++ };
++
++ L2: l2-cache {
++ compatible = "cache";
++ cache-level = <2>;
++ };
++ };
++
++ cpu-pmu {
++ compatible = "qcom,krait-pmu";
++ interrupts = <1 10 0x304>;
++ };
++
++ soc: soc {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "simple-bus";
++
++ intc: interrupt-controller@2000000 {
++ compatible = "qcom,msm-qgic2";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = <0x02000000 0x1000>,
++ <0x02002000 0x1000>;
++ };
++
++ timer@200a000 {
++ compatible = "qcom,kpss-timer", "qcom,msm-timer";
++ interrupts = <1 1 0x301>,
++ <1 2 0x301>,
++ <1 3 0x301>;
++ reg = <0x0200a000 0x100>;
++ clock-frequency = <27000000>,
++ <32768>;
++ cpu-offset = <0x80000>;
++ };
++
++ acc0: clock-controller@2088000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ acc1: clock-controller@2098000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ acc2: clock-controller@20a8000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x020a8000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ acc3: clock-controller@20b8000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x020b8000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ saw0: regulator@2089000 {
++ compatible = "qcom,saw2";
++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ saw1: regulator@2099000 {
++ compatible = "qcom,saw2";
++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ saw2: regulator@20a9000 {
++ compatible = "qcom,saw2";
++ reg = <0x020a9000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ saw3: regulator@20b9000 {
++ compatible = "qcom,saw2";
++ reg = <0x020b9000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ gsbi7: gsbi@16600000 {
++ status = "disabled";
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x16600000 0x100>;
++ clocks = <&gcc GSBI7_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ serial@16640000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x16640000 0x1000>,
++ <0x16600000 0x1000>;
++ interrupts = <0 158 0x0>;
++ clocks = <&gcc GSBI7_UART_CLK>, <&gcc GSBI7_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++ };
++
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x00500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
++
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-apq8064";
++ reg = <0x00900000 0x4000>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++ };
++};
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+index bae617e..350fa8d 100644
+--- a/arch/arm/mach-qcom/board.c
++++ b/arch/arm/mach-qcom/board.c
+@@ -15,9 +15,10 @@
+ #include <asm/mach/arch.h>
+
+ static const char * const qcom_dt_match[] __initconst = {
++ "qcom,apq8064",
++ "qcom,apq8074-dragonboard",
+ "qcom,msm8660-surf",
+ "qcom,msm8960-cdp",
+- "qcom,apq8074-dragonboard",
+ NULL
+ };
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch
new file mode 100644
index 0000000..a797266
--- /dev/null
+++ b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch
@@ -0,0 +1,43 @@
+From 9b06bc14f21463cf687ac6fdd07ca04b99c15466 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Fri, 23 May 2014 18:12:30 +0300
+Subject: [PATCH 095/182] ARM: dts: qcom: Add APQ8084-MTP board support
+
+Add device-tree file for APQ8084-MTP board, which belongs
+to the Snapdragon 805 family.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-apq8084-mtp.dts | 6 ++++++
+ 2 files changed, 7 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-apq8084-mtp.dts
+
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index ee3dfea..f2aeb95 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -234,6 +234,7 @@ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
+ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8064-ifc6410.dtb \
+ qcom-apq8074-dragonboard.dtb \
++ qcom-apq8084-mtp.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb
+ dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \
+diff --git a/arch/arm/boot/dts/qcom-apq8084-mtp.dts b/arch/arm/boot/dts/qcom-apq8084-mtp.dts
+new file mode 100644
+index 0000000..9dae387
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-apq8084-mtp.dts
+@@ -0,0 +1,6 @@
++#include "qcom-apq8084.dtsi"
++
++/ {
++ model = "Qualcomm APQ 8084-MTP";
++ compatible = "qcom,apq8084-mtp", "qcom,apq8084";
++};
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch
new file mode 100644
index 0000000..87d70a5
--- /dev/null
+++ b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch
@@ -0,0 +1,216 @@
+From 8c52931421759b70fc37771be3390813a2a2f9f5 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Fri, 23 May 2014 18:12:29 +0300
+Subject: [PATCH 096/182] ARM: dts: qcom: Add APQ8084 SoC support
+
+Add support for the Qualcomm Snapdragon 805 APQ8084 SoC. It is
+used on APQ8084-MTP and other boards.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-apq8084.dtsi | 179 +++++++++++++++++++++++++++++++++++
+ arch/arm/mach-qcom/board.c | 1 +
+ 2 files changed, 180 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-apq8084.dtsi
+
+diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi
+new file mode 100644
+index 0000000..e3e009a
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-apq8084.dtsi
+@@ -0,0 +1,179 @@
++/dts-v1/;
++
++#include "skeleton.dtsi"
++
++/ {
++ model = "Qualcomm APQ 8084";
++ compatible = "qcom,apq8084";
++ interrupt-parent = <&intc>;
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cpu@0 {
++ device_type = "cpu";
++ compatible = "qcom,krait";
++ reg = <0>;
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc0>;
++ };
++
++ cpu@1 {
++ device_type = "cpu";
++ compatible = "qcom,krait";
++ reg = <1>;
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc1>;
++ };
++
++ cpu@2 {
++ device_type = "cpu";
++ compatible = "qcom,krait";
++ reg = <2>;
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc2>;
++ };
++
++ cpu@3 {
++ device_type = "cpu";
++ compatible = "qcom,krait";
++ reg = <3>;
++ enable-method = "qcom,kpss-acc-v2";
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc3>;
++ };
++
++ L2: l2-cache {
++ compatible = "qcom,arch-cache";
++ cache-level = <2>;
++ qcom,saw = <&saw_l2>;
++ };
++ };
++
++ cpu-pmu {
++ compatible = "qcom,krait-pmu";
++ interrupts = <1 7 0xf04>;
++ };
++
++ timer {
++ compatible = "arm,armv7-timer";
++ interrupts = <1 2 0xf08>,
++ <1 3 0xf08>,
++ <1 4 0xf08>,
++ <1 1 0xf08>;
++ clock-frequency = <19200000>;
++ };
++
++ soc: soc {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "simple-bus";
++
++ intc: interrupt-controller@f9000000 {
++ compatible = "qcom,msm-qgic2";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = <0xf9000000 0x1000>,
++ <0xf9002000 0x1000>;
++ };
++
++ timer@f9020000 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "arm,armv7-timer-mem";
++ reg = <0xf9020000 0x1000>;
++ clock-frequency = <19200000>;
++
++ frame@f9021000 {
++ frame-number = <0>;
++ interrupts = <0 8 0x4>,
++ <0 7 0x4>;
++ reg = <0xf9021000 0x1000>,
++ <0xf9022000 0x1000>;
++ };
++
++ frame@f9023000 {
++ frame-number = <1>;
++ interrupts = <0 9 0x4>;
++ reg = <0xf9023000 0x1000>;
++ status = "disabled";
++ };
++
++ frame@f9024000 {
++ frame-number = <2>;
++ interrupts = <0 10 0x4>;
++ reg = <0xf9024000 0x1000>;
++ status = "disabled";
++ };
++
++ frame@f9025000 {
++ frame-number = <3>;
++ interrupts = <0 11 0x4>;
++ reg = <0xf9025000 0x1000>;
++ status = "disabled";
++ };
++
++ frame@f9026000 {
++ frame-number = <4>;
++ interrupts = <0 12 0x4>;
++ reg = <0xf9026000 0x1000>;
++ status = "disabled";
++ };
++
++ frame@f9027000 {
++ frame-number = <5>;
++ interrupts = <0 13 0x4>;
++ reg = <0xf9027000 0x1000>;
++ status = "disabled";
++ };
++
++ frame@f9028000 {
++ frame-number = <6>;
++ interrupts = <0 14 0x4>;
++ reg = <0xf9028000 0x1000>;
++ status = "disabled";
++ };
++ };
++
++ saw_l2: regulator@f9012000 {
++ compatible = "qcom,saw2";
++ reg = <0xf9012000 0x1000>;
++ regulator;
++ };
++
++ acc0: clock-controller@f9088000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf9088000 0x1000>,
++ <0xf9008000 0x1000>;
++ };
++
++ acc1: clock-controller@f9098000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf9098000 0x1000>,
++ <0xf9008000 0x1000>;
++ };
++
++ acc2: clock-controller@f90a8000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf90a8000 0x1000>,
++ <0xf9008000 0x1000>;
++ };
++
++ acc3: clock-controller@f90b8000 {
++ compatible = "qcom,kpss-acc-v2";
++ reg = <0xf90b8000 0x1000>,
++ <0xf9008000 0x1000>;
++ };
++
++ restart@fc4ab000 {
++ compatible = "qcom,pshold";
++ reg = <0xfc4ab000 0x4>;
++ };
++ };
++};
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+index 350fa8d..c437a99 100644
+--- a/arch/arm/mach-qcom/board.c
++++ b/arch/arm/mach-qcom/board.c
+@@ -17,6 +17,7 @@
+ static const char * const qcom_dt_match[] __initconst = {
+ "qcom,apq8064",
+ "qcom,apq8074-dragonboard",
++ "qcom,apq8084",
+ "qcom,msm8660-surf",
+ "qcom,msm8960-cdp",
+ NULL
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch
new file mode 100644
index 0000000..2f9cad7
--- /dev/null
+++ b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch
@@ -0,0 +1,267 @@
+From 48167d4a55890a783cc8b1590bc8071253ae4b83 Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Mon, 14 Apr 2014 16:47:34 +0300
+Subject: [PATCH 097/182] ARM: debug: qcom: make UART address selection
+ configuration option
+
+Separate Qualcomm low-level debugging UART to two options.
+
+DEBUG_MSM_UART is used in earlier non-multi platform arches,
+like MSM7X00A, QSD8X50 and MSM7X30.
+
+DEBUG_QCOM_UARTDM is used in multi-plafrom arches and have
+embedded data mover.
+
+Make DEBUG_UART_PHYS and DEBUG_UART_BASE user adjustable by
+Kconfig menu.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Tested-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/Kconfig.debug | 81 +++++++++++++++++-------------------------
+ arch/arm/include/debug/msm.S | 46 +++---------------------
+ arch/arm/mach-msm/Kconfig | 3 --
+ 3 files changed, 38 insertions(+), 92 deletions(-)
+
+diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
+index 4491c7b..1a5895d 100644
+--- a/arch/arm/Kconfig.debug
++++ b/arch/arm/Kconfig.debug
+@@ -353,56 +353,39 @@ choice
+ Say Y here if you want kernel low-level debugging support
+ on MMP UART3.
+
+- config DEBUG_MSM_UART1
+- bool "Kernel low-level debugging messages via MSM UART1"
+- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50
+- select DEBUG_MSM_UART
++ config DEBUG_MSM_UART
++ bool "Kernel low-level debugging messages via MSM UART"
++ depends on ARCH_MSM
+ help
+ Say Y here if you want the debug print routines to direct
+- their output to the first serial port on MSM devices.
++ their output to the serial port on MSM devices.
+
+- config DEBUG_MSM_UART2
+- bool "Kernel low-level debugging messages via MSM UART2"
+- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50
+- select DEBUG_MSM_UART
+- help
+- Say Y here if you want the debug print routines to direct
+- their output to the second serial port on MSM devices.
++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE #
++ MSM7X00A, QSD8X50 0xa9a00000 0xe1000000 UART1
++ MSM7X00A, QSD8X50 0xa9b00000 0xe1000000 UART2
++ MSM7X00A, QSD8X50 0xa9c00000 0xe1000000 UART3
+
+- config DEBUG_MSM_UART3
+- bool "Kernel low-level debugging messages via MSM UART3"
+- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50
+- select DEBUG_MSM_UART
+- help
+- Say Y here if you want the debug print routines to direct
+- their output to the third serial port on MSM devices.
++ MSM7X30 0xaca00000 0xe1000000 UART1
++ MSM7X30 0xacb00000 0xe1000000 UART2
++ MSM7X30 0xacc00000 0xe1000000 UART3
+
+- config DEBUG_MSM8660_UART
+- bool "Kernel low-level debugging messages via MSM 8660 UART"
+- depends on ARCH_MSM8X60
+- select MSM_HAS_DEBUG_UART_HS
+- select DEBUG_MSM_UART
+- help
+- Say Y here if you want the debug print routines to direct
+- their output to the serial port on MSM 8660 devices.
++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration
++ options based on your needs.
+
+- config DEBUG_MSM8960_UART
+- bool "Kernel low-level debugging messages via MSM 8960 UART"
+- depends on ARCH_MSM8960
+- select MSM_HAS_DEBUG_UART_HS
+- select DEBUG_MSM_UART
++ config DEBUG_QCOM_UARTDM
++ bool "Kernel low-level debugging messages via QCOM UARTDM"
++ depends on ARCH_QCOM
+ help
+ Say Y here if you want the debug print routines to direct
+- their output to the serial port on MSM 8960 devices.
++ their output to the serial port on Qualcomm devices.
+
+- config DEBUG_MSM8974_UART
+- bool "Kernel low-level debugging messages via MSM 8974 UART"
+- depends on ARCH_MSM8974
+- select MSM_HAS_DEBUG_UART_HS
+- select DEBUG_MSM_UART
+- help
+- Say Y here if you want the debug print routines to direct
+- their output to the serial port on MSM 8974 devices.
++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE
++ MSM8X60 0x19c40000 0xf0040000
++ MSM8960 0x16440000 0xf0040000
++ MSM8974 0xf991e000 0xfa71e000
++
++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration
++ options based on your needs.
+
+ config DEBUG_MVEBU_UART
+ bool "Kernel low-level debugging messages via MVEBU UART (old bootloaders)"
+@@ -954,10 +937,6 @@ config DEBUG_STI_UART
+ bool
+ depends on ARCH_STI
+
+-config DEBUG_MSM_UART
+- bool
+- depends on ARCH_MSM || ARCH_QCOM
+-
+ config DEBUG_LL_INCLUDE
+ string
+ default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250
+@@ -975,7 +954,7 @@ config DEBUG_LL_INCLUDE
+ DEBUG_IMX53_UART ||\
+ DEBUG_IMX6Q_UART || \
+ DEBUG_IMX6SL_UART
+- default "debug/msm.S" if DEBUG_MSM_UART
++ default "debug/msm.S" if DEBUG_MSM_UART || DEBUG_QCOM_UARTDM
+ default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART
+ default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1
+ default "debug/sti.S" if DEBUG_STI_UART
+@@ -1039,6 +1018,7 @@ config DEBUG_UART_PHYS
+ default 0x80074000 if DEBUG_IMX28_UART
+ default 0x808c0000 if ARCH_EP93XX
+ default 0x90020000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART
++ default 0xa9a00000 if DEBUG_MSM_UART
+ default 0xb0090000 if DEBUG_VEXPRESS_UART0_CRX
+ default 0xc0013000 if DEBUG_U300_UART
+ default 0xc8000000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN
+@@ -1054,6 +1034,7 @@ config DEBUG_UART_PHYS
+ ARCH_ORION5X
+ default 0xf7fc9000 if DEBUG_BERLIN_UART
+ default 0xf8b00000 if DEBUG_HI3716_UART
++ default 0xf991e000 if DEBUG_QCOM_UARTDM
+ default 0xfcb00000 if DEBUG_HI3620_UART
+ default 0xfe800000 if ARCH_IOP32X
+ default 0xffc02000 if DEBUG_SOCFPGA_UART
+@@ -1062,11 +1043,13 @@ config DEBUG_UART_PHYS
+ default 0xfffff700 if ARCH_IOP33X
+ depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \
+ DEBUG_LL_UART_EFM32 || \
+- DEBUG_UART_8250 || DEBUG_UART_PL01X
++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \
++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM
+
+ config DEBUG_UART_VIRT
+ hex "Virtual base address of debug UART"
+ default 0xe0010fe0 if ARCH_RPC
++ default 0xe1000000 if DEBUG_MSM_UART
+ default 0xf0000be0 if ARCH_EBSA110
+ default 0xf0009000 if DEBUG_CNS3XXX
+ default 0xf01fb000 if DEBUG_NOMADIK_UART
+@@ -1081,6 +1064,7 @@ config DEBUG_UART_VIRT
+ default 0xf7fc9000 if DEBUG_BERLIN_UART
+ default 0xf8009000 if DEBUG_VEXPRESS_UART0_CA9
+ default 0xf8090000 if DEBUG_VEXPRESS_UART0_RS1
++ default 0xfa71e000 if DEBUG_QCOM_UARTDM
+ default 0xfb009000 if DEBUG_REALVIEW_STD_PORT
+ default 0xfb10c000 if DEBUG_REALVIEW_PB1176_PORT
+ default 0xfd000000 if ARCH_SPEAR3XX || ARCH_SPEAR6XX
+@@ -1120,7 +1104,8 @@ config DEBUG_UART_VIRT
+ default 0xff003000 if DEBUG_U300_UART
+ default DEBUG_UART_PHYS if !MMU
+ depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \
+- DEBUG_UART_8250 || DEBUG_UART_PL01X
++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \
++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM
+
+ config DEBUG_UART_8250_SHIFT
+ int "Register offset shift for the 8250 debug UART"
+diff --git a/arch/arm/include/debug/msm.S b/arch/arm/include/debug/msm.S
+index 9d653d4..9ef5761 100644
+--- a/arch/arm/include/debug/msm.S
++++ b/arch/arm/include/debug/msm.S
+@@ -15,51 +15,15 @@
+ *
+ */
+
+-#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_QSD8X50)
+-#define MSM_UART1_PHYS 0xA9A00000
+-#define MSM_UART2_PHYS 0xA9B00000
+-#define MSM_UART3_PHYS 0xA9C00000
+-#elif defined(CONFIG_ARCH_MSM7X30)
+-#define MSM_UART1_PHYS 0xACA00000
+-#define MSM_UART2_PHYS 0xACB00000
+-#define MSM_UART3_PHYS 0xACC00000
+-#endif
+-
+-#if defined(CONFIG_DEBUG_MSM_UART1)
+-#define MSM_DEBUG_UART_BASE 0xE1000000
+-#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS
+-#elif defined(CONFIG_DEBUG_MSM_UART2)
+-#define MSM_DEBUG_UART_BASE 0xE1000000
+-#define MSM_DEBUG_UART_PHYS MSM_UART2_PHYS
+-#elif defined(CONFIG_DEBUG_MSM_UART3)
+-#define MSM_DEBUG_UART_BASE 0xE1000000
+-#define MSM_DEBUG_UART_PHYS MSM_UART3_PHYS
+-#endif
+-
+-#ifdef CONFIG_DEBUG_MSM8660_UART
+-#define MSM_DEBUG_UART_BASE 0xF0040000
+-#define MSM_DEBUG_UART_PHYS 0x19C40000
+-#endif
+-
+-#ifdef CONFIG_DEBUG_MSM8960_UART
+-#define MSM_DEBUG_UART_BASE 0xF0040000
+-#define MSM_DEBUG_UART_PHYS 0x16440000
+-#endif
+-
+-#ifdef CONFIG_DEBUG_MSM8974_UART
+-#define MSM_DEBUG_UART_BASE 0xFA71E000
+-#define MSM_DEBUG_UART_PHYS 0xF991E000
+-#endif
+-
+ .macro addruart, rp, rv, tmp
+-#ifdef MSM_DEBUG_UART_PHYS
+- ldr \rp, =MSM_DEBUG_UART_PHYS
+- ldr \rv, =MSM_DEBUG_UART_BASE
++#ifdef CONFIG_DEBUG_UART_PHYS
++ ldr \rp, =CONFIG_DEBUG_UART_PHYS
++ ldr \rv, =CONFIG_DEBUG_UART_VIRT
+ #endif
+ .endm
+
+ .macro senduart, rd, rx
+-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS
++#ifdef CONFIG_DEBUG_QCOM_UARTDM
+ @ Write the 1 character to UARTDM_TF
+ str \rd, [\rx, #0x70]
+ #else
+@@ -68,7 +32,7 @@
+ .endm
+
+ .macro waituart, rd, rx
+-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS
++#ifdef CONFIG_DEBUG_QCOM_UARTDM
+ @ check for TX_EMT in UARTDM_SR
+ ldr \rd, [\rx, #0x08]
+ tst \rd, #0x08
+diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
+index a7f959e..9b26976 100644
+--- a/arch/arm/mach-msm/Kconfig
++++ b/arch/arm/mach-msm/Kconfig
+@@ -42,9 +42,6 @@ config ARCH_QSD8X50
+
+ endchoice
+
+-config MSM_HAS_DEBUG_UART_HS
+- bool
+-
+ config MSM_SOC_REV_A
+ bool
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch
new file mode 100644
index 0000000..fdb5c67
--- /dev/null
+++ b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch
@@ -0,0 +1,30 @@
+From 0a551e04e9a36ca1f8332071787b0e26bc5f8ecc Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Fri, 23 May 2014 18:12:32 +0300
+Subject: [PATCH 098/182] ARM: debug: qcom: add UART addresses to Kconfig help
+ for APQ8084
+
+Add information about the APQ8084 debug UART physical and virtual
+addresses in the DEBUG_QCOM_UARTDM Kconfig help section.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/Kconfig.debug | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
+index 1a5895d..7820af1 100644
+--- a/arch/arm/Kconfig.debug
++++ b/arch/arm/Kconfig.debug
+@@ -380,6 +380,7 @@ choice
+ their output to the serial port on Qualcomm devices.
+
+ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE
++ APQ8084 0xf995e000 0xfa75e000
+ MSM8X60 0x19c40000 0xf0040000
+ MSM8960 0x16440000 0xf0040000
+ MSM8974 0xf991e000 0xfa71e000
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch
new file mode 100644
index 0000000..4e603ea
--- /dev/null
+++ b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch
@@ -0,0 +1,31 @@
+From b6ee2071ec3af3ec90d93b8b70aa17d741590148 Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Date: Thu, 15 May 2014 11:08:55 +0100
+Subject: [PATCH 099/182] ARM: qcom: Enable ARM_AMBA option for Qualcomm SOCs.
+
+As some of the IPs on Qualcomm SOCs are based on ARM PrimeCell IPs.
+For example SDCC controller is PrimeCell MCI pl180. Adding this option will
+give flexibility to reuse the existing drivers as it is without major
+modifications.
+
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
+index 6440c11..63502cc 100644
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -2,6 +2,7 @@ config ARCH_QCOM
+ bool "Qualcomm Support" if ARCH_MULTI_V7
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_GIC
++ select ARM_AMBA
+ select CLKSRC_OF
+ select GENERIC_CLOCKEVENTS
+ select HAVE_SMP
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch
new file mode 100644
index 0000000..1fbd806
--- /dev/null
+++ b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch
@@ -0,0 +1,42 @@
+From d54996ddc69b69fe21f776e1de1eace04bca01c3 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 10:05:14 -0700
+Subject: [PATCH 100/182] clk: qcom: Fix msm8660 GCC probe
+
+When consolidating the msm8660 GCC probe code I forgot to keep
+around these temporary clock registrations. Put them back so the
+clock tree is not entirely orphaned.
+
+Fixes: 49fc825f0cc2 (clk: qcom: Consolidate common probe code)
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/gcc-msm8660.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c
+index 44bc6fa..0c4b727 100644
+--- a/drivers/clk/qcom/gcc-msm8660.c
++++ b/drivers/clk/qcom/gcc-msm8660.c
+@@ -2718,6 +2718,18 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table);
+
+ static int gcc_msm8660_probe(struct platform_device *pdev)
+ {
++ struct clk *clk;
++ struct device *dev = &pdev->dev;
++
++ /* Temporary until RPM clocks supported */
++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
+ return qcom_cc_probe(pdev, &gcc_msm8660_desc);
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch
new file mode 100644
index 0000000..9555e6b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch
@@ -0,0 +1,30 @@
+From b11c02becb4dadf5c375fef4b98f92af67106f18 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <gdjakov@mm-sol.com>
+Date: Tue, 20 May 2014 19:50:54 +0300
+Subject: [PATCH 101/182] clk: qcom: Fix blsp2_ahb_clk register offset
+
+The address of the blsp2_ahb_clk register is incorrect. Fix it.
+
+Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/gcc-msm8974.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c
+index 0d1edc1..7a420fc 100644
+--- a/drivers/clk/qcom/gcc-msm8974.c
++++ b/drivers/clk/qcom/gcc-msm8974.c
+@@ -1341,7 +1341,7 @@ static struct clk_branch gcc_blsp1_uart6_apps_clk = {
+ };
+
+ static struct clk_branch gcc_blsp2_ahb_clk = {
+- .halt_reg = 0x05c4,
++ .halt_reg = 0x0944,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x1484,
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch
new file mode 100644
index 0000000..8c959a5
--- /dev/null
+++ b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch
@@ -0,0 +1,36 @@
+From 6417bb8469eb495d7f4e4b6b0ac08cbc1b8606cb Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 16:07:10 -0700
+Subject: [PATCH 104/182] clk: qcom: Return highest rate when round_rate()
+ exceeds plan
+
+Some drivers may want to call clk_set_rate() with a very large
+number to force the clock to go as fast as it possibly can
+without having to know the range between the highest rate and
+second highest rate. Add support for this by defaulting to the
+highest rate in the frequency table if we can't find a frequency
+greater than what is requested.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/clk-rcg2.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
+index 0996a3a..cbecaec 100644
+--- a/drivers/clk/qcom/clk-rcg2.c
++++ b/drivers/clk/qcom/clk-rcg2.c
+@@ -181,7 +181,8 @@ struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
+ if (rate <= f->freq)
+ return f;
+
+- return NULL;
++ /* Default to our fastest rate */
++ return f - 1;
+ }
+
+ static long _freq_tbl_determine_rate(struct clk_hw *hw,
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch
new file mode 100644
index 0000000..b955629
--- /dev/null
+++ b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch
@@ -0,0 +1,378 @@
+From 3123079878e29eb8c541111e30de4d1bb42ac6f9 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 16:07:11 -0700
+Subject: [PATCH 105/182] clk: qcom: Support display RCG clocks
+
+Add support for the DSI/EDP/HDMI RCG clocks. With the proper
+display driver in place this should allow us to support display
+clocks on msm8974 based devices.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/clk-rcg.h | 3 +
+ drivers/clk/qcom/clk-rcg2.c | 299 ++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 287 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
+index 1d6b6de..b9ec11d 100644
+--- a/drivers/clk/qcom/clk-rcg.h
++++ b/drivers/clk/qcom/clk-rcg.h
+@@ -155,5 +155,8 @@ struct clk_rcg2 {
+ #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
+
+ extern const struct clk_ops clk_rcg2_ops;
++extern const struct clk_ops clk_edp_pixel_ops;
++extern const struct clk_ops clk_byte_ops;
++extern const struct clk_ops clk_pixel_ops;
+
+ #endif
+diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
+index cbecaec..cd185d5 100644
+--- a/drivers/clk/qcom/clk-rcg2.c
++++ b/drivers/clk/qcom/clk-rcg2.c
+@@ -19,6 +19,7 @@
+ #include <linux/clk-provider.h>
+ #include <linux/delay.h>
+ #include <linux/regmap.h>
++#include <linux/math64.h>
+
+ #include <asm/div64.h>
+
+@@ -225,31 +226,25 @@ static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
+ return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
+ }
+
+-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
++static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
+ {
+- struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+- const struct freq_tbl *f;
+ u32 cfg, mask;
+ int ret;
+
+- f = find_freq(rcg->freq_tbl, rate);
+- if (!f)
+- return -EINVAL;
+-
+ if (rcg->mnd_width && f->n) {
+ mask = BIT(rcg->mnd_width) - 1;
+- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG,
+- mask, f->m);
++ ret = regmap_update_bits(rcg->clkr.regmap,
++ rcg->cmd_rcgr + M_REG, mask, f->m);
+ if (ret)
+ return ret;
+
+- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG,
+- mask, ~(f->n - f->m));
++ ret = regmap_update_bits(rcg->clkr.regmap,
++ rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
+ if (ret)
+ return ret;
+
+- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG,
+- mask, ~f->n);
++ ret = regmap_update_bits(rcg->clkr.regmap,
++ rcg->cmd_rcgr + D_REG, mask, ~f->n);
+ if (ret)
+ return ret;
+ }
+@@ -260,14 +255,26 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+ cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT;
+ if (rcg->mnd_width && f->n)
+ cfg |= CFG_MODE_DUAL_EDGE;
+- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask,
+- cfg);
++ ret = regmap_update_bits(rcg->clkr.regmap,
++ rcg->cmd_rcgr + CFG_REG, mask, cfg);
+ if (ret)
+ return ret;
+
+ return update_config(rcg);
+ }
+
++static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ const struct freq_tbl *f;
++
++ f = find_freq(rcg->freq_tbl, rate);
++ if (!f)
++ return -EINVAL;
++
++ return clk_rcg2_configure(rcg, f);
++}
++
+ static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+ {
+@@ -290,3 +297,265 @@ const struct clk_ops clk_rcg2_ops = {
+ .set_rate_and_parent = clk_rcg2_set_rate_and_parent,
+ };
+ EXPORT_SYMBOL_GPL(clk_rcg2_ops);
++
++struct frac_entry {
++ int num;
++ int den;
++};
++
++static const struct frac_entry frac_table_675m[] = { /* link rate of 270M */
++ { 52, 295 }, /* 119 M */
++ { 11, 57 }, /* 130.25 M */
++ { 63, 307 }, /* 138.50 M */
++ { 11, 50 }, /* 148.50 M */
++ { 47, 206 }, /* 154 M */
++ { 31, 100 }, /* 205.25 M */
++ { 107, 269 }, /* 268.50 M */
++ { },
++};
++
++static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
++ { 31, 211 }, /* 119 M */
++ { 32, 199 }, /* 130.25 M */
++ { 63, 307 }, /* 138.50 M */
++ { 11, 60 }, /* 148.50 M */
++ { 50, 263 }, /* 154 M */
++ { 31, 120 }, /* 205.25 M */
++ { 119, 359 }, /* 268.50 M */
++ { },
++};
++
++static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ struct freq_tbl f = *rcg->freq_tbl;
++ const struct frac_entry *frac;
++ int delta = 100000;
++ s64 src_rate = parent_rate;
++ s64 request;
++ u32 mask = BIT(rcg->hid_width) - 1;
++ u32 hid_div;
++
++ if (src_rate == 810000000)
++ frac = frac_table_810m;
++ else
++ frac = frac_table_675m;
++
++ for (; frac->num; frac++) {
++ request = rate;
++ request *= frac->den;
++ request = div_s64(request, frac->num);
++ if ((src_rate < (request - delta)) ||
++ (src_rate > (request + delta)))
++ continue;
++
++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
++ &hid_div);
++ f.pre_div = hid_div;
++ f.pre_div >>= CFG_SRC_DIV_SHIFT;
++ f.pre_div &= mask;
++ f.m = frac->num;
++ f.n = frac->den;
++
++ return clk_rcg2_configure(rcg, &f);
++ }
++
++ return -EINVAL;
++}
++
++static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate, u8 index)
++{
++ /* Parent index is set statically in frequency table */
++ return clk_edp_pixel_set_rate(hw, rate, parent_rate);
++}
++
++static long clk_edp_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *p_rate, struct clk **p)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ const struct freq_tbl *f = rcg->freq_tbl;
++ const struct frac_entry *frac;
++ int delta = 100000;
++ s64 src_rate = *p_rate;
++ s64 request;
++ u32 mask = BIT(rcg->hid_width) - 1;
++ u32 hid_div;
++
++ /* Force the correct parent */
++ *p = clk_get_parent_by_index(hw->clk, f->src);
++
++ if (src_rate == 810000000)
++ frac = frac_table_810m;
++ else
++ frac = frac_table_675m;
++
++ for (; frac->num; frac++) {
++ request = rate;
++ request *= frac->den;
++ request = div_s64(request, frac->num);
++ if ((src_rate < (request - delta)) ||
++ (src_rate > (request + delta)))
++ continue;
++
++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
++ &hid_div);
++ hid_div >>= CFG_SRC_DIV_SHIFT;
++ hid_div &= mask;
++
++ return calc_rate(src_rate, frac->num, frac->den, !!frac->den,
++ hid_div);
++ }
++
++ return -EINVAL;
++}
++
++const struct clk_ops clk_edp_pixel_ops = {
++ .is_enabled = clk_rcg2_is_enabled,
++ .get_parent = clk_rcg2_get_parent,
++ .set_parent = clk_rcg2_set_parent,
++ .recalc_rate = clk_rcg2_recalc_rate,
++ .set_rate = clk_edp_pixel_set_rate,
++ .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent,
++ .determine_rate = clk_edp_pixel_determine_rate,
++};
++EXPORT_SYMBOL_GPL(clk_edp_pixel_ops);
++
++static long clk_byte_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *p_rate, struct clk **p)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ const struct freq_tbl *f = rcg->freq_tbl;
++ unsigned long parent_rate, div;
++ u32 mask = BIT(rcg->hid_width) - 1;
++
++ if (rate == 0)
++ return -EINVAL;
++
++ *p = clk_get_parent_by_index(hw->clk, f->src);
++ *p_rate = parent_rate = __clk_round_rate(*p, rate);
++
++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
++ div = min_t(u32, div, mask);
++
++ return calc_rate(parent_rate, 0, 0, 0, div);
++}
++
++static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ struct freq_tbl f = *rcg->freq_tbl;
++ unsigned long div;
++ u32 mask = BIT(rcg->hid_width) - 1;
++
++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
++ div = min_t(u32, div, mask);
++
++ f.pre_div = div;
++
++ return clk_rcg2_configure(rcg, &f);
++}
++
++static int clk_byte_set_rate_and_parent(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate, u8 index)
++{
++ /* Parent index is set statically in frequency table */
++ return clk_byte_set_rate(hw, rate, parent_rate);
++}
++
++const struct clk_ops clk_byte_ops = {
++ .is_enabled = clk_rcg2_is_enabled,
++ .get_parent = clk_rcg2_get_parent,
++ .set_parent = clk_rcg2_set_parent,
++ .recalc_rate = clk_rcg2_recalc_rate,
++ .set_rate = clk_byte_set_rate,
++ .set_rate_and_parent = clk_byte_set_rate_and_parent,
++ .determine_rate = clk_byte_determine_rate,
++};
++EXPORT_SYMBOL_GPL(clk_byte_ops);
++
++static const struct frac_entry frac_table_pixel[] = {
++ { 3, 8 },
++ { 2, 9 },
++ { 4, 9 },
++ { 1, 1 },
++ { }
++};
++
++static long clk_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *p_rate, struct clk **p)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ unsigned long request, src_rate;
++ int delta = 100000;
++ const struct freq_tbl *f = rcg->freq_tbl;
++ const struct frac_entry *frac = frac_table_pixel;
++ struct clk *parent = *p = clk_get_parent_by_index(hw->clk, f->src);
++
++ for (; frac->num; frac++) {
++ request = (rate * frac->den) / frac->num;
++
++ src_rate = __clk_round_rate(parent, request);
++ if ((src_rate < (request - delta)) ||
++ (src_rate > (request + delta)))
++ continue;
++
++ *p_rate = src_rate;
++ return (src_rate * frac->num) / frac->den;
++ }
++
++ return -EINVAL;
++}
++
++static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
++ struct freq_tbl f = *rcg->freq_tbl;
++ const struct frac_entry *frac = frac_table_pixel;
++ unsigned long request, src_rate;
++ int delta = 100000;
++ u32 mask = BIT(rcg->hid_width) - 1;
++ u32 hid_div;
++ struct clk *parent = clk_get_parent_by_index(hw->clk, f.src);
++
++ for (; frac->num; frac++) {
++ request = (rate * frac->den) / frac->num;
++
++ src_rate = __clk_round_rate(parent, request);
++ if ((src_rate < (request - delta)) ||
++ (src_rate > (request + delta)))
++ continue;
++
++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
++ &hid_div);
++ f.pre_div = hid_div;
++ f.pre_div >>= CFG_SRC_DIV_SHIFT;
++ f.pre_div &= mask;
++ f.m = frac->num;
++ f.n = frac->den;
++
++ return clk_rcg2_configure(rcg, &f);
++ }
++ return -EINVAL;
++}
++
++static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate, u8 index)
++{
++ /* Parent index is set statically in frequency table */
++ return clk_pixel_set_rate(hw, rate, parent_rate);
++}
++
++const struct clk_ops clk_pixel_ops = {
++ .is_enabled = clk_rcg2_is_enabled,
++ .get_parent = clk_rcg2_get_parent,
++ .set_parent = clk_rcg2_set_parent,
++ .recalc_rate = clk_rcg2_recalc_rate,
++ .set_rate = clk_pixel_set_rate,
++ .set_rate_and_parent = clk_pixel_set_rate_and_parent,
++ .determine_rate = clk_pixel_determine_rate,
++};
++EXPORT_SYMBOL_GPL(clk_pixel_ops);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch
new file mode 100644
index 0000000..ce9499a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch
@@ -0,0 +1,259 @@
+From 4ccbe584ecb970f86bab58c0ca93998cccc9e810 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 16:07:12 -0700
+Subject: [PATCH 106/182] clk: qcom: Properly support display clocks on
+ msm8974
+
+The display clocks all source from dedicated phy PLLs within their
+respective multimedia hardware block. Hook up these PLLs to the
+display clocks with the appropriate parent mappings, clock flags,
+and the appropriate clock ops. This should allow the display
+clocks to work once the appropriate phy PLL driver registers their
+PLL clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/mmcc-msm8974.c | 105 ++++++++++++++++++++-------------------
+ 1 file changed, 54 insertions(+), 51 deletions(-)
+
+diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c
+index 62200bb..c65b905 100644
+--- a/drivers/clk/qcom/mmcc-msm8974.c
++++ b/drivers/clk/qcom/mmcc-msm8974.c
+@@ -41,9 +41,11 @@
+ #define P_EDPVCO 3
+ #define P_GPLL1 4
+ #define P_DSI0PLL 4
++#define P_DSI0PLL_BYTE 4
+ #define P_MMPLL2 4
+ #define P_MMPLL3 4
+ #define P_DSI1PLL 5
++#define P_DSI1PLL_BYTE 5
+
+ static const u8 mmcc_xo_mmpll0_mmpll1_gpll0_map[] = {
+ [P_XO] = 0,
+@@ -161,6 +163,24 @@ static const char *mmcc_xo_dsi_hdmi_edp_gpll0[] = {
+ "dsi1pll",
+ };
+
++static const u8 mmcc_xo_dsibyte_hdmi_edp_gpll0_map[] = {
++ [P_XO] = 0,
++ [P_EDPLINK] = 4,
++ [P_HDMIPLL] = 3,
++ [P_GPLL0] = 5,
++ [P_DSI0PLL_BYTE] = 1,
++ [P_DSI1PLL_BYTE] = 2,
++};
++
++static const char *mmcc_xo_dsibyte_hdmi_edp_gpll0[] = {
++ "xo",
++ "edp_link_clk",
++ "hdmipll",
++ "gpll0_vote",
++ "dsi0pllbyte",
++ "dsi1pllbyte",
++};
++
+ #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+ static struct clk_pll mmpll0 = {
+@@ -500,15 +520,8 @@ static struct clk_rcg2 jpeg2_clk_src = {
+ },
+ };
+
+-static struct freq_tbl ftbl_mdss_pclk0_clk[] = {
+- F(125000000, P_DSI0PLL, 2, 0, 0),
+- F(250000000, P_DSI0PLL, 1, 0, 0),
+- { }
+-};
+-
+-static struct freq_tbl ftbl_mdss_pclk1_clk[] = {
+- F(125000000, P_DSI1PLL, 2, 0, 0),
+- F(250000000, P_DSI1PLL, 1, 0, 0),
++static struct freq_tbl pixel_freq_tbl[] = {
++ { .src = P_DSI0PLL },
+ { }
+ };
+
+@@ -517,12 +530,13 @@ static struct clk_rcg2 pclk0_clk_src = {
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
+- .freq_tbl = ftbl_mdss_pclk0_clk,
++ .freq_tbl = pixel_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pclk0_clk_src",
+ .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_pixel_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+@@ -531,12 +545,13 @@ static struct clk_rcg2 pclk1_clk_src = {
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
+- .freq_tbl = ftbl_mdss_pclk1_clk,
++ .freq_tbl = pixel_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pclk1_clk_src",
+ .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_pixel_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+@@ -754,41 +769,36 @@ static struct clk_rcg2 cpp_clk_src = {
+ },
+ };
+
+-static struct freq_tbl ftbl_mdss_byte0_clk[] = {
+- F(93750000, P_DSI0PLL, 8, 0, 0),
+- F(187500000, P_DSI0PLL, 4, 0, 0),
+- { }
+-};
+-
+-static struct freq_tbl ftbl_mdss_byte1_clk[] = {
+- F(93750000, P_DSI1PLL, 8, 0, 0),
+- F(187500000, P_DSI1PLL, 4, 0, 0),
++static struct freq_tbl byte_freq_tbl[] = {
++ { .src = P_DSI0PLL_BYTE },
+ { }
+ };
+
+ static struct clk_rcg2 byte0_clk_src = {
+ .cmd_rcgr = 0x2120,
+ .hid_width = 5,
+- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
+- .freq_tbl = ftbl_mdss_byte0_clk,
++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map,
++ .freq_tbl = byte_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "byte0_clk_src",
+- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_byte_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+ static struct clk_rcg2 byte1_clk_src = {
+ .cmd_rcgr = 0x2140,
+ .hid_width = 5,
+- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
+- .freq_tbl = ftbl_mdss_byte1_clk,
++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map,
++ .freq_tbl = byte_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "byte1_clk_src",
+- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_byte_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+@@ -826,12 +836,12 @@ static struct clk_rcg2 edplink_clk_src = {
+ .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
+ .num_parents = 6,
+ .ops = &clk_rcg2_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+-static struct freq_tbl ftbl_mdss_edppixel_clk[] = {
+- F(175000000, P_EDPVCO, 2, 0, 0),
+- F(350000000, P_EDPVCO, 11, 0, 0),
++static struct freq_tbl edp_pixel_freq_tbl[] = {
++ { .src = P_EDPVCO },
+ { }
+ };
+
+@@ -840,12 +850,12 @@ static struct clk_rcg2 edppixel_clk_src = {
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = mmcc_xo_dsi_hdmi_edp_map,
+- .freq_tbl = ftbl_mdss_edppixel_clk,
++ .freq_tbl = edp_pixel_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "edppixel_clk_src",
+ .parent_names = mmcc_xo_dsi_hdmi_edp,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_edp_pixel_ops,
+ },
+ };
+
+@@ -857,11 +867,11 @@ static struct freq_tbl ftbl_mdss_esc0_1_clk[] = {
+ static struct clk_rcg2 esc0_clk_src = {
+ .cmd_rcgr = 0x2160,
+ .hid_width = 5,
+- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map,
+ .freq_tbl = ftbl_mdss_esc0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "esc0_clk_src",
+- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0,
+ .num_parents = 6,
+ .ops = &clk_rcg2_ops,
+ },
+@@ -870,26 +880,18 @@ static struct clk_rcg2 esc0_clk_src = {
+ static struct clk_rcg2 esc1_clk_src = {
+ .cmd_rcgr = 0x2180,
+ .hid_width = 5,
+- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map,
+ .freq_tbl = ftbl_mdss_esc0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "esc1_clk_src",
+- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0,
+ .num_parents = 6,
+ .ops = &clk_rcg2_ops,
+ },
+ };
+
+-static struct freq_tbl ftbl_mdss_extpclk_clk[] = {
+- F(25200000, P_HDMIPLL, 1, 0, 0),
+- F(27000000, P_HDMIPLL, 1, 0, 0),
+- F(27030000, P_HDMIPLL, 1, 0, 0),
+- F(65000000, P_HDMIPLL, 1, 0, 0),
+- F(74250000, P_HDMIPLL, 1, 0, 0),
+- F(108000000, P_HDMIPLL, 1, 0, 0),
+- F(148500000, P_HDMIPLL, 1, 0, 0),
+- F(268500000, P_HDMIPLL, 1, 0, 0),
+- F(297000000, P_HDMIPLL, 1, 0, 0),
++static struct freq_tbl extpclk_freq_tbl[] = {
++ { .src = P_HDMIPLL },
+ { }
+ };
+
+@@ -897,12 +899,13 @@ static struct clk_rcg2 extpclk_clk_src = {
+ .cmd_rcgr = 0x2060,
+ .hid_width = 5,
+ .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map,
+- .freq_tbl = ftbl_mdss_extpclk_clk,
++ .freq_tbl = extpclk_freq_tbl,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "extpclk_clk_src",
+ .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0,
+ .num_parents = 6,
+- .ops = &clk_rcg2_ops,
++ .ops = &clk_byte_ops,
++ .flags = CLK_SET_RATE_PARENT,
+ },
+ };
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch
new file mode 100644
index 0000000..1d0ee45
--- /dev/null
+++ b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch
@@ -0,0 +1,245 @@
+From a740d2b024c5b71c6f9989976049f03b634bb13d Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 16:07:13 -0700
+Subject: [PATCH 107/182] clk: qcom: Support msm8974pro global clock control
+ hardware
+
+A new PLL (gpll4) is added on msm8974 PRO devices to support a
+faster sdc1 clock rate. Add support for this and the two new sdcc
+cal clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ .../devicetree/bindings/clock/qcom,gcc.txt | 2 +
+ drivers/clk/qcom/gcc-msm8974.c | 130 +++++++++++++++++++-
+ include/dt-bindings/clock/qcom,gcc-msm8974.h | 4 +
+ 3 files changed, 130 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+index 7b7104e..9cfcb4f 100644
+--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+@@ -8,6 +8,8 @@ Required properties :
+ "qcom,gcc-msm8660"
+ "qcom,gcc-msm8960"
+ "qcom,gcc-msm8974"
++ "qcom,gcc-msm8974pro"
++ "qcom,gcc-msm8974pro-ac"
+
+ - reg : shall contain base register location and length
+ - #clock-cells : shall contain 1
+diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c
+index 7a420fc..7af7c18 100644
+--- a/drivers/clk/qcom/gcc-msm8974.c
++++ b/drivers/clk/qcom/gcc-msm8974.c
+@@ -35,6 +35,7 @@
+ #define P_XO 0
+ #define P_GPLL0 1
+ #define P_GPLL1 1
++#define P_GPLL4 2
+
+ static const u8 gcc_xo_gpll0_map[] = {
+ [P_XO] = 0,
+@@ -46,6 +47,18 @@ static const char *gcc_xo_gpll0[] = {
+ "gpll0_vote",
+ };
+
++static const u8 gcc_xo_gpll0_gpll4_map[] = {
++ [P_XO] = 0,
++ [P_GPLL0] = 1,
++ [P_GPLL4] = 5,
++};
++
++static const char *gcc_xo_gpll0_gpll4[] = {
++ "xo",
++ "gpll0_vote",
++ "gpll4_vote",
++};
++
+ #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+ static struct clk_pll gpll0 = {
+@@ -138,6 +151,33 @@ static struct clk_regmap gpll1_vote = {
+ },
+ };
+
++static struct clk_pll gpll4 = {
++ .l_reg = 0x1dc4,
++ .m_reg = 0x1dc8,
++ .n_reg = 0x1dcc,
++ .config_reg = 0x1dd4,
++ .mode_reg = 0x1dc0,
++ .status_reg = 0x1ddc,
++ .status_bit = 17,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "gpll4",
++ .parent_names = (const char *[]){ "xo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
++static struct clk_regmap gpll4_vote = {
++ .enable_reg = 0x1480,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gpll4_vote",
++ .parent_names = (const char *[]){ "gpll4" },
++ .num_parents = 1,
++ .ops = &clk_pll_vote_ops,
++ },
++};
++
+ static const struct freq_tbl ftbl_gcc_usb30_master_clk[] = {
+ F(125000000, P_GPLL0, 1, 5, 24),
+ { }
+@@ -812,18 +852,33 @@ static const struct freq_tbl ftbl_gcc_sdcc1_4_apps_clk[] = {
+ { }
+ };
+
++static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_pro[] = {
++ F(144000, P_XO, 16, 3, 25),
++ F(400000, P_XO, 12, 1, 4),
++ F(20000000, P_GPLL0, 15, 1, 2),
++ F(25000000, P_GPLL0, 12, 1, 2),
++ F(50000000, P_GPLL0, 12, 0, 0),
++ F(100000000, P_GPLL0, 6, 0, 0),
++ F(192000000, P_GPLL4, 4, 0, 0),
++ F(200000000, P_GPLL0, 3, 0, 0),
++ F(384000000, P_GPLL4, 2, 0, 0),
++ { }
++};
++
++static struct clk_init_data sdcc1_apps_clk_src_init = {
++ .name = "sdcc1_apps_clk_src",
++ .parent_names = gcc_xo_gpll0,
++ .num_parents = 2,
++ .ops = &clk_rcg2_ops,
++};
++
+ static struct clk_rcg2 sdcc1_apps_clk_src = {
+ .cmd_rcgr = 0x04d0,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk,
+- .clkr.hw.init = &(struct clk_init_data){
+- .name = "sdcc1_apps_clk_src",
+- .parent_names = gcc_xo_gpll0,
+- .num_parents = 2,
+- .ops = &clk_rcg2_ops,
+- },
++ .clkr.hw.init = &sdcc1_apps_clk_src_init,
+ };
+
+ static struct clk_rcg2 sdcc2_apps_clk_src = {
+@@ -1995,6 +2050,38 @@ static struct clk_branch gcc_sdcc1_apps_clk = {
+ },
+ };
+
++static struct clk_branch gcc_sdcc1_cdccal_ff_clk = {
++ .halt_reg = 0x04e8,
++ .clkr = {
++ .enable_reg = 0x04e8,
++ .enable_mask = BIT(0),
++ .hw.init = &(struct clk_init_data){
++ .name = "gcc_sdcc1_cdccal_ff_clk",
++ .parent_names = (const char *[]){
++ "xo"
++ },
++ .num_parents = 1,
++ .ops = &clk_branch2_ops,
++ },
++ },
++};
++
++static struct clk_branch gcc_sdcc1_cdccal_sleep_clk = {
++ .halt_reg = 0x04e4,
++ .clkr = {
++ .enable_reg = 0x04e4,
++ .enable_mask = BIT(0),
++ .hw.init = &(struct clk_init_data){
++ .name = "gcc_sdcc1_cdccal_sleep_clk",
++ .parent_names = (const char *[]){
++ "sleep_clk_src"
++ },
++ .num_parents = 1,
++ .ops = &clk_branch2_ops,
++ },
++ },
++};
++
+ static struct clk_branch gcc_sdcc2_ahb_clk = {
+ .halt_reg = 0x0508,
+ .clkr = {
+@@ -2484,6 +2571,10 @@ static struct clk_regmap *gcc_msm8974_clocks[] = {
+ [GCC_USB_HSIC_IO_CAL_SLEEP_CLK] = &gcc_usb_hsic_io_cal_sleep_clk.clkr,
+ [GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr,
+ [GCC_MMSS_GPLL0_CLK_SRC] = &gcc_mmss_gpll0_clk_src,
++ [GPLL4] = NULL,
++ [GPLL4_VOTE] = NULL,
++ [GCC_SDCC1_CDCCAL_SLEEP_CLK] = NULL,
++ [GCC_SDCC1_CDCCAL_FF_CLK] = NULL,
+ };
+
+ static const struct qcom_reset_map gcc_msm8974_resets[] = {
+@@ -2585,14 +2676,41 @@ static const struct qcom_cc_desc gcc_msm8974_desc = {
+
+ static const struct of_device_id gcc_msm8974_match_table[] = {
+ { .compatible = "qcom,gcc-msm8974" },
++ { .compatible = "qcom,gcc-msm8974pro" , .data = (void *)1UL },
++ { .compatible = "qcom,gcc-msm8974pro-ac", .data = (void *)1UL },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table);
+
++static void msm8974_pro_clock_override(void)
++{
++ sdcc1_apps_clk_src_init.parent_names = gcc_xo_gpll0_gpll4;
++ sdcc1_apps_clk_src_init.num_parents = 3;
++ sdcc1_apps_clk_src.freq_tbl = ftbl_gcc_sdcc1_apps_clk_pro;
++ sdcc1_apps_clk_src.parent_map = gcc_xo_gpll0_gpll4_map;
++
++ gcc_msm8974_clocks[GPLL4] = &gpll4.clkr;
++ gcc_msm8974_clocks[GPLL4_VOTE] = &gpll4_vote;
++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_SLEEP_CLK] =
++ &gcc_sdcc1_cdccal_sleep_clk.clkr;
++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_FF_CLK] =
++ &gcc_sdcc1_cdccal_ff_clk.clkr;
++}
++
+ static int gcc_msm8974_probe(struct platform_device *pdev)
+ {
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
++ bool pro;
++ const struct of_device_id *id;
++
++ id = of_match_device(gcc_msm8974_match_table, dev);
++ if (!id)
++ return -ENODEV;
++ pro = !!(id->data);
++
++ if (pro)
++ msm8974_pro_clock_override();
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000);
+diff --git a/include/dt-bindings/clock/qcom,gcc-msm8974.h b/include/dt-bindings/clock/qcom,gcc-msm8974.h
+index 223ca17..51e51c8 100644
+--- a/include/dt-bindings/clock/qcom,gcc-msm8974.h
++++ b/include/dt-bindings/clock/qcom,gcc-msm8974.h
+@@ -316,5 +316,9 @@
+ #define GCC_CE2_CLK_SLEEP_ENA 299
+ #define GCC_CE2_AXI_CLK_SLEEP_ENA 300
+ #define GCC_CE2_AHB_CLK_SLEEP_ENA 301
++#define GPLL4 302
++#define GPLL4_VOTE 303
++#define GCC_SDCC1_CDCCAL_SLEEP_CLK 304
++#define GCC_SDCC1_CDCCAL_FF_CLK 305
+
+ #endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch
new file mode 100644
index 0000000..37806f1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch
@@ -0,0 +1,37 @@
+From 77b7f864b03d6b49c143a9c8c19432feef3032b5 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 16 May 2014 16:07:14 -0700
+Subject: [PATCH 108/182] clk: qcom: Return error pointers for unimplemented
+ clocks
+
+Not all clocks are implemented but client drivers can still
+request them. Currently we will return a NULL pointer to them if
+the clock isn't implemented in software but NULL pointers are
+valid clock pointers. Return an error pointer so that driver's
+don't proceed without a clock they may actually need.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/qcom/common.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
+index 86b45fb..9b5a1cf 100644
+--- a/drivers/clk/qcom/common.c
++++ b/drivers/clk/qcom/common.c
+@@ -62,8 +62,10 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
+ data->clk_num = num_clks;
+
+ for (i = 0; i < num_clks; i++) {
+- if (!rclks[i])
++ if (!rclks[i]) {
++ clks[i] = ERR_PTR(-ENOENT);
+ continue;
++ }
+ clk = devm_clk_register_regmap(dev, rclks[i]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch
new file mode 100644
index 0000000..102fd4f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch
@@ -0,0 +1,223 @@
+From 10f3c772363e549c3dbd3cc3755d270c5656d5b8 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:30 +0100
+Subject: [PATCH 109/182] libahci: Allow drivers to override start_engine
+
+Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a
+special register to be poked before starting the DMA engine.
+
+This register gets reset on an ahci_stop_engine call, so there is no other
+place then ahci_start_engine where this poking can be done.
+
+This commit allows drivers to override ahci_start_engine behavior for use by
+the Allwinner AHCI driver (and potentially other drivers in the future).
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci.c | 6 ++++--
+ drivers/ata/ahci.h | 6 ++++++
+ drivers/ata/libahci.c | 26 +++++++++++++++++++-------
+ drivers/ata/sata_highbank.c | 3 ++-
+ 4 files changed, 31 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
+index c81d809..8bfc477 100644
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -578,6 +578,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+ {
+ struct ata_port *ap = link->ap;
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ bool online;
+ int rc;
+
+@@ -588,7 +589,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
+ rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
+ deadline, &online, NULL);
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
+
+@@ -603,6 +604,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
+ {
+ struct ata_port *ap = link->ap;
+ struct ahci_port_priv *pp = ap->private_data;
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
+ struct ata_taskfile tf;
+ bool online;
+@@ -618,7 +620,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
+ rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
+ deadline, &online, NULL);
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ /* The pseudo configuration device on SIMG4726 attached to
+ * ASUS P5W-DH Deluxe doesn't send signature FIS after
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 2289efd..64d1a99 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -323,6 +323,12 @@ struct ahci_host_priv {
+ u32 em_msg_type; /* EM message type */
+ struct clk *clk; /* Only for platforms supporting clk */
+ void *plat_data; /* Other platform data */
++ /*
++ * Optional ahci_start_engine override, if not set this gets set to the
++ * default ahci_start_engine during ahci_save_initial_config, this can
++ * be overridden anytime before the host is activated.
++ */
++ void (*start_engine)(struct ata_port *ap);
+ };
+
+ extern int ahci_ignore_sss;
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 36605ab..f839bb3 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev,
+ *
+ * If inconsistent, config values are fixed up by this function.
+ *
++ * If it is not set already this function sets hpriv->start_engine to
++ * ahci_start_engine.
++ *
+ * LOCKING:
+ * None.
+ */
+@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev,
+ hpriv->cap = cap;
+ hpriv->cap2 = cap2;
+ hpriv->port_map = port_map;
++
++ if (!hpriv->start_engine)
++ hpriv->start_engine = ahci_start_engine;
+ }
+ EXPORT_SYMBOL_GPL(ahci_save_initial_config);
+
+@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap)
+
+ /* enable DMA */
+ if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ /* turn on LEDs */
+ if (ap->flags & ATA_FLAG_EM) {
+@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap)
+
+ /* restart engine */
+ out_restart:
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+ return rc;
+ }
+ EXPORT_SYMBOL_GPL(ahci_kick_engine);
+@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,
+ const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ struct ata_port *ap = link->ap;
+ struct ahci_port_priv *pp = ap->private_data;
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
+ struct ata_taskfile tf;
+ bool online;
+@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,
+ rc = sata_link_hardreset(link, timing, deadline, &online,
+ ahci_check_ready);
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ if (online)
+ *class = ahci_dev_classify(ap);
+@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap)
+
+ void ahci_error_handler(struct ata_port *ap)
+ {
++ struct ahci_host_priv *hpriv = ap->host->private_data;
++
+ if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
+ /* restart engine */
+ ahci_stop_engine(ap);
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+ }
+
+ sata_pmp_error_handler(ap);
+@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
+
+ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
+ {
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ struct ata_device *dev = ap->link.device;
+ u32 devslp, dm, dito, mdat, deto;
+@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
+ PORT_DEVSLP_ADSE);
+ writel(devslp, port_mmio + PORT_DEVSLP);
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ /* enable device sleep feature for the drive */
+ err_mask = ata_dev_set_feature(dev,
+@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
+
+ static void ahci_enable_fbs(struct ata_port *ap)
+ {
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ struct ahci_port_priv *pp = ap->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ u32 fbs;
+@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap)
+ } else
+ dev_err(ap->host->dev, "Failed to enable FBS\n");
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+ }
+
+ static void ahci_disable_fbs(struct ata_port *ap)
+ {
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ struct ahci_port_priv *pp = ap->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ u32 fbs;
+@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap)
+ pp->fbs_enabled = false;
+ }
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+ }
+
+ static void ahci_pmp_attach(struct ata_port *ap)
+diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
+index 870b11e..b3b18d1 100644
+--- a/drivers/ata/sata_highbank.c
++++ b/drivers/ata/sata_highbank.c
+@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
+ static const unsigned long timing[] = { 5, 100, 500};
+ struct ata_port *ap = link->ap;
+ struct ahci_port_priv *pp = ap->private_data;
++ struct ahci_host_priv *hpriv = ap->host->private_data;
+ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
+ struct ata_taskfile tf;
+ bool online;
+@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
+ break;
+ } while (!online && retry--);
+
+- ahci_start_engine(ap);
++ hpriv->start_engine(ap);
+
+ if (online)
+ *class = ahci_dev_classify(ap);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch
new file mode 100644
index 0000000..09c4f22
--- /dev/null
+++ b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch
@@ -0,0 +1,253 @@
+From b7ad421c184b827806c7f65be3a980cfc855b589 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:31 +0100
+Subject: [PATCH 110/182] ahci-platform: Add support for devices with more
+ then 1 clock
+
+The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the
+imx AHCI controller needs 3 clocks to be enabled.
+
+tj: Minor comment formatting updates.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ .../devicetree/bindings/ata/ahci-platform.txt | 1 +
+ drivers/ata/ahci.h | 3 +-
+ drivers/ata/ahci_platform.c | 113 +++++++++++++++-----
+ include/linux/ahci_platform.h | 4 +
+ 4 files changed, 93 insertions(+), 28 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt
+index 89de156..3ced07d 100644
+--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt
++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt
+@@ -10,6 +10,7 @@ Required properties:
+
+ Optional properties:
+ - dma-coherent : Present if dma operations are coherent
++- clocks : a list of phandle + clock specifier pairs
+
+ Example:
+ sata@ffe08000 {
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 64d1a99..c12862b 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -51,6 +51,7 @@
+
+ enum {
+ AHCI_MAX_PORTS = 32,
++ AHCI_MAX_CLKS = 3,
+ AHCI_MAX_SG = 168, /* hardware max is 64K */
+ AHCI_DMA_BOUNDARY = 0xffffffff,
+ AHCI_MAX_CMDS = 32,
+@@ -321,7 +322,7 @@ struct ahci_host_priv {
+ u32 em_loc; /* enclosure management location */
+ u32 em_buf_sz; /* EM buffer size in byte */
+ u32 em_msg_type; /* EM message type */
+- struct clk *clk; /* Only for platforms supporting clk */
++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
+ void *plat_data; /* Other platform data */
+ /*
+ * Optional ahci_start_engine override, if not set this gets set to the
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 4b231ba..2342a42 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -87,6 +87,60 @@ static struct scsi_host_template ahci_platform_sht = {
+ AHCI_SHT("ahci_platform"),
+ };
+
++/**
++ * ahci_platform_enable_clks - Enable platform clocks
++ * @hpriv: host private area to store config values
++ *
++ * This function enables all the clks found in hpriv->clks, starting at
++ * index 0. If any clk fails to enable it disables all the clks already
++ * enabled in reverse order, and then returns an error.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
++{
++ int c, rc;
++
++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
++ rc = clk_prepare_enable(hpriv->clks[c]);
++ if (rc)
++ goto disable_unprepare_clk;
++ }
++ return 0;
++
++disable_unprepare_clk:
++ while (--c >= 0)
++ clk_disable_unprepare(hpriv->clks[c]);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
++
++/**
++ * ahci_platform_disable_clks - Disable platform clocks
++ * @hpriv: host private area to store config values
++ *
++ * This function disables all the clks found in hpriv->clks, in reverse
++ * order of ahci_platform_enable_clks (starting at the end of the array).
++ */
++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
++{
++ int c;
++
++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
++ if (hpriv->clks[c])
++ clk_disable_unprepare(hpriv->clks[c]);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
++
++static void ahci_put_clks(struct ahci_host_priv *hpriv)
++{
++ int c;
++
++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
++ clk_put(hpriv->clks[c]);
++}
++
+ static int ahci_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -97,6 +151,7 @@ static int ahci_probe(struct platform_device *pdev)
+ struct ahci_host_priv *hpriv;
+ struct ata_host *host;
+ struct resource *mem;
++ struct clk *clk;
+ int irq;
+ int n_ports;
+ int i;
+@@ -131,17 +186,31 @@ static int ahci_probe(struct platform_device *pdev)
+ return -ENOMEM;
+ }
+
+- hpriv->clk = clk_get(dev, NULL);
+- if (IS_ERR(hpriv->clk)) {
+- dev_err(dev, "can't get clock\n");
+- } else {
+- rc = clk_prepare_enable(hpriv->clk);
+- if (rc) {
+- dev_err(dev, "clock prepare enable failed");
+- goto free_clk;
++ for (i = 0; i < AHCI_MAX_CLKS; i++) {
++ /*
++ * For now we must use clk_get(dev, NULL) for the first clock,
++ * because some platforms (da850, spear13xx) are not yet
++ * converted to use devicetree for clocks. For new platforms
++ * this is equivalent to of_clk_get(dev->of_node, 0).
++ */
++ if (i == 0)
++ clk = clk_get(dev, NULL);
++ else
++ clk = of_clk_get(dev->of_node, i);
++
++ if (IS_ERR(clk)) {
++ rc = PTR_ERR(clk);
++ if (rc == -EPROBE_DEFER)
++ goto free_clk;
++ break;
+ }
++ hpriv->clks[i] = clk;
+ }
+
++ rc = ahci_enable_clks(dev, hpriv);
++ if (rc)
++ goto free_clk;
++
+ /*
+ * Some platforms might need to prepare for mmio region access,
+ * which could be done in the following init call. So, the mmio
+@@ -222,11 +291,9 @@ pdata_exit:
+ if (pdata && pdata->exit)
+ pdata->exit(dev);
+ disable_unprepare_clk:
+- if (!IS_ERR(hpriv->clk))
+- clk_disable_unprepare(hpriv->clk);
++ ahci_disable_clks(hpriv);
+ free_clk:
+- if (!IS_ERR(hpriv->clk))
+- clk_put(hpriv->clk);
++ ahci_put_clks(hpriv);
+ return rc;
+ }
+
+@@ -239,10 +306,8 @@ static void ahci_host_stop(struct ata_host *host)
+ if (pdata && pdata->exit)
+ pdata->exit(dev);
+
+- if (!IS_ERR(hpriv->clk)) {
+- clk_disable_unprepare(hpriv->clk);
+- clk_put(hpriv->clk);
+- }
++ ahci_disable_clks(hpriv);
++ ahci_put_clks(hpriv);
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+@@ -277,8 +342,7 @@ static int ahci_suspend(struct device *dev)
+ if (pdata && pdata->suspend)
+ return pdata->suspend(dev);
+
+- if (!IS_ERR(hpriv->clk))
+- clk_disable_unprepare(hpriv->clk);
++ ahci_disable_clks(hpriv);
+
+ return 0;
+ }
+@@ -290,13 +354,9 @@ static int ahci_resume(struct device *dev)
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+- if (!IS_ERR(hpriv->clk)) {
+- rc = clk_prepare_enable(hpriv->clk);
+- if (rc) {
+- dev_err(dev, "clock prepare enable failed");
+- return rc;
+- }
+- }
++ rc = ahci_enable_clks(dev, hpriv);
++ if (rc)
++ return rc;
+
+ if (pdata && pdata->resume) {
+ rc = pdata->resume(dev);
+@@ -317,8 +377,7 @@ static int ahci_resume(struct device *dev)
+ return 0;
+
+ disable_unprepare_clk:
+- if (!IS_ERR(hpriv->clk))
+- clk_disable_unprepare(hpriv->clk);
++ ahci_disable_clks(hpriv);
+
+ return rc;
+ }
+diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
+index 73a2500..769d065 100644
+--- a/include/linux/ahci_platform.h
++++ b/include/linux/ahci_platform.h
+@@ -19,6 +19,7 @@
+
+ struct device;
+ struct ata_port_info;
++struct ahci_host_priv;
+
+ struct ahci_platform_data {
+ int (*init)(struct device *dev, void __iomem *addr);
+@@ -30,4 +31,7 @@ struct ahci_platform_data {
+ unsigned int mask_port_map;
+ };
+
++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
++
+ #endif /* _AHCI_PLATFORM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch
new file mode 100644
index 0000000..2d6ac0b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch
@@ -0,0 +1,142 @@
+From 967f4a7821a3356d9d3c8b641537cd6d5eb15439 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:32 +0100
+Subject: [PATCH 111/182] ahci-platform: Add support for an optional regulator
+ for sata-target power
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ .../devicetree/bindings/ata/ahci-platform.txt | 1 +
+ drivers/ata/ahci.h | 2 ++
+ drivers/ata/ahci_platform.c | 36 ++++++++++++++++++--
+ 3 files changed, 37 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt
+index 3ced07d..1ac807f 100644
+--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt
++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt
+@@ -11,6 +11,7 @@ Required properties:
+ Optional properties:
+ - dma-coherent : Present if dma operations are coherent
+ - clocks : a list of phandle + clock specifier pairs
++- target-supply : regulator for SATA target power
+
+ Example:
+ sata@ffe08000 {
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index c12862b..bf8100c 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -37,6 +37,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/libata.h>
++#include <linux/regulator/consumer.h>
+
+ /* Enclosure Management Control */
+ #define EM_CTRL_MSG_TYPE 0x000f0000
+@@ -323,6 +324,7 @@ struct ahci_host_priv {
+ u32 em_buf_sz; /* EM buffer size in byte */
+ u32 em_msg_type; /* EM message type */
+ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
++ struct regulator *target_pwr; /* Optional */
+ void *plat_data; /* Other platform data */
+ /*
+ * Optional ahci_start_engine override, if not set this gets set to the
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 2342a42..8f18ebe 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -186,6 +186,14 @@ static int ahci_probe(struct platform_device *pdev)
+ return -ENOMEM;
+ }
+
++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
++ if (IS_ERR(hpriv->target_pwr)) {
++ rc = PTR_ERR(hpriv->target_pwr);
++ if (rc == -EPROBE_DEFER)
++ return -EPROBE_DEFER;
++ hpriv->target_pwr = NULL;
++ }
++
+ for (i = 0; i < AHCI_MAX_CLKS; i++) {
+ /*
+ * For now we must use clk_get(dev, NULL) for the first clock,
+@@ -207,9 +215,15 @@ static int ahci_probe(struct platform_device *pdev)
+ hpriv->clks[i] = clk;
+ }
+
++ if (hpriv->target_pwr) {
++ rc = regulator_enable(hpriv->target_pwr);
++ if (rc)
++ goto free_clk;
++ }
++
+ rc = ahci_enable_clks(dev, hpriv);
+ if (rc)
+- goto free_clk;
++ goto disable_regulator;
+
+ /*
+ * Some platforms might need to prepare for mmio region access,
+@@ -292,6 +306,9 @@ pdata_exit:
+ pdata->exit(dev);
+ disable_unprepare_clk:
+ ahci_disable_clks(hpriv);
++disable_regulator:
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
+ free_clk:
+ ahci_put_clks(hpriv);
+ return rc;
+@@ -308,6 +325,9 @@ static void ahci_host_stop(struct ata_host *host)
+
+ ahci_disable_clks(hpriv);
+ ahci_put_clks(hpriv);
++
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+@@ -344,6 +364,9 @@ static int ahci_suspend(struct device *dev)
+
+ ahci_disable_clks(hpriv);
+
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
++
+ return 0;
+ }
+
+@@ -354,9 +377,15 @@ static int ahci_resume(struct device *dev)
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
++ if (hpriv->target_pwr) {
++ rc = regulator_enable(hpriv->target_pwr);
++ if (rc)
++ return rc;
++ }
++
+ rc = ahci_enable_clks(dev, hpriv);
+ if (rc)
+- return rc;
++ goto disable_regulator;
+
+ if (pdata && pdata->resume) {
+ rc = pdata->resume(dev);
+@@ -378,6 +407,9 @@ static int ahci_resume(struct device *dev)
+
+ disable_unprepare_clk:
+ ahci_disable_clks(hpriv);
++disable_regulator:
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
+
+ return rc;
+ }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch
new file mode 100644
index 0000000..ad6adbe
--- /dev/null
+++ b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch
@@ -0,0 +1,208 @@
+From 3b2df84624b38362cb84c2d4c6d1d3540c5069d3 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:33 +0100
+Subject: [PATCH 112/182] ahci-platform: Add enable_ / disable_resources
+ helper functions
+
+tj: Minor comment formatting updates.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 106 +++++++++++++++++++++++++++--------------
+ include/linux/ahci_platform.h | 2 +
+ 2 files changed, 71 insertions(+), 37 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 8f18ebe..656d285 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -133,6 +133,62 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
+ }
+ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
+
++/**
++ * ahci_platform_enable_resources - Enable platform resources
++ * @hpriv: host private area to store config values
++ *
++ * This function enables all ahci_platform managed resources in the
++ * following order:
++ * 1) Regulator
++ * 2) Clocks (through ahci_platform_enable_clks)
++ *
++ * If resource enabling fails at any point the previous enabled resources
++ * are disabled in reverse order.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
++{
++ int rc;
++
++ if (hpriv->target_pwr) {
++ rc = regulator_enable(hpriv->target_pwr);
++ if (rc)
++ return rc;
++ }
++
++ rc = ahci_platform_enable_clks(hpriv);
++ if (rc)
++ goto disable_regulator;
++
++ return 0;
++
++disable_regulator:
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
++
++/**
++ * ahci_platform_disable_resources - Disable platform resources
++ * @hpriv: host private area to store config values
++ *
++ * This function disables all ahci_platform managed resources in the
++ * following order:
++ * 1) Clocks (through ahci_platform_disable_clks)
++ * 2) Regulator
++ */
++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
++{
++ ahci_platform_disable_clks(hpriv);
++
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
++
+ static void ahci_put_clks(struct ahci_host_priv *hpriv)
+ {
+ int c;
+@@ -215,15 +271,9 @@ static int ahci_probe(struct platform_device *pdev)
+ hpriv->clks[i] = clk;
+ }
+
+- if (hpriv->target_pwr) {
+- rc = regulator_enable(hpriv->target_pwr);
+- if (rc)
+- goto free_clk;
+- }
+-
+- rc = ahci_enable_clks(dev, hpriv);
++ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+- goto disable_regulator;
++ goto free_clk;
+
+ /*
+ * Some platforms might need to prepare for mmio region access,
+@@ -234,7 +284,7 @@ static int ahci_probe(struct platform_device *pdev)
+ if (pdata && pdata->init) {
+ rc = pdata->init(dev, hpriv->mmio);
+ if (rc)
+- goto disable_unprepare_clk;
++ goto disable_resources;
+ }
+
+ ahci_save_initial_config(dev, hpriv,
+@@ -304,11 +354,8 @@ static int ahci_probe(struct platform_device *pdev)
+ pdata_exit:
+ if (pdata && pdata->exit)
+ pdata->exit(dev);
+-disable_unprepare_clk:
+- ahci_disable_clks(hpriv);
+-disable_regulator:
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
++disable_resources:
++ ahci_platform_disable_resources(hpriv);
+ free_clk:
+ ahci_put_clks(hpriv);
+ return rc;
+@@ -323,11 +370,8 @@ static void ahci_host_stop(struct ata_host *host)
+ if (pdata && pdata->exit)
+ pdata->exit(dev);
+
+- ahci_disable_clks(hpriv);
++ ahci_platform_disable_resources(hpriv);
+ ahci_put_clks(hpriv);
+-
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+@@ -362,10 +406,7 @@ static int ahci_suspend(struct device *dev)
+ if (pdata && pdata->suspend)
+ return pdata->suspend(dev);
+
+- ahci_disable_clks(hpriv);
+-
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
++ ahci_platform_disable_resources(hpriv);
+
+ return 0;
+ }
+@@ -377,26 +418,20 @@ static int ahci_resume(struct device *dev)
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+- if (hpriv->target_pwr) {
+- rc = regulator_enable(hpriv->target_pwr);
+- if (rc)
+- return rc;
+- }
+-
+- rc = ahci_enable_clks(dev, hpriv);
++ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+- goto disable_regulator;
++ return rc;
+
+ if (pdata && pdata->resume) {
+ rc = pdata->resume(dev);
+ if (rc)
+- goto disable_unprepare_clk;
++ goto disable_resources;
+ }
+
+ if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
+ rc = ahci_reset_controller(host);
+ if (rc)
+- goto disable_unprepare_clk;
++ goto disable_resources;
+
+ ahci_init_controller(host);
+ }
+@@ -405,11 +440,8 @@ static int ahci_resume(struct device *dev)
+
+ return 0;
+
+-disable_unprepare_clk:
+- ahci_disable_clks(hpriv);
+-disable_regulator:
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
++disable_resources:
++ ahci_platform_disable_resources(hpriv);
+
+ return rc;
+ }
+diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
+index 769d065..b674b01 100644
+--- a/include/linux/ahci_platform.h
++++ b/include/linux/ahci_platform.h
+@@ -33,5 +33,7 @@ struct ahci_platform_data {
+
+ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
+ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv);
+
+ #endif /* _AHCI_PLATFORM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch
new file mode 100644
index 0000000..c7db740
--- /dev/null
+++ b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch
@@ -0,0 +1,904 @@
+From 01b380db3c35bbd87cb3765573fd049bce3d7640 Mon Sep 17 00:00:00 2001
+From: Paul Gortmaker <paul.gortmaker@windriver.com>
+Date: Tue, 21 Jan 2014 16:22:51 -0500
+Subject: [PATCH 113/182] ata: delete non-required instances of include
+ <linux/init.h>
+
+None of these files are actually using any __init type directives
+and hence don't need to include <linux/init.h>. Most are just a
+left over from __devinit and __cpuinit removal, or simply due to
+code getting copied from one driver to the next.
+
+Cc: linux-ide@vger.kernel.org
+Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/acard-ahci.c | 1 -
+ drivers/ata/ahci.c | 1 -
+ drivers/ata/ahci_platform.c | 1 -
+ drivers/ata/ata_generic.c | 1 -
+ drivers/ata/libahci.c | 1 -
+ drivers/ata/pata_acpi.c | 1 -
+ drivers/ata/pata_amd.c | 1 -
+ drivers/ata/pata_artop.c | 1 -
+ drivers/ata/pata_at91.c | 1 -
+ drivers/ata/pata_atiixp.c | 1 -
+ drivers/ata/pata_atp867x.c | 1 -
+ drivers/ata/pata_cmd640.c | 1 -
+ drivers/ata/pata_cmd64x.c | 1 -
+ drivers/ata/pata_cs5520.c | 1 -
+ drivers/ata/pata_cs5530.c | 1 -
+ drivers/ata/pata_cs5535.c | 1 -
+ drivers/ata/pata_cs5536.c | 1 -
+ drivers/ata/pata_cypress.c | 1 -
+ drivers/ata/pata_efar.c | 1 -
+ drivers/ata/pata_ep93xx.c | 1 -
+ drivers/ata/pata_hpt366.c | 1 -
+ drivers/ata/pata_hpt37x.c | 1 -
+ drivers/ata/pata_hpt3x2n.c | 1 -
+ drivers/ata/pata_hpt3x3.c | 1 -
+ drivers/ata/pata_imx.c | 1 -
+ drivers/ata/pata_it8213.c | 1 -
+ drivers/ata/pata_it821x.c | 1 -
+ drivers/ata/pata_jmicron.c | 1 -
+ drivers/ata/pata_marvell.c | 1 -
+ drivers/ata/pata_mpiix.c | 1 -
+ drivers/ata/pata_netcell.c | 1 -
+ drivers/ata/pata_ninja32.c | 1 -
+ drivers/ata/pata_ns87410.c | 1 -
+ drivers/ata/pata_ns87415.c | 1 -
+ drivers/ata/pata_oldpiix.c | 1 -
+ drivers/ata/pata_opti.c | 1 -
+ drivers/ata/pata_optidma.c | 1 -
+ drivers/ata/pata_pcmcia.c | 1 -
+ drivers/ata/pata_pdc2027x.c | 1 -
+ drivers/ata/pata_pdc202xx_old.c | 1 -
+ drivers/ata/pata_piccolo.c | 1 -
+ drivers/ata/pata_platform.c | 1 -
+ drivers/ata/pata_pxa.c | 1 -
+ drivers/ata/pata_radisys.c | 1 -
+ drivers/ata/pata_rdc.c | 1 -
+ drivers/ata/pata_rz1000.c | 1 -
+ drivers/ata/pata_sc1200.c | 1 -
+ drivers/ata/pata_scc.c | 1 -
+ drivers/ata/pata_sch.c | 1 -
+ drivers/ata/pata_serverworks.c | 1 -
+ drivers/ata/pata_sil680.c | 1 -
+ drivers/ata/pata_sis.c | 1 -
+ drivers/ata/pata_sl82c105.c | 1 -
+ drivers/ata/pata_triflex.c | 1 -
+ drivers/ata/pata_via.c | 1 -
+ drivers/ata/pdc_adma.c | 1 -
+ drivers/ata/sata_dwc_460ex.c | 1 -
+ drivers/ata/sata_highbank.c | 1 -
+ drivers/ata/sata_nv.c | 1 -
+ drivers/ata/sata_promise.c | 1 -
+ drivers/ata/sata_qstor.c | 1 -
+ drivers/ata/sata_sil.c | 1 -
+ drivers/ata/sata_sis.c | 1 -
+ drivers/ata/sata_svw.c | 1 -
+ drivers/ata/sata_sx4.c | 1 -
+ drivers/ata/sata_uli.c | 1 -
+ drivers/ata/sata_via.c | 1 -
+ drivers/ata/sata_vsc.c | 1 -
+ 68 files changed, 68 deletions(-)
+
+diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
+index fd665d9..b51605a 100644
+--- a/drivers/ata/acard-ahci.c
++++ b/drivers/ata/acard-ahci.c
+@@ -36,7 +36,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
+index 8bfc477..a52a5b6 100644
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -35,7 +35,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 656d285..a32df31 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -17,7 +17,6 @@
+ #include <linux/gfp.h>
+ #include <linux/module.h>
+ #include <linux/pm.h>
+-#include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
+diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
+index 7d19665..9498a7d 100644
+--- a/drivers/ata/ata_generic.c
++++ b/drivers/ata/ata_generic.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index f839bb3..fa02770 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -35,7 +35,6 @@
+ #include <linux/kernel.h>
+ #include <linux/gfp.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c
+index 62c9ac8..5108b87 100644
+--- a/drivers/ata/pata_acpi.c
++++ b/drivers/ata/pata_acpi.c
+@@ -7,7 +7,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
+index d23e2b3..1206fa6 100644
+--- a/drivers/ata/pata_amd.c
++++ b/drivers/ata/pata_amd.c
+@@ -17,7 +17,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
+index 1581dee..3aa4e65 100644
+--- a/drivers/ata/pata_artop.c
++++ b/drivers/ata/pata_artop.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c
+index d63ee8f..e9c8727 100644
+--- a/drivers/ata/pata_at91.c
++++ b/drivers/ata/pata_at91.c
+@@ -18,7 +18,6 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/gfp.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
+index 24e5105..30fa4ca 100644
+--- a/drivers/ata/pata_atiixp.c
++++ b/drivers/ata/pata_atiixp.c
+@@ -15,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c
+index 2ca5026..7e73a0f 100644
+--- a/drivers/ata/pata_atp867x.c
++++ b/drivers/ata/pata_atp867x.c
+@@ -29,7 +29,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c
+index 8fb69e5..57f1be6 100644
+--- a/drivers/ata/pata_cmd640.c
++++ b/drivers/ata/pata_cmd640.c
+@@ -15,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/gfp.h>
+diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
+index 1275a8d..6bca350 100644
+--- a/drivers/ata/pata_cmd64x.c
++++ b/drivers/ata/pata_cmd64x.c
+@@ -26,7 +26,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
+index f10baab..bcde4b7 100644
+--- a/drivers/ata/pata_cs5520.c
++++ b/drivers/ata/pata_cs5520.c
+@@ -34,7 +34,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
+index f07f229..8afe854 100644
+--- a/drivers/ata/pata_cs5530.c
++++ b/drivers/ata/pata_cs5530.c
+@@ -26,7 +26,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
+index 997e16a..2c0986f 100644
+--- a/drivers/ata/pata_cs5535.c
++++ b/drivers/ata/pata_cs5535.c
+@@ -31,7 +31,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
+index 0448860..32ddcae 100644
+--- a/drivers/ata/pata_cs5536.c
++++ b/drivers/ata/pata_cs5536.c
+@@ -33,7 +33,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/libata.h>
+diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
+index 810bc99..3435bd6 100644
+--- a/drivers/ata/pata_cypress.c
++++ b/drivers/ata/pata_cypress.c
+@@ -11,7 +11,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
+index 3c12fd7..f440892 100644
+--- a/drivers/ata/pata_efar.c
++++ b/drivers/ata/pata_efar.c
+@@ -14,7 +14,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
+index 980b88e..cad9d45 100644
+--- a/drivers/ata/pata_ep93xx.c
++++ b/drivers/ata/pata_ep93xx.c
+@@ -34,7 +34,6 @@
+ #include <linux/err.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <scsi/scsi_host.h>
+ #include <linux/ata.h>
+diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
+index 35b5213..8e76f79 100644
+--- a/drivers/ata/pata_hpt366.c
++++ b/drivers/ata/pata_hpt366.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
+index a9d74ef..3ba843f 100644
+--- a/drivers/ata/pata_hpt37x.c
++++ b/drivers/ata/pata_hpt37x.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
+index 4be0398..b93c0f0 100644
+--- a/drivers/ata/pata_hpt3x2n.c
++++ b/drivers/ata/pata_hpt3x2n.c
+@@ -20,7 +20,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
+index 85cf286..255c5aa 100644
+--- a/drivers/ata/pata_hpt3x3.c
++++ b/drivers/ata/pata_hpt3x3.c
+@@ -16,7 +16,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
+index b0b18ec..7e69797 100644
+--- a/drivers/ata/pata_imx.c
++++ b/drivers/ata/pata_imx.c
+@@ -15,7 +15,6 @@
+ */
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <scsi/scsi_host.h>
+ #include <linux/ata.h>
+diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
+index 2a8dd95..81369d1 100644
+--- a/drivers/ata/pata_it8213.c
++++ b/drivers/ata/pata_it8213.c
+@@ -10,7 +10,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
+index 581e04d..dc3d787 100644
+--- a/drivers/ata/pata_it821x.c
++++ b/drivers/ata/pata_it821x.c
+@@ -72,7 +72,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c
+index 76e739b0..b1cfa02 100644
+--- a/drivers/ata/pata_jmicron.c
++++ b/drivers/ata/pata_jmicron.c
+@@ -10,7 +10,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c
+index a4f5e78..6bad3df 100644
+--- a/drivers/ata/pata_marvell.c
++++ b/drivers/ata/pata_marvell.c
+@@ -11,7 +11,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
+index 1f5f28b..f39a537 100644
+--- a/drivers/ata/pata_mpiix.c
++++ b/drivers/ata/pata_mpiix.c
+@@ -28,7 +28,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
+index ad1a0fe..e3b9709 100644
+--- a/drivers/ata/pata_netcell.c
++++ b/drivers/ata/pata_netcell.c
+@@ -7,7 +7,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c
+index 9513e07..56201a6 100644
+--- a/drivers/ata/pata_ninja32.c
++++ b/drivers/ata/pata_ninja32.c
+@@ -37,7 +37,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
+index 0c424da..6154c3e 100644
+--- a/drivers/ata/pata_ns87410.c
++++ b/drivers/ata/pata_ns87410.c
+@@ -20,7 +20,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c
+index 16dc3a6..d44df7c 100644
+--- a/drivers/ata/pata_ns87415.c
++++ b/drivers/ata/pata_ns87415.c
+@@ -25,7 +25,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
+index d77b2e1..319b644 100644
+--- a/drivers/ata/pata_oldpiix.c
++++ b/drivers/ata/pata_oldpiix.c
+@@ -16,7 +16,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
+index 4ea70cd..fb042e0 100644
+--- a/drivers/ata/pata_opti.c
++++ b/drivers/ata/pata_opti.c
+@@ -26,7 +26,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
+index 78ede3f..bb71ea2 100644
+--- a/drivers/ata/pata_optidma.c
++++ b/drivers/ata/pata_optidma.c
+@@ -25,7 +25,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
+index 40254f4..bcc4b96 100644
+--- a/drivers/ata/pata_pcmcia.c
++++ b/drivers/ata/pata_pcmcia.c
+@@ -26,7 +26,6 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
+index 9d874c8..1151f23 100644
+--- a/drivers/ata/pata_pdc2027x.c
++++ b/drivers/ata/pata_pdc2027x.c
+@@ -25,7 +25,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c
+index c34fc50..defa050 100644
+--- a/drivers/ata/pata_pdc202xx_old.c
++++ b/drivers/ata/pata_pdc202xx_old.c
+@@ -15,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c
+index 2beb6b5..0b46be1 100644
+--- a/drivers/ata/pata_piccolo.c
++++ b/drivers/ata/pata_piccolo.c
+@@ -18,7 +18,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
+index 0279488..a5579b5 100644
+--- a/drivers/ata/pata_platform.c
++++ b/drivers/ata/pata_platform.c
+@@ -13,7 +13,6 @@
+ */
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <scsi/scsi_host.h>
+ #include <linux/ata.h>
+diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c
+index a6f05ac..73259bf 100644
+--- a/drivers/ata/pata_pxa.c
++++ b/drivers/ata/pata_pxa.c
+@@ -20,7 +20,6 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/ata.h>
+ #include <linux/libata.h>
+diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
+index f582ba1..be3f102 100644
+--- a/drivers/ata/pata_radisys.c
++++ b/drivers/ata/pata_radisys.c
+@@ -15,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c
+index 79a970f..521b213 100644
+--- a/drivers/ata/pata_rdc.c
++++ b/drivers/ata/pata_rdc.c
+@@ -24,7 +24,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
+index 040b093..caedc90 100644
+--- a/drivers/ata/pata_rz1000.c
++++ b/drivers/ata/pata_rz1000.c
+@@ -14,7 +14,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
+index ce2f828..96a232f 100644
+--- a/drivers/ata/pata_sc1200.c
++++ b/drivers/ata/pata_sc1200.c
+@@ -32,7 +32,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
+index f35f15f..f1f5b5a 100644
+--- a/drivers/ata/pata_scc.c
++++ b/drivers/ata/pata_scc.c
+@@ -35,7 +35,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c
+index d3830c4..5a1cde0 100644
+--- a/drivers/ata/pata_sch.c
++++ b/drivers/ata/pata_sch.c
+@@ -27,7 +27,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
+index 96c6a79..e27f31f 100644
+--- a/drivers/ata/pata_serverworks.c
++++ b/drivers/ata/pata_serverworks.c
+@@ -34,7 +34,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
+index c4b0b07..73fe362 100644
+--- a/drivers/ata/pata_sil680.c
++++ b/drivers/ata/pata_sil680.c
+@@ -25,7 +25,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
+index 1e83636..78d913a 100644
+--- a/drivers/ata/pata_sis.c
++++ b/drivers/ata/pata_sis.c
+@@ -26,7 +26,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
+index 6816911..900f0e4 100644
+--- a/drivers/ata/pata_sl82c105.c
++++ b/drivers/ata/pata_sl82c105.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
+index 94473da..7bc78e2 100644
+--- a/drivers/ata/pata_triflex.c
++++ b/drivers/ata/pata_triflex.c
+@@ -36,7 +36,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <scsi/scsi_host.h>
+diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
+index c3ab9a6..f6c9632 100644
+--- a/drivers/ata/pata_via.c
++++ b/drivers/ata/pata_via.c
+@@ -55,7 +55,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/gfp.h>
+diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
+index 8ea6e6a..f10631b 100644
+--- a/drivers/ata/pdc_adma.c
++++ b/drivers/ata/pdc_adma.c
+@@ -36,7 +36,6 @@
+ #include <linux/module.h>
+ #include <linux/gfp.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
+index 523524b..73510d0 100644
+--- a/drivers/ata/sata_dwc_460ex.c
++++ b/drivers/ata/sata_dwc_460ex.c
+@@ -29,7 +29,6 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/device.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
+index b3b18d1..d4df0bf 100644
+--- a/drivers/ata/sata_highbank.c
++++ b/drivers/ata/sata_highbank.c
+@@ -19,7 +19,6 @@
+ #include <linux/kernel.h>
+ #include <linux/gfp.h>
+ #include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/types.h>
+ #include <linux/err.h>
+ #include <linux/io.h>
+diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
+index d74def8..ba5f271 100644
+--- a/drivers/ata/sata_nv.c
++++ b/drivers/ata/sata_nv.c
+@@ -40,7 +40,6 @@
+ #include <linux/module.h>
+ #include <linux/gfp.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
+index 97f4acb..3638887 100644
+--- a/drivers/ata/sata_promise.c
++++ b/drivers/ata/sata_promise.c
+@@ -35,7 +35,6 @@
+ #include <linux/module.h>
+ #include <linux/gfp.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
+index 3b0dd57..9a6bd4c 100644
+--- a/drivers/ata/sata_qstor.c
++++ b/drivers/ata/sata_qstor.c
+@@ -31,7 +31,6 @@
+ #include <linux/module.h>
+ #include <linux/gfp.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
+index b7695e8..3062f86 100644
+--- a/drivers/ata/sata_sil.c
++++ b/drivers/ata/sata_sil.c
+@@ -37,7 +37,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
+index 1ad2f62..b513428 100644
+--- a/drivers/ata/sata_sis.c
++++ b/drivers/ata/sata_sis.c
+@@ -33,7 +33,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
+index dc4f701..c630fa8 100644
+--- a/drivers/ata/sata_svw.c
++++ b/drivers/ata/sata_svw.c
+@@ -39,7 +39,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
+index 9947010..6cd0312 100644
+--- a/drivers/ata/sata_sx4.c
++++ b/drivers/ata/sata_sx4.c
+@@ -82,7 +82,6 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/slab.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
+index 6d64891..08f98c3 100644
+--- a/drivers/ata/sata_uli.c
++++ b/drivers/ata/sata_uli.c
+@@ -28,7 +28,6 @@
+ #include <linux/module.h>
+ #include <linux/gfp.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
+index 87f056e..f72e842 100644
+--- a/drivers/ata/sata_via.c
++++ b/drivers/ata/sata_via.c
+@@ -36,7 +36,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
+index 44f304b..29e847a 100644
+--- a/drivers/ata/sata_vsc.c
++++ b/drivers/ata/sata_vsc.c
+@@ -37,7 +37,6 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+-#include <linux/init.h>
+ #include <linux/blkdev.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch
new file mode 100644
index 0000000..9b9c7af
--- /dev/null
+++ b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch
@@ -0,0 +1,341 @@
+From 8be4987340a101253fb871556894a002f1afb51f Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:34 +0100
+Subject: [PATCH 114/182] ahci-platform: "Library-ise" ahci_probe
+ functionality
+
+ahci_probe consists of 3 steps:
+1) Get resources (get mmio, clks, regulator)
+2) Enable resources, handled by ahci_platform_enable_resouces
+3) The more or less standard ahci-host controller init sequence
+
+This commit refactors step 1 and 3 into separate functions, so the platform
+drivers for AHCI implementations which need a specific order in step 2,
+and / or need to do some custom register poking at some time, can re-use
+ahci-platform.c code without needing to copy and paste it.
+
+Note that ahci_platform_init_host's prototype takes the 3 non function
+members of ahci_platform_data as arguments, the idea is that drivers using
+the new exported utility functions will not use ahci_platform_data at all,
+and hopefully in the future ahci_platform_data can go away entirely.
+
+tj: Minor comment formatting updates.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 188 +++++++++++++++++++++++++++--------------
+ include/linux/ahci_platform.h | 14 +++
+ 2 files changed, 137 insertions(+), 65 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index a32df31..19e9eaa 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -188,64 +188,60 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
+ }
+ EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
+
+-static void ahci_put_clks(struct ahci_host_priv *hpriv)
++static void ahci_platform_put_resources(struct device *dev, void *res)
+ {
++ struct ahci_host_priv *hpriv = res;
+ int c;
+
+ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
+ clk_put(hpriv->clks[c]);
+ }
+
+-static int ahci_probe(struct platform_device *pdev)
++/**
++ * ahci_platform_get_resources - Get platform resources
++ * @pdev: platform device to get resources for
++ *
++ * This function allocates an ahci_host_priv struct, and gets the following
++ * resources, storing a reference to them inside the returned struct:
++ *
++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory)
++ * 2) regulator for controlling the targets power (optional)
++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
++ * or for non devicetree enabled platforms a single clock
++ *
++ * RETURNS:
++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
++ */
++struct ahci_host_priv *ahci_platform_get_resources(
++ struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- const struct platform_device_id *id = platform_get_device_id(pdev);
+- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
+- const struct ata_port_info *ppi[] = { &pi, NULL };
+ struct ahci_host_priv *hpriv;
+- struct ata_host *host;
+- struct resource *mem;
+ struct clk *clk;
+- int irq;
+- int n_ports;
+- int i;
+- int rc;
++ int i, rc = -ENOMEM;
+
+- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- if (!mem) {
+- dev_err(dev, "no mmio space\n");
+- return -EINVAL;
+- }
+-
+- irq = platform_get_irq(pdev, 0);
+- if (irq <= 0) {
+- dev_err(dev, "no irq\n");
+- return -EINVAL;
+- }
++ if (!devres_open_group(dev, NULL, GFP_KERNEL))
++ return ERR_PTR(-ENOMEM);
+
+- if (pdata && pdata->ata_port_info)
+- pi = *pdata->ata_port_info;
+-
+- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
+- if (!hpriv) {
+- dev_err(dev, "can't alloc ahci_host_priv\n");
+- return -ENOMEM;
+- }
++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
++ GFP_KERNEL);
++ if (!hpriv)
++ goto err_out;
+
+- hpriv->flags |= (unsigned long)pi.private_data;
++ devres_add(dev, hpriv);
+
+- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
++ hpriv->mmio = devm_ioremap_resource(dev,
++ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (!hpriv->mmio) {
+- dev_err(dev, "can't map %pR\n", mem);
+- return -ENOMEM;
++ dev_err(dev, "no mmio space\n");
++ goto err_out;
+ }
+
+ hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
+ if (IS_ERR(hpriv->target_pwr)) {
+ rc = PTR_ERR(hpriv->target_pwr);
+ if (rc == -EPROBE_DEFER)
+- return -EPROBE_DEFER;
++ goto err_out;
+ hpriv->target_pwr = NULL;
+ }
+
+@@ -264,33 +260,59 @@ static int ahci_probe(struct platform_device *pdev)
+ if (IS_ERR(clk)) {
+ rc = PTR_ERR(clk);
+ if (rc == -EPROBE_DEFER)
+- goto free_clk;
++ goto err_out;
+ break;
+ }
+ hpriv->clks[i] = clk;
+ }
+
+- rc = ahci_platform_enable_resources(hpriv);
+- if (rc)
+- goto free_clk;
++ devres_remove_group(dev, NULL);
++ return hpriv;
+
+- /*
+- * Some platforms might need to prepare for mmio region access,
+- * which could be done in the following init call. So, the mmio
+- * region shouldn't be accessed before init (if provided) has
+- * returned successfully.
+- */
+- if (pdata && pdata->init) {
+- rc = pdata->init(dev, hpriv->mmio);
+- if (rc)
+- goto disable_resources;
+- }
++err_out:
++ devres_release_group(dev, NULL);
++ return ERR_PTR(rc);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
++
++/**
++ * ahci_platform_init_host - Bring up an ahci-platform host
++ * @pdev: platform device pointer for the host
++ * @hpriv: ahci-host private data for the host
++ * @pi_template: template for the ata_port_info to use
++ * @force_port_map: param passed to ahci_save_initial_config
++ * @mask_port_map: param passed to ahci_save_initial_config
++ *
++ * This function does all the usual steps needed to bring up an
++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
++ * must be initialized / enabled before calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_init_host(struct platform_device *pdev,
++ struct ahci_host_priv *hpriv,
++ const struct ata_port_info *pi_template,
++ unsigned int force_port_map,
++ unsigned int mask_port_map)
++{
++ struct device *dev = &pdev->dev;
++ struct ata_port_info pi = *pi_template;
++ const struct ata_port_info *ppi[] = { &pi, NULL };
++ struct ata_host *host;
++ int i, irq, n_ports, rc;
+
+- ahci_save_initial_config(dev, hpriv,
+- pdata ? pdata->force_port_map : 0,
+- pdata ? pdata->mask_port_map : 0);
++ irq = platform_get_irq(pdev, 0);
++ if (irq <= 0) {
++ dev_err(dev, "no irq\n");
++ return -EINVAL;
++ }
+
+ /* prepare host */
++ hpriv->flags |= (unsigned long)pi.private_data;
++
++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
++
+ if (hpriv->cap & HOST_CAP_NCQ)
+ pi.flags |= ATA_FLAG_NCQ;
+
+@@ -307,10 +329,8 @@ static int ahci_probe(struct platform_device *pdev)
+ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
+
+ host = ata_host_alloc_pinfo(dev, ppi, n_ports);
+- if (!host) {
+- rc = -ENOMEM;
+- goto pdata_exit;
+- }
++ if (!host)
++ return -ENOMEM;
+
+ host->private_data = hpriv;
+
+@@ -325,7 +345,8 @@ static int ahci_probe(struct platform_device *pdev)
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
+- ata_port_desc(ap, "mmio %pR", mem);
++ ata_port_desc(ap, "mmio %pR",
++ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
+
+ /* set enclosure management message type */
+@@ -339,13 +360,53 @@ static int ahci_probe(struct platform_device *pdev)
+
+ rc = ahci_reset_controller(host);
+ if (rc)
+- goto pdata_exit;
++ return rc;
+
+ ahci_init_controller(host);
+ ahci_print_info(host, "platform");
+
+- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
+- &ahci_platform_sht);
++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
++ &ahci_platform_sht);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_init_host);
++
++static int ahci_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct ahci_platform_data *pdata = dev_get_platdata(dev);
++ const struct platform_device_id *id = platform_get_device_id(pdev);
++ const struct ata_port_info *pi_template;
++ struct ahci_host_priv *hpriv;
++ int rc;
++
++ hpriv = ahci_platform_get_resources(pdev);
++ if (IS_ERR(hpriv))
++ return PTR_ERR(hpriv);
++
++ rc = ahci_platform_enable_resources(hpriv);
++ if (rc)
++ return rc;
++
++ /*
++ * Some platforms might need to prepare for mmio region access,
++ * which could be done in the following init call. So, the mmio
++ * region shouldn't be accessed before init (if provided) has
++ * returned successfully.
++ */
++ if (pdata && pdata->init) {
++ rc = pdata->init(dev, hpriv->mmio);
++ if (rc)
++ goto disable_resources;
++ }
++
++ if (pdata && pdata->ata_port_info)
++ pi_template = pdata->ata_port_info;
++ else
++ pi_template = &ahci_port_info[id ? id->driver_data : 0];
++
++ rc = ahci_platform_init_host(pdev, hpriv, pi_template,
++ pdata ? pdata->force_port_map : 0,
++ pdata ? pdata->mask_port_map : 0);
+ if (rc)
+ goto pdata_exit;
+
+@@ -355,8 +416,6 @@ pdata_exit:
+ pdata->exit(dev);
+ disable_resources:
+ ahci_platform_disable_resources(hpriv);
+-free_clk:
+- ahci_put_clks(hpriv);
+ return rc;
+ }
+
+@@ -370,7 +429,6 @@ static void ahci_host_stop(struct ata_host *host)
+ pdata->exit(dev);
+
+ ahci_platform_disable_resources(hpriv);
+- ahci_put_clks(hpriv);
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
+index b674b01..b80c51c 100644
+--- a/include/linux/ahci_platform.h
++++ b/include/linux/ahci_platform.h
+@@ -20,7 +20,14 @@
+ struct device;
+ struct ata_port_info;
+ struct ahci_host_priv;
++struct platform_device;
+
++/*
++ * Note ahci_platform_data is deprecated, it is only kept around for use
++ * by the old da850 and spear13xx ahci code.
++ * New drivers should instead declare their own platform_driver struct, and
++ * use ahci_platform* functions in their own probe, suspend and resume methods.
++ */
+ struct ahci_platform_data {
+ int (*init)(struct device *dev, void __iomem *addr);
+ void (*exit)(struct device *dev);
+@@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
+ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
+ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
+ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv);
++struct ahci_host_priv *ahci_platform_get_resources(
++ struct platform_device *pdev);
++int ahci_platform_init_host(struct platform_device *pdev,
++ struct ahci_host_priv *hpriv,
++ const struct ata_port_info *pi_template,
++ unsigned int force_port_map,
++ unsigned int mask_port_map);
+
+ #endif /* _AHCI_PLATFORM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch
new file mode 100644
index 0000000..9a9cb07
--- /dev/null
+++ b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch
@@ -0,0 +1,180 @@
+From 8121ed9c7ea2d56f5e0a98c2db396214f85d7485 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 16:53:35 +0100
+Subject: [PATCH 115/182] ahci-platform: "Library-ise" suspend / resume
+ functionality
+
+Split suspend / resume code into host suspend / resume functionality and
+resource enable / disabling phases, and export the new suspend_ / resume_host
+functions.
+
+tj: Minor comment formatting updates.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 97 ++++++++++++++++++++++++++++++++++-------
+ include/linux/ahci_platform.h | 5 +++
+ 2 files changed, 87 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 19e9eaa..01f7bbe 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -432,14 +432,23 @@ static void ahci_host_stop(struct ata_host *host)
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+-static int ahci_suspend(struct device *dev)
++/**
++ * ahci_platform_suspend_host - Suspend an ahci-platform host
++ * @dev: device pointer for the host
++ *
++ * This function does all the usual steps needed to suspend an
++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
++ * must be disabled after calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_suspend_host(struct device *dev)
+ {
+- struct ahci_platform_data *pdata = dev_get_platdata(dev);
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ void __iomem *mmio = hpriv->mmio;
+ u32 ctl;
+- int rc;
+
+ if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
+ dev_err(dev, "firmware update required for suspend/resume\n");
+@@ -456,7 +465,58 @@ static int ahci_suspend(struct device *dev)
+ writel(ctl, mmio + HOST_CTL);
+ readl(mmio + HOST_CTL); /* flush */
+
+- rc = ata_host_suspend(host, PMSG_SUSPEND);
++ return ata_host_suspend(host, PMSG_SUSPEND);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
++
++/**
++ * ahci_platform_resume_host - Resume an ahci-platform host
++ * @dev: device pointer for the host
++ *
++ * This function does all the usual steps needed to resume an ahci-platform
++ * host, note any necessary resources (ie clks, phy, etc.) must be
++ * initialized / enabled before calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_resume_host(struct device *dev)
++{
++ struct ata_host *host = dev_get_drvdata(dev);
++ int rc;
++
++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
++ rc = ahci_reset_controller(host);
++ if (rc)
++ return rc;
++
++ ahci_init_controller(host);
++ }
++
++ ata_host_resume(host);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
++
++/**
++ * ahci_platform_suspend - Suspend an ahci-platform device
++ * @dev: the platform device to suspend
++ *
++ * This function suspends the host associated with the device, followed by
++ * disabling all the resources of the device.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_suspend(struct device *dev)
++{
++ struct ahci_platform_data *pdata = dev_get_platdata(dev);
++ struct ata_host *host = dev_get_drvdata(dev);
++ struct ahci_host_priv *hpriv = host->private_data;
++ int rc;
++
++ rc = ahci_platform_suspend_host(dev);
+ if (rc)
+ return rc;
+
+@@ -467,8 +527,19 @@ static int ahci_suspend(struct device *dev)
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(ahci_platform_suspend);
+
+-static int ahci_resume(struct device *dev)
++/**
++ * ahci_platform_resume - Resume an ahci-platform device
++ * @dev: the platform device to resume
++ *
++ * This function enables all the resources of the device followed by
++ * resuming the host associated with the device.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_resume(struct device *dev)
+ {
+ struct ahci_platform_data *pdata = dev_get_platdata(dev);
+ struct ata_host *host = dev_get_drvdata(dev);
+@@ -485,15 +556,9 @@ static int ahci_resume(struct device *dev)
+ goto disable_resources;
+ }
+
+- if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
+- rc = ahci_reset_controller(host);
+- if (rc)
+- goto disable_resources;
+-
+- ahci_init_controller(host);
+- }
+-
+- ata_host_resume(host);
++ rc = ahci_platform_resume_host(dev);
++ if (rc)
++ goto disable_resources;
+
+ return 0;
+
+@@ -502,9 +567,11 @@ disable_resources:
+
+ return rc;
+ }
++EXPORT_SYMBOL_GPL(ahci_platform_resume);
+ #endif
+
+-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
++ ahci_platform_resume);
+
+ static const struct of_device_id ahci_of_match[] = {
+ { .compatible = "snps,spear-ahci", },
+diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
+index b80c51c..542f268 100644
+--- a/include/linux/ahci_platform.h
++++ b/include/linux/ahci_platform.h
+@@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev,
+ unsigned int force_port_map,
+ unsigned int mask_port_map);
+
++int ahci_platform_suspend_host(struct device *dev);
++int ahci_platform_resume_host(struct device *dev);
++int ahci_platform_suspend(struct device *dev);
++int ahci_platform_resume(struct device *dev);
++
+ #endif /* _AHCI_PLATFORM_H */
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch
new file mode 100644
index 0000000..72c2c2e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch
@@ -0,0 +1,32 @@
+From bab3492791ade083c172a898b6f7a3692ffb89d5 Mon Sep 17 00:00:00 2001
+From: Roger Quadros <rogerq@ti.com>
+Date: Sat, 22 Feb 2014 16:53:38 +0100
+Subject: [PATCH 116/182] ata: ahci_platform: Add DT compatible for Synopsis
+ DWC AHCI controller
+
+Add compatible string "snps,dwc-ahci", which should be used
+for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms.
+
+Signed-off-by: Roger Quadros <rogerq@ti.com>
+Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 01f7bbe..968e7d9 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -577,6 +577,7 @@ static const struct of_device_id ahci_of_match[] = {
+ { .compatible = "snps,spear-ahci", },
+ { .compatible = "snps,exynos5440-ahci", },
+ { .compatible = "ibm,476gtr-ahci", },
++ { .compatible = "snps,dwc-ahci", },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, ahci_of_match);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch
new file mode 100644
index 0000000..fab7e57
--- /dev/null
+++ b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch
@@ -0,0 +1,141 @@
+From 05b9b8d1035944af25694d2ebe53077cf1ccb067 Mon Sep 17 00:00:00 2001
+From: Roger Quadros <rogerq@ti.com>
+Date: Sat, 22 Feb 2014 16:53:40 +0100
+Subject: [PATCH 117/182] ata: ahci_platform: Manage SATA PHY
+
+Some platforms have a PHY hooked up to the SATA controller. The PHY
+needs to be initialized and powered up for SATA to work. We do that
+using the PHY framework.
+
+tj: Minor comment formatting updates.
+
+CC: Balaji T K <balajitk@ti.com>
+Signed-off-by: Roger Quadros <rogerq@ti.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo<tj@kernel.org>
+---
+ drivers/ata/ahci.h | 2 ++
+ drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 47 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index bf8100c..3ab7ac9 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -37,6 +37,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/libata.h>
++#include <linux/phy/phy.h>
+ #include <linux/regulator/consumer.h>
+
+ /* Enclosure Management Control */
+@@ -325,6 +326,7 @@ struct ahci_host_priv {
+ u32 em_msg_type; /* EM message type */
+ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
+ struct regulator *target_pwr; /* Optional */
++ struct phy *phy; /* If platform uses phy */
+ void *plat_data; /* Other platform data */
+ /*
+ * Optional ahci_start_engine override, if not set this gets set to the
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 968e7d9..243dde3 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -22,6 +22,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/libata.h>
+ #include <linux/ahci_platform.h>
++#include <linux/phy/phy.h>
+ #include "ahci.h"
+
+ static void ahci_host_stop(struct ata_host *host);
+@@ -140,6 +141,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
+ * following order:
+ * 1) Regulator
+ * 2) Clocks (through ahci_platform_enable_clks)
++ * 3) Phy
+ *
+ * If resource enabling fails at any point the previous enabled resources
+ * are disabled in reverse order.
+@@ -161,8 +163,23 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+ if (rc)
+ goto disable_regulator;
+
++ if (hpriv->phy) {
++ rc = phy_init(hpriv->phy);
++ if (rc)
++ goto disable_clks;
++
++ rc = phy_power_on(hpriv->phy);
++ if (rc) {
++ phy_exit(hpriv->phy);
++ goto disable_clks;
++ }
++ }
++
+ return 0;
+
++disable_clks:
++ ahci_platform_disable_clks(hpriv);
++
+ disable_regulator:
+ if (hpriv->target_pwr)
+ regulator_disable(hpriv->target_pwr);
+@@ -176,11 +193,17 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
+ *
+ * This function disables all ahci_platform managed resources in the
+ * following order:
+- * 1) Clocks (through ahci_platform_disable_clks)
+- * 2) Regulator
++ * 1) Phy
++ * 2) Clocks (through ahci_platform_disable_clks)
++ * 3) Regulator
+ */
+ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
+ {
++ if (hpriv->phy) {
++ phy_power_off(hpriv->phy);
++ phy_exit(hpriv->phy);
++ }
++
+ ahci_platform_disable_clks(hpriv);
+
+ if (hpriv->target_pwr)
+@@ -208,6 +231,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
+ * 2) regulator for controlling the targets power (optional)
+ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
+ * or for non devicetree enabled platforms a single clock
++ * 4) phy (optional)
+ *
+ * RETURNS:
+ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
+@@ -266,6 +290,25 @@ struct ahci_host_priv *ahci_platform_get_resources(
+ hpriv->clks[i] = clk;
+ }
+
++ hpriv->phy = devm_phy_get(dev, "sata-phy");
++ if (IS_ERR(hpriv->phy)) {
++ rc = PTR_ERR(hpriv->phy);
++ switch (rc) {
++ case -ENODEV:
++ case -ENOSYS:
++ /* continue normally */
++ hpriv->phy = NULL;
++ break;
++
++ case -EPROBE_DEFER:
++ goto err_out;
++
++ default:
++ dev_err(dev, "couldn't get sata-phy\n");
++ goto err_out;
++ }
++ }
++
+ devres_remove_group(dev, NULL);
+ return hpriv;
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch
new file mode 100644
index 0000000..2f3e707
--- /dev/null
+++ b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch
@@ -0,0 +1,86 @@
+From abe309ab531f22b9b89329bd825606f6b68a95a1 Mon Sep 17 00:00:00 2001
+From: Roger Quadros <rogerq@ti.com>
+Date: Sat, 22 Feb 2014 16:53:41 +0100
+Subject: [PATCH 118/182] ata: ahci_platform: runtime resume the device before
+ use
+
+On OMAP platforms the device needs to be runtime resumed before it can
+be accessed. The OMAP HWMOD framework takes care of enabling the
+module and its resources based on the device's runtime PM state.
+
+In this patch we runtime resume during .probe() and runtime suspend
+after .remove().
+
+We also update the runtime PM state during .resume().
+
+CC: Balaji T K <balajitk@ti.com>
+Signed-off-by: Roger Quadros <rogerq@ti.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci.h | 1 +
+ drivers/ata/ahci_platform.c | 15 +++++++++++++++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 3ab7ac9..51af275 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -324,6 +324,7 @@ struct ahci_host_priv {
+ u32 em_loc; /* enclosure management location */
+ u32 em_buf_sz; /* EM buffer size in byte */
+ u32 em_msg_type; /* EM message type */
++ bool got_runtime_pm; /* Did we do pm_runtime_get? */
+ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
+ struct regulator *target_pwr; /* Optional */
+ struct phy *phy; /* If platform uses phy */
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 243dde3..fc32863 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -23,6 +23,7 @@
+ #include <linux/libata.h>
+ #include <linux/ahci_platform.h>
+ #include <linux/phy/phy.h>
++#include <linux/pm_runtime.h>
+ #include "ahci.h"
+
+ static void ahci_host_stop(struct ata_host *host);
+@@ -216,6 +217,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
+ struct ahci_host_priv *hpriv = res;
+ int c;
+
++ if (hpriv->got_runtime_pm) {
++ pm_runtime_put_sync(dev);
++ pm_runtime_disable(dev);
++ }
++
+ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
+ clk_put(hpriv->clks[c]);
+ }
+@@ -309,6 +315,10 @@ struct ahci_host_priv *ahci_platform_get_resources(
+ }
+ }
+
++ pm_runtime_enable(dev);
++ pm_runtime_get_sync(dev);
++ hpriv->got_runtime_pm = true;
++
+ devres_remove_group(dev, NULL);
+ return hpriv;
+
+@@ -603,6 +613,11 @@ int ahci_platform_resume(struct device *dev)
+ if (rc)
+ goto disable_resources;
+
++ /* We resumed so update PM runtime state */
++ pm_runtime_disable(dev);
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
+ return 0;
+
+ disable_resources:
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch
new file mode 100644
index 0000000..efccae4
--- /dev/null
+++ b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch
@@ -0,0 +1,54 @@
+From 474c436aebe260b0e6e7042ce5291137b7c87b57 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 17:22:53 +0100
+Subject: [PATCH 119/182] ahci_platform: Drop support for ahci-strict platform
+ device type
+
+I've done a grep over the entire kernel tree and nothing is using this
+(anymore?).
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 11 -----------
+ 1 file changed, 11 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index fc32863..d3d2bad 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -31,7 +31,6 @@ static void ahci_host_stop(struct ata_host *host);
+ enum ahci_type {
+ AHCI, /* standard platform ahci */
+ IMX53_AHCI, /* ahci on i.mx53 */
+- STRICT_AHCI, /* delayed DMA engine start */
+ };
+
+ static struct platform_device_id ahci_devtype[] = {
+@@ -42,9 +41,6 @@ static struct platform_device_id ahci_devtype[] = {
+ .name = "imx53-ahci",
+ .driver_data = IMX53_AHCI,
+ }, {
+- .name = "strict-ahci",
+- .driver_data = STRICT_AHCI,
+- }, {
+ /* sentinel */
+ }
+ };
+@@ -75,13 +71,6 @@ static const struct ata_port_info ahci_port_info[] = {
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_platform_retry_srst_ops,
+ },
+- [STRICT_AHCI] = {
+- AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE),
+- .flags = AHCI_FLAG_COMMON,
+- .pio_mask = ATA_PIO4,
+- .udma_mask = ATA_UDMA6,
+- .port_ops = &ahci_platform_ops,
+- },
+ };
+
+ static struct scsi_host_template ahci_platform_sht = {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch
new file mode 100644
index 0000000..8d0f64c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch
@@ -0,0 +1,110 @@
+From 0d153bacb749d9291324ef0282ea9d235baca499 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 17:22:54 +0100
+Subject: [PATCH 120/182] ahci_platform: Drop support for imx53-ahci platform
+ device type
+
+Since the 3.13 release the ahci_imx driver has proper devicetree enabled
+support for ahci on imx53 and that is used instead of the old board file
+created imx53-ahci platform device.
+
+Note this patch also complete drops the id-table, an id-table is not needed
+for a single id platform driver, the name field in the driver struct suffices.
+
+And the code already has an explicit "MODULE_ALIAS("platform:ahci");" so the
+id-table is not needed for that either.
+
+Cc: Marek Vasut <marex@denx.de>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 46 ++++++-------------------------------------
+ 1 file changed, 6 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index d3d2bad..8fab4bf 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -28,49 +28,17 @@
+
+ static void ahci_host_stop(struct ata_host *host);
+
+-enum ahci_type {
+- AHCI, /* standard platform ahci */
+- IMX53_AHCI, /* ahci on i.mx53 */
+-};
+-
+-static struct platform_device_id ahci_devtype[] = {
+- {
+- .name = "ahci",
+- .driver_data = AHCI,
+- }, {
+- .name = "imx53-ahci",
+- .driver_data = IMX53_AHCI,
+- }, {
+- /* sentinel */
+- }
+-};
+-MODULE_DEVICE_TABLE(platform, ahci_devtype);
+-
+ struct ata_port_operations ahci_platform_ops = {
+ .inherits = &ahci_ops,
+ .host_stop = ahci_host_stop,
+ };
+ EXPORT_SYMBOL_GPL(ahci_platform_ops);
+
+-static struct ata_port_operations ahci_platform_retry_srst_ops = {
+- .inherits = &ahci_pmp_retry_srst_ops,
+- .host_stop = ahci_host_stop,
+-};
+-
+-static const struct ata_port_info ahci_port_info[] = {
+- /* by features */
+- [AHCI] = {
+- .flags = AHCI_FLAG_COMMON,
+- .pio_mask = ATA_PIO4,
+- .udma_mask = ATA_UDMA6,
+- .port_ops = &ahci_platform_ops,
+- },
+- [IMX53_AHCI] = {
+- .flags = AHCI_FLAG_COMMON,
+- .pio_mask = ATA_PIO4,
+- .udma_mask = ATA_UDMA6,
+- .port_ops = &ahci_platform_retry_srst_ops,
+- },
++static const struct ata_port_info ahci_port_info = {
++ .flags = AHCI_FLAG_COMMON,
++ .pio_mask = ATA_PIO4,
++ .udma_mask = ATA_UDMA6,
++ .port_ops = &ahci_platform_ops,
+ };
+
+ static struct scsi_host_template ahci_platform_sht = {
+@@ -416,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+ struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- const struct platform_device_id *id = platform_get_device_id(pdev);
+ const struct ata_port_info *pi_template;
+ struct ahci_host_priv *hpriv;
+ int rc;
+@@ -444,7 +411,7 @@ static int ahci_probe(struct platform_device *pdev)
+ if (pdata && pdata->ata_port_info)
+ pi_template = pdata->ata_port_info;
+ else
+- pi_template = &ahci_port_info[id ? id->driver_data : 0];
++ pi_template = &ahci_port_info;
+
+ rc = ahci_platform_init_host(pdev, hpriv, pi_template,
+ pdata ? pdata->force_port_map : 0,
+@@ -638,7 +605,6 @@ static struct platform_driver ahci_driver = {
+ .of_match_table = ahci_of_match,
+ .pm = &ahci_pm_ops,
+ },
+- .id_table = ahci_devtype,
+ };
+ module_platform_driver(ahci_driver);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch
new file mode 100644
index 0000000..e8f2378
--- /dev/null
+++ b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch
@@ -0,0 +1,62 @@
+From 438df60c6bff1b12a407fefcfb8a2e5d9b29ac6a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sat, 22 Feb 2014 17:22:55 +0100
+Subject: [PATCH 121/182] ahci_platform: Drop unused ahci_platform_data
+ members
+
+These members are not used anywhere, and in the future we want
+ahci_platform_data to go away entirely so there is no reason to keep these
+around.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 10 +---------
+ include/linux/ahci_platform.h | 3 ---
+ 2 files changed, 1 insertion(+), 12 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 8fab4bf..db24d2a 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -384,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+ struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- const struct ata_port_info *pi_template;
+ struct ahci_host_priv *hpriv;
+ int rc;
+
+@@ -408,14 +407,7 @@ static int ahci_probe(struct platform_device *pdev)
+ goto disable_resources;
+ }
+
+- if (pdata && pdata->ata_port_info)
+- pi_template = pdata->ata_port_info;
+- else
+- pi_template = &ahci_port_info;
+-
+- rc = ahci_platform_init_host(pdev, hpriv, pi_template,
+- pdata ? pdata->force_port_map : 0,
+- pdata ? pdata->mask_port_map : 0);
++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0);
+ if (rc)
+ goto pdata_exit;
+
+diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
+index 542f268..1f16d50 100644
+--- a/include/linux/ahci_platform.h
++++ b/include/linux/ahci_platform.h
+@@ -33,9 +33,6 @@ struct ahci_platform_data {
+ void (*exit)(struct device *dev);
+ int (*suspend)(struct device *dev);
+ int (*resume)(struct device *dev);
+- const struct ata_port_info *ata_port_info;
+- unsigned int force_port_map;
+- unsigned int mask_port_map;
+ };
+
+ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch
new file mode 100644
index 0000000..1a5925b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch
@@ -0,0 +1,46 @@
+From 0db91c83bcb5f7307aaa16c093b8c6bb3f44be52 Mon Sep 17 00:00:00 2001
+From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Date: Fri, 14 Mar 2014 18:22:09 +0100
+Subject: [PATCH 122/182] ata: ahci_platform: fix devm_ioremap_resource()
+ return value checking
+
+devm_ioremap_resource() returns a pointer to the remapped memory or
+an ERR_PTR() encoded error code on failure. Fix the check inside
+ahci_platform_get_resources() accordingly.
+
+Also while at it remove a needless line break.
+
+Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index db24d2a..70fbf66 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -199,8 +199,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
+ * RETURNS:
+ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
+ */
+-struct ahci_host_priv *ahci_platform_get_resources(
+- struct platform_device *pdev)
++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+ struct ahci_host_priv *hpriv;
+@@ -219,8 +218,9 @@ struct ahci_host_priv *ahci_platform_get_resources(
+
+ hpriv->mmio = devm_ioremap_resource(dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+- if (!hpriv->mmio) {
++ if (IS_ERR(hpriv->mmio)) {
+ dev_err(dev, "no mmio space\n");
++ rc = PTR_ERR(hpriv->mmio);
+ goto err_out;
+ }
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch
new file mode 100644
index 0000000..ce1d840
--- /dev/null
+++ b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch
@@ -0,0 +1,50 @@
+From bc26554ffa0223015edb474a4877ec544167dfba Mon Sep 17 00:00:00 2001
+From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Date: Tue, 25 Mar 2014 19:51:38 +0100
+Subject: [PATCH 123/182] ata: ahci_platform: fix ahci_platform_data->suspend
+ method handling
+
+Looking at ST SPEAr1340 AHCI code (the only user of the deprecated
+pdata->suspend and pdata->resume) it is obvious the we should return
+after calling pdata->suspend() only if the function have returned
+non-zero return value. The code has been broken since commit 1e70c2
+("ata/ahci_platform: Add clock framework support"). Fix it.
+
+Cc: Viresh Kumar <viresh.linux@gmail.com>
+Cc: Shiraz Hashim <shiraz.hashim@st.com>
+Acked-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+---
+ drivers/ata/ahci_platform.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 70fbf66..7bd6adf 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -521,12 +521,19 @@ int ahci_platform_suspend(struct device *dev)
+ if (rc)
+ return rc;
+
+- if (pdata && pdata->suspend)
+- return pdata->suspend(dev);
++ if (pdata && pdata->suspend) {
++ rc = pdata->suspend(dev);
++ if (rc)
++ goto resume_host;
++ }
+
+ ahci_platform_disable_resources(hpriv);
+
+ return 0;
++
++resume_host:
++ ahci_platform_resume_host(dev);
++ return rc;
+ }
+ EXPORT_SYMBOL_GPL(ahci_platform_suspend);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch
new file mode 100644
index 0000000..bd45720
--- /dev/null
+++ b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch
@@ -0,0 +1,1157 @@
+From 04800db1047afbf6701379435bff1a6fa64215f7 Mon Sep 17 00:00:00 2001
+From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Date: Tue, 25 Mar 2014 19:51:39 +0100
+Subject: [PATCH 124/182] ata: move library code from ahci_platform.c to
+ libahci_platform.c
+
+Move AHCI platform library code from ahci_platform.c to
+libahci_platform.c and fix dependencies for ahci_st,
+ahci_imx and ahci_sunxi drivers.
+
+Acked-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+
+Conflicts:
+ drivers/ata/Kconfig
+ drivers/ata/Makefile
+---
+ drivers/ata/Kconfig | 2 +-
+ drivers/ata/Makefile | 4 +-
+ drivers/ata/ahci_platform.c | 515 --------------------------------------
+ drivers/ata/libahci_platform.c | 541 ++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 544 insertions(+), 518 deletions(-)
+ create mode 100644 drivers/ata/libahci_platform.c
+
+diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
+index 868429a..dc950f3 100644
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -99,7 +99,7 @@ config SATA_AHCI_PLATFORM
+
+ config AHCI_IMX
+ tristate "Freescale i.MX AHCI SATA support"
+- depends on SATA_AHCI_PLATFORM && MFD_SYSCON
++ depends on MFD_SYSCON
+ help
+ This option enables support for the Freescale i.MX SoC's
+ onboard AHCI SATA.
+diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
+index 46518c6..366b743 100644
+--- a/drivers/ata/Makefile
++++ b/drivers/ata/Makefile
+@@ -4,13 +4,13 @@ obj-$(CONFIG_ATA) += libata.o
+ # non-SFF interface
+ obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o
+ obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o
+-obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o
++obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o
+ obj-$(CONFIG_SATA_FSL) += sata_fsl.o
+ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
+ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
+ obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+ obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
+-obj-$(CONFIG_AHCI_IMX) += ahci_imx.o
++obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
+
+ # SFF w/ custom DMA
+ obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
+diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
+index 7bd6adf..ef67e79 100644
+--- a/drivers/ata/ahci_platform.c
++++ b/drivers/ata/ahci_platform.c
+@@ -12,28 +12,15 @@
+ * any later version.
+ */
+
+-#include <linux/clk.h>
+ #include <linux/kernel.h>
+-#include <linux/gfp.h>
+ #include <linux/module.h>
+ #include <linux/pm.h>
+-#include <linux/interrupt.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
+ #include <linux/libata.h>
+ #include <linux/ahci_platform.h>
+-#include <linux/phy/phy.h>
+-#include <linux/pm_runtime.h>
+ #include "ahci.h"
+
+-static void ahci_host_stop(struct ata_host *host);
+-
+-struct ata_port_operations ahci_platform_ops = {
+- .inherits = &ahci_ops,
+- .host_stop = ahci_host_stop,
+-};
+-EXPORT_SYMBOL_GPL(ahci_platform_ops);
+-
+ static const struct ata_port_info ahci_port_info = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+@@ -41,345 +28,6 @@ static const struct ata_port_info ahci_port_info = {
+ .port_ops = &ahci_platform_ops,
+ };
+
+-static struct scsi_host_template ahci_platform_sht = {
+- AHCI_SHT("ahci_platform"),
+-};
+-
+-/**
+- * ahci_platform_enable_clks - Enable platform clocks
+- * @hpriv: host private area to store config values
+- *
+- * This function enables all the clks found in hpriv->clks, starting at
+- * index 0. If any clk fails to enable it disables all the clks already
+- * enabled in reverse order, and then returns an error.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
+-{
+- int c, rc;
+-
+- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
+- rc = clk_prepare_enable(hpriv->clks[c]);
+- if (rc)
+- goto disable_unprepare_clk;
+- }
+- return 0;
+-
+-disable_unprepare_clk:
+- while (--c >= 0)
+- clk_disable_unprepare(hpriv->clks[c]);
+- return rc;
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
+-
+-/**
+- * ahci_platform_disable_clks - Disable platform clocks
+- * @hpriv: host private area to store config values
+- *
+- * This function disables all the clks found in hpriv->clks, in reverse
+- * order of ahci_platform_enable_clks (starting at the end of the array).
+- */
+-void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
+-{
+- int c;
+-
+- for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
+- if (hpriv->clks[c])
+- clk_disable_unprepare(hpriv->clks[c]);
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
+-
+-/**
+- * ahci_platform_enable_resources - Enable platform resources
+- * @hpriv: host private area to store config values
+- *
+- * This function enables all ahci_platform managed resources in the
+- * following order:
+- * 1) Regulator
+- * 2) Clocks (through ahci_platform_enable_clks)
+- * 3) Phy
+- *
+- * If resource enabling fails at any point the previous enabled resources
+- * are disabled in reverse order.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+-{
+- int rc;
+-
+- if (hpriv->target_pwr) {
+- rc = regulator_enable(hpriv->target_pwr);
+- if (rc)
+- return rc;
+- }
+-
+- rc = ahci_platform_enable_clks(hpriv);
+- if (rc)
+- goto disable_regulator;
+-
+- if (hpriv->phy) {
+- rc = phy_init(hpriv->phy);
+- if (rc)
+- goto disable_clks;
+-
+- rc = phy_power_on(hpriv->phy);
+- if (rc) {
+- phy_exit(hpriv->phy);
+- goto disable_clks;
+- }
+- }
+-
+- return 0;
+-
+-disable_clks:
+- ahci_platform_disable_clks(hpriv);
+-
+-disable_regulator:
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
+- return rc;
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
+-
+-/**
+- * ahci_platform_disable_resources - Disable platform resources
+- * @hpriv: host private area to store config values
+- *
+- * This function disables all ahci_platform managed resources in the
+- * following order:
+- * 1) Phy
+- * 2) Clocks (through ahci_platform_disable_clks)
+- * 3) Regulator
+- */
+-void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
+-{
+- if (hpriv->phy) {
+- phy_power_off(hpriv->phy);
+- phy_exit(hpriv->phy);
+- }
+-
+- ahci_platform_disable_clks(hpriv);
+-
+- if (hpriv->target_pwr)
+- regulator_disable(hpriv->target_pwr);
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
+-
+-static void ahci_platform_put_resources(struct device *dev, void *res)
+-{
+- struct ahci_host_priv *hpriv = res;
+- int c;
+-
+- if (hpriv->got_runtime_pm) {
+- pm_runtime_put_sync(dev);
+- pm_runtime_disable(dev);
+- }
+-
+- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
+- clk_put(hpriv->clks[c]);
+-}
+-
+-/**
+- * ahci_platform_get_resources - Get platform resources
+- * @pdev: platform device to get resources for
+- *
+- * This function allocates an ahci_host_priv struct, and gets the following
+- * resources, storing a reference to them inside the returned struct:
+- *
+- * 1) mmio registers (IORESOURCE_MEM 0, mandatory)
+- * 2) regulator for controlling the targets power (optional)
+- * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
+- * or for non devicetree enabled platforms a single clock
+- * 4) phy (optional)
+- *
+- * RETURNS:
+- * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
+- */
+-struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
+-{
+- struct device *dev = &pdev->dev;
+- struct ahci_host_priv *hpriv;
+- struct clk *clk;
+- int i, rc = -ENOMEM;
+-
+- if (!devres_open_group(dev, NULL, GFP_KERNEL))
+- return ERR_PTR(-ENOMEM);
+-
+- hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
+- GFP_KERNEL);
+- if (!hpriv)
+- goto err_out;
+-
+- devres_add(dev, hpriv);
+-
+- hpriv->mmio = devm_ioremap_resource(dev,
+- platform_get_resource(pdev, IORESOURCE_MEM, 0));
+- if (IS_ERR(hpriv->mmio)) {
+- dev_err(dev, "no mmio space\n");
+- rc = PTR_ERR(hpriv->mmio);
+- goto err_out;
+- }
+-
+- hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
+- if (IS_ERR(hpriv->target_pwr)) {
+- rc = PTR_ERR(hpriv->target_pwr);
+- if (rc == -EPROBE_DEFER)
+- goto err_out;
+- hpriv->target_pwr = NULL;
+- }
+-
+- for (i = 0; i < AHCI_MAX_CLKS; i++) {
+- /*
+- * For now we must use clk_get(dev, NULL) for the first clock,
+- * because some platforms (da850, spear13xx) are not yet
+- * converted to use devicetree for clocks. For new platforms
+- * this is equivalent to of_clk_get(dev->of_node, 0).
+- */
+- if (i == 0)
+- clk = clk_get(dev, NULL);
+- else
+- clk = of_clk_get(dev->of_node, i);
+-
+- if (IS_ERR(clk)) {
+- rc = PTR_ERR(clk);
+- if (rc == -EPROBE_DEFER)
+- goto err_out;
+- break;
+- }
+- hpriv->clks[i] = clk;
+- }
+-
+- hpriv->phy = devm_phy_get(dev, "sata-phy");
+- if (IS_ERR(hpriv->phy)) {
+- rc = PTR_ERR(hpriv->phy);
+- switch (rc) {
+- case -ENODEV:
+- case -ENOSYS:
+- /* continue normally */
+- hpriv->phy = NULL;
+- break;
+-
+- case -EPROBE_DEFER:
+- goto err_out;
+-
+- default:
+- dev_err(dev, "couldn't get sata-phy\n");
+- goto err_out;
+- }
+- }
+-
+- pm_runtime_enable(dev);
+- pm_runtime_get_sync(dev);
+- hpriv->got_runtime_pm = true;
+-
+- devres_remove_group(dev, NULL);
+- return hpriv;
+-
+-err_out:
+- devres_release_group(dev, NULL);
+- return ERR_PTR(rc);
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
+-
+-/**
+- * ahci_platform_init_host - Bring up an ahci-platform host
+- * @pdev: platform device pointer for the host
+- * @hpriv: ahci-host private data for the host
+- * @pi_template: template for the ata_port_info to use
+- * @force_port_map: param passed to ahci_save_initial_config
+- * @mask_port_map: param passed to ahci_save_initial_config
+- *
+- * This function does all the usual steps needed to bring up an
+- * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
+- * must be initialized / enabled before calling this.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_init_host(struct platform_device *pdev,
+- struct ahci_host_priv *hpriv,
+- const struct ata_port_info *pi_template,
+- unsigned int force_port_map,
+- unsigned int mask_port_map)
+-{
+- struct device *dev = &pdev->dev;
+- struct ata_port_info pi = *pi_template;
+- const struct ata_port_info *ppi[] = { &pi, NULL };
+- struct ata_host *host;
+- int i, irq, n_ports, rc;
+-
+- irq = platform_get_irq(pdev, 0);
+- if (irq <= 0) {
+- dev_err(dev, "no irq\n");
+- return -EINVAL;
+- }
+-
+- /* prepare host */
+- hpriv->flags |= (unsigned long)pi.private_data;
+-
+- ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
+-
+- if (hpriv->cap & HOST_CAP_NCQ)
+- pi.flags |= ATA_FLAG_NCQ;
+-
+- if (hpriv->cap & HOST_CAP_PMP)
+- pi.flags |= ATA_FLAG_PMP;
+-
+- ahci_set_em_messages(hpriv, &pi);
+-
+- /* CAP.NP sometimes indicate the index of the last enabled
+- * port, at other times, that of the last possible port, so
+- * determining the maximum port number requires looking at
+- * both CAP.NP and port_map.
+- */
+- n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
+-
+- host = ata_host_alloc_pinfo(dev, ppi, n_ports);
+- if (!host)
+- return -ENOMEM;
+-
+- host->private_data = hpriv;
+-
+- if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
+- host->flags |= ATA_HOST_PARALLEL_SCAN;
+- else
+- dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
+-
+- if (pi.flags & ATA_FLAG_EM)
+- ahci_reset_em(host);
+-
+- for (i = 0; i < host->n_ports; i++) {
+- struct ata_port *ap = host->ports[i];
+-
+- ata_port_desc(ap, "mmio %pR",
+- platform_get_resource(pdev, IORESOURCE_MEM, 0));
+- ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
+-
+- /* set enclosure management message type */
+- if (ap->flags & ATA_FLAG_EM)
+- ap->em_message_type = hpriv->em_msg_type;
+-
+- /* disabled/not-implemented port */
+- if (!(hpriv->port_map & (1 << i)))
+- ap->ops = &ata_dummy_port_ops;
+- }
+-
+- rc = ahci_reset_controller(host);
+- if (rc)
+- return rc;
+-
+- ahci_init_controller(host);
+- ahci_print_info(host, "platform");
+-
+- return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
+- &ahci_platform_sht);
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_init_host);
+-
+ static int ahci_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -420,169 +68,6 @@ disable_resources:
+ return rc;
+ }
+
+-static void ahci_host_stop(struct ata_host *host)
+-{
+- struct device *dev = host->dev;
+- struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- struct ahci_host_priv *hpriv = host->private_data;
+-
+- if (pdata && pdata->exit)
+- pdata->exit(dev);
+-
+- ahci_platform_disable_resources(hpriv);
+-}
+-
+-#ifdef CONFIG_PM_SLEEP
+-/**
+- * ahci_platform_suspend_host - Suspend an ahci-platform host
+- * @dev: device pointer for the host
+- *
+- * This function does all the usual steps needed to suspend an
+- * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
+- * must be disabled after calling this.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_suspend_host(struct device *dev)
+-{
+- struct ata_host *host = dev_get_drvdata(dev);
+- struct ahci_host_priv *hpriv = host->private_data;
+- void __iomem *mmio = hpriv->mmio;
+- u32 ctl;
+-
+- if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
+- dev_err(dev, "firmware update required for suspend/resume\n");
+- return -EIO;
+- }
+-
+- /*
+- * AHCI spec rev1.1 section 8.3.3:
+- * Software must disable interrupts prior to requesting a
+- * transition of the HBA to D3 state.
+- */
+- ctl = readl(mmio + HOST_CTL);
+- ctl &= ~HOST_IRQ_EN;
+- writel(ctl, mmio + HOST_CTL);
+- readl(mmio + HOST_CTL); /* flush */
+-
+- return ata_host_suspend(host, PMSG_SUSPEND);
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
+-
+-/**
+- * ahci_platform_resume_host - Resume an ahci-platform host
+- * @dev: device pointer for the host
+- *
+- * This function does all the usual steps needed to resume an ahci-platform
+- * host, note any necessary resources (ie clks, phy, etc.) must be
+- * initialized / enabled before calling this.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_resume_host(struct device *dev)
+-{
+- struct ata_host *host = dev_get_drvdata(dev);
+- int rc;
+-
+- if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
+- rc = ahci_reset_controller(host);
+- if (rc)
+- return rc;
+-
+- ahci_init_controller(host);
+- }
+-
+- ata_host_resume(host);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
+-
+-/**
+- * ahci_platform_suspend - Suspend an ahci-platform device
+- * @dev: the platform device to suspend
+- *
+- * This function suspends the host associated with the device, followed by
+- * disabling all the resources of the device.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_suspend(struct device *dev)
+-{
+- struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- struct ata_host *host = dev_get_drvdata(dev);
+- struct ahci_host_priv *hpriv = host->private_data;
+- int rc;
+-
+- rc = ahci_platform_suspend_host(dev);
+- if (rc)
+- return rc;
+-
+- if (pdata && pdata->suspend) {
+- rc = pdata->suspend(dev);
+- if (rc)
+- goto resume_host;
+- }
+-
+- ahci_platform_disable_resources(hpriv);
+-
+- return 0;
+-
+-resume_host:
+- ahci_platform_resume_host(dev);
+- return rc;
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_suspend);
+-
+-/**
+- * ahci_platform_resume - Resume an ahci-platform device
+- * @dev: the platform device to resume
+- *
+- * This function enables all the resources of the device followed by
+- * resuming the host associated with the device.
+- *
+- * RETURNS:
+- * 0 on success otherwise a negative error code
+- */
+-int ahci_platform_resume(struct device *dev)
+-{
+- struct ahci_platform_data *pdata = dev_get_platdata(dev);
+- struct ata_host *host = dev_get_drvdata(dev);
+- struct ahci_host_priv *hpriv = host->private_data;
+- int rc;
+-
+- rc = ahci_platform_enable_resources(hpriv);
+- if (rc)
+- return rc;
+-
+- if (pdata && pdata->resume) {
+- rc = pdata->resume(dev);
+- if (rc)
+- goto disable_resources;
+- }
+-
+- rc = ahci_platform_resume_host(dev);
+- if (rc)
+- goto disable_resources;
+-
+- /* We resumed so update PM runtime state */
+- pm_runtime_disable(dev);
+- pm_runtime_set_active(dev);
+- pm_runtime_enable(dev);
+-
+- return 0;
+-
+-disable_resources:
+- ahci_platform_disable_resources(hpriv);
+-
+- return rc;
+-}
+-EXPORT_SYMBOL_GPL(ahci_platform_resume);
+-#endif
+-
+ static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
+ ahci_platform_resume);
+
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+new file mode 100644
+index 0000000..7cb3a85
+--- /dev/null
++++ b/drivers/ata/libahci_platform.c
+@@ -0,0 +1,541 @@
++/*
++ * AHCI SATA platform library
++ *
++ * Copyright 2004-2005 Red Hat, Inc.
++ * Jeff Garzik <jgarzik@pobox.com>
++ * Copyright 2010 MontaVista Software, LLC.
++ * Anton Vorontsov <avorontsov@ru.mvista.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ */
++
++#include <linux/clk.h>
++#include <linux/kernel.h>
++#include <linux/gfp.h>
++#include <linux/module.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/libata.h>
++#include <linux/ahci_platform.h>
++#include <linux/phy/phy.h>
++#include <linux/pm_runtime.h>
++#include "ahci.h"
++
++static void ahci_host_stop(struct ata_host *host);
++
++struct ata_port_operations ahci_platform_ops = {
++ .inherits = &ahci_ops,
++ .host_stop = ahci_host_stop,
++};
++EXPORT_SYMBOL_GPL(ahci_platform_ops);
++
++static struct scsi_host_template ahci_platform_sht = {
++ AHCI_SHT("ahci_platform"),
++};
++
++/**
++ * ahci_platform_enable_clks - Enable platform clocks
++ * @hpriv: host private area to store config values
++ *
++ * This function enables all the clks found in hpriv->clks, starting at
++ * index 0. If any clk fails to enable it disables all the clks already
++ * enabled in reverse order, and then returns an error.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
++{
++ int c, rc;
++
++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
++ rc = clk_prepare_enable(hpriv->clks[c]);
++ if (rc)
++ goto disable_unprepare_clk;
++ }
++ return 0;
++
++disable_unprepare_clk:
++ while (--c >= 0)
++ clk_disable_unprepare(hpriv->clks[c]);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
++
++/**
++ * ahci_platform_disable_clks - Disable platform clocks
++ * @hpriv: host private area to store config values
++ *
++ * This function disables all the clks found in hpriv->clks, in reverse
++ * order of ahci_platform_enable_clks (starting at the end of the array).
++ */
++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
++{
++ int c;
++
++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
++ if (hpriv->clks[c])
++ clk_disable_unprepare(hpriv->clks[c]);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
++
++/**
++ * ahci_platform_enable_resources - Enable platform resources
++ * @hpriv: host private area to store config values
++ *
++ * This function enables all ahci_platform managed resources in the
++ * following order:
++ * 1) Regulator
++ * 2) Clocks (through ahci_platform_enable_clks)
++ * 3) Phy
++ *
++ * If resource enabling fails at any point the previous enabled resources
++ * are disabled in reverse order.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
++{
++ int rc;
++
++ if (hpriv->target_pwr) {
++ rc = regulator_enable(hpriv->target_pwr);
++ if (rc)
++ return rc;
++ }
++
++ rc = ahci_platform_enable_clks(hpriv);
++ if (rc)
++ goto disable_regulator;
++
++ if (hpriv->phy) {
++ rc = phy_init(hpriv->phy);
++ if (rc)
++ goto disable_clks;
++
++ rc = phy_power_on(hpriv->phy);
++ if (rc) {
++ phy_exit(hpriv->phy);
++ goto disable_clks;
++ }
++ }
++
++ return 0;
++
++disable_clks:
++ ahci_platform_disable_clks(hpriv);
++
++disable_regulator:
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
++
++/**
++ * ahci_platform_disable_resources - Disable platform resources
++ * @hpriv: host private area to store config values
++ *
++ * This function disables all ahci_platform managed resources in the
++ * following order:
++ * 1) Phy
++ * 2) Clocks (through ahci_platform_disable_clks)
++ * 3) Regulator
++ */
++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
++{
++ if (hpriv->phy) {
++ phy_power_off(hpriv->phy);
++ phy_exit(hpriv->phy);
++ }
++
++ ahci_platform_disable_clks(hpriv);
++
++ if (hpriv->target_pwr)
++ regulator_disable(hpriv->target_pwr);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
++
++static void ahci_platform_put_resources(struct device *dev, void *res)
++{
++ struct ahci_host_priv *hpriv = res;
++ int c;
++
++ if (hpriv->got_runtime_pm) {
++ pm_runtime_put_sync(dev);
++ pm_runtime_disable(dev);
++ }
++
++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
++ clk_put(hpriv->clks[c]);
++}
++
++/**
++ * ahci_platform_get_resources - Get platform resources
++ * @pdev: platform device to get resources for
++ *
++ * This function allocates an ahci_host_priv struct, and gets the following
++ * resources, storing a reference to them inside the returned struct:
++ *
++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory)
++ * 2) regulator for controlling the targets power (optional)
++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
++ * or for non devicetree enabled platforms a single clock
++ * 4) phy (optional)
++ *
++ * RETURNS:
++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
++ */
++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct ahci_host_priv *hpriv;
++ struct clk *clk;
++ int i, rc = -ENOMEM;
++
++ if (!devres_open_group(dev, NULL, GFP_KERNEL))
++ return ERR_PTR(-ENOMEM);
++
++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
++ GFP_KERNEL);
++ if (!hpriv)
++ goto err_out;
++
++ devres_add(dev, hpriv);
++
++ hpriv->mmio = devm_ioremap_resource(dev,
++ platform_get_resource(pdev, IORESOURCE_MEM, 0));
++ if (IS_ERR(hpriv->mmio)) {
++ dev_err(dev, "no mmio space\n");
++ rc = PTR_ERR(hpriv->mmio);
++ goto err_out;
++ }
++
++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
++ if (IS_ERR(hpriv->target_pwr)) {
++ rc = PTR_ERR(hpriv->target_pwr);
++ if (rc == -EPROBE_DEFER)
++ goto err_out;
++ hpriv->target_pwr = NULL;
++ }
++
++ for (i = 0; i < AHCI_MAX_CLKS; i++) {
++ /*
++ * For now we must use clk_get(dev, NULL) for the first clock,
++ * because some platforms (da850, spear13xx) are not yet
++ * converted to use devicetree for clocks. For new platforms
++ * this is equivalent to of_clk_get(dev->of_node, 0).
++ */
++ if (i == 0)
++ clk = clk_get(dev, NULL);
++ else
++ clk = of_clk_get(dev->of_node, i);
++
++ if (IS_ERR(clk)) {
++ rc = PTR_ERR(clk);
++ if (rc == -EPROBE_DEFER)
++ goto err_out;
++ break;
++ }
++ hpriv->clks[i] = clk;
++ }
++
++ hpriv->phy = devm_phy_get(dev, "sata-phy");
++ if (IS_ERR(hpriv->phy)) {
++ rc = PTR_ERR(hpriv->phy);
++ switch (rc) {
++ case -ENODEV:
++ case -ENOSYS:
++ /* continue normally */
++ hpriv->phy = NULL;
++ break;
++
++ case -EPROBE_DEFER:
++ goto err_out;
++
++ default:
++ dev_err(dev, "couldn't get sata-phy\n");
++ goto err_out;
++ }
++ }
++
++ pm_runtime_enable(dev);
++ pm_runtime_get_sync(dev);
++ hpriv->got_runtime_pm = true;
++
++ devres_remove_group(dev, NULL);
++ return hpriv;
++
++err_out:
++ devres_release_group(dev, NULL);
++ return ERR_PTR(rc);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
++
++/**
++ * ahci_platform_init_host - Bring up an ahci-platform host
++ * @pdev: platform device pointer for the host
++ * @hpriv: ahci-host private data for the host
++ * @pi_template: template for the ata_port_info to use
++ * @force_port_map: param passed to ahci_save_initial_config
++ * @mask_port_map: param passed to ahci_save_initial_config
++ *
++ * This function does all the usual steps needed to bring up an
++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
++ * must be initialized / enabled before calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_init_host(struct platform_device *pdev,
++ struct ahci_host_priv *hpriv,
++ const struct ata_port_info *pi_template,
++ unsigned int force_port_map,
++ unsigned int mask_port_map)
++{
++ struct device *dev = &pdev->dev;
++ struct ata_port_info pi = *pi_template;
++ const struct ata_port_info *ppi[] = { &pi, NULL };
++ struct ata_host *host;
++ int i, irq, n_ports, rc;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq <= 0) {
++ dev_err(dev, "no irq\n");
++ return -EINVAL;
++ }
++
++ /* prepare host */
++ hpriv->flags |= (unsigned long)pi.private_data;
++
++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
++
++ if (hpriv->cap & HOST_CAP_NCQ)
++ pi.flags |= ATA_FLAG_NCQ;
++
++ if (hpriv->cap & HOST_CAP_PMP)
++ pi.flags |= ATA_FLAG_PMP;
++
++ ahci_set_em_messages(hpriv, &pi);
++
++ /* CAP.NP sometimes indicate the index of the last enabled
++ * port, at other times, that of the last possible port, so
++ * determining the maximum port number requires looking at
++ * both CAP.NP and port_map.
++ */
++ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
++
++ host = ata_host_alloc_pinfo(dev, ppi, n_ports);
++ if (!host)
++ return -ENOMEM;
++
++ host->private_data = hpriv;
++
++ if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
++ host->flags |= ATA_HOST_PARALLEL_SCAN;
++ else
++ dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
++
++ if (pi.flags & ATA_FLAG_EM)
++ ahci_reset_em(host);
++
++ for (i = 0; i < host->n_ports; i++) {
++ struct ata_port *ap = host->ports[i];
++
++ ata_port_desc(ap, "mmio %pR",
++ platform_get_resource(pdev, IORESOURCE_MEM, 0));
++ ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
++
++ /* set enclosure management message type */
++ if (ap->flags & ATA_FLAG_EM)
++ ap->em_message_type = hpriv->em_msg_type;
++
++ /* disabled/not-implemented port */
++ if (!(hpriv->port_map & (1 << i)))
++ ap->ops = &ata_dummy_port_ops;
++ }
++
++ rc = ahci_reset_controller(host);
++ if (rc)
++ return rc;
++
++ ahci_init_controller(host);
++ ahci_print_info(host, "platform");
++
++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
++ &ahci_platform_sht);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_init_host);
++
++static void ahci_host_stop(struct ata_host *host)
++{
++ struct device *dev = host->dev;
++ struct ahci_platform_data *pdata = dev_get_platdata(dev);
++ struct ahci_host_priv *hpriv = host->private_data;
++
++ if (pdata && pdata->exit)
++ pdata->exit(dev);
++
++ ahci_platform_disable_resources(hpriv);
++}
++
++#ifdef CONFIG_PM_SLEEP
++/**
++ * ahci_platform_suspend_host - Suspend an ahci-platform host
++ * @dev: device pointer for the host
++ *
++ * This function does all the usual steps needed to suspend an
++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
++ * must be disabled after calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_suspend_host(struct device *dev)
++{
++ struct ata_host *host = dev_get_drvdata(dev);
++ struct ahci_host_priv *hpriv = host->private_data;
++ void __iomem *mmio = hpriv->mmio;
++ u32 ctl;
++
++ if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
++ dev_err(dev, "firmware update required for suspend/resume\n");
++ return -EIO;
++ }
++
++ /*
++ * AHCI spec rev1.1 section 8.3.3:
++ * Software must disable interrupts prior to requesting a
++ * transition of the HBA to D3 state.
++ */
++ ctl = readl(mmio + HOST_CTL);
++ ctl &= ~HOST_IRQ_EN;
++ writel(ctl, mmio + HOST_CTL);
++ readl(mmio + HOST_CTL); /* flush */
++
++ return ata_host_suspend(host, PMSG_SUSPEND);
++}
++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
++
++/**
++ * ahci_platform_resume_host - Resume an ahci-platform host
++ * @dev: device pointer for the host
++ *
++ * This function does all the usual steps needed to resume an ahci-platform
++ * host, note any necessary resources (ie clks, phy, etc.) must be
++ * initialized / enabled before calling this.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_resume_host(struct device *dev)
++{
++ struct ata_host *host = dev_get_drvdata(dev);
++ int rc;
++
++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
++ rc = ahci_reset_controller(host);
++ if (rc)
++ return rc;
++
++ ahci_init_controller(host);
++ }
++
++ ata_host_resume(host);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
++
++/**
++ * ahci_platform_suspend - Suspend an ahci-platform device
++ * @dev: the platform device to suspend
++ *
++ * This function suspends the host associated with the device, followed by
++ * disabling all the resources of the device.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_suspend(struct device *dev)
++{
++ struct ahci_platform_data *pdata = dev_get_platdata(dev);
++ struct ata_host *host = dev_get_drvdata(dev);
++ struct ahci_host_priv *hpriv = host->private_data;
++ int rc;
++
++ rc = ahci_platform_suspend_host(dev);
++ if (rc)
++ return rc;
++
++ if (pdata && pdata->suspend) {
++ rc = pdata->suspend(dev);
++ if (rc)
++ goto resume_host;
++ }
++
++ ahci_platform_disable_resources(hpriv);
++
++ return 0;
++
++resume_host:
++ ahci_platform_resume_host(dev);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_suspend);
++
++/**
++ * ahci_platform_resume - Resume an ahci-platform device
++ * @dev: the platform device to resume
++ *
++ * This function enables all the resources of the device followed by
++ * resuming the host associated with the device.
++ *
++ * RETURNS:
++ * 0 on success otherwise a negative error code
++ */
++int ahci_platform_resume(struct device *dev)
++{
++ struct ahci_platform_data *pdata = dev_get_platdata(dev);
++ struct ata_host *host = dev_get_drvdata(dev);
++ struct ahci_host_priv *hpriv = host->private_data;
++ int rc;
++
++ rc = ahci_platform_enable_resources(hpriv);
++ if (rc)
++ return rc;
++
++ if (pdata && pdata->resume) {
++ rc = pdata->resume(dev);
++ if (rc)
++ goto disable_resources;
++ }
++
++ rc = ahci_platform_resume_host(dev);
++ if (rc)
++ goto disable_resources;
++
++ /* We resumed so update PM runtime state */
++ pm_runtime_disable(dev);
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
++ return 0;
++
++disable_resources:
++ ahci_platform_disable_resources(hpriv);
++
++ return rc;
++}
++EXPORT_SYMBOL_GPL(ahci_platform_resume);
++#endif
++
++MODULE_DESCRIPTION("AHCI SATA platform library");
++MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
++MODULE_LICENSE("GPL");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch
new file mode 100644
index 0000000..250ca6e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch
@@ -0,0 +1,2939 @@
+From 2e6dfaa714ba4bd70fa5dda07c525b6c15e44552 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 3 Apr 2014 13:47:07 -0500
+Subject: [PATCH 125/182] clk: qcom: Add support for IPQ8064's global clock
+ controller (GCC)
+
+Add a driver for the global clock controller found on IPQ8064 based
+platforms. This should allow most non-multimedia device drivers to probe
+and control their clocks.
+
+This is currently missing clocks for USB HSIC and networking devices.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ .../devicetree/bindings/clock/qcom,gcc.txt | 1 +
+ drivers/clk/qcom/Kconfig | 8 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/gcc-ipq806x.c | 2424 ++++++++++++++++++++
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 293 +++
+ include/dt-bindings/reset/qcom,gcc-ipq806x.h | 132 ++
+ 6 files changed, 2859 insertions(+)
+ create mode 100644 drivers/clk/qcom/gcc-ipq806x.c
+ create mode 100644 include/dt-bindings/clock/qcom,gcc-ipq806x.h
+ create mode 100644 include/dt-bindings/reset/qcom,gcc-ipq806x.h
+
+diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+index 9cfcb4f..0171509 100644
+--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+@@ -5,6 +5,7 @@ Required properties :
+ - compatible : shall contain only one of the following:
+
+ "qcom,gcc-apq8064"
++ "qcom,gcc-ipq8064"
+ "qcom,gcc-msm8660"
+ "qcom,gcc-msm8960"
+ "qcom,gcc-msm8974"
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index 7f696b7..cfaa54c 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -4,6 +4,14 @@ config COMMON_CLK_QCOM
+ select REGMAP_MMIO
+ select RESET_CONTROLLER
+
++config IPQ_GCC_806X
++ tristate "IPQ806x Global Clock Controller"
++ depends on COMMON_CLK_QCOM
++ help
++ Support for the global clock controller on ipq806x devices.
++ Say Y if you want to use peripheral devices such as UART, SPI,
++ i2c, USB, SD/eMMC, etc.
++
+ config MSM_GCC_8660
+ tristate "MSM8660 Global Clock Controller"
+ depends on COMMON_CLK_QCOM
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 689e05b..df2a1b3 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += reset.o
+
++obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
+ obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
+ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
+ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
+diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
+new file mode 100644
+index 0000000..278c5fe
+--- /dev/null
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -0,0 +1,2424 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/err.h>
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk-provider.h>
++#include <linux/regmap.h>
++#include <linux/reset-controller.h>
++
++#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
++
++#include "common.h"
++#include "clk-regmap.h"
++#include "clk-pll.h"
++#include "clk-rcg.h"
++#include "clk-branch.h"
++#include "reset.h"
++
++static struct clk_pll pll3 = {
++ .l_reg = 0x3164,
++ .m_reg = 0x3168,
++ .n_reg = 0x316c,
++ .config_reg = 0x3174,
++ .mode_reg = 0x3160,
++ .status_reg = 0x3178,
++ .status_bit = 16,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll3",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
++static struct clk_pll pll8 = {
++ .l_reg = 0x3144,
++ .m_reg = 0x3148,
++ .n_reg = 0x314c,
++ .config_reg = 0x3154,
++ .mode_reg = 0x3140,
++ .status_reg = 0x3158,
++ .status_bit = 16,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll8",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
++static struct clk_regmap pll8_vote = {
++ .enable_reg = 0x34c0,
++ .enable_mask = BIT(8),
++ .hw.init = &(struct clk_init_data){
++ .name = "pll8_vote",
++ .parent_names = (const char *[]){ "pll8" },
++ .num_parents = 1,
++ .ops = &clk_pll_vote_ops,
++ },
++};
++
++static struct clk_pll pll14 = {
++ .l_reg = 0x31c4,
++ .m_reg = 0x31c8,
++ .n_reg = 0x31cc,
++ .config_reg = 0x31d4,
++ .mode_reg = 0x31c0,
++ .status_reg = 0x31d8,
++ .status_bit = 16,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll14",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
++static struct clk_regmap pll14_vote = {
++ .enable_reg = 0x34c0,
++ .enable_mask = BIT(14),
++ .hw.init = &(struct clk_init_data){
++ .name = "pll14_vote",
++ .parent_names = (const char *[]){ "pll14" },
++ .num_parents = 1,
++ .ops = &clk_pll_vote_ops,
++ },
++};
++
++#define P_PXO 0
++#define P_PLL8 1
++#define P_PLL3 1
++#define P_PLL0 2
++#define P_CXO 2
++
++static const u8 gcc_pxo_pll8_map[] = {
++ [P_PXO] = 0,
++ [P_PLL8] = 3,
++};
++
++static const char *gcc_pxo_pll8[] = {
++ "pxo",
++ "pll8_vote",
++};
++
++static const u8 gcc_pxo_pll8_cxo_map[] = {
++ [P_PXO] = 0,
++ [P_PLL8] = 3,
++ [P_CXO] = 5,
++};
++
++static const char *gcc_pxo_pll8_cxo[] = {
++ "pxo",
++ "pll8_vote",
++ "cxo",
++};
++
++static const u8 gcc_pxo_pll3_map[] = {
++ [P_PXO] = 0,
++ [P_PLL3] = 1,
++};
++
++static const u8 gcc_pxo_pll3_sata_map[] = {
++ [P_PXO] = 0,
++ [P_PLL3] = 6,
++};
++
++static const char *gcc_pxo_pll3[] = {
++ "pxo",
++ "pll3",
++};
++
++static const u8 gcc_pxo_pll8_pll0[] = {
++ [P_PXO] = 0,
++ [P_PLL8] = 3,
++ [P_PLL0] = 2,
++};
++
++static const char *gcc_pxo_pll8_pll0_map[] = {
++ "pxo",
++ "pll8_vote",
++ "pll0",
++};
++
++static struct freq_tbl clk_tbl_gsbi_uart[] = {
++ { 1843200, P_PLL8, 2, 6, 625 },
++ { 3686400, P_PLL8, 2, 12, 625 },
++ { 7372800, P_PLL8, 2, 24, 625 },
++ { 14745600, P_PLL8, 2, 48, 625 },
++ { 16000000, P_PLL8, 4, 1, 6 },
++ { 24000000, P_PLL8, 4, 1, 4 },
++ { 32000000, P_PLL8, 4, 1, 3 },
++ { 40000000, P_PLL8, 1, 5, 48 },
++ { 46400000, P_PLL8, 1, 29, 240 },
++ { 48000000, P_PLL8, 4, 1, 2 },
++ { 51200000, P_PLL8, 1, 2, 15 },
++ { 56000000, P_PLL8, 1, 7, 48 },
++ { 58982400, P_PLL8, 1, 96, 625 },
++ { 64000000, P_PLL8, 2, 1, 3 },
++ { }
++};
++
++static struct clk_rcg gsbi1_uart_src = {
++ .ns_reg = 0x29d4,
++ .md_reg = 0x29d0,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x29d4,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi1_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi1_uart_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x29d4,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi1_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi1_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi2_uart_src = {
++ .ns_reg = 0x29f4,
++ .md_reg = 0x29f0,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x29f4,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi2_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi2_uart_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 8,
++ .clkr = {
++ .enable_reg = 0x29f4,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi2_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi2_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi4_uart_src = {
++ .ns_reg = 0x2a34,
++ .md_reg = 0x2a30,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x2a34,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi4_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi4_uart_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 26,
++ .clkr = {
++ .enable_reg = 0x2a34,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi4_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi4_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi5_uart_src = {
++ .ns_reg = 0x2a54,
++ .md_reg = 0x2a50,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x2a54,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi5_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi5_uart_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 22,
++ .clkr = {
++ .enable_reg = 0x2a54,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi5_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi5_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi6_uart_src = {
++ .ns_reg = 0x2a74,
++ .md_reg = 0x2a70,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x2a74,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi6_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi6_uart_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 18,
++ .clkr = {
++ .enable_reg = 0x2a74,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi6_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi6_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi7_uart_src = {
++ .ns_reg = 0x2a94,
++ .md_reg = 0x2a90,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_uart,
++ .clkr = {
++ .enable_reg = 0x2a94,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi7_uart_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi7_uart_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 14,
++ .clkr = {
++ .enable_reg = 0x2a94,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi7_uart_clk",
++ .parent_names = (const char *[]){
++ "gsbi7_uart_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct freq_tbl clk_tbl_gsbi_qup[] = {
++ { 1100000, P_PXO, 1, 2, 49 },
++ { 5400000, P_PXO, 1, 1, 5 },
++ { 10800000, P_PXO, 1, 2, 5 },
++ { 15060000, P_PLL8, 1, 2, 51 },
++ { 24000000, P_PLL8, 4, 1, 4 },
++ { 25600000, P_PLL8, 1, 1, 15 },
++ { 27000000, P_PXO, 1, 0, 0 },
++ { 48000000, P_PLL8, 4, 1, 2 },
++ { 51200000, P_PLL8, 1, 2, 15 },
++ { }
++};
++
++static struct clk_rcg gsbi1_qup_src = {
++ .ns_reg = 0x29cc,
++ .md_reg = 0x29c8,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x29cc,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi1_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi1_qup_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 11,
++ .clkr = {
++ .enable_reg = 0x29cc,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi1_qup_clk",
++ .parent_names = (const char *[]){ "gsbi1_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi2_qup_src = {
++ .ns_reg = 0x29ec,
++ .md_reg = 0x29e8,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x29ec,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi2_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi2_qup_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 6,
++ .clkr = {
++ .enable_reg = 0x29ec,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi2_qup_clk",
++ .parent_names = (const char *[]){ "gsbi2_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi4_qup_src = {
++ .ns_reg = 0x2a2c,
++ .md_reg = 0x2a28,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x2a2c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi4_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi4_qup_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 24,
++ .clkr = {
++ .enable_reg = 0x2a2c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi4_qup_clk",
++ .parent_names = (const char *[]){ "gsbi4_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi5_qup_src = {
++ .ns_reg = 0x2a4c,
++ .md_reg = 0x2a48,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x2a4c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi5_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi5_qup_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 20,
++ .clkr = {
++ .enable_reg = 0x2a4c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi5_qup_clk",
++ .parent_names = (const char *[]){ "gsbi5_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi6_qup_src = {
++ .ns_reg = 0x2a6c,
++ .md_reg = 0x2a68,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x2a6c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi6_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi6_qup_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 16,
++ .clkr = {
++ .enable_reg = 0x2a6c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi6_qup_clk",
++ .parent_names = (const char *[]){ "gsbi6_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gsbi7_qup_src = {
++ .ns_reg = 0x2a8c,
++ .md_reg = 0x2a88,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_gsbi_qup,
++ .clkr = {
++ .enable_reg = 0x2a8c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi7_qup_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ },
++};
++
++static struct clk_branch gsbi7_qup_clk = {
++ .halt_reg = 0x2fd0,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x2a8c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi7_qup_clk",
++ .parent_names = (const char *[]){ "gsbi7_qup_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch gsbi1_h_clk = {
++ .hwcg_reg = 0x29c0,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fcc,
++ .halt_bit = 13,
++ .clkr = {
++ .enable_reg = 0x29c0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch gsbi2_h_clk = {
++ .hwcg_reg = 0x29e0,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fcc,
++ .halt_bit = 9,
++ .clkr = {
++ .enable_reg = 0x29e0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi2_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch gsbi4_h_clk = {
++ .hwcg_reg = 0x2a20,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd0,
++ .halt_bit = 27,
++ .clkr = {
++ .enable_reg = 0x2a20,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi4_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch gsbi5_h_clk = {
++ .hwcg_reg = 0x2a40,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd0,
++ .halt_bit = 23,
++ .clkr = {
++ .enable_reg = 0x2a40,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi5_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch gsbi6_h_clk = {
++ .hwcg_reg = 0x2a60,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd0,
++ .halt_bit = 19,
++ .clkr = {
++ .enable_reg = 0x2a60,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi6_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch gsbi7_h_clk = {
++ .hwcg_reg = 0x2a80,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd0,
++ .halt_bit = 15,
++ .clkr = {
++ .enable_reg = 0x2a80,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gsbi7_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_gp[] = {
++ { 12500000, P_PXO, 2, 0, 0 },
++ { 25000000, P_PXO, 1, 0, 0 },
++ { 64000000, P_PLL8, 2, 1, 3 },
++ { 76800000, P_PLL8, 1, 1, 5 },
++ { 96000000, P_PLL8, 4, 0, 0 },
++ { 128000000, P_PLL8, 3, 0, 0 },
++ { 192000000, P_PLL8, 2, 0, 0 },
++ { }
++};
++
++static struct clk_rcg gp0_src = {
++ .ns_reg = 0x2d24,
++ .md_reg = 0x2d00,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_cxo_map,
++ },
++ .freq_tbl = clk_tbl_gp,
++ .clkr = {
++ .enable_reg = 0x2d24,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp0_src",
++ .parent_names = gcc_pxo_pll8_cxo,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_PARENT_GATE,
++ },
++ }
++};
++
++static struct clk_branch gp0_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_bit = 7,
++ .clkr = {
++ .enable_reg = 0x2d24,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp0_clk",
++ .parent_names = (const char *[]){ "gp0_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gp1_src = {
++ .ns_reg = 0x2d44,
++ .md_reg = 0x2d40,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_cxo_map,
++ },
++ .freq_tbl = clk_tbl_gp,
++ .clkr = {
++ .enable_reg = 0x2d44,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp1_src",
++ .parent_names = gcc_pxo_pll8_cxo,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ }
++};
++
++static struct clk_branch gp1_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_bit = 6,
++ .clkr = {
++ .enable_reg = 0x2d44,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp1_clk",
++ .parent_names = (const char *[]){ "gp1_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg gp2_src = {
++ .ns_reg = 0x2d64,
++ .md_reg = 0x2d60,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_cxo_map,
++ },
++ .freq_tbl = clk_tbl_gp,
++ .clkr = {
++ .enable_reg = 0x2d64,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp2_src",
++ .parent_names = gcc_pxo_pll8_cxo,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ }
++};
++
++static struct clk_branch gp2_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_bit = 5,
++ .clkr = {
++ .enable_reg = 0x2d64,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "gp2_clk",
++ .parent_names = (const char *[]){ "gp2_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch pmem_clk = {
++ .hwcg_reg = 0x25a0,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fc8,
++ .halt_bit = 20,
++ .clkr = {
++ .enable_reg = 0x25a0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pmem_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_rcg prng_src = {
++ .ns_reg = 0x2e80,
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .clkr = {
++ .hw.init = &(struct clk_init_data){
++ .name = "prng_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch prng_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 10,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(10),
++ .hw.init = &(struct clk_init_data){
++ .name = "prng_clk",
++ .parent_names = (const char *[]){ "prng_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_sdc[] = {
++ { 144000, P_PXO, 5, 18,625 },
++ { 400000, P_PLL8, 4, 1, 240 },
++ { 16000000, P_PLL8, 4, 1, 6 },
++ { 17070000, P_PLL8, 1, 2, 45 },
++ { 20210000, P_PLL8, 1, 1, 19 },
++ { 24000000, P_PLL8, 4, 1, 4 },
++ { 48000000, P_PLL8, 4, 1, 2 },
++ { 64000000, P_PLL8, 3, 1, 2 },
++ { 96000000, P_PLL8, 4, 0, 0 },
++ { 192000000, P_PLL8, 2, 0, 0 },
++ { }
++};
++
++static struct clk_rcg sdc1_src = {
++ .ns_reg = 0x282c,
++ .md_reg = 0x2828,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_sdc,
++ .clkr = {
++ .enable_reg = 0x282c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc1_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ }
++};
++
++static struct clk_branch sdc1_clk = {
++ .halt_reg = 0x2fc8,
++ .halt_bit = 6,
++ .clkr = {
++ .enable_reg = 0x282c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc1_clk",
++ .parent_names = (const char *[]){ "sdc1_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_rcg sdc3_src = {
++ .ns_reg = 0x286c,
++ .md_reg = 0x2868,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_sdc,
++ .clkr = {
++ .enable_reg = 0x286c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc3_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ }
++};
++
++static struct clk_branch sdc3_clk = {
++ .halt_reg = 0x2fc8,
++ .halt_bit = 4,
++ .clkr = {
++ .enable_reg = 0x286c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc3_clk",
++ .parent_names = (const char *[]){ "sdc3_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch sdc1_h_clk = {
++ .hwcg_reg = 0x2820,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fc8,
++ .halt_bit = 11,
++ .clkr = {
++ .enable_reg = 0x2820,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch sdc3_h_clk = {
++ .hwcg_reg = 0x2860,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fc8,
++ .halt_bit = 9,
++ .clkr = {
++ .enable_reg = 0x2860,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sdc3_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_tsif_ref[] = {
++ { 105000, P_PXO, 1, 1, 256 },
++ { }
++};
++
++static struct clk_rcg tsif_ref_src = {
++ .ns_reg = 0x2710,
++ .md_reg = 0x270c,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 16,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_map,
++ },
++ .freq_tbl = clk_tbl_tsif_ref,
++ .clkr = {
++ .enable_reg = 0x2710,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "tsif_ref_src",
++ .parent_names = gcc_pxo_pll8,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ }
++};
++
++static struct clk_branch tsif_ref_clk = {
++ .halt_reg = 0x2fd4,
++ .halt_bit = 5,
++ .clkr = {
++ .enable_reg = 0x2710,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "tsif_ref_clk",
++ .parent_names = (const char *[]){ "tsif_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch tsif_h_clk = {
++ .hwcg_reg = 0x2700,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd4,
++ .halt_bit = 7,
++ .clkr = {
++ .enable_reg = 0x2700,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "tsif_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch dma_bam_h_clk = {
++ .hwcg_reg = 0x25c0,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fc8,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x25c0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "dma_bam_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch adm0_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(2),
++ .hw.init = &(struct clk_init_data){
++ .name = "adm0_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch adm0_pbus_clk = {
++ .hwcg_reg = 0x2208,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fdc,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 11,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(3),
++ .hw.init = &(struct clk_init_data){
++ .name = "adm0_pbus_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pmic_arb0_h_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 22,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(8),
++ .hw.init = &(struct clk_init_data){
++ .name = "pmic_arb0_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pmic_arb1_h_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 21,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "pmic_arb1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pmic_ssbi2_clk = {
++ .halt_reg = 0x2fd8,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 23,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(7),
++ .hw.init = &(struct clk_init_data){
++ .name = "pmic_ssbi2_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch rpm_msg_ram_h_clk = {
++ .hwcg_reg = 0x27e0,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fd8,
++ .halt_check = BRANCH_HALT_VOTED,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x3080,
++ .enable_mask = BIT(6),
++ .hw.init = &(struct clk_init_data){
++ .name = "rpm_msg_ram_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_pcie_ref[] = {
++ { 100000000, P_PLL3, 12, 0, 0 },
++ { }
++};
++
++static struct clk_rcg pcie_ref_src = {
++ .ns_reg = 0x3860,
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll3_map,
++ },
++ .freq_tbl = clk_tbl_pcie_ref,
++ .clkr = {
++ .enable_reg = 0x3860,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_ref_src",
++ .parent_names = gcc_pxo_pll3,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch pcie_ref_src_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 30,
++ .clkr = {
++ .enable_reg = 0x3860,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_ref_src_clk",
++ .parent_names = (const char *[]){ "pcie_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch pcie_a_clk = {
++ .halt_reg = 0x2fc0,
++ .halt_bit = 13,
++ .clkr = {
++ .enable_reg = 0x22c0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_a_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie_aux_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 31,
++ .clkr = {
++ .enable_reg = 0x22c8,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_aux_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie_h_clk = {
++ .halt_reg = 0x2fd4,
++ .halt_bit = 8,
++ .clkr = {
++ .enable_reg = 0x22cc,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie_phy_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 29,
++ .clkr = {
++ .enable_reg = 0x22d0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie_phy_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_rcg pcie1_ref_src = {
++ .ns_reg = 0x3aa0,
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll3_map,
++ },
++ .freq_tbl = clk_tbl_pcie_ref,
++ .clkr = {
++ .enable_reg = 0x3aa0,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_ref_src",
++ .parent_names = gcc_pxo_pll3,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch pcie1_ref_src_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 27,
++ .clkr = {
++ .enable_reg = 0x3aa0,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_ref_src_clk",
++ .parent_names = (const char *[]){ "pcie1_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch pcie1_a_clk = {
++ .halt_reg = 0x2fc0,
++ .halt_bit = 10,
++ .clkr = {
++ .enable_reg = 0x3a80,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_a_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie1_aux_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 28,
++ .clkr = {
++ .enable_reg = 0x3a88,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_aux_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie1_h_clk = {
++ .halt_reg = 0x2fd4,
++ .halt_bit = 9,
++ .clkr = {
++ .enable_reg = 0x3a8c,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie1_phy_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 26,
++ .clkr = {
++ .enable_reg = 0x3a90,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie1_phy_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_rcg pcie2_ref_src = {
++ .ns_reg = 0x3ae0,
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll3_map,
++ },
++ .freq_tbl = clk_tbl_pcie_ref,
++ .clkr = {
++ .enable_reg = 0x3ae0,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_ref_src",
++ .parent_names = gcc_pxo_pll3,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch pcie2_ref_src_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 24,
++ .clkr = {
++ .enable_reg = 0x3ae0,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_ref_src_clk",
++ .parent_names = (const char *[]){ "pcie2_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch pcie2_a_clk = {
++ .halt_reg = 0x2fc0,
++ .halt_bit = 9,
++ .clkr = {
++ .enable_reg = 0x3ac0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_a_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie2_aux_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 25,
++ .clkr = {
++ .enable_reg = 0x3ac8,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_aux_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie2_h_clk = {
++ .halt_reg = 0x2fd4,
++ .halt_bit = 10,
++ .clkr = {
++ .enable_reg = 0x3acc,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch pcie2_phy_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 23,
++ .clkr = {
++ .enable_reg = 0x3ad0,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "pcie2_phy_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_sata_ref[] = {
++ { 100000000, P_PLL3, 12, 0, 0 },
++ { }
++};
++
++static struct clk_rcg sata_ref_src = {
++ .ns_reg = 0x2c08,
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll3_sata_map,
++ },
++ .freq_tbl = clk_tbl_sata_ref,
++ .clkr = {
++ .enable_reg = 0x2c08,
++ .enable_mask = BIT(7),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_ref_src",
++ .parent_names = gcc_pxo_pll3,
++ .num_parents = 2,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch sata_rxoob_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 20,
++ .clkr = {
++ .enable_reg = 0x2c0c,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_rxoob_clk",
++ .parent_names = (const char *[]){ "sata_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch sata_pmalive_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 19,
++ .clkr = {
++ .enable_reg = 0x2c10,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_pmalive_clk",
++ .parent_names = (const char *[]){ "sata_ref_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch sata_phy_ref_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 18,
++ .clkr = {
++ .enable_reg = 0x2c14,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_phy_ref_clk",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ },
++ },
++};
++
++static struct clk_branch sata_a_clk = {
++ .halt_reg = 0x2fc0,
++ .halt_bit = 12,
++ .clkr = {
++ .enable_reg = 0x2c20,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_a_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch sata_h_clk = {
++ .halt_reg = 0x2fdc,
++ .halt_bit = 21,
++ .clkr = {
++ .enable_reg = 0x2c00,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch sfab_sata_s_h_clk = {
++ .halt_reg = 0x2fc4,
++ .halt_bit = 14,
++ .clkr = {
++ .enable_reg = 0x2480,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sfab_sata_s_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_branch sata_phy_cfg_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 14,
++ .clkr = {
++ .enable_reg = 0x2c40,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "sata_phy_cfg_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_usb30_master[] = {
++ { 125000000, P_PLL0, 1, 5, 32 },
++ { }
++};
++
++static struct clk_rcg usb30_master_clk_src = {
++ .ns_reg = 0x3b2c,
++ .md_reg = 0x3b28,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll0,
++ },
++ .freq_tbl = clk_tbl_usb30_master,
++ .clkr = {
++ .enable_reg = 0x3b2c,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_master_ref_src",
++ .parent_names = gcc_pxo_pll8_pll0_map,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch usb30_0_branch_clk = {
++ .halt_reg = 0x2fc4,
++ .halt_bit = 22,
++ .clkr = {
++ .enable_reg = 0x3b24,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_0_branch_clk",
++ .parent_names = (const char *[]){ "usb30_master_ref_src", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch usb30_1_branch_clk = {
++ .halt_reg = 0x2fc4,
++ .halt_bit = 17,
++ .clkr = {
++ .enable_reg = 0x3b34,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_1_branch_clk",
++ .parent_names = (const char *[]){ "usb30_master_ref_src", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_usb30_utmi[] = {
++ { 60000000, P_PLL0, 1, 1, 40 },
++ { }
++};
++
++static struct clk_rcg usb30_utmi_clk = {
++ .ns_reg = 0x3b44,
++ .md_reg = 0x3b40,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll0,
++ },
++ .freq_tbl = clk_tbl_usb30_utmi,
++ .clkr = {
++ .enable_reg = 0x3b44,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_utmi_clk",
++ .parent_names = gcc_pxo_pll8_pll0_map,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch usb30_0_utmi_clk_ctl = {
++ .halt_reg = 0x2fc4,
++ .halt_bit = 21,
++ .clkr = {
++ .enable_reg = 0x3b48,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_0_utmi_clk_ctl",
++ .parent_names = (const char *[]){ "usb30_utmi_clk", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch usb30_1_utmi_clk_ctl = {
++ .halt_reg = 0x2fc4,
++ .halt_bit = 15,
++ .clkr = {
++ .enable_reg = 0x3b4c,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb30_1_utmi_clk_ctl",
++ .parent_names = (const char *[]){ "usb30_utmi_clk", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_usb[] = {
++ { 60000000, P_PLL8, 1, 5, 32 },
++ { }
++};
++
++static struct clk_rcg usb_hs1_xcvr_clk_src = {
++ .ns_reg = 0x290C,
++ .md_reg = 0x2908,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll0,
++ },
++ .freq_tbl = clk_tbl_usb,
++ .clkr = {
++ .enable_reg = 0x2968,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_hs1_xcvr_src",
++ .parent_names = gcc_pxo_pll8_pll0_map,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch usb_hs1_xcvr_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 17,
++ .clkr = {
++ .enable_reg = 0x290c,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_hs1_xcvr_clk",
++ .parent_names = (const char *[]){ "usb_hs1_xcvr_src" },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch usb_hs1_h_clk = {
++ .hwcg_reg = 0x2900,
++ .hwcg_bit = 6,
++ .halt_reg = 0x2fc8,
++ .halt_bit = 1,
++ .clkr = {
++ .enable_reg = 0x2900,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_hs1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_rcg usb_fs1_xcvr_clk_src = {
++ .ns_reg = 0x2968,
++ .md_reg = 0x2964,
++ .mn = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .p = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .s = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll0,
++ },
++ .freq_tbl = clk_tbl_usb,
++ .clkr = {
++ .enable_reg = 0x2968,
++ .enable_mask = BIT(11),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_fs1_xcvr_src",
++ .parent_names = gcc_pxo_pll8_pll0_map,
++ .num_parents = 3,
++ .ops = &clk_rcg_ops,
++ .flags = CLK_SET_RATE_GATE,
++ },
++ },
++};
++
++static struct clk_branch usb_fs1_xcvr_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 17,
++ .clkr = {
++ .enable_reg = 0x2968,
++ .enable_mask = BIT(9),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_fs1_xcvr_clk",
++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch usb_fs1_sys_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 18,
++ .clkr = {
++ .enable_reg = 0x296c,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_fs1_sys_clk",
++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_branch usb_fs1_h_clk = {
++ .halt_reg = 0x2fcc,
++ .halt_bit = 19,
++ .clkr = {
++ .enable_reg = 0x2960,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "usb_fs1_h_clk",
++ .ops = &clk_branch_ops,
++ .flags = CLK_IS_ROOT,
++ },
++ },
++};
++
++static struct clk_regmap *gcc_ipq806x_clks[] = {
++ [PLL3] = &pll3.clkr,
++ [PLL8] = &pll8.clkr,
++ [PLL8_VOTE] = &pll8_vote,
++ [PLL14] = &pll14.clkr,
++ [PLL14_VOTE] = &pll14_vote,
++ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
++ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
++ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
++ [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr,
++ [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr,
++ [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr,
++ [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr,
++ [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr,
++ [GSBI6_UART_SRC] = &gsbi6_uart_src.clkr,
++ [GSBI6_UART_CLK] = &gsbi6_uart_clk.clkr,
++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr,
++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr,
++ [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr,
++ [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr,
++ [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr,
++ [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr,
++ [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr,
++ [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr,
++ [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr,
++ [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr,
++ [GSBI6_QUP_SRC] = &gsbi6_qup_src.clkr,
++ [GSBI6_QUP_CLK] = &gsbi6_qup_clk.clkr,
++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr,
++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr,
++ [GP0_SRC] = &gp0_src.clkr,
++ [GP0_CLK] = &gp0_clk.clkr,
++ [GP1_SRC] = &gp1_src.clkr,
++ [GP1_CLK] = &gp1_clk.clkr,
++ [GP2_SRC] = &gp2_src.clkr,
++ [GP2_CLK] = &gp2_clk.clkr,
++ [PMEM_A_CLK] = &pmem_clk.clkr,
++ [PRNG_SRC] = &prng_src.clkr,
++ [PRNG_CLK] = &prng_clk.clkr,
++ [SDC1_SRC] = &sdc1_src.clkr,
++ [SDC1_CLK] = &sdc1_clk.clkr,
++ [SDC3_SRC] = &sdc3_src.clkr,
++ [SDC3_CLK] = &sdc3_clk.clkr,
++ [TSIF_REF_SRC] = &tsif_ref_src.clkr,
++ [TSIF_REF_CLK] = &tsif_ref_clk.clkr,
++ [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr,
++ [GSBI1_H_CLK] = &gsbi1_h_clk.clkr,
++ [GSBI2_H_CLK] = &gsbi2_h_clk.clkr,
++ [GSBI4_H_CLK] = &gsbi4_h_clk.clkr,
++ [GSBI5_H_CLK] = &gsbi5_h_clk.clkr,
++ [GSBI6_H_CLK] = &gsbi6_h_clk.clkr,
++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr,
++ [TSIF_H_CLK] = &tsif_h_clk.clkr,
++ [SDC1_H_CLK] = &sdc1_h_clk.clkr,
++ [SDC3_H_CLK] = &sdc3_h_clk.clkr,
++ [ADM0_CLK] = &adm0_clk.clkr,
++ [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr,
++ [PCIE_A_CLK] = &pcie_a_clk.clkr,
++ [PCIE_AUX_CLK] = &pcie_aux_clk.clkr,
++ [PCIE_H_CLK] = &pcie_h_clk.clkr,
++ [PCIE_PHY_CLK] = &pcie_phy_clk.clkr,
++ [SFAB_SATA_S_H_CLK] = &sfab_sata_s_h_clk.clkr,
++ [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr,
++ [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
++ [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
++ [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
++ [SATA_H_CLK] = &sata_h_clk.clkr,
++ [SATA_CLK_SRC] = &sata_ref_src.clkr,
++ [SATA_RXOOB_CLK] = &sata_rxoob_clk.clkr,
++ [SATA_PMALIVE_CLK] = &sata_pmalive_clk.clkr,
++ [SATA_PHY_REF_CLK] = &sata_phy_ref_clk.clkr,
++ [SATA_A_CLK] = &sata_a_clk.clkr,
++ [SATA_PHY_CFG_CLK] = &sata_phy_cfg_clk.clkr,
++ [PCIE_ALT_REF_SRC] = &pcie_ref_src.clkr,
++ [PCIE_ALT_REF_CLK] = &pcie_ref_src_clk.clkr,
++ [PCIE_1_A_CLK] = &pcie1_a_clk.clkr,
++ [PCIE_1_AUX_CLK] = &pcie1_aux_clk.clkr,
++ [PCIE_1_H_CLK] = &pcie1_h_clk.clkr,
++ [PCIE_1_PHY_CLK] = &pcie1_phy_clk.clkr,
++ [PCIE_1_ALT_REF_SRC] = &pcie1_ref_src.clkr,
++ [PCIE_1_ALT_REF_CLK] = &pcie1_ref_src_clk.clkr,
++ [PCIE_2_A_CLK] = &pcie2_a_clk.clkr,
++ [PCIE_2_AUX_CLK] = &pcie2_aux_clk.clkr,
++ [PCIE_2_H_CLK] = &pcie2_h_clk.clkr,
++ [PCIE_2_PHY_CLK] = &pcie2_phy_clk.clkr,
++ [PCIE_2_ALT_REF_SRC] = &pcie2_ref_src.clkr,
++ [PCIE_2_ALT_REF_CLK] = &pcie2_ref_src_clk.clkr,
++ [USB30_MASTER_SRC] = &usb30_master_clk_src.clkr,
++ [USB30_0_MASTER_CLK] = &usb30_0_branch_clk.clkr,
++ [USB30_1_MASTER_CLK] = &usb30_1_branch_clk.clkr,
++ [USB30_UTMI_SRC] = &usb30_utmi_clk.clkr,
++ [USB30_0_UTMI_CLK] = &usb30_0_utmi_clk_ctl.clkr,
++ [USB30_1_UTMI_CLK] = &usb30_1_utmi_clk_ctl.clkr,
++ [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr,
++ [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_clk_src.clkr,
++ [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr,
++ [USB_FS1_H_CLK] = &usb_fs1_h_clk.clkr,
++ [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
++ [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
++ [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
++};
++
++static const struct qcom_reset_map gcc_ipq806x_resets[] = {
++ [QDSS_STM_RESET] = { 0x2060, 6 },
++ [AFAB_SMPSS_S_RESET] = { 0x20b8, 2 },
++ [AFAB_SMPSS_M1_RESET] = { 0x20b8, 1 },
++ [AFAB_SMPSS_M0_RESET] = { 0x20b8, 0 },
++ [AFAB_EBI1_CH0_RESET] = { 0x20c0, 7 },
++ [AFAB_EBI1_CH1_RESET] = { 0x20c4, 7 },
++ [SFAB_ADM0_M0_RESET] = { 0x21e0, 7 },
++ [SFAB_ADM0_M1_RESET] = { 0x21e4, 7 },
++ [SFAB_ADM0_M2_RESET] = { 0x21e8, 7 },
++ [ADM0_C2_RESET] = { 0x220c, 4 },
++ [ADM0_C1_RESET] = { 0x220c, 3 },
++ [ADM0_C0_RESET] = { 0x220c, 2 },
++ [ADM0_PBUS_RESET] = { 0x220c, 1 },
++ [ADM0_RESET] = { 0x220c, 0 },
++ [QDSS_CLKS_SW_RESET] = { 0x2260, 5 },
++ [QDSS_POR_RESET] = { 0x2260, 4 },
++ [QDSS_TSCTR_RESET] = { 0x2260, 3 },
++ [QDSS_HRESET_RESET] = { 0x2260, 2 },
++ [QDSS_AXI_RESET] = { 0x2260, 1 },
++ [QDSS_DBG_RESET] = { 0x2260, 0 },
++ [SFAB_PCIE_M_RESET] = { 0x22d8, 1 },
++ [SFAB_PCIE_S_RESET] = { 0x22d8, 0 },
++ [PCIE_EXT_RESET] = { 0x22dc, 6 },
++ [PCIE_PHY_RESET] = { 0x22dc, 5 },
++ [PCIE_PCI_RESET] = { 0x22dc, 4 },
++ [PCIE_POR_RESET] = { 0x22dc, 3 },
++ [PCIE_HCLK_RESET] = { 0x22dc, 2 },
++ [PCIE_ACLK_RESET] = { 0x22dc, 0 },
++ [SFAB_LPASS_RESET] = { 0x23a0, 7 },
++ [SFAB_AFAB_M_RESET] = { 0x23e0, 7 },
++ [AFAB_SFAB_M0_RESET] = { 0x2420, 7 },
++ [AFAB_SFAB_M1_RESET] = { 0x2424, 7 },
++ [SFAB_SATA_S_RESET] = { 0x2480, 7 },
++ [SFAB_DFAB_M_RESET] = { 0x2500, 7 },
++ [DFAB_SFAB_M_RESET] = { 0x2520, 7 },
++ [DFAB_SWAY0_RESET] = { 0x2540, 7 },
++ [DFAB_SWAY1_RESET] = { 0x2544, 7 },
++ [DFAB_ARB0_RESET] = { 0x2560, 7 },
++ [DFAB_ARB1_RESET] = { 0x2564, 7 },
++ [PPSS_PROC_RESET] = { 0x2594, 1 },
++ [PPSS_RESET] = { 0x2594, 0 },
++ [DMA_BAM_RESET] = { 0x25c0, 7 },
++ [SPS_TIC_H_RESET] = { 0x2600, 7 },
++ [SFAB_CFPB_M_RESET] = { 0x2680, 7 },
++ [SFAB_CFPB_S_RESET] = { 0x26c0, 7 },
++ [TSIF_H_RESET] = { 0x2700, 7 },
++ [CE1_H_RESET] = { 0x2720, 7 },
++ [CE1_CORE_RESET] = { 0x2724, 7 },
++ [CE1_SLEEP_RESET] = { 0x2728, 7 },
++ [CE2_H_RESET] = { 0x2740, 7 },
++ [CE2_CORE_RESET] = { 0x2744, 7 },
++ [SFAB_SFPB_M_RESET] = { 0x2780, 7 },
++ [SFAB_SFPB_S_RESET] = { 0x27a0, 7 },
++ [RPM_PROC_RESET] = { 0x27c0, 7 },
++ [PMIC_SSBI2_RESET] = { 0x280c, 12 },
++ [SDC1_RESET] = { 0x2830, 0 },
++ [SDC2_RESET] = { 0x2850, 0 },
++ [SDC3_RESET] = { 0x2870, 0 },
++ [SDC4_RESET] = { 0x2890, 0 },
++ [USB_HS1_RESET] = { 0x2910, 0 },
++ [USB_HSIC_RESET] = { 0x2934, 0 },
++ [USB_FS1_XCVR_RESET] = { 0x2974, 1 },
++ [USB_FS1_RESET] = { 0x2974, 0 },
++ [GSBI1_RESET] = { 0x29dc, 0 },
++ [GSBI2_RESET] = { 0x29fc, 0 },
++ [GSBI3_RESET] = { 0x2a1c, 0 },
++ [GSBI4_RESET] = { 0x2a3c, 0 },
++ [GSBI5_RESET] = { 0x2a5c, 0 },
++ [GSBI6_RESET] = { 0x2a7c, 0 },
++ [GSBI7_RESET] = { 0x2a9c, 0 },
++ [SPDM_RESET] = { 0x2b6c, 0 },
++ [SEC_CTRL_RESET] = { 0x2b80, 7 },
++ [TLMM_H_RESET] = { 0x2ba0, 7 },
++ [SFAB_SATA_M_RESET] = { 0x2c18, 0 },
++ [SATA_RESET] = { 0x2c1c, 0 },
++ [TSSC_RESET] = { 0x2ca0, 7 },
++ [PDM_RESET] = { 0x2cc0, 12 },
++ [MPM_H_RESET] = { 0x2da0, 7 },
++ [MPM_RESET] = { 0x2da4, 0 },
++ [SFAB_SMPSS_S_RESET] = { 0x2e00, 7 },
++ [PRNG_RESET] = { 0x2e80, 12 },
++ [SFAB_CE3_M_RESET] = { 0x36c8, 1 },
++ [SFAB_CE3_S_RESET] = { 0x36c8, 0 },
++ [CE3_SLEEP_RESET] = { 0x36d0, 7 },
++ [PCIE_1_M_RESET] = { 0x3a98, 1 },
++ [PCIE_1_S_RESET] = { 0x3a98, 0 },
++ [PCIE_1_EXT_RESET] = { 0x3a9c, 6 },
++ [PCIE_1_PHY_RESET] = { 0x3a9c, 5 },
++ [PCIE_1_PCI_RESET] = { 0x3a9c, 4 },
++ [PCIE_1_POR_RESET] = { 0x3a9c, 3 },
++ [PCIE_1_HCLK_RESET] = { 0x3a9c, 2 },
++ [PCIE_1_ACLK_RESET] = { 0x3a9c, 0 },
++ [PCIE_2_M_RESET] = { 0x3ad8, 1 },
++ [PCIE_2_S_RESET] = { 0x3ad8, 0 },
++ [PCIE_2_EXT_RESET] = { 0x3adc, 6 },
++ [PCIE_2_PHY_RESET] = { 0x3adc, 5 },
++ [PCIE_2_PCI_RESET] = { 0x3adc, 4 },
++ [PCIE_2_POR_RESET] = { 0x3adc, 3 },
++ [PCIE_2_HCLK_RESET] = { 0x3adc, 2 },
++ [PCIE_2_ACLK_RESET] = { 0x3adc, 0 },
++ [SFAB_USB30_S_RESET] = { 0x3b54, 1 },
++ [SFAB_USB30_M_RESET] = { 0x3b54, 0 },
++ [USB30_0_PORT2_HS_PHY_RESET] = { 0x3b50, 5 },
++ [USB30_0_MASTER_RESET] = { 0x3b50, 4 },
++ [USB30_0_SLEEP_RESET] = { 0x3b50, 3 },
++ [USB30_0_UTMI_PHY_RESET] = { 0x3b50, 2 },
++ [USB30_0_POWERON_RESET] = { 0x3b50, 1 },
++ [USB30_0_PHY_RESET] = { 0x3b50, 0 },
++ [USB30_1_MASTER_RESET] = { 0x3b58, 4 },
++ [USB30_1_SLEEP_RESET] = { 0x3b58, 3 },
++ [USB30_1_UTMI_PHY_RESET] = { 0x3b58, 2 },
++ [USB30_1_POWERON_RESET] = { 0x3b58, 1 },
++ [USB30_1_PHY_RESET] = { 0x3b58, 0 },
++ [NSSFB0_RESET] = { 0x3b60, 6 },
++ [NSSFB1_RESET] = { 0x3b60, 7 },
++};
++
++static const struct regmap_config gcc_ipq806x_regmap_config = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = 0x3e40,
++ .fast_io = true,
++};
++
++static const struct qcom_cc_desc gcc_ipq806x_desc = {
++ .config = &gcc_ipq806x_regmap_config,
++ .clks = gcc_ipq806x_clks,
++ .num_clks = ARRAY_SIZE(gcc_ipq806x_clks),
++ .resets = gcc_ipq806x_resets,
++ .num_resets = ARRAY_SIZE(gcc_ipq806x_resets),
++};
++
++static const struct of_device_id gcc_ipq806x_match_table[] = {
++ { .compatible = "qcom,gcc-ipq8064" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, gcc_ipq806x_match_table);
++
++static int gcc_ipq806x_probe(struct platform_device *pdev)
++{
++ struct clk *clk;
++ struct device *dev = &pdev->dev;
++
++ /* Temporary until RPM clocks supported */
++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ return qcom_cc_probe(pdev, &gcc_ipq806x_desc);
++}
++
++static int gcc_ipq806x_remove(struct platform_device *pdev)
++{
++ qcom_cc_remove(pdev);
++ return 0;
++}
++
++static struct platform_driver gcc_ipq806x_driver = {
++ .probe = gcc_ipq806x_probe,
++ .remove = gcc_ipq806x_remove,
++ .driver = {
++ .name = "gcc-ipq806x",
++ .owner = THIS_MODULE,
++ .of_match_table = gcc_ipq806x_match_table,
++ },
++};
++
++static int __init gcc_ipq806x_init(void)
++{
++ return platform_driver_register(&gcc_ipq806x_driver);
++}
++core_initcall(gcc_ipq806x_init);
++
++static void __exit gcc_ipq806x_exit(void)
++{
++ platform_driver_unregister(&gcc_ipq806x_driver);
++}
++module_exit(gcc_ipq806x_exit);
++
++MODULE_DESCRIPTION("QCOM GCC IPQ806x Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:gcc-ipq806x");
+diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+new file mode 100644
+index 0000000..3b0f8e7
+--- /dev/null
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -0,0 +1,293 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef _DT_BINDINGS_CLK_GCC_IPQ806X_H
++#define _DT_BINDINGS_CLK_GCC_IPQ806X_H
++
++#define AFAB_CLK_SRC 0
++#define QDSS_STM_CLK 1
++#define SCSS_A_CLK 2
++#define SCSS_H_CLK 3
++#define AFAB_CORE_CLK 4
++#define SCSS_XO_SRC_CLK 5
++#define AFAB_EBI1_CH0_A_CLK 6
++#define AFAB_EBI1_CH1_A_CLK 7
++#define AFAB_AXI_S0_FCLK 8
++#define AFAB_AXI_S1_FCLK 9
++#define AFAB_AXI_S2_FCLK 10
++#define AFAB_AXI_S3_FCLK 11
++#define AFAB_AXI_S4_FCLK 12
++#define SFAB_CORE_CLK 13
++#define SFAB_AXI_S0_FCLK 14
++#define SFAB_AXI_S1_FCLK 15
++#define SFAB_AXI_S2_FCLK 16
++#define SFAB_AXI_S3_FCLK 17
++#define SFAB_AXI_S4_FCLK 18
++#define SFAB_AXI_S5_FCLK 19
++#define SFAB_AHB_S0_FCLK 20
++#define SFAB_AHB_S1_FCLK 21
++#define SFAB_AHB_S2_FCLK 22
++#define SFAB_AHB_S3_FCLK 23
++#define SFAB_AHB_S4_FCLK 24
++#define SFAB_AHB_S5_FCLK 25
++#define SFAB_AHB_S6_FCLK 26
++#define SFAB_AHB_S7_FCLK 27
++#define QDSS_AT_CLK_SRC 28
++#define QDSS_AT_CLK 29
++#define QDSS_TRACECLKIN_CLK_SRC 30
++#define QDSS_TRACECLKIN_CLK 31
++#define QDSS_TSCTR_CLK_SRC 32
++#define QDSS_TSCTR_CLK 33
++#define SFAB_ADM0_M0_A_CLK 34
++#define SFAB_ADM0_M1_A_CLK 35
++#define SFAB_ADM0_M2_H_CLK 36
++#define ADM0_CLK 37
++#define ADM0_PBUS_CLK 38
++#define IMEM0_A_CLK 39
++#define QDSS_H_CLK 40
++#define PCIE_A_CLK 41
++#define PCIE_AUX_CLK 42
++#define PCIE_H_CLK 43
++#define PCIE_PHY_CLK 44
++#define SFAB_CLK_SRC 45
++#define SFAB_LPASS_Q6_A_CLK 46
++#define SFAB_AFAB_M_A_CLK 47
++#define AFAB_SFAB_M0_A_CLK 48
++#define AFAB_SFAB_M1_A_CLK 49
++#define SFAB_SATA_S_H_CLK 50
++#define DFAB_CLK_SRC 51
++#define DFAB_CLK 52
++#define SFAB_DFAB_M_A_CLK 53
++#define DFAB_SFAB_M_A_CLK 54
++#define DFAB_SWAY0_H_CLK 55
++#define DFAB_SWAY1_H_CLK 56
++#define DFAB_ARB0_H_CLK 57
++#define DFAB_ARB1_H_CLK 58
++#define PPSS_H_CLK 59
++#define PPSS_PROC_CLK 60
++#define PPSS_TIMER0_CLK 61
++#define PPSS_TIMER1_CLK 62
++#define PMEM_A_CLK 63
++#define DMA_BAM_H_CLK 64
++#define SIC_H_CLK 65
++#define SPS_TIC_H_CLK 66
++#define CFPB_2X_CLK_SRC 67
++#define CFPB_CLK 68
++#define CFPB0_H_CLK 69
++#define CFPB1_H_CLK 70
++#define CFPB2_H_CLK 71
++#define SFAB_CFPB_M_H_CLK 72
++#define CFPB_MASTER_H_CLK 73
++#define SFAB_CFPB_S_H_CLK 74
++#define CFPB_SPLITTER_H_CLK 75
++#define TSIF_H_CLK 76
++#define TSIF_INACTIVITY_TIMERS_CLK 77
++#define TSIF_REF_SRC 78
++#define TSIF_REF_CLK 79
++#define CE1_H_CLK 80
++#define CE1_CORE_CLK 81
++#define CE1_SLEEP_CLK 82
++#define CE2_H_CLK 83
++#define CE2_CORE_CLK 84
++#define SFPB_H_CLK_SRC 85
++#define SFPB_H_CLK 86
++#define SFAB_SFPB_M_H_CLK 87
++#define SFAB_SFPB_S_H_CLK 88
++#define RPM_PROC_CLK 89
++#define RPM_BUS_H_CLK 90
++#define RPM_SLEEP_CLK 91
++#define RPM_TIMER_CLK 92
++#define RPM_MSG_RAM_H_CLK 93
++#define PMIC_ARB0_H_CLK 94
++#define PMIC_ARB1_H_CLK 95
++#define PMIC_SSBI2_SRC 96
++#define PMIC_SSBI2_CLK 97
++#define SDC1_H_CLK 98
++#define SDC2_H_CLK 99
++#define SDC3_H_CLK 100
++#define SDC4_H_CLK 101
++#define SDC1_SRC 102
++#define SDC1_CLK 103
++#define SDC2_SRC 104
++#define SDC2_CLK 105
++#define SDC3_SRC 106
++#define SDC3_CLK 107
++#define SDC4_SRC 108
++#define SDC4_CLK 109
++#define USB_HS1_H_CLK 110
++#define USB_HS1_XCVR_SRC 111
++#define USB_HS1_XCVR_CLK 112
++#define USB_HSIC_H_CLK 113
++#define USB_HSIC_XCVR_SRC 114
++#define USB_HSIC_XCVR_CLK 115
++#define USB_HSIC_SYSTEM_CLK_SRC 116
++#define USB_HSIC_SYSTEM_CLK 117
++#define CFPB0_C0_H_CLK 118
++#define CFPB0_D0_H_CLK 119
++#define CFPB0_C1_H_CLK 120
++#define CFPB0_D1_H_CLK 121
++#define USB_FS1_H_CLK 122
++#define USB_FS1_XCVR_SRC 123
++#define USB_FS1_XCVR_CLK 124
++#define USB_FS1_SYSTEM_CLK 125
++#define GSBI_COMMON_SIM_SRC 126
++#define GSBI1_H_CLK 127
++#define GSBI2_H_CLK 128
++#define GSBI3_H_CLK 129
++#define GSBI4_H_CLK 130
++#define GSBI5_H_CLK 131
++#define GSBI6_H_CLK 132
++#define GSBI7_H_CLK 133
++#define GSBI1_QUP_SRC 134
++#define GSBI1_QUP_CLK 135
++#define GSBI2_QUP_SRC 136
++#define GSBI2_QUP_CLK 137
++#define GSBI3_QUP_SRC 138
++#define GSBI3_QUP_CLK 139
++#define GSBI4_QUP_SRC 140
++#define GSBI4_QUP_CLK 141
++#define GSBI5_QUP_SRC 142
++#define GSBI5_QUP_CLK 143
++#define GSBI6_QUP_SRC 144
++#define GSBI6_QUP_CLK 145
++#define GSBI7_QUP_SRC 146
++#define GSBI7_QUP_CLK 147
++#define GSBI1_UART_SRC 148
++#define GSBI1_UART_CLK 149
++#define GSBI2_UART_SRC 150
++#define GSBI2_UART_CLK 151
++#define GSBI3_UART_SRC 152
++#define GSBI3_UART_CLK 153
++#define GSBI4_UART_SRC 154
++#define GSBI4_UART_CLK 155
++#define GSBI5_UART_SRC 156
++#define GSBI5_UART_CLK 157
++#define GSBI6_UART_SRC 158
++#define GSBI6_UART_CLK 159
++#define GSBI7_UART_SRC 160
++#define GSBI7_UART_CLK 161
++#define GSBI1_SIM_CLK 162
++#define GSBI2_SIM_CLK 163
++#define GSBI3_SIM_CLK 164
++#define GSBI4_SIM_CLK 165
++#define GSBI5_SIM_CLK 166
++#define GSBI6_SIM_CLK 167
++#define GSBI7_SIM_CLK 168
++#define USB_HSIC_HSIC_CLK_SRC 169
++#define USB_HSIC_HSIC_CLK 170
++#define USB_HSIC_HSIO_CAL_CLK 171
++#define SPDM_CFG_H_CLK 172
++#define SPDM_MSTR_H_CLK 173
++#define SPDM_FF_CLK_SRC 174
++#define SPDM_FF_CLK 175
++#define SEC_CTRL_CLK 176
++#define SEC_CTRL_ACC_CLK_SRC 177
++#define SEC_CTRL_ACC_CLK 178
++#define TLMM_H_CLK 179
++#define TLMM_CLK 180
++#define SATA_H_CLK 181
++#define SATA_CLK_SRC 182
++#define SATA_RXOOB_CLK 183
++#define SATA_PMALIVE_CLK 184
++#define SATA_PHY_REF_CLK 185
++#define SATA_A_CLK 186
++#define SATA_PHY_CFG_CLK 187
++#define TSSC_CLK_SRC 188
++#define TSSC_CLK 189
++#define PDM_SRC 190
++#define PDM_CLK 191
++#define GP0_SRC 192
++#define GP0_CLK 193
++#define GP1_SRC 194
++#define GP1_CLK 195
++#define GP2_SRC 196
++#define GP2_CLK 197
++#define MPM_CLK 198
++#define EBI1_CLK_SRC 199
++#define EBI1_CH0_CLK 200
++#define EBI1_CH1_CLK 201
++#define EBI1_2X_CLK 202
++#define EBI1_CH0_DQ_CLK 203
++#define EBI1_CH1_DQ_CLK 204
++#define EBI1_CH0_CA_CLK 205
++#define EBI1_CH1_CA_CLK 206
++#define EBI1_XO_CLK 207
++#define SFAB_SMPSS_S_H_CLK 208
++#define PRNG_SRC 209
++#define PRNG_CLK 210
++#define PXO_SRC 211
++#define SPDM_CY_PORT0_CLK 212
++#define SPDM_CY_PORT1_CLK 213
++#define SPDM_CY_PORT2_CLK 214
++#define SPDM_CY_PORT3_CLK 215
++#define SPDM_CY_PORT4_CLK 216
++#define SPDM_CY_PORT5_CLK 217
++#define SPDM_CY_PORT6_CLK 218
++#define SPDM_CY_PORT7_CLK 219
++#define PLL0 220
++#define PLL0_VOTE 221
++#define PLL3 222
++#define PLL3_VOTE 223
++#define PLL4 224
++#define PLL4_VOTE 225
++#define PLL8 226
++#define PLL8_VOTE 227
++#define PLL9 228
++#define PLL10 229
++#define PLL11 230
++#define PLL12 231
++#define PLL14 232
++#define PLL14_VOTE 233
++#define PLL18 234
++#define CE5_SRC 235
++#define CE5_H_CLK 236
++#define CE5_CORE_CLK 237
++#define CE3_SLEEP_CLK 238
++#define SFAB_AHB_S8_FCLK 239
++#define SPDM_CY_PORT8_CLK 246
++#define PCIE_ALT_REF_SRC 247
++#define PCIE_ALT_REF_CLK 248
++#define PCIE_1_A_CLK 249
++#define PCIE_1_AUX_CLK 250
++#define PCIE_1_H_CLK 251
++#define PCIE_1_PHY_CLK 252
++#define PCIE_1_ALT_REF_SRC 253
++#define PCIE_1_ALT_REF_CLK 254
++#define PCIE_2_A_CLK 255
++#define PCIE_2_AUX_CLK 256
++#define PCIE_2_H_CLK 257
++#define PCIE_2_PHY_CLK 258
++#define PCIE_2_ALT_REF_SRC 259
++#define PCIE_2_ALT_REF_CLK 260
++#define EBI2_CLK 261
++#define USB30_SLEEP_CLK 262
++#define USB30_UTMI_SRC 263
++#define USB30_0_UTMI_CLK 264
++#define USB30_1_UTMI_CLK 264
++#define USB30_MASTER_SRC 265
++#define USB30_0_MASTER_CLK 266
++#define USB30_1_MASTER_CLK 267
++#define GMAC_CORE1_CLK_SRC 268
++#define GMAC_CORE2_CLK_SRC 269
++#define GMAC_CORE3_CLK_SRC 270
++#define GMAC_CORE4_CLK_SRC 271
++#define GMAC_CORE1_CLK 272
++#define GMAC_CORE2_CLK 273
++#define GMAC_CORE3_CLK 274
++#define GMAC_CORE4_CLK 275
++#define UBI32_CORE1_CLK_SRC 276
++#define UBI32_CORE2_CLK_SRC 277
++#define UBI32_CORE1_CLK 278
++#define UBI32_CORE2_CLK 279
++
++#endif
+diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
+new file mode 100644
+index 0000000..0ad5ef9
+--- /dev/null
++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
+@@ -0,0 +1,132 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef _DT_BINDINGS_RESET_IPQ_806X_H
++#define _DT_BINDINGS_RESET_IPQ_806X_H
++
++#define QDSS_STM_RESET 0
++#define AFAB_SMPSS_S_RESET 1
++#define AFAB_SMPSS_M1_RESET 2
++#define AFAB_SMPSS_M0_RESET 3
++#define AFAB_EBI1_CH0_RESET 4
++#define AFAB_EBI1_CH1_RESET 5
++#define SFAB_ADM0_M0_RESET 6
++#define SFAB_ADM0_M1_RESET 7
++#define SFAB_ADM0_M2_RESET 8
++#define ADM0_C2_RESET 9
++#define ADM0_C1_RESET 10
++#define ADM0_C0_RESET 11
++#define ADM0_PBUS_RESET 12
++#define ADM0_RESET 13
++#define QDSS_CLKS_SW_RESET 14
++#define QDSS_POR_RESET 15
++#define QDSS_TSCTR_RESET 16
++#define QDSS_HRESET_RESET 17
++#define QDSS_AXI_RESET 18
++#define QDSS_DBG_RESET 19
++#define SFAB_PCIE_M_RESET 20
++#define SFAB_PCIE_S_RESET 21
++#define PCIE_EXT_RESET 22
++#define PCIE_PHY_RESET 23
++#define PCIE_PCI_RESET 24
++#define PCIE_POR_RESET 25
++#define PCIE_HCLK_RESET 26
++#define PCIE_ACLK_RESET 27
++#define SFAB_LPASS_RESET 28
++#define SFAB_AFAB_M_RESET 29
++#define AFAB_SFAB_M0_RESET 30
++#define AFAB_SFAB_M1_RESET 31
++#define SFAB_SATA_S_RESET 32
++#define SFAB_DFAB_M_RESET 33
++#define DFAB_SFAB_M_RESET 34
++#define DFAB_SWAY0_RESET 35
++#define DFAB_SWAY1_RESET 36
++#define DFAB_ARB0_RESET 37
++#define DFAB_ARB1_RESET 38
++#define PPSS_PROC_RESET 39
++#define PPSS_RESET 40
++#define DMA_BAM_RESET 41
++#define SPS_TIC_H_RESET 42
++#define SFAB_CFPB_M_RESET 43
++#define SFAB_CFPB_S_RESET 44
++#define TSIF_H_RESET 45
++#define CE1_H_RESET 46
++#define CE1_CORE_RESET 47
++#define CE1_SLEEP_RESET 48
++#define CE2_H_RESET 49
++#define CE2_CORE_RESET 50
++#define SFAB_SFPB_M_RESET 51
++#define SFAB_SFPB_S_RESET 52
++#define RPM_PROC_RESET 53
++#define PMIC_SSBI2_RESET 54
++#define SDC1_RESET 55
++#define SDC2_RESET 56
++#define SDC3_RESET 57
++#define SDC4_RESET 58
++#define USB_HS1_RESET 59
++#define USB_HSIC_RESET 60
++#define USB_FS1_XCVR_RESET 61
++#define USB_FS1_RESET 62
++#define GSBI1_RESET 63
++#define GSBI2_RESET 64
++#define GSBI3_RESET 65
++#define GSBI4_RESET 66
++#define GSBI5_RESET 67
++#define GSBI6_RESET 68
++#define GSBI7_RESET 69
++#define SPDM_RESET 70
++#define SEC_CTRL_RESET 71
++#define TLMM_H_RESET 72
++#define SFAB_SATA_M_RESET 73
++#define SATA_RESET 74
++#define TSSC_RESET 75
++#define PDM_RESET 76
++#define MPM_H_RESET 77
++#define MPM_RESET 78
++#define SFAB_SMPSS_S_RESET 79
++#define PRNG_RESET 80
++#define SFAB_CE3_M_RESET 81
++#define SFAB_CE3_S_RESET 82
++#define CE3_SLEEP_RESET 83
++#define PCIE_1_M_RESET 84
++#define PCIE_1_S_RESET 85
++#define PCIE_1_EXT_RESET 86
++#define PCIE_1_PHY_RESET 87
++#define PCIE_1_PCI_RESET 88
++#define PCIE_1_POR_RESET 89
++#define PCIE_1_HCLK_RESET 90
++#define PCIE_1_ACLK_RESET 91
++#define PCIE_2_M_RESET 92
++#define PCIE_2_S_RESET 93
++#define PCIE_2_EXT_RESET 94
++#define PCIE_2_PHY_RESET 95
++#define PCIE_2_PCI_RESET 96
++#define PCIE_2_POR_RESET 97
++#define PCIE_2_HCLK_RESET 98
++#define PCIE_2_ACLK_RESET 99
++#define SFAB_USB30_S_RESET 100
++#define SFAB_USB30_M_RESET 101
++#define USB30_0_PORT2_HS_PHY_RESET 102
++#define USB30_0_MASTER_RESET 103
++#define USB30_0_SLEEP_RESET 104
++#define USB30_0_UTMI_PHY_RESET 105
++#define USB30_0_POWERON_RESET 106
++#define USB30_0_PHY_RESET 107
++#define USB30_1_MASTER_RESET 108
++#define USB30_1_SLEEP_RESET 109
++#define USB30_1_UTMI_PHY_RESET 110
++#define USB30_1_POWERON_RESET 111
++#define USB30_1_PHY_RESET 112
++#define NSSFB0_RESET 113
++#define NSSFB1_RESET 114
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch
new file mode 100644
index 0000000..bda4093
--- /dev/null
+++ b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch
@@ -0,0 +1,155 @@
+From b7b3ceec506179d59e46e3a8ea8b370872cc7fcb Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 31 Mar 2014 16:52:29 -0700
+Subject: [PATCH 126/182] clk: Add safe switch hook
+
+Sometimes clocks can't accept their parent source turning off
+while the source is reprogrammed to a different rate. Most
+notably CPU clocks require a way to switch away from the current
+PLL they're running on, reprogram that PLL to a new rate, and
+then switch back to the PLL with the new rate once they're done.
+Add a hook that drivers can implement allowing them to return a
+'safe parent' that they can switch their parent to while the
+upstream source is reprogrammed to support this.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/clk.c | 53 ++++++++++++++++++++++++++++++++++++------
+ include/linux/clk-private.h | 2 ++
+ include/linux/clk-provider.h | 1 +
+ 3 files changed, 49 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
+index b94a311..0582068 100644
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -1356,6 +1356,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
+ struct clk *new_parent, u8 p_index)
+ {
+ struct clk *child;
++ struct clk *parent;
+
+ clk->new_rate = new_rate;
+ clk->new_parent = new_parent;
+@@ -1365,6 +1366,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
+ if (new_parent && new_parent != clk->parent)
+ new_parent->new_child = clk;
+
++ if (clk->ops->get_safe_parent) {
++ parent = clk->ops->get_safe_parent(clk->hw);
++ if (parent) {
++ p_index = clk_fetch_parent_index(clk, parent);
++ clk->safe_parent_index = p_index;
++ clk->safe_parent = parent;
++ }
++ } else {
++ clk->safe_parent = NULL;
++ }
++
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ if (child->ops->recalc_rate)
+ child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
+@@ -1450,14 +1462,42 @@ out:
+ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
+ {
+ struct clk *child, *tmp_clk, *fail_clk = NULL;
++ struct clk *old_parent;
+ int ret = NOTIFY_DONE;
+
+- if (clk->rate == clk->new_rate)
++ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
+ return NULL;
+
++ switch (event) {
++ case PRE_RATE_CHANGE:
++ if (clk->safe_parent)
++ clk->ops->set_parent(clk->hw, clk->safe_parent_index);
++ break;
++ case POST_RATE_CHANGE:
++ if (clk->safe_parent) {
++ old_parent = __clk_set_parent_before(clk,
++ clk->new_parent);
++ if (clk->ops->set_rate_and_parent) {
++ clk->ops->set_rate_and_parent(clk->hw,
++ clk->new_rate,
++ clk->new_parent ?
++ clk->new_parent->rate : 0,
++ clk->new_parent_index);
++ } else if (clk->ops->set_parent) {
++ clk->ops->set_parent(clk->hw,
++ clk->new_parent_index);
++ }
++ __clk_set_parent_after(clk, clk->new_parent,
++ old_parent);
++ }
++ break;
++ }
++
+ if (clk->notifier_count) {
+- ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
+- if (ret & NOTIFY_STOP_MASK)
++ if (event != POST_RATE_CHANGE)
++ ret = __clk_notify(clk, event, clk->rate,
++ clk->new_rate);
++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
+ fail_clk = clk;
+ }
+
+@@ -1499,7 +1539,8 @@ static void clk_change_rate(struct clk *clk)
+ else if (clk->parent)
+ best_parent_rate = clk->parent->rate;
+
+- if (clk->new_parent && clk->new_parent != clk->parent) {
++ if (clk->new_parent && clk->new_parent != clk->parent &&
++ !clk->safe_parent) {
+ old_parent = __clk_set_parent_before(clk, clk->new_parent);
+
+ if (clk->ops->set_rate_and_parent) {
+@@ -1522,9 +1563,6 @@ static void clk_change_rate(struct clk *clk)
+ else
+ clk->rate = best_parent_rate;
+
+- if (clk->notifier_count && old_rate != clk->rate)
+- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+-
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ /* Skip children who will be reparented to another clock */
+ if (child->new_parent && child->new_parent != clk)
+@@ -1598,6 +1636,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
+ /* change the rates */
+ clk_change_rate(top);
+
++ clk_propagate_rate_change(top, POST_RATE_CHANGE);
+ out:
+ clk_prepare_unlock();
+
+diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
+index efbf70b..f48684a 100644
+--- a/include/linux/clk-private.h
++++ b/include/linux/clk-private.h
+@@ -38,8 +38,10 @@ struct clk {
+ struct clk **parents;
+ u8 num_parents;
+ u8 new_parent_index;
++ u8 safe_parent_index;
+ unsigned long rate;
+ unsigned long new_rate;
++ struct clk *safe_parent;
+ struct clk *new_parent;
+ struct clk *new_child;
+ unsigned long flags;
+diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
+index 939533d..300fcb8 100644
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -157,6 +157,7 @@ struct clk_ops {
+ struct clk **best_parent_clk);
+ int (*set_parent)(struct clk_hw *hw, u8 index);
+ u8 (*get_parent)(struct clk_hw *hw);
++ struct clk *(*get_safe_parent)(struct clk_hw *hw);
+ int (*set_rate)(struct clk_hw *hw, unsigned long,
+ unsigned long);
+ int (*set_rate_and_parent)(struct clk_hw *hw,
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch
new file mode 100644
index 0000000..2983b84
--- /dev/null
+++ b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch
@@ -0,0 +1,155 @@
+From fd06a2cc719296f65a280cb1533b125f63cfcb34 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 28 Apr 2014 15:58:11 -0700
+Subject: [PATCH 127/182] clk: qcom: Add support for setting rates on PLLs
+
+Some PLLs may require changing their rate at runtime. Add support
+for these PLLs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/clk-pll.c | 68 +++++++++++++++++++++++++++++++++++++++++++-
+ drivers/clk/qcom/clk-pll.h | 20 +++++++++++++
+ 2 files changed, 87 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c
+index 0f927c5..80c7a76 100644
+--- a/drivers/clk/qcom/clk-pll.c
++++ b/drivers/clk/qcom/clk-pll.c
+@@ -97,7 +97,7 @@ static unsigned long
+ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+ {
+ struct clk_pll *pll = to_clk_pll(hw);
+- u32 l, m, n;
++ u32 l, m, n, config;
+ unsigned long rate;
+ u64 tmp;
+
+@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+ do_div(tmp, n);
+ rate += tmp;
+ }
++ if (pll->post_div_width) {
++ regmap_read(pll->clkr.regmap, pll->config_reg, &config);
++ config >>= pll->post_div_shift;
++ config &= BIT(pll->post_div_width) - 1;
++ rate /= config + 1;
++ }
++
+ return rate;
+ }
+
++static const
++struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
++{
++ if (!f)
++ return NULL;
++
++ for (; f->freq; f++)
++ if (rate <= f->freq)
++ return f;
++
++ return NULL;
++}
++
++static long
++clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *p_rate, struct clk **p)
++{
++ struct clk_pll *pll = to_clk_pll(hw);
++ const struct pll_freq_tbl *f;
++
++ f = find_freq(pll->freq_tbl, rate);
++ if (!f)
++ return clk_pll_recalc_rate(hw, *p_rate);
++
++ return f->freq;
++}
++
++static int
++clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
++{
++ struct clk_pll *pll = to_clk_pll(hw);
++ const struct pll_freq_tbl *f;
++ bool enabled;
++ u32 mode;
++ u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
++
++ f = find_freq(pll->freq_tbl, rate);
++ if (!f)
++ return -EINVAL;
++
++ regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
++ enabled = (mode & enable_mask) == enable_mask;
++
++ if (enabled)
++ clk_pll_disable(hw);
++
++ regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
++ regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
++ regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
++ regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
++
++ if (enabled)
++ clk_pll_enable(hw);
++
++ return 0;
++}
++
+ const struct clk_ops clk_pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
++ .determine_rate = clk_pll_determine_rate,
++ .set_rate = clk_pll_set_rate,
+ };
+ EXPORT_SYMBOL_GPL(clk_pll_ops);
+
+diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h
+index 0775a99..5f9928b 100644
+--- a/drivers/clk/qcom/clk-pll.h
++++ b/drivers/clk/qcom/clk-pll.h
+@@ -18,6 +18,21 @@
+ #include "clk-regmap.h"
+
+ /**
++ * struct pll_freq_tbl - PLL frequency table
++ * @l: L value
++ * @m: M value
++ * @n: N value
++ * @ibits: internal values
++ */
++struct pll_freq_tbl {
++ unsigned long freq;
++ u16 l;
++ u16 m;
++ u16 n;
++ u32 ibits;
++};
++
++/**
+ * struct clk_pll - phase locked loop (PLL)
+ * @l_reg: L register
+ * @m_reg: M register
+@@ -26,6 +41,7 @@
+ * @mode_reg: mode register
+ * @status_reg: status register
+ * @status_bit: ANDed with @status_reg to determine if PLL is enabled
++ * @freq_tbl: PLL frequency table
+ * @hw: handle between common and hardware-specific interfaces
+ */
+ struct clk_pll {
+@@ -36,6 +52,10 @@ struct clk_pll {
+ u32 mode_reg;
+ u32 status_reg;
+ u8 status_bit;
++ u8 post_div_width;
++ u8 post_div_shift;
++
++ const struct pll_freq_tbl *freq_tbl;
+
+ struct clk_regmap clkr;
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch
new file mode 100644
index 0000000..0b9e237
--- /dev/null
+++ b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch
@@ -0,0 +1,317 @@
+From 856324d2daa3246ac62d7920d6a274e1fa35bcf5 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 28 Apr 2014 15:59:16 -0700
+Subject: [PATCH 128/182] clk: qcom: Add support for banked MD RCGs
+
+The banked MD RCGs in global clock control have a different
+register layout than the ones implemented in multimedia clock
+control. Add support for these types of clocks so we can change
+the rates of the UBI32 clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/clk-rcg.c | 99 ++++++++++++++++++++-------------------
+ drivers/clk/qcom/clk-rcg.h | 5 +-
+ drivers/clk/qcom/mmcc-msm8960.c | 24 +++++++---
+ 3 files changed, 73 insertions(+), 55 deletions(-)
+
+diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c
+index abfc2b6..7bce729 100644
+--- a/drivers/clk/qcom/clk-rcg.c
++++ b/drivers/clk/qcom/clk-rcg.c
+@@ -67,16 +67,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
+ {
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+- u32 ns, ctl;
++ u32 ns, reg;
+ int bank;
+ int i;
+ struct src_sel *s;
+
+- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
+- bank = reg_to_bank(rcg, ctl);
++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
++ bank = reg_to_bank(rcg, reg);
+ s = &rcg->s[bank];
+
+- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+ ns = ns_to_src(s, ns);
+
+ for (i = 0; i < num_parents; i++)
+@@ -192,90 +192,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
+
+ static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
+ {
+- u32 ns, md, ctl, *regp;
++ u32 ns, md, reg;
+ int bank, new_bank;
+ struct mn *mn;
+ struct pre_div *p;
+ struct src_sel *s;
+ bool enabled;
+- u32 md_reg;
+- u32 bank_reg;
++ u32 md_reg, ns_reg;
+ bool banked_mn = !!rcg->mn[1].width;
++ bool banked_p = !!rcg->p[1].pre_div_width;
+ struct clk_hw *hw = &rcg->clkr.hw;
+
+ enabled = __clk_is_enabled(hw->clk);
+
+- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
+-
+- if (banked_mn) {
+- regp = &ctl;
+- bank_reg = rcg->clkr.enable_reg;
+- } else {
+- regp = &ns;
+- bank_reg = rcg->ns_reg;
+- }
+-
+- bank = reg_to_bank(rcg, *regp);
++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
++ bank = reg_to_bank(rcg, reg);
+ new_bank = enabled ? !bank : bank;
+
++ ns_reg = rcg->ns_reg[new_bank];
++ regmap_read(rcg->clkr.regmap, ns_reg, &ns);
++
+ if (banked_mn) {
+ mn = &rcg->mn[new_bank];
+ md_reg = rcg->md_reg[new_bank];
+
+ ns |= BIT(mn->mnctr_reset_bit);
+- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
++ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+
+ regmap_read(rcg->clkr.regmap, md_reg, &md);
+ md = mn_to_md(mn, f->m, f->n, md);
+ regmap_write(rcg->clkr.regmap, md_reg, md);
+
+ ns = mn_to_ns(mn, f->m, f->n, ns);
+- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
++ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+
+- ctl = mn_to_reg(mn, f->m, f->n, ctl);
+- regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
++ /* Two NS registers means mode control is in NS register */
++ if (rcg->ns_reg[0] != rcg->ns_reg[1]) {
++ ns = mn_to_reg(mn, f->m, f->n, ns);
++ regmap_write(rcg->clkr.regmap, ns_reg, ns);
++ } else {
++ reg = mn_to_reg(mn, f->m, f->n, reg);
++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
++ }
+
+ ns &= ~BIT(mn->mnctr_reset_bit);
+- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+- } else {
++ regmap_write(rcg->clkr.regmap, ns_reg, ns);
++ }
++
++ if (banked_p) {
+ p = &rcg->p[new_bank];
+ ns = pre_div_to_ns(p, f->pre_div - 1, ns);
+ }
+
+ s = &rcg->s[new_bank];
+ ns = src_to_ns(s, s->parent_map[f->src], ns);
+- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
++ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+
+ if (enabled) {
+- *regp ^= BIT(rcg->mux_sel_bit);
+- regmap_write(rcg->clkr.regmap, bank_reg, *regp);
++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
++ reg ^= BIT(rcg->mux_sel_bit);
++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
+ }
+ }
+
+ static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
+ {
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+- u32 ns, ctl, md, reg;
++ u32 ns, md, reg;
+ int bank;
+ struct freq_tbl f = { 0 };
+ bool banked_mn = !!rcg->mn[1].width;
++ bool banked_p = !!rcg->p[1].pre_div_width;
+
+- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
+- reg = banked_mn ? ctl : ns;
+-
++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
+
++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
++
+ if (banked_mn) {
+ regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
+ f.m = md_to_m(&rcg->mn[bank], md);
+ f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
+- } else {
+- f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
+ }
+- f.src = index;
+
++ if (banked_p)
++ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
++
++ f.src = index;
+ configure_bank(rcg, &f);
+
+ return 0;
+@@ -336,28 +339,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+ u32 m, n, pre_div, ns, md, mode, reg;
+ int bank;
+ struct mn *mn;
++ bool banked_p = !!rcg->p[1].pre_div_width;
+ bool banked_mn = !!rcg->mn[1].width;
+
+- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+-
+- if (banked_mn)
+- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &reg);
+- else
+- reg = ns;
+-
++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
+
++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
++ m = n = pre_div = mode = 0;
++
+ if (banked_mn) {
+ mn = &rcg->mn[bank];
+ regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
+ m = md_to_m(mn, md);
+ n = ns_m_to_n(mn, ns, m);
++ /* Two NS registers means mode control is in NS register */
++ if (rcg->ns_reg[0] != rcg->ns_reg[1])
++ reg = ns;
+ mode = reg_to_mnctr_mode(mn, reg);
+- return calc_rate(parent_rate, m, n, mode, 0);
+- } else {
+- pre_div = ns_to_pre_div(&rcg->p[bank], ns);
+- return calc_rate(parent_rate, 0, 0, 0, pre_div);
+ }
++
++ if (banked_p)
++ pre_div = ns_to_pre_div(&rcg->p[bank], ns);
++
++ return calc_rate(parent_rate, m, n, mode, pre_div);
+ }
+
+ static const
+diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
+index b9ec11d..5f8b06d 100644
+--- a/drivers/clk/qcom/clk-rcg.h
++++ b/drivers/clk/qcom/clk-rcg.h
+@@ -102,7 +102,7 @@ extern const struct clk_ops clk_rcg_ops;
+ * struct clk_dyn_rcg - root clock generator with glitch free mux
+ *
+ * @mux_sel_bit: bit to switch glitch free mux
+- * @ns_reg: NS register
++ * @ns_reg: NS0 and NS1 register
+ * @md_reg: MD0 and MD1 register
+ * @mn: mn counter (banked)
+ * @s: source selector (banked)
+@@ -112,8 +112,9 @@ extern const struct clk_ops clk_rcg_ops;
+ *
+ */
+ struct clk_dyn_rcg {
+- u32 ns_reg;
++ u32 ns_reg[2];
+ u32 md_reg[2];
++ u32 bank_reg;
+
+ u8 mux_sel_bit;
+
+diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c
+index 12f3c0b..ce48ad1 100644
+--- a/drivers/clk/qcom/mmcc-msm8960.c
++++ b/drivers/clk/qcom/mmcc-msm8960.c
+@@ -726,9 +726,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = {
+ };
+
+ static struct clk_dyn_rcg gfx2d0_src = {
+- .ns_reg = 0x0070,
++ .ns_reg[0] = 0x0070,
++ .ns_reg[1] = 0x0070,
+ .md_reg[0] = 0x0064,
+ .md_reg[1] = 0x0068,
++ .bank_reg = 0x0060,
+ .mn[0] = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 25,
+@@ -784,9 +786,11 @@ static struct clk_branch gfx2d0_clk = {
+ };
+
+ static struct clk_dyn_rcg gfx2d1_src = {
+- .ns_reg = 0x007c,
++ .ns_reg[0] = 0x007c,
++ .ns_reg[1] = 0x007c,
+ .md_reg[0] = 0x0078,
+ .md_reg[1] = 0x006c,
++ .bank_reg = 0x0074,
+ .mn[0] = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 25,
+@@ -862,9 +866,11 @@ static struct freq_tbl clk_tbl_gfx3d[] = {
+ };
+
+ static struct clk_dyn_rcg gfx3d_src = {
+- .ns_reg = 0x008c,
++ .ns_reg[0] = 0x008c,
++ .ns_reg[1] = 0x008c,
+ .md_reg[0] = 0x0084,
+ .md_reg[1] = 0x0088,
++ .bank_reg = 0x0080,
+ .mn[0] = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 25,
+@@ -1051,9 +1057,11 @@ static struct freq_tbl clk_tbl_mdp[] = {
+ };
+
+ static struct clk_dyn_rcg mdp_src = {
+- .ns_reg = 0x00d0,
++ .ns_reg[0] = 0x00d0,
++ .ns_reg[1] = 0x00d0,
+ .md_reg[0] = 0x00c4,
+ .md_reg[1] = 0x00c8,
++ .bank_reg = 0x00c0,
+ .mn[0] = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 31,
+@@ -1158,7 +1166,9 @@ static struct freq_tbl clk_tbl_rot[] = {
+ };
+
+ static struct clk_dyn_rcg rot_src = {
+- .ns_reg = 0x00e8,
++ .ns_reg[0] = 0x00e8,
++ .ns_reg[1] = 0x00e8,
++ .bank_reg = 0x00e8,
+ .p[0] = {
+ .pre_div_shift = 22,
+ .pre_div_width = 4,
+@@ -1355,9 +1365,11 @@ static struct freq_tbl clk_tbl_vcodec[] = {
+ };
+
+ static struct clk_dyn_rcg vcodec_src = {
+- .ns_reg = 0x0100,
++ .ns_reg[0] = 0x0100,
++ .ns_reg[1] = 0x0100,
+ .md_reg[0] = 0x00fc,
+ .md_reg[1] = 0x0128,
++ .bank_reg = 0x00f8,
+ .mn[0] = {
+ .mnctr_en_bit = 5,
+ .mnctr_reset_bit = 31,
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
new file mode 100644
index 0000000..4c59580
--- /dev/null
+++ b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
@@ -0,0 +1,869 @@
+From 92fde23153240a6173645dabe4d99f4aa6570bb7 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 28 Apr 2014 16:01:03 -0700
+Subject: [PATCH 129/182] clk: qcom: Add support for NSS/GMAC clocks and
+ resets
+
+The NSS driver expects one virtual clock that actually represents
+two clocks (one for each UBI32 core). Register the ubi32 core
+clocks and also make a wrapper virtual clock called nss_core_clk
+to be used by the driver. This will properly handle switching the
+rates of both clocks at the same time like how the NSS driver
+expects it. Also add the TCM clock and the NSS resets.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 710 +++++++++++++++++++++++++-
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 3 +
+ include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++
+ 3 files changed, 755 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
+index 278c5fe..f7916be 100644
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -32,6 +32,33 @@
+ #include "clk-branch.h"
+ #include "reset.h"
+
++static struct clk_pll pll0 = {
++ .l_reg = 0x30c4,
++ .m_reg = 0x30c8,
++ .n_reg = 0x30cc,
++ .config_reg = 0x30d4,
++ .mode_reg = 0x30c0,
++ .status_reg = 0x30d8,
++ .status_bit = 16,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll0",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
++static struct clk_regmap pll0_vote = {
++ .enable_reg = 0x34c0,
++ .enable_mask = BIT(0),
++ .hw.init = &(struct clk_init_data){
++ .name = "pll0_vote",
++ .parent_names = (const char *[]){ "pll0" },
++ .num_parents = 1,
++ .ops = &clk_pll_vote_ops,
++ },
++};
++
+ static struct clk_pll pll3 = {
+ .l_reg = 0x3164,
+ .m_reg = 0x3168,
+@@ -102,11 +129,46 @@ static struct clk_regmap pll14_vote = {
+ },
+ };
+
++#define NSS_PLL_RATE(f, _l, _m, _n, i) \
++ { \
++ .freq = f, \
++ .l = _l, \
++ .m = _m, \
++ .n = _n, \
++ .ibits = i, \
++ }
++
++static struct pll_freq_tbl pll18_freq_tbl[] = {
++ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625),
++ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625),
++};
++
++static struct clk_pll pll18 = {
++ .l_reg = 0x31a4,
++ .m_reg = 0x31a8,
++ .n_reg = 0x31ac,
++ .config_reg = 0x31b4,
++ .mode_reg = 0x31a0,
++ .status_reg = 0x31b8,
++ .status_bit = 16,
++ .post_div_shift = 16,
++ .post_div_width = 1,
++ .freq_tbl = pll18_freq_tbl,
++ .clkr.hw.init = &(struct clk_init_data){
++ .name = "pll18",
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .ops = &clk_pll_ops,
++ },
++};
++
+ #define P_PXO 0
+ #define P_PLL8 1
+ #define P_PLL3 1
+ #define P_PLL0 2
+ #define P_CXO 2
++#define P_PLL14 3
++#define P_PLL18 4
+
+ static const u8 gcc_pxo_pll8_map[] = {
+ [P_PXO] = 0,
+@@ -157,6 +219,22 @@ static const char *gcc_pxo_pll8_pll0_map[] = {
+ "pll0",
+ };
+
++static const u8 gcc_pxo_pll8_pll14_pll18_pll0_map[] = {
++ [P_PXO] = 0,
++ [P_PLL8] = 4,
++ [P_PLL0] = 2,
++ [P_PLL14] = 5,
++ [P_PLL18] = 1,
++};
++
++static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = {
++ "pxo",
++ "pll8_vote",
++ "pll0_vote",
++ "pll14",
++ "pll18",
++};
++
+ static struct freq_tbl clk_tbl_gsbi_uart[] = {
+ { 1843200, P_PLL8, 2, 6, 625 },
+ { 3686400, P_PLL8, 2, 12, 625 },
+@@ -2132,12 +2210,567 @@ static struct clk_branch usb_fs1_h_clk = {
+ },
+ };
+
++static const struct freq_tbl clk_tbl_gmac[] = {
++ { 133000000, P_PLL0, 1, 50, 301 },
++ { }
++};
++
++static struct clk_dyn_rcg gmac_core1_src = {
++ .ns_reg[0] = 0x3cac,
++ .ns_reg[1] = 0x3cb0,
++ .md_reg[0] = 0x3ca4,
++ .md_reg[1] = 0x3ca8,
++ .bank_reg = 0x3ca0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3ca0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core1_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core1_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 4,
++ .hwcg_reg = 0x3cb4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cb4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core1_clk",
++ .parent_names = (const char *[]){
++ "gmac_core1_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core2_src = {
++ .ns_reg[0] = 0x3ccc,
++ .ns_reg[1] = 0x3cd0,
++ .md_reg[0] = 0x3cc4,
++ .md_reg[1] = 0x3cc8,
++ .bank_reg = 0x3ca0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3cc0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core2_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core2_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 5,
++ .hwcg_reg = 0x3cd4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cd4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core2_clk",
++ .parent_names = (const char *[]){
++ "gmac_core2_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core3_src = {
++ .ns_reg[0] = 0x3cec,
++ .ns_reg[1] = 0x3cf0,
++ .md_reg[0] = 0x3ce4,
++ .md_reg[1] = 0x3ce8,
++ .bank_reg = 0x3ce0,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3ce0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core3_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core3_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 6,
++ .hwcg_reg = 0x3cf4,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3cf4,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core3_clk",
++ .parent_names = (const char *[]){
++ "gmac_core3_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static struct clk_dyn_rcg gmac_core4_src = {
++ .ns_reg[0] = 0x3d0c,
++ .ns_reg[1] = 0x3d10,
++ .md_reg[0] = 0x3d04,
++ .md_reg[1] = 0x3d08,
++ .bank_reg = 0x3d00,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_gmac,
++ .clkr = {
++ .enable_reg = 0x3d00,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core4_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch gmac_core4_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 7,
++ .hwcg_reg = 0x3d14,
++ .hwcg_bit = 6,
++ .clkr = {
++ .enable_reg = 0x3d14,
++ .enable_mask = BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "gmac_core4_clk",
++ .parent_names = (const char *[]){
++ "gmac_core4_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_nss_tcm[] = {
++ { 266000000, P_PLL0, 3, 0, 0 },
++ { 400000000, P_PLL0, 2, 0, 0 },
++ { }
++};
++
++static struct clk_dyn_rcg nss_tcm_src = {
++ .ns_reg[0] = 0x3dc4,
++ .ns_reg[1] = 0x3dc8,
++ .bank_reg = 0x3dc0,
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 4,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss_tcm,
++ .clkr = {
++ .enable_reg = 0x3dc0,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "nss_tcm_src",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ },
++ },
++};
++
++static struct clk_branch nss_tcm_clk = {
++ .halt_reg = 0x3c20,
++ .halt_bit = 14,
++ .clkr = {
++ .enable_reg = 0x3dd0,
++ .enable_mask = BIT(6) | BIT(4),
++ .hw.init = &(struct clk_init_data){
++ .name = "nss_tcm_clk",
++ .parent_names = (const char *[]){
++ "nss_tcm_src",
++ },
++ .num_parents = 1,
++ .ops = &clk_branch_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++ },
++};
++
++static const struct freq_tbl clk_tbl_nss[] = {
++ { 110000000, P_PLL18, 1, 1, 5 },
++ { 275000000, P_PLL18, 2, 0, 0 },
++ { 550000000, P_PLL18, 1, 0, 0 },
++ { 733000000, P_PLL18, 1, 0, 0 },
++ { }
++};
++
++static struct clk_dyn_rcg ubi32_core1_src_clk = {
++ .ns_reg[0] = 0x3d2c,
++ .ns_reg[1] = 0x3d30,
++ .md_reg[0] = 0x3d24,
++ .md_reg[1] = 0x3d28,
++ .bank_reg = 0x3d20,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss,
++ .clkr = {
++ .enable_reg = 0x3d20,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "ubi32_core1_src_clk",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
++ },
++ },
++};
++
++static struct clk_dyn_rcg ubi32_core2_src_clk = {
++ .ns_reg[0] = 0x3d4c,
++ .ns_reg[1] = 0x3d50,
++ .md_reg[0] = 0x3d44,
++ .md_reg[1] = 0x3d48,
++ .bank_reg = 0x3d40,
++ .mn[0] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .mn[1] = {
++ .mnctr_en_bit = 8,
++ .mnctr_reset_bit = 7,
++ .mnctr_mode_shift = 5,
++ .n_val_shift = 16,
++ .m_val_shift = 16,
++ .width = 8,
++ },
++ .s[0] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .s[1] = {
++ .src_sel_shift = 0,
++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
++ },
++ .p[0] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .p[1] = {
++ .pre_div_shift = 3,
++ .pre_div_width = 2,
++ },
++ .mux_sel_bit = 0,
++ .freq_tbl = clk_tbl_nss,
++ .clkr = {
++ .enable_reg = 0x3d40,
++ .enable_mask = BIT(1),
++ .hw.init = &(struct clk_init_data){
++ .name = "ubi32_core2_src_clk",
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .ops = &clk_dyn_rcg_ops,
++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
++ },
++ },
++};
++
++static int nss_core_clk_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ int ret;
++
++ ret = clk_dyn_rcg_ops.set_rate(&ubi32_core1_src_clk.clkr.hw, rate,
++ parent_rate);
++ if (ret)
++ return ret;
++
++ return clk_dyn_rcg_ops.set_rate(&ubi32_core2_src_clk.clkr.hw, rate,
++ parent_rate);
++}
++
++static int
++nss_core_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate, u8 index)
++{
++ int ret;
++
++ ret = clk_dyn_rcg_ops.set_rate_and_parent(
++ &ubi32_core1_src_clk.clkr.hw, rate, parent_rate, index);
++ if (ret)
++ return ret;
++
++ ret = clk_dyn_rcg_ops.set_rate_and_parent(
++ &ubi32_core2_src_clk.clkr.hw, rate, parent_rate, index);
++ return ret;
++}
++
++static long nss_core_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *p_rate, struct clk **p)
++{
++ return clk_dyn_rcg_ops.determine_rate(&ubi32_core1_src_clk.clkr.hw,
++ rate, p_rate, p);
++}
++
++static unsigned long
++nss_core_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
++{
++ return clk_dyn_rcg_ops.recalc_rate(&ubi32_core1_src_clk.clkr.hw,
++ parent_rate);
++}
++
++static u8 nss_core_clk_get_parent(struct clk_hw *hw)
++{
++ return clk_dyn_rcg_ops.get_parent(&ubi32_core1_src_clk.clkr.hw);
++}
++
++static int nss_core_clk_set_parent(struct clk_hw *hw, u8 i)
++{
++ int ret;
++
++ ret = clk_dyn_rcg_ops.set_parent(&ubi32_core1_src_clk.clkr.hw, i);
++ if (ret)
++ return ret;
++
++ return clk_dyn_rcg_ops.set_parent(&ubi32_core2_src_clk.clkr.hw, i);
++}
++
++static struct clk *nss_core_clk_get_safe_parent(struct clk_hw *hw)
++{
++ return clk_get_parent_by_index(hw->clk,
++ ubi32_core2_src_clk.s[0].parent_map[P_PLL8]);
++}
++
++static const struct clk_ops clk_ops_nss_core = {
++ .set_rate = nss_core_clk_set_rate,
++ .set_rate_and_parent = nss_core_clk_set_rate_and_parent,
++ .determine_rate = nss_core_clk_determine_rate,
++ .recalc_rate = nss_core_clk_recalc_rate,
++ .get_parent = nss_core_clk_get_parent,
++ .set_parent = nss_core_clk_set_parent,
++ .get_safe_parent = nss_core_clk_get_safe_parent,
++};
++
++/* Virtual clock for nss core clocks */
++static struct clk_regmap nss_core_clk = {
++ .hw.init = &(struct clk_init_data){
++ .name = "nss_core_clk",
++ .ops = &clk_ops_nss_core,
++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
++ .num_parents = 5,
++ .flags = CLK_SET_RATE_PARENT,
++ },
++};
++
+ static struct clk_regmap *gcc_ipq806x_clks[] = {
++ [PLL0] = &pll0.clkr,
++ [PLL0_VOTE] = &pll0_vote,
+ [PLL3] = &pll3.clkr,
+ [PLL8] = &pll8.clkr,
+ [PLL8_VOTE] = &pll8_vote,
+ [PLL14] = &pll14.clkr,
+ [PLL14_VOTE] = &pll14_vote,
++ [PLL18] = &pll18.clkr,
+ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
+ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
+ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
+@@ -2232,6 +2865,19 @@ static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
+ [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
+ [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
++ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr,
++ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr,
++ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr,
++ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr,
++ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr,
++ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr,
++ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr,
++ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr,
++ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr,
++ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
++ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
++ [NSSTCM_CLK] = &nss_tcm_clk.clkr,
++ [NSS_CORE_CLK] = &nss_core_clk,
+ };
+
+ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
+@@ -2350,6 +2996,48 @@ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
+ [USB30_1_PHY_RESET] = { 0x3b58, 0 },
+ [NSSFB0_RESET] = { 0x3b60, 6 },
+ [NSSFB1_RESET] = { 0x3b60, 7 },
++ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3},
++ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 },
++ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 },
++ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 },
++ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 },
++ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 },
++ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 },
++ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 },
++ [GMAC_CORE1_RESET] = { 0x3cbc, 0 },
++ [GMAC_CORE2_RESET] = { 0x3cdc, 0 },
++ [GMAC_CORE3_RESET] = { 0x3cfc, 0 },
++ [GMAC_CORE4_RESET] = { 0x3d1c, 0 },
++ [GMAC_AHB_RESET] = { 0x3e24, 0 },
++ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 },
++ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 },
++ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 },
++ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 },
++ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 },
++ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 },
++ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 },
++ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 },
++ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 },
++ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 },
++ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 },
++ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 },
++ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 },
++ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 },
++ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 },
++ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 },
++ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 },
++ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 },
++ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 },
++ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 },
++ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 },
++ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 },
++ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 },
++ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 },
++ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 },
++ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 },
++ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 },
++ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 },
++ [NSS_SRDS_N_RESET] = { 0x3b60, 28 },
+ };
+
+ static const struct regmap_config gcc_ipq806x_regmap_config = {
+@@ -2378,6 +3066,8 @@ static int gcc_ipq806x_probe(struct platform_device *pdev)
+ {
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
++ struct regmap *regmap;
++ int ret;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000);
+@@ -2388,7 +3078,25 @@ static int gcc_ipq806x_probe(struct platform_device *pdev)
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+- return qcom_cc_probe(pdev, &gcc_ipq806x_desc);
++ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc);
++ if (ret)
++ return ret;
++
++ regmap = dev_get_regmap(dev, NULL);
++ if (!regmap)
++ return -ENODEV;
++
++ /* Setup PLL18 static bits */
++ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400);
++ regmap_write(regmap, 0x31b0, 0x3080);
++
++ /* Set GMAC footswitch sleep/wakeup values */
++ regmap_write(regmap, 0x3cb8, 8);
++ regmap_write(regmap, 0x3cd8, 8);
++ regmap_write(regmap, 0x3cf8, 8);
++ regmap_write(regmap, 0x3d18, 8);
++
++ return 0;
+ }
+
+ static int gcc_ipq806x_remove(struct platform_device *pdev)
+diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+index 3b0f8e7..0fd3e8a 100644
+--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -289,5 +289,8 @@
+ #define UBI32_CORE2_CLK_SRC 277
+ #define UBI32_CORE1_CLK 278
+ #define UBI32_CORE2_CLK 279
++#define NSSTCM_CLK_SRC 280
++#define NSSTCM_CLK 281
++#define NSS_CORE_CLK 282 /* Virtual */
+
+ #endif
+diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
+index 0ad5ef9..de9c814 100644
+--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
+@@ -129,4 +129,47 @@
+ #define USB30_1_PHY_RESET 112
+ #define NSSFB0_RESET 113
+ #define NSSFB1_RESET 114
++#define UBI32_CORE1_CLKRST_CLAMP_RESET 115
++#define UBI32_CORE1_CLAMP_RESET 116
++#define UBI32_CORE1_AHB_RESET 117
++#define UBI32_CORE1_AXI_RESET 118
++#define UBI32_CORE2_CLKRST_CLAMP_RESET 119
++#define UBI32_CORE2_CLAMP_RESET 120
++#define UBI32_CORE2_AHB_RESET 121
++#define UBI32_CORE2_AXI_RESET 122
++#define GMAC_CORE1_RESET 123
++#define GMAC_CORE2_RESET 124
++#define GMAC_CORE3_RESET 125
++#define GMAC_CORE4_RESET 126
++#define GMAC_AHB_RESET 127
++#define NSS_CH0_RST_RX_CLK_N_RESET 128
++#define NSS_CH0_RST_TX_CLK_N_RESET 129
++#define NSS_CH0_RST_RX_125M_N_RESET 130
++#define NSS_CH0_HW_RST_RX_125M_N_RESET 131
++#define NSS_CH0_RST_TX_125M_N_RESET 132
++#define NSS_CH1_RST_RX_CLK_N_RESET 133
++#define NSS_CH1_RST_TX_CLK_N_RESET 134
++#define NSS_CH1_RST_RX_125M_N_RESET 135
++#define NSS_CH1_HW_RST_RX_125M_N_RESET 136
++#define NSS_CH1_RST_TX_125M_N_RESET 137
++#define NSS_CH2_RST_RX_CLK_N_RESET 138
++#define NSS_CH2_RST_TX_CLK_N_RESET 139
++#define NSS_CH2_RST_RX_125M_N_RESET 140
++#define NSS_CH2_HW_RST_RX_125M_N_RESET 141
++#define NSS_CH2_RST_TX_125M_N_RESET 142
++#define NSS_CH3_RST_RX_CLK_N_RESET 143
++#define NSS_CH3_RST_TX_CLK_N_RESET 144
++#define NSS_CH3_RST_RX_125M_N_RESET 145
++#define NSS_CH3_HW_RST_RX_125M_N_RESET 146
++#define NSS_CH3_RST_TX_125M_N_RESET 147
++#define NSS_RST_RX_250M_125M_N_RESET 148
++#define NSS_RST_TX_250M_125M_N_RESET 149
++#define NSS_QSGMII_TXPI_RST_N_RESET 150
++#define NSS_QSGMII_CDR_RST_N_RESET 151
++#define NSS_SGMII2_CDR_RST_N_RESET 152
++#define NSS_SGMII3_CDR_RST_N_RESET 153
++#define NSS_CAL_PRBS_RST_N_RESET 154
++#define NSS_LCKDT_RST_N_RESET 155
++#define NSS_SRDS_N_RESET 156
++
+ #endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch
new file mode 100644
index 0000000..4c0eed8
--- /dev/null
+++ b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch
@@ -0,0 +1,269 @@
+From 1c6e51ffb10f5bf93a3018c7c1e04d7ed93f944e Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 7 Mar 2014 10:56:59 -0600
+Subject: [PATCH 130/182] ARM: qcom: Add initial IPQ8064 SoC and AP148 device
+ trees
+
+Add basic IPQ8064 SoC include device tree and support for basic booting on
+the AP148 Reference board.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 25 +++++
+ arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi | 1 +
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 176 ++++++++++++++++++++++++++++++
+ arch/arm/mach-qcom/board.c | 2 +
+ 5 files changed, 205 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064.dtsi
+
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index f2aeb95..f22c51d 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -235,6 +235,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+ qcom-apq8064-ifc6410.dtb \
+ qcom-apq8074-dragonboard.dtb \
+ qcom-apq8084-mtp.dtb \
++ qcom-ipq8064-ap148.dtb \
+ qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb
+ dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+new file mode 100644
+index 0000000..100b6eb
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -0,0 +1,25 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++/ {
++ model = "Qualcomm IPQ8064/AP148";
++ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ rsvd@41200000 {
++ reg = <0x41200000 0x300000>;
++ no-map;
++ };
++ };
++
++ soc {
++ gsbi@16300000 {
++ qcom,mode = <GSBI_PROT_I2C_UART>;
++ status = "ok";
++ serial@16340000 {
++ status = "ok";
++ };
++ };
++ };
++};
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi
+new file mode 100644
+index 0000000..7093b07
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi
+@@ -0,0 +1 @@
++#include "qcom-ipq8064.dtsi"
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+new file mode 100644
+index 0000000..952afb7
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -0,0 +1,176 @@
++/dts-v1/;
++
++#include "skeleton.dtsi"
++#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
++
++/ {
++ model = "Qualcomm IPQ8064";
++ compatible = "qcom,ipq8064";
++ interrupt-parent = <&intc>;
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cpu@0 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <0>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc0>;
++ qcom,saw = <&saw0>;
++ };
++
++ cpu@1 {
++ compatible = "qcom,krait";
++ enable-method = "qcom,kpss-acc-v1";
++ device_type = "cpu";
++ reg = <1>;
++ next-level-cache = <&L2>;
++ qcom,acc = <&acc1>;
++ qcom,saw = <&saw1>;
++ };
++
++ L2: l2-cache {
++ compatible = "cache";
++ cache-level = <2>;
++ };
++ };
++
++ cpu-pmu {
++ compatible = "qcom,krait-pmu";
++ interrupts = <1 10 0x304>;
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ nss@40000000 {
++ reg = <0x40000000 0x1000000>;
++ no-map;
++ };
++
++ smem@41000000 {
++ reg = <0x41000000 0x200000>;
++ no-map;
++ };
++ };
++
++ soc: soc {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ compatible = "simple-bus";
++
++ qcom_pinmux: pinmux@800000 {
++ compatible = "qcom,ipq8064-pinctrl";
++ reg = <0x800000 0x4000>;
++
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ interrupts = <0 32 0x4>;
++ };
++
++ intc: interrupt-controller@2000000 {
++ compatible = "qcom,msm-qgic2";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ reg = <0x02000000 0x1000>,
++ <0x02002000 0x1000>;
++ };
++
++ timer@200a000 {
++ compatible = "qcom,kpss-timer", "qcom,msm-timer";
++ interrupts = <1 1 0x301>,
++ <1 2 0x301>,
++ <1 3 0x301>;
++ reg = <0x0200a000 0x100>;
++ clock-frequency = <25000000>,
++ <32768>;
++ cpu-offset = <0x80000>;
++ };
++
++ acc0: clock-controller@2088000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ acc1: clock-controller@2098000 {
++ compatible = "qcom,kpss-acc-v1";
++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ };
++
++ saw0: regulator@2089000 {
++ compatible = "qcom,saw2";
++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ saw1: regulator@2099000 {
++ compatible = "qcom,saw2";
++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
++ regulator;
++ };
++
++ gsbi2: gsbi@12480000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x12480000 0x100>;
++ clocks = <&gcc GSBI2_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ status = "disabled";
++
++ serial@12490000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x12490000 0x1000>,
++ <0x12480000 0x1000>;
++ interrupts = <0 195 0x0>;
++ clocks = <&gcc GSBI2_UART_CLK>, <&gcc GSBI2_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++ };
++
++ gsbi4: gsbi@16300000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x16300000 0x100>;
++ clocks = <&gcc GSBI4_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ status = "disabled";
++
++ serial@16340000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x16340000 0x1000>,
++ <0x16300000 0x1000>;
++ interrupts = <0 152 0x0>;
++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++ };
++
++ qcom,ssbi@500000 {
++ compatible = "qcom,ssbi";
++ reg = <0x00500000 0x1000>;
++ qcom,controller-type = "pmic-arbiter";
++ };
++
++ gcc: clock-controller@900000 {
++ compatible = "qcom,gcc-ipq8064";
++ reg = <0x00900000 0x4000>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++ };
++};
+diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
+index c437a99..6d8bbf7 100644
+--- a/arch/arm/mach-qcom/board.c
++++ b/arch/arm/mach-qcom/board.c
+@@ -18,6 +18,8 @@ static const char * const qcom_dt_match[] __initconst = {
+ "qcom,apq8064",
+ "qcom,apq8074-dragonboard",
+ "qcom,apq8084",
++ "qcom,ipq8062",
++ "qcom,ipq8064",
+ "qcom,msm8660-surf",
+ "qcom,msm8960-cdp",
+ NULL
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch
new file mode 100644
index 0000000..8b20c4b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch
@@ -0,0 +1,25 @@
+From df1564829c4abe206d354f7c1b745cc1e864f4bd Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 11 Apr 2014 14:18:29 -0500
+Subject: [PATCH 131/182] ARM: qcom: config: Enable IPQ806x support
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 42ebd72..1752a4d 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -133,6 +133,7 @@ CONFIG_QCOM_BAM_DMA=y
+ CONFIG_STAGING=y
+ CONFIG_QCOM_GSBI=y
+ CONFIG_COMMON_CLK_QCOM=y
++CONFIG_IPQ_GCC_806X=y
+ CONFIG_MSM_GCC_8660=y
+ CONFIG_MSM_MMCC_8960=y
+ CONFIG_MSM_MMCC_8974=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch
new file mode 100644
index 0000000..c94c75a
--- /dev/null
+++ b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch
@@ -0,0 +1,55 @@
+From 776fff11c1bf57e564a386521e56c277287a568c Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 8 May 2014 13:40:16 -0500
+Subject: [PATCH 132/182] XXX: Add boot support for u-boot.ipq image
+
+---
+ arch/arm/Makefile | 2 +-
+ arch/arm/boot/Makefile | 11 ++++++++++-
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/Makefile b/arch/arm/Makefile
+index 51e5bed..e9fc731 100644
+--- a/arch/arm/Makefile
++++ b/arch/arm/Makefile
+@@ -297,7 +297,7 @@ archprepare:
+ # Convert bzImage to zImage
+ bzImage: zImage
+
+-BOOT_TARGETS = zImage Image xipImage bootpImage uImage
++BOOT_TARGETS = zImage Image xipImage bootpImage uImage uImage.ipq Image.gz
+ INSTALL_TARGETS = zinstall uinstall install
+
+ PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS)
+diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
+index ec2f806..3cbc83e 100644
+--- a/arch/arm/boot/Makefile
++++ b/arch/arm/boot/Makefile
+@@ -25,7 +25,7 @@ INITRD_PHYS := $(initrd_phys-y)
+
+ export ZRELADDR INITRD_PHYS PARAMS_PHYS
+
+-targets := Image zImage xipImage bootpImage uImage
++targets := Image zImage xipImage bootpImage uImage uImage.ipq Image.gz
+
+ ifeq ($(CONFIG_XIP_KERNEL),y)
+
+@@ -80,6 +80,15 @@ $(obj)/uImage: $(obj)/zImage FORCE
+ $(call if_changed,uimage)
+ @$(kecho) ' Image $@ is ready'
+
++$(obj)/uImage.ipq: $(obj)/Image.gz FORCE
++ @$(check_for_multiple_loadaddr)
++ $(call if_changed,uimage,gzip)
++ @$(kecho) ' Image $@ is ready'
++
++$(obj)/Image.gz: $(obj)/Image FORCE
++ $(call if_changed,gzip)
++ @$(kecho) ' Image $@ is ready'
++
+ $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE
+ $(Q)$(MAKE) $(build)=$(obj)/bootp $@
+ @:
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch
new file mode 100644
index 0000000..3881064
--- /dev/null
+++ b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch
@@ -0,0 +1,90 @@
+From 9bc674f40f22596ef8c2ff6d7f9e53da0baa57e9 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 12 Jun 2014 14:34:10 -0500
+Subject: [PATCH 133/182] spi: qup: Remove chip select function
+
+This patch removes the chip select function. Chip select should instead be
+supported using GPIOs, defining the DT entry "cs-gpios", and letting the SPI
+core assert/deassert the chip select as it sees fit.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 ++++
+ drivers/spi/spi-qup.c | 33 ++++----------------
+ 2 files changed, 12 insertions(+), 27 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+index b82a268..bee6ff2 100644
+--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -23,6 +23,12 @@ Optional properties:
+ - spi-max-frequency: Specifies maximum SPI clock frequency,
+ Units - Hz. Definition as per
+ Documentation/devicetree/bindings/spi/spi-bus.txt
++- num-cs: total number of chipselects
++- cs-gpios: should specify GPIOs used for chipselects.
++ The gpios will be referred to as reg = <index> in the SPI child
++ nodes. If unspecified, a single SPI device without a chip
++ select can be used.
++
+
+ SPI slave nodes must be children of the SPI master node and can contain
+ properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 65bf18e..dc128ac 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -424,31 +424,6 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ return 0;
+ }
+
+-static void spi_qup_set_cs(struct spi_device *spi, bool enable)
+-{
+- struct spi_qup *controller = spi_master_get_devdata(spi->master);
+-
+- u32 iocontol, mask;
+-
+- iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
+-
+- /* Disable auto CS toggle and use manual */
+- iocontol &= ~SPI_IO_C_MX_CS_MODE;
+- iocontol |= SPI_IO_C_FORCE_CS;
+-
+- iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
+- iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select);
+-
+- mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+-
+- if (enable)
+- iocontol |= mask;
+- else
+- iocontol &= ~mask;
+-
+- writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
+-}
+-
+ static int spi_qup_transfer_one(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+@@ -571,12 +546,16 @@ static int spi_qup_probe(struct platform_device *pdev)
+ return -ENOMEM;
+ }
+
++ /* use num-cs unless not present or out of range */
++ if (of_property_read_u16(dev->of_node, "num-cs",
++ &master->num_chipselect) ||
++ (master->num_chipselect > SPI_NUM_CHIPSELECTS))
++ master->num_chipselect = SPI_NUM_CHIPSELECTS;
++
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
+- master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+ master->max_speed_hz = max_freq;
+- master->set_cs = spi_qup_set_cs;
+ master->transfer_one = spi_qup_transfer_one;
+ master->dev.of_node = pdev->dev.of_node;
+ master->auto_runtime_pm = true;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch
new file mode 100644
index 0000000..c8738cc
--- /dev/null
+++ b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch
@@ -0,0 +1,46 @@
+From a7357be131e30fd1fb987b2cb343bc81db487f96 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 12 Jun 2014 14:34:11 -0500
+Subject: [PATCH 134/182] spi: qup: Fix order of spi_register_master
+
+This patch moves the devm_spi_register_master below the initialization of the
+runtime_pm. If done in the wrong order, the spi_register_master fails if any
+probed slave devices issue SPI transactions.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+---
+ drivers/spi/spi-qup.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index dc128ac..a404298 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -619,16 +619,19 @@ static int spi_qup_probe(struct platform_device *pdev)
+ if (ret)
+ goto error;
+
+- ret = devm_spi_register_master(dev, master);
+- if (ret)
+- goto error;
+-
+ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
++
++ ret = devm_spi_register_master(dev, master);
++ if (ret)
++ goto disable_pm;
++
+ return 0;
+
++disable_pm:
++ pm_runtime_disable(&pdev->dev);
+ error:
+ clk_disable_unprepare(cclk);
+ clk_disable_unprepare(iclk);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch
new file mode 100644
index 0000000..c1ccc21
--- /dev/null
+++ b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch
@@ -0,0 +1,132 @@
+From 8b9de04ef3aaa154f30baf1ac703a2d3b474ad4e Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 12 Jun 2014 14:34:12 -0500
+Subject: [PATCH 135/182] spi: qup: Add support for v1.1.1
+
+This patch adds support for v1.1.1 of the SPI QUP controller.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 +++-
+ drivers/spi/spi-qup.c | 36 ++++++++++++--------
+ 2 files changed, 27 insertions(+), 15 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+index bee6ff2..e2c88df 100644
+--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -7,7 +7,11 @@ SPI in master mode supports up to 50MHz, up to four chip selects, programmable
+ data path from 4 bits to 32 bits and numerous protocol variants.
+
+ Required properties:
+-- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1"
++- compatible: Should contain:
++ "qcom,spi-qup-v1.1.1" for 8660, 8960 and 8064.
++ "qcom,spi-qup-v2.1.1" for 8974 and later
++ "qcom,spi-qup-v2.2.1" for 8974 v2 and later.
++
+ - reg: Should contain base register location and length
+ - interrupts: Interrupt number used by this controller
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index a404298..c137226 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -142,6 +142,7 @@ struct spi_qup {
+ int w_size; /* bytes per SPI word */
+ int tx_bytes;
+ int rx_bytes;
++ int qup_v1;
+ };
+
+
+@@ -420,7 +421,9 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ config |= QUP_CONFIG_SPI_MODE;
+ writel_relaxed(config, controller->base + QUP_CONFIG);
+
+- writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
++ /* only write to OPERATIONAL_MASK when register is present */
++ if (!controller->qup_v1)
++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
+ return 0;
+ }
+
+@@ -486,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev)
+ struct resource *res;
+ struct device *dev;
+ void __iomem *base;
+- u32 data, max_freq, iomode;
++ u32 max_freq, iomode;
+ int ret, irq, size;
+
+ dev = &pdev->dev;
+@@ -529,15 +532,6 @@ static int spi_qup_probe(struct platform_device *pdev)
+ return ret;
+ }
+
+- data = readl_relaxed(base + QUP_HW_VERSION);
+-
+- if (data < QUP_HW_VERSION_2_1_1) {
+- clk_disable_unprepare(cclk);
+- clk_disable_unprepare(iclk);
+- dev_err(dev, "v.%08x is not supported\n", data);
+- return -ENXIO;
+- }
+-
+ master = spi_alloc_master(dev, sizeof(struct spi_qup));
+ if (!master) {
+ clk_disable_unprepare(cclk);
+@@ -570,6 +564,10 @@ static int spi_qup_probe(struct platform_device *pdev)
+ controller->cclk = cclk;
+ controller->irq = irq;
+
++ /* set v1 flag if device is version 1 */
++ if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
++ controller->qup_v1 = 1;
++
+ spin_lock_init(&controller->lock);
+ init_completion(&controller->done);
+
+@@ -593,8 +591,8 @@ static int spi_qup_probe(struct platform_device *pdev)
+ size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
+ controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
+
+- dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
+- data, controller->in_blk_sz, controller->in_fifo_sz,
++ dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
++ controller->in_blk_sz, controller->in_fifo_sz,
+ controller->out_blk_sz, controller->out_fifo_sz);
+
+ writel_relaxed(1, base + QUP_SW_RESET);
+@@ -607,10 +605,19 @@ static int spi_qup_probe(struct platform_device *pdev)
+
+ writel_relaxed(0, base + QUP_OPERATIONAL);
+ writel_relaxed(0, base + QUP_IO_M_MODES);
+- writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
++
++ if (!controller->qup_v1)
++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
++
+ writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
+ base + SPI_ERROR_FLAGS_EN);
+
++ /* if earlier version of the QUP, disable INPUT_OVERRUN */
++ if (controller->qup_v1)
++ writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
++ QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
++ base + QUP_ERROR_FLAGS_EN);
++
+ writel_relaxed(0, base + SPI_CONFIG);
+ writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
+
+@@ -732,6 +739,7 @@ static int spi_qup_remove(struct platform_device *pdev)
+ }
+
+ static struct of_device_id spi_qup_dt_match[] = {
++ { .compatible = "qcom,spi-qup-v1.1.1", },
+ { .compatible = "qcom,spi-qup-v2.1.1", },
+ { .compatible = "qcom,spi-qup-v2.2.1", },
+ { }
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch
new file mode 100644
index 0000000..7c4757d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch
@@ -0,0 +1,93 @@
+From e93b9480667cbd0e3a4276e8749279693fe239f4 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Wed, 14 May 2014 22:49:03 -0500
+Subject: [PATCH 136/182] ARM: ipq8064-ap148: Add i2c pinctrl nodes
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 17 +++++++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 27 +++++++++++++++++++++++++++
+ 2 files changed, 44 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index 100b6eb..dbb546d 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -14,12 +14,29 @@
+ };
+
+ soc {
++ pinmux@800000 {
++ i2c4_pins: i2c4_pinmux {
++ pins = "gpio12", "gpio13";
++ function = "gsbi4";
++ bias-disable;
++ };
++ };
++
+ gsbi@16300000 {
+ qcom,mode = <GSBI_PROT_I2C_UART>;
+ status = "ok";
+ serial@16340000 {
+ status = "ok";
+ };
++
++ i2c4: i2c@16380000 {
++ status = "ok";
++
++ clock-frequency = <200000>;
++
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++ };
+ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 952afb7..b39c1ef 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -137,6 +137,20 @@
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
++
++ i2c@124a0000 {
++ compatible = "qcom,i2c-qup-v1.1.1";
++ reg = <0x124a0000 0x1000>;
++ interrupts = <0 196 0>;
++
++ clocks = <&gcc GSBI2_QUP_CLK>, <&gcc GSBI2_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
+ };
+
+ gsbi4: gsbi@16300000 {
+@@ -158,6 +172,19 @@
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
++
++ i2c@16380000 {
++ compatible = "qcom,i2c-qup-v1.1.1";
++ reg = <0x16380000 0x1000>;
++ interrupts = <0 153 0>;
++
++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
+ };
+
+ qcom,ssbi@500000 {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch
new file mode 100644
index 0000000..4a0385d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch
@@ -0,0 +1,131 @@
+From b9eaa80146abb09bcc7e6d8b33fca476453c839c Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Wed, 14 May 2014 22:01:16 -0500
+Subject: [PATCH 137/182] ARM: qcom-ipq8064-ap148: Add SPI related bindings
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 42 ++++++++++++++++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 47 ++++++++++++++++++++++++++++++
+ 2 files changed, 89 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index dbb546d..158a09f 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -20,6 +20,15 @@
+ function = "gsbi4";
+ bias-disable;
+ };
++
++ spi_pins: spi_pins {
++ mux {
++ pins = "gpio18", "gpio19", "gpio21";
++ function = "gsbi5";
++ drive-strength = <10>;
++ bias-none;
++ };
++ };
+ };
+
+ gsbi@16300000 {
+@@ -38,5 +47,38 @@
+ pinctrl-names = "default";
+ };
+ };
++
++ gsbi5: gsbi@1a200000 {
++ qcom,mode = <GSBI_PROT_SPI>;
++ status = "ok";
++
++ spi4: spi@1a280000 {
++ status = "ok";
++ spi-max-frequency = <50000000>;
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 0>;
++
++ flash: m25p80@0 {
++ compatible = "s25fl256s1";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <50000000>;
++ reg = <0>;
++
++ partition@0 {
++ label = "rootfs";
++ reg = <0x0 0x1000000>;
++ };
++
++ partition@1 {
++ label = "scratch";
++ reg = <0x1000000 0x1000000>;
++ };
++ };
++ };
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index b39c1ef..244f857 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -187,6 +187,53 @@
+ };
+ };
+
++ gsbi5: gsbi@1a200000 {
++ compatible = "qcom,gsbi-v1.0.0";
++ reg = <0x1a200000 0x100>;
++ clocks = <&gcc GSBI5_H_CLK>;
++ clock-names = "iface";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ status = "disabled";
++
++ serial@1a240000 {
++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
++ reg = <0x1a240000 0x1000>,
++ <0x1a200000 0x1000>;
++ interrupts = <0 154 0x0>;
++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++ };
++
++ i2c@1a280000 {
++ compatible = "qcom,i2c-qup-v1.1.1";
++ reg = <0x1a280000 0x1000>;
++ interrupts = <0 155 0>;
++
++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ spi@1a280000 {
++ compatible = "qcom,spi-qup-v1.1.1";
++ reg = <0x1a280000 0x1000>;
++ interrupts = <0 155 0>;
++
++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>;
++ clock-names = "core", "iface";
++ status = "disabled";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++ };
++
+ qcom,ssbi@500000 {
+ compatible = "qcom,ssbi";
+ reg = <0x00500000 0x1000>;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch
new file mode 100644
index 0000000..b1ff04b
--- /dev/null
+++ b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch
@@ -0,0 +1,726 @@
+From 98567c99b4dcd80fc9e5dd97229ebb9a7f6dab03 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 16 May 2014 11:53:23 -0500
+Subject: [PATCH 138/182] PCI: qcom: Add support for pcie controllers on
+ IPQ8064
+
+---
+ arch/arm/mach-qcom/Kconfig | 2 +
+ drivers/pci/host/Makefile | 1 +
+ drivers/pci/host/pci-qcom.c | 682 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 685 insertions(+)
+ create mode 100644 drivers/pci/host/pci-qcom.c
+
+diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
+index 63502cc..4d242e5 100644
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -7,6 +7,8 @@ config ARCH_QCOM
+ select GENERIC_CLOCKEVENTS
+ select HAVE_SMP
+ select PINCTRL
++ select MIGHT_HAVE_PCI
++ select PCI_DOMAINS if PCI
+ select QCOM_SCM if SMP
+ help
+ Support for Qualcomm's devicetree based systems.
+diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
+index 13fb333..be80744 100644
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
+ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
+ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
++obj-$(CONFIG_ARCH_QCOM) += pci-qcom.o
+diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c
+new file mode 100644
+index 0000000..76d7b88
+--- /dev/null
++++ b/drivers/pci/host/pci-qcom.c
+@@ -0,0 +1,682 @@
++/* Copyright (c) 2012-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.
++ */
++
++/*
++ * QCOM MSM PCIe controller driver.
++ */
++
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/gpio.h>
++#include <linux/of_gpio.h>
++#include <linux/platform_device.h>
++#include <linux/of_address.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/delay.h>
++
++/* Root Complex Port vendor/device IDs */
++#define PCIE_VENDOR_ID_RCP 0x17cb
++#define PCIE_DEVICE_ID_RCP 0x0101
++
++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
++
++#define PCIE20_PARF_PCS_DEEMPH 0x34
++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16)
++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8)
++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0)
++
++#define PCIE20_PARF_PCS_SWING 0x38
++#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8)
++#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0)
++
++#define PCIE20_PARF_PHY_CTRL 0x40
++#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16)
++#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8)
++#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4)
++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2)
++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1)
++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0)
++
++#define PCIE20_PARF_PHY_REFCLK 0x4C
++#define PCIE20_PARF_CONFIG_BITS 0x50
++
++#define PCIE20_ELBI_SYS_CTRL 0x04
++#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01
++
++#define PCIE20_CAP 0x70
++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
++
++#define PCIE20_COMMAND_STATUS 0x04
++#define PCIE20_BUSNUMBERS 0x18
++#define PCIE20_MEMORY_BASE_LIMIT 0x20
++
++#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_CTRL1 0x904
++#define PCIE20_PLR_IATU_CTRL2 0x908
++#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
++
++#define RD 0
++#define WR 1
++
++#define MAX_RC_NUM 3
++#define PCIE_BUS_PRIV_DATA(pdev) \
++ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data)
++
++/* PCIe TLP types that we are interested in */
++#define PCI_CFG0_RDWR 0x4
++#define PCI_CFG1_RDWR 0x5
++
++#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
++({ \
++ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \
++ might_sleep_if(timeout_us); \
++ for (;;) { \
++ (val) = readl(addr); \
++ if (cond) \
++ break; \
++ if (timeout_us && time_after(jiffies, timeout)) { \
++ (val) = readl(addr); \
++ break; \
++ } \
++ if (sleep_us) \
++ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
++ } \
++ (cond) ? 0 : -ETIMEDOUT; \
++})
++
++struct qcom_pcie {
++ void __iomem *elbi_base;
++ void __iomem *parf_base;
++ void __iomem *dwc_base;
++ void __iomem *cfg_base;
++ int reset_gpio;
++ struct clk *iface_clk;
++ struct clk *bus_clk;
++ struct clk *phy_clk;
++ int irq_int[4];
++ struct reset_control *axi_reset;
++ struct reset_control *ahb_reset;
++ struct reset_control *por_reset;
++ struct reset_control *pci_reset;
++ struct reset_control *phy_reset;
++
++ struct resource conf;
++ struct resource io;
++ struct resource mem;
++};
++
++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys);
++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
++ int size, u32 *val);
++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
++ int where, int size, u32 val);
++
++static struct pci_ops qcom_pcie_ops = {
++ .read = msm_pcie_rd_conf,
++ .write = msm_pcie_wr_conf,
++};
++
++static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = {
++ {
++#ifdef CONFIG_PCI_DOMAINS
++ .domain = 0,
++#endif
++ .ops = &qcom_pcie_ops,
++ .nr_controllers = 1,
++ .swizzle = pci_common_swizzle,
++ .setup = qcom_pcie_setup,
++ .map_irq = qcom_pcie_map_irq,
++ },
++ {
++#ifdef CONFIG_PCI_DOMAINS
++ .domain = 1,
++#endif
++ .ops = &qcom_pcie_ops,
++ .nr_controllers = 1,
++ .swizzle = pci_common_swizzle,
++ .setup = qcom_pcie_setup,
++ .map_irq = qcom_pcie_map_irq,
++ },
++ {
++#ifdef CONFIG_PCI_DOMAINS
++ .domain = 2,
++#endif
++ .ops = &qcom_pcie_ops,
++ .nr_controllers = 1,
++ .swizzle = pci_common_swizzle,
++ .setup = qcom_pcie_setup,
++ .map_irq = qcom_pcie_map_irq,
++ },
++};
++
++static int nr_controllers;
++static DEFINE_SPINLOCK(qcom_hw_pci_lock);
++
++static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys)
++{
++ return sys->private_data;
++}
++
++inline int is_msm_pcie_rc(struct pci_bus *bus)
++{
++ return (bus->number == 0);
++}
++
++static int qcom_pcie_is_link_up(struct qcom_pcie *dev)
++{
++ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) & BIT(29);
++}
++
++inline int msm_pcie_get_cfgtype(struct pci_bus *bus)
++{
++ /*
++ * http://www.tldp.org/LDP/tlk/dd/pci.html
++ * Pass it onto the secondary bus interface unchanged if the
++ * bus number specified is greater than the secondary bus
++ * number and less than or equal to the subordinate bus
++ * number.
++ *
++ * Read/Write to the RC and Device/Switch connected to the RC
++ * are CFG0 type transactions. Rest have to be forwarded
++ * down stream as CFG1 transactions.
++ *
++ */
++ if (bus->number == 0)
++ return PCI_CFG0_RDWR;
++
++ return PCI_CFG0_RDWR;
++}
++
++void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn)
++{
++ uint32_t bdf, cfgtype;
++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
++
++ cfgtype = msm_pcie_get_cfgtype(bus);
++
++ if (cfgtype == PCI_CFG0_RDWR) {
++ bdf = MSM_PCIE_DEV_CFG_ADDR;
++ } else {
++ /*
++ * iATU Lower Target Address Register
++ * Bits Description
++ * *-1:0 Forms bits [*:0] of the
++ * start address of the new
++ * address of the translated
++ * region. The start address
++ * must be aligned to a
++ * CX_ATU_MIN_REGION_SIZE kB
++ * boundary, so these bits are
++ * always 0. A write to this
++ * location is ignored by the
++ * PCIe core.
++ * 31:*1 Forms bits [31:*] of the of
++ * the new address of the
++ * translated region.
++ *
++ * * is log2(CX_ATU_MIN_REGION_SIZE)
++ */
++ bdf = (((bus->number & 0xff) << 24) & 0xff000000) |
++ (((devfn & 0xff) << 16) & 0x00ff0000);
++ }
++
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
++ wmb();
++
++ /* Program Bdf Address */
++ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR);
++ wmb();
++
++ /* Write Config Request Type */
++ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
++ wmb();
++}
++
++static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
++ int where, int size, u32 *val)
++{
++ uint32_t word_offset, byte_offset, mask;
++ uint32_t rd_val, wr_val;
++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
++ void __iomem *config_base;
++ int rc;
++
++ rc = is_msm_pcie_rc(bus);
++
++ /*
++ * For downstream bus, make sure link is up
++ */
++ if (rc && (devfn != 0)) {
++ *val = ~0;
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) {
++ *val = ~0;
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++
++ msm_pcie_config_cfgtype(bus, devfn);
++
++ word_offset = where & ~0x3;
++ byte_offset = where & 0x3;
++ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset);
++
++ config_base = (rc) ? dev->dwc_base : dev->cfg_base;
++ rd_val = readl_relaxed(config_base + word_offset);
++
++ if (oper == RD) {
++ *val = ((rd_val & mask) >> (8 * byte_offset));
++ } else {
++ wr_val = (rd_val & ~mask) |
++ ((*val << (8 * byte_offset)) & mask);
++ writel_relaxed(wr_val, config_base + word_offset);
++ wmb(); /* ensure config data is written to hardware register */
++ }
++
++ return 0;
++}
++
++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
++ int size, u32 *val)
++{
++ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val);
++}
++
++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
++ int where, int size, u32 val)
++{
++ /*
++ *Attempt to reset secondary bus is causing PCIE core to reset.
++ *Disable secondary bus reset functionality.
++ */
++ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) &&
++ (val & PCI_BRIDGE_CTL_BUS_RESET)) {
++ pr_info("PCIE secondary bus reset not supported\n");
++ val &= ~PCI_BRIDGE_CTL_BUS_RESET;
++ }
++
++ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
++}
++
++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev);
++
++ return pcie_dev->irq_int[pin-1];
++}
++
++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys)
++{
++ struct qcom_pcie *qcom_pcie = sys->private_data;
++
++ /*
++ * specify linux PCI framework to allocate device memory (BARs)
++ * from msm_pcie_dev.dev_mem_res resource.
++ */
++ sys->mem_offset = 0;
++ sys->io_offset = 0;
++
++ pci_add_resource(&sys->resources, &qcom_pcie->mem);
++ pci_add_resource(&sys->resources, &qcom_pcie->io);
++
++ return 1;
++}
++
++static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg)
++{
++ writel_relaxed(val, pcie->elbi_base + reg);
++}
++
++static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
++{
++ return readl_relaxed(pcie->elbi_base + reg);
++}
++
++static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg)
++{
++ writel_relaxed(val, pcie->parf_base + reg);
++}
++
++static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
++{
++ return readl_relaxed(pcie->parf_base + reg);
++}
++
++static void msm_pcie_write_mask(void __iomem *addr,
++ uint32_t clear_mask, uint32_t set_mask)
++{
++ uint32_t val;
++
++ val = (readl_relaxed(addr) & ~clear_mask) | set_mask;
++ writel_relaxed(val, addr);
++ wmb(); /* ensure data is written to hardware register */
++}
++
++static void qcom_pcie_config_controller(struct qcom_pcie *dev)
++{
++ /*
++ * program and enable address translation region 0 (device config
++ * address space); region type config;
++ * axi config address range to device config address range
++ */
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
++ /* ensure that hardware locks the region before programming it */
++ wmb();
++
++ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
++ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
++ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
++ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR,
++ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
++ /* ensure that hardware registers the configuration */
++ wmb();
++
++ /*
++ * 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_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
++ /* ensure that hardware locks the region before programming it */
++ wmb();
++
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
++ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
++ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
++ writel_relaxed(dev->mem.start,
++ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
++ /* ensure that hardware registers the configuration */
++ wmb();
++
++ /* 1K PCIE buffer setting */
++ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
++ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
++ /* ensure that hardware registers the configuration */
++ wmb();
++}
++
++static int qcom_pcie_probe(struct platform_device *pdev)
++{
++ unsigned long flags;
++ struct qcom_pcie *qcom_pcie;
++ struct device_node *np = pdev->dev.of_node;
++ struct resource *elbi_base, *parf_base, *dwc_base;
++ struct hw_pci *hw;
++ struct of_pci_range range;
++ struct of_pci_range_parser parser;
++ int ret, i;
++ u32 val;
++
++ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL);
++ if (!qcom_pcie) {
++ dev_err(&pdev->dev, "no memory for qcom_pcie\n");
++ return -ENOMEM;
++ }
++
++ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
++ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
++ if (IS_ERR(qcom_pcie->elbi_base)) {
++ dev_err(&pdev->dev, "Failed to ioremap elbi space\n");
++ return PTR_ERR(qcom_pcie->elbi_base);
++ }
++
++ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
++ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base);
++ if (IS_ERR(qcom_pcie->parf_base)) {
++ dev_err(&pdev->dev, "Failed to ioremap parf space\n");
++ return PTR_ERR(qcom_pcie->parf_base);
++ }
++
++ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
++ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base);
++ if (IS_ERR(qcom_pcie->dwc_base)) {
++ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n");
++ return PTR_ERR(qcom_pcie->dwc_base);
++ }
++
++ if (of_pci_range_parser_init(&parser, np)) {
++ dev_err(&pdev->dev, "missing ranges property\n");
++ return -EINVAL;
++ }
++
++ /* Get the I/O and memory ranges from DT */
++ for_each_of_pci_range(&parser, &range) {
++ switch (range.pci_space & 0x3) {
++ case 0: /* cfg */
++ of_pci_range_to_resource(&range, np, &qcom_pcie->conf);
++ qcom_pcie->conf.flags = IORESOURCE_MEM;
++ break;
++ case 1: /* io */
++ of_pci_range_to_resource(&range, np, &qcom_pcie->io);
++ break;
++ default: /* mem */
++ of_pci_range_to_resource(&range, np, &qcom_pcie->mem);
++ break;
++ }
++ }
++
++ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev, &qcom_pcie->conf);
++ if (IS_ERR(qcom_pcie->cfg_base)) {
++ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n");
++ return PTR_ERR(qcom_pcie->cfg_base);
++ }
++
++ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
++ if (!gpio_is_valid(qcom_pcie->reset_gpio)) {
++ dev_err(&pdev->dev, "pcie reset gpio is not valid\n");
++ return -EINVAL;
++ }
++
++ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio,
++ GPIOF_DIR_OUT, "pcie_reset");
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n");
++ return ret;
++ }
++
++ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface");
++ if (IS_ERR(qcom_pcie->iface_clk)) {
++ dev_err(&pdev->dev, "Failed to get pcie iface clock\n");
++ return PTR_ERR(qcom_pcie->iface_clk);
++ }
++
++ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy");
++ if (IS_ERR(qcom_pcie->phy_clk)) {
++ dev_err(&pdev->dev, "Failed to get pcie phy clock\n");
++ return PTR_ERR(qcom_pcie->phy_clk);
++ }
++
++ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core");
++ if (IS_ERR(qcom_pcie->bus_clk)) {
++ dev_err(&pdev->dev, "Failed to get pcie core clock\n");
++ return PTR_ERR(qcom_pcie->bus_clk);
++ }
++
++ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi");
++ if (IS_ERR(qcom_pcie->axi_reset)) {
++ dev_err(&pdev->dev, "Failed to get axi reset\n");
++ return PTR_ERR(qcom_pcie->axi_reset);
++ }
++
++ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb");
++ if (IS_ERR(qcom_pcie->ahb_reset)) {
++ dev_err(&pdev->dev, "Failed to get ahb reset\n");
++ return PTR_ERR(qcom_pcie->ahb_reset);
++ }
++
++ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por");
++ if (IS_ERR(qcom_pcie->por_reset)) {
++ dev_err(&pdev->dev, "Failed to get por reset\n");
++ return PTR_ERR(qcom_pcie->por_reset);
++ }
++
++ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci");
++ if (IS_ERR(qcom_pcie->pci_reset)) {
++ dev_err(&pdev->dev, "Failed to get pci reset\n");
++ return PTR_ERR(qcom_pcie->pci_reset);
++ }
++
++ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
++ if (IS_ERR(qcom_pcie->phy_reset)) {
++ dev_err(&pdev->dev, "Failed to get phy reset\n");
++ return PTR_ERR(qcom_pcie->phy_reset);
++ }
++
++ for (i = 0; i < 4; i++) {
++ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1);
++ if (qcom_pcie->irq_int[i] < 0) {
++ dev_err(&pdev->dev, "failed to get irq resource\n");
++ return qcom_pcie->irq_int[i];
++ }
++ }
++
++ gpio_set_value(qcom_pcie->reset_gpio, 0);
++ usleep_range(10000, 15000);
++
++ /* assert PCIe PARF reset while powering the core */
++ reset_control_assert(qcom_pcie->ahb_reset);
++
++ /* enable clocks */
++ ret = clk_prepare_enable(qcom_pcie->iface_clk);
++ if (ret)
++ return ret;
++ ret = clk_prepare_enable(qcom_pcie->phy_clk);
++ if (ret)
++ return ret;
++ ret = clk_prepare_enable(qcom_pcie->bus_clk);
++ if (ret)
++ return ret;
++
++ /*
++ * de-assert PCIe PARF reset;
++ * wait 1us before accessing PARF registers
++ */
++ reset_control_deassert(qcom_pcie->ahb_reset);
++ udelay(1);
++
++ /* enable PCIe clocks and resets */
++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
++
++ /* Set Tx Termination Offset */
++ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL);
++ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7);
++ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL);
++
++ /* PARF programming */
++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
++ PCIE20_PARF_PCS_DEEMPH);
++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) |
++ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78),
++ PCIE20_PARF_PCS_SWING);
++ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS);
++ /* ensure that hardware registers the PARF configuration */
++ wmb();
++
++ /* enable reference clock */
++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK, BIT(12), BIT(16));
++
++ /* ensure that access is enabled before proceeding */
++ wmb();
++
++ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */
++ reset_control_deassert(qcom_pcie->phy_reset);
++ reset_control_deassert(qcom_pcie->pci_reset);
++ reset_control_deassert(qcom_pcie->por_reset);
++ reset_control_deassert(qcom_pcie->axi_reset);
++
++ /* wait 150ms for clock acquisition */
++ usleep_range(10000, 15000);
++
++ /* de-assert PCIe reset link to bring EP out of reset */
++ gpio_set_value(qcom_pcie->reset_gpio, 1 - 0);
++ usleep_range(10000, 15000);
++
++ /* enable link training */
++ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL);
++ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN;
++ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL);
++ wmb();
++
++ /* poll for link to come up for upto 100ms */
++ ret = readl_poll_timeout(
++ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS),
++ val, (val & BIT(29)), 10000, 100000);
++
++ printk("link initialized %d\n", ret);
++
++ qcom_pcie_config_controller(qcom_pcie);
++
++ platform_set_drvdata(pdev, qcom_pcie);
++
++ spin_lock_irqsave(&qcom_hw_pci_lock, flags);
++ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie;
++ hw = &qcom_hw_pci[nr_controllers];
++ nr_controllers++;
++ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags);
++
++ pci_common_init(hw);
++
++ return 0;
++}
++
++static int __exit qcom_pcie_remove(struct platform_device *pdev)
++{
++ struct qcom_pcie *qcom_pcie = platform_get_drvdata(pdev);
++
++ return 0;
++}
++
++static struct of_device_id qcom_pcie_match[] = {
++ { .compatible = "qcom,pcie-ipq8064", },
++ {}
++};
++
++static struct platform_driver qcom_pcie_driver = {
++ .probe = qcom_pcie_probe,
++ .remove = qcom_pcie_remove,
++ .driver = {
++ .name = "qcom_pcie",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_pcie_match,
++ },
++};
++
++static int qcom_pcie_init(void)
++{
++ return platform_driver_register(&qcom_pcie_driver);
++}
++subsys_initcall(qcom_pcie_init);
++
++/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */
++static void msm_pcie_fixup_early(struct pci_dev *dev)
++{
++ if (dev->hdr_type == 1)
++ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
++}
++DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch
new file mode 100644
index 0000000..1105f14
--- /dev/null
+++ b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch
@@ -0,0 +1,179 @@
+From 7c6525a0d5cf88f9244187fbe8ee293fa4ee43c1 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Mon, 12 May 2014 19:36:23 -0500
+Subject: [PATCH 139/182] ARM: dts: msm: Add PCIe related nodes for
+ IPQ8064/AP148
+
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 38 ++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 93 ++++++++++++++++++++++++++++++
+ 2 files changed, 131 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index 158a09f..11f7a77 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -21,6 +21,22 @@
+ bias-disable;
+ };
+
++ pcie1_pins: pcie1_pinmux {
++ mux {
++ pins = "gpio3";
++ drive-strength = <2>;
++ bias-disable;
++ };
++ };
++
++ pcie2_pins: pcie2_pinmux {
++ mux {
++ pins = "gpio48";
++ drive-strength = <2>;
++ bias-disable;
++ };
++ };
++
+ spi_pins: spi_pins {
+ mux {
+ pins = "gpio18", "gpio19", "gpio21";
+@@ -80,5 +96,27 @@
+ };
+ };
+ };
++
++ pci@1b500000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 3 0>;
++ pinctrl-0 = <&pcie1_pins>;
++ pinctrl-names = "default";
++
++ ranges = <0x00000000 0 0x00000000 0x0ff00000 0 0x00100000 /* configuration space */
++ 0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */
++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
++ };
++
++ pci@1b700000 {
++ status = "ok";
++ reset-gpio = <&qcom_pinmux 48 0>;
++ pinctrl-0 = <&pcie2_pins>;
++ pinctrl-names = "default";
++
++ ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */
++ 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */
++ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 244f857..42a651f 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/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
+
+ / {
+@@ -246,5 +247,97 @@
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
++
++ pci@1b500000 {
++ compatible = "qcom,pcie-ipq8064";
++ reg = <0x1b500000 0x1000
++ 0x1b502000 0x80
++ 0x1b600000 0x100
++ >;
++ reg-names = "base", "elbi", "parf";
++
++ #address-cells = <3>;
++ #size-cells = <2>;
++ device_type = "pci";
++ interrupts = <0 35 0x0
++ 0 36 0x0
++ 0 37 0x0
++ 0 38 0x0
++ 0 39 0x0>;
++ 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";
++
++ clocks = <&gcc PCIE_A_CLK>,
++ <&gcc PCIE_H_CLK>,
++ <&gcc PCIE_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++ status = "disabled";
++ };
++
++ pci@1b700000 {
++ compatible = "qcom,pcie-ipq8064";
++ reg = <0x1b700000 0x1000
++ 0x1b702000 0x80
++ 0x1b800000 0x100
++ >;
++ reg-names = "base", "elbi", "parf";
++
++ #address-cells = <3>;
++ #size-cells = <2>;
++ device_type = "pci";
++
++ interrupts = <0 57 0x0
++ 0 58 0x0
++ 0 59 0x0
++ 0 60 0x0
++ 0 61 0x0>;
++ 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";
++
++ clocks = <&gcc PCIE_1_A_CLK>,
++ <&gcc PCIE_1_H_CLK>,
++ <&gcc PCIE_1_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++ status = "disabled";
++ };
++
++ pci@1b900000 {
++ compatible = "qcom,pcie-ipq8064";
++ reg = <0x1b900000 0x1000
++ 0x1b902000 0x80
++ 0x1ba00000 0x100
++ >;
++ reg-names = "base", "elbi", "parf";
++
++ #address-cells = <3>;
++ #size-cells = <2>;
++ device_type = "pci";
++
++ interrupts = <0 71 0x0
++ 0 72 0x0
++ 0 73 0x0
++ 0 74 0x0
++ 0 75 0x0>;
++ 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";
++
++ clocks = <&gcc PCIE_2_A_CLK>,
++ <&gcc PCIE_2_H_CLK>,
++ <&gcc PCIE_2_PHY_CLK>;
++ clock-names = "core", "iface", "phy";
++ status = "disabled";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch
new file mode 100644
index 0000000..e8519e1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch
@@ -0,0 +1,33 @@
+From a7e90802be401083098511e72866a0f824c10e15 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 16 May 2014 16:54:48 -0500
+Subject: [PATCH 140/182] ARM: qcom: config: Enable PCI support for IPQ806x
+
+---
+ arch/arm/configs/qcom_defconfig | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 1752a4d..4866dec 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -21,6 +21,8 @@ CONFIG_ARCH_QCOM=y
+ CONFIG_ARCH_MSM8X60=y
+ CONFIG_ARCH_MSM8960=y
+ CONFIG_ARCH_MSM8974=y
++CONFIG_PCI=y
++CONFIG_PCI_MSI=y
+ CONFIG_SMP=y
+ CONFIG_PREEMPT=y
+ CONFIG_AEABI=y
+@@ -83,6 +85,7 @@ CONFIG_INPUT_MISC=y
+ CONFIG_INPUT_UINPUT=y
+ CONFIG_SERIO_LIBPS2=y
+ # CONFIG_LEGACY_PTYS is not set
++CONFIG_SERIAL_8250=y
+ CONFIG_SERIAL_MSM=y
+ CONFIG_SERIAL_MSM_CONSOLE=y
+ CONFIG_HW_RANDOM=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch
new file mode 100644
index 0000000..0a41b3e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch
@@ -0,0 +1,28 @@
+From fd475809eefc0870515d0b04815e2bbae67be906 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 6 Jun 2014 12:09:01 -0500
+Subject: [PATCH 141/182] ahci-platform: Bump max number of clocks to 5
+
+Qualcomm IPQ806x SoCs with SATA controllers need 5 clocks to be enabled.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/ata/ahci.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 51af275..6357e34 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -53,7 +53,7 @@
+
+ enum {
+ AHCI_MAX_PORTS = 32,
+- AHCI_MAX_CLKS = 3,
++ AHCI_MAX_CLKS = 5,
+ AHCI_MAX_SG = 168, /* hardware max is 64K */
+ AHCI_DMA_BOUNDARY = 0xffffffff,
+ AHCI_MAX_CMDS = 32,
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch
new file mode 100644
index 0000000..678a33f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch
@@ -0,0 +1,146 @@
+From c5ea64dcf7b5d45e402e78b9f291d521669b7b80 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Fri, 30 May 2014 15:35:40 -0500
+Subject: [PATCH 142/182] ata: Add Qualcomm ARM SoC AHCI SATA host controller
+ driver
+
+Add support for the Qualcomm AHCI SATA controller that exists on several
+SoC and specifically the IPQ806x family of chips. The IPQ806x SATA support
+requires the associated IPQ806x SATA PHY Driver to be enabled as well.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/ata/Kconfig | 10 ++++++
+ drivers/ata/Makefile | 1 +
+ drivers/ata/ahci_qcom.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 97 insertions(+)
+ create mode 100644 drivers/ata/ahci_qcom.c
+
+diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
+index dc950f3..da05034 100644
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -106,6 +106,16 @@ config AHCI_IMX
+
+ If unsure, say N.
+
++config AHCI_QCOM
++ tristate "Qualcomm AHCI SATA support"
++ depends on ARCH_QCOM
++ help
++ This option enables support for AHCI SATA controller
++ integrated into Qualcomm ARM SoC chipsets. For more
++ information please refer to http://www.qualcomm.com/chipsets.
++
++ If unsure, say N.
++
+ config SATA_FSL
+ tristate "Freescale 3.0Gbps SATA support"
+ depends on FSL_SOC
+diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
+index 366b743..e08044f 100644
+--- a/drivers/ata/Makefile
++++ b/drivers/ata/Makefile
+@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
+ obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+ obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
+ obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
++obj-$(CONFIG_AHCI_QCOM) += ahci_qcom.o libahci.o libahci_platform.o
+
+ # SFF w/ custom DMA
+ obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
+diff --git a/drivers/ata/ahci_qcom.c b/drivers/ata/ahci_qcom.c
+new file mode 100644
+index 0000000..bfb7a77
+--- /dev/null
++++ b/drivers/ata/ahci_qcom.c
+@@ -0,0 +1,86 @@
++/*
++ * Qualcomm ARM SoC AHCI SATA platform driver
++ *
++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/pm.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/libata.h>
++#include <linux/ahci_platform.h>
++#include "ahci.h"
++
++static const struct ata_port_info qcom_ahci_port_info = {
++ .flags = AHCI_FLAG_COMMON,
++ .pio_mask = ATA_PIO4,
++ .udma_mask = ATA_UDMA6,
++ .port_ops = &ahci_platform_ops,
++};
++
++static int qcom_ahci_probe(struct platform_device *pdev)
++{
++ struct ahci_host_priv *hpriv;
++ struct clk *rxoob_clk;
++ int rc;
++
++ hpriv = ahci_platform_get_resources(pdev);
++ if (IS_ERR(hpriv))
++ return PTR_ERR(hpriv);
++
++ /* Try and set the rxoob clk to 100Mhz */
++ rxoob_clk = of_clk_get_by_name(pdev->dev.of_node, "rxoob");
++ if (IS_ERR(rxoob_clk))
++ return PTR_ERR(rxoob_clk);
++
++ rc = clk_set_rate(rxoob_clk, 100000000);
++ if (rc)
++ return rc;
++
++ rc = ahci_platform_enable_resources(hpriv);
++ if (rc)
++ return rc;
++
++ rc = ahci_platform_init_host(pdev, hpriv, &qcom_ahci_port_info, 0, 0);
++ if (rc)
++ goto disable_resources;
++
++ return 0;
++disable_resources:
++ ahci_platform_disable_resources(hpriv);
++ return rc;
++}
++
++static const struct of_device_id qcom_ahci_of_match[] = {
++ { .compatible = "qcom,msm-ahci", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, qcom_ahci_of_match);
++
++static struct platform_driver qcom_ahci_driver = {
++ .probe = qcom_ahci_probe,
++ .remove = ata_platform_remove_one,
++ .driver = {
++ .name = "qcom_ahci_qcom",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_ahci_of_match,
++ },
++};
++module_platform_driver(qcom_ahci_driver);
++
++MODULE_DESCRIPTION("Qualcomm AHCI SATA platform driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("ahci:qcom");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch
new file mode 100644
index 0000000..ef795bf
--- /dev/null
+++ b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch
@@ -0,0 +1,63 @@
+From dd280a4cf6b826144f74d3fc4285633f1c337a1d Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 12 Jun 2014 11:28:47 -0500
+Subject: [PATCH 143/182] ata: qcom: Add device tree bindings information
+
+Add device tree binding for Qualcomm AHCI SATA controller and specifically
+the sata controller on the IPQ806x family of SoCs.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ .../devicetree/bindings/ata/qcom-sata.txt | 40 ++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/ata/qcom-sata.txt
+
+diff --git a/Documentation/devicetree/bindings/ata/qcom-sata.txt b/Documentation/devicetree/bindings/ata/qcom-sata.txt
+new file mode 100644
+index 0000000..5e74e41
+--- /dev/null
++++ b/Documentation/devicetree/bindings/ata/qcom-sata.txt
+@@ -0,0 +1,40 @@
++* Qualcomm AHCI SATA Controller
++
++SATA nodes are defined to describe on-chip Serial ATA controllers.
++Each SATA controller should have its own node.
++
++Required properties:
++- compatible : compatible list, contains "qcom,msm-ahci"
++- interrupts : <interrupt mapping for SATA IRQ>
++- reg : <registers mapping>
++- phys : Must contain exactly one entry as specified
++ in phy-bindings.txt
++- phy-names : Must be "sata-phy"
++
++Required properties for "qcom,ipq806x-ahci" compatible:
++- clocks : Must contain an entry for each entry in clock-names.
++- clock-names : Shall be:
++ "slave_iface" - Fabric port AHB clock for SATA
++ "iface" - AHB clock
++ "core" - core clock
++ "rxoob" - RX out-of-band clock
++ "pmalive" - Power Module Alive clock
++
++Example:
++ sata@29000000 {
++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci";
++ reg = <0x29000000 0x180>;
++
++ interrupts = <0 209 0x0>;
++
++ clocks = <&gcc SFAB_SATA_S_H_CLK>,
++ <&gcc SATA_H_CLK>,
++ <&gcc SATA_A_CLK>,
++ <&gcc SATA_RXOOB_CLK>,
++ <&gcc SATA_PMALIVE_CLK>;
++ clock-names = "slave_iface", "iface", "core",
++ "rxoob", "pmalive";
++
++ phys = <&sata_phy>;
++ phy-names = "sata-phy";
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch
new file mode 100644
index 0000000..862be08
--- /dev/null
+++ b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch
@@ -0,0 +1,261 @@
+From 7554dfbbec255e4cd5dd4445841c28c48fd4a855 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 3 Jun 2014 11:59:09 -0500
+Subject: [PATCH 144/182] phy: qcom: Add driver for QCOM IPQ806x SATA PHY
+
+Add a PHY driver for uses with AHCI based SATA controller driver on the
+IPQ806x family of SoCs.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ drivers/phy/Kconfig | 7 ++
+ drivers/phy/Makefile | 1 +
+ drivers/phy/phy-qcom-ipq806x-sata.c | 211 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 219 insertions(+)
+ create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c
+
+diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
+index c7a551c..3642a65 100644
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -65,4 +65,11 @@ config BCM_KONA_USB2_PHY
+ help
+ Enable this to support the Broadcom Kona USB 2.0 PHY.
+
++config PHY_QCOM_IPQ806X_SATA
++ tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
++ depends on ARCH_QCOM
++ depends on HAS_IOMEM
++ depends on OF
++ select GENERIC_PHY
++
+ endmenu
+diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
+index b57c253..74f78cc 100644
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
+ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
+ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
+ obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
++obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
+diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c
+new file mode 100644
+index 0000000..e931aee
+--- /dev/null
++++ b/drivers/phy/phy-qcom-ipq806x-sata.c
+@@ -0,0 +1,211 @@
++/*
++ * 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/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/time.h>
++#include <linux/delay.h>
++#include <linux/clk.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/phy/phy.h>
++
++struct qcom_ipq806x_sata_phy {
++ void __iomem *mmio;
++ struct clk *cfg_clk;
++};
++
++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
++
++#define SATA_PHY_P0_PARAM0 0x200
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
++
++#define SATA_PHY_P0_PARAM1 0x204
++#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
++
++#define SATA_PHY_P0_PARAM2 0x208
++#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
++#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
++
++#define SATA_PHY_P0_PARAM3 0x20C
++#define SATA_PHY_SSC_EN 0x8
++#define SATA_PHY_P0_PARAM4 0x210
++#define SATA_PHY_REF_SSP_EN 0x2
++#define SATA_PHY_RESET 0x1
++
++static inline void qcom_ipq806x_sata_delay_us(unsigned int delay)
++{
++ /* sleep for max. 50us more to combine processor wakeups */
++ usleep_range(delay, delay + 50);
++}
++
++static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
++{
++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
++ u32 reg;
++
++ /* Setting SSC_EN to 1 */
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
++ reg = reg | SATA_PHY_SSC_EN;
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
++
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
++ ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
++ reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
++
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
++ ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
++ reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
++
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
++ ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
++ reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
++
++ /* Setting PHY_RESET to 1 */
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
++ reg = reg | SATA_PHY_RESET;
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
++
++ /* Setting REF_SSP_EN to 1 */
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
++ reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
++ mb();
++ qcom_ipq806x_sata_delay_us(20);
++
++ /* Clearing PHY_RESET to 0 */
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
++ reg = reg & ~SATA_PHY_RESET;
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
++
++ return 0;
++}
++
++static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
++{
++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
++ u32 reg;
++
++ /* Setting PHY_RESET to 1 */
++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
++ reg = reg | SATA_PHY_RESET;
++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
++
++ return 0;
++}
++
++static struct phy_ops qcom_ipq806x_sata_phy_ops = {
++ .init = qcom_ipq806x_sata_phy_init,
++ .exit = qcom_ipq806x_sata_phy_exit,
++ .owner = THIS_MODULE,
++};
++
++static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
++{
++ struct qcom_ipq806x_sata_phy *phy;
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++ struct phy_provider *phy_provider;
++ struct phy *generic_phy;
++ int ret;
++
++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
++ if (!phy) {
++ dev_err(dev, "%s: failed to allocate phy\n", __func__);
++ return -ENOMEM;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ phy->mmio = devm_ioremap_resource(dev, res);
++ if (IS_ERR(phy->mmio))
++ return PTR_ERR(phy->mmio);
++
++ generic_phy = devm_phy_create(dev, &qcom_ipq806x_sata_phy_ops, NULL);
++ if (IS_ERR(generic_phy)) {
++ dev_err(dev, "%s: failed to create phy\n", __func__);
++ return PTR_ERR(generic_phy);
++ }
++
++ phy_set_drvdata(generic_phy, phy);
++
++ phy->cfg_clk = devm_clk_get(dev, "cfg");
++ if (IS_ERR(phy->cfg_clk)) {
++ dev_err(dev, "Failed to get sata cfg clock\n");
++ return PTR_ERR(phy->cfg_clk);
++ }
++
++ ret = clk_prepare_enable(phy->cfg_clk);
++ if (ret)
++ return ret;
++
++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
++ if (IS_ERR(phy_provider)) {
++ clk_disable_unprepare(phy->cfg_clk);
++ dev_err(dev, "%s: failed to register phy\n", __func__);
++ return PTR_ERR(phy_provider);
++ }
++
++ return 0;
++}
++
++static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
++{
++ struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(phy->cfg_clk);
++
++ return 0;
++}
++
++static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
++ { .compatible = "qcom,ipq806x-sata-phy" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
++
++static struct platform_driver qcom_ipq806x_sata_phy_driver = {
++ .probe = qcom_ipq806x_sata_phy_probe,
++ .remove = qcom_ipq806x_sata_phy_remove,
++ .driver = {
++ .name = "qcom-ipq806x-sata-phy",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_ipq806x_sata_phy_of_match,
++ }
++};
++module_platform_driver(qcom_ipq806x_sata_phy_driver);
++
++MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch
new file mode 100644
index 0000000..1791228
--- /dev/null
+++ b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch
@@ -0,0 +1,46 @@
+From 37258bc8fe832e4c681593a864686f627f6d3455 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 10 Jun 2014 13:09:01 -0500
+Subject: [PATCH 145/182] phy: qcom: Add device tree bindings information
+
+Add binding spec for Qualcomm SoC PHYs, starting with the SATA PHY on
+the IPQ806x family of SoCs.
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ Documentation/devicetree/bindings/phy/qcom-phy.txt | 23 ++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/phy/qcom-phy.txt
+
+diff --git a/Documentation/devicetree/bindings/phy/qcom-phy.txt b/Documentation/devicetree/bindings/phy/qcom-phy.txt
+new file mode 100644
+index 0000000..76bfbd0
+--- /dev/null
++++ b/Documentation/devicetree/bindings/phy/qcom-phy.txt
+@@ -0,0 +1,23 @@
++Qualcomm IPQ806x SATA PHY Controller
++------------------------------------
++
++SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
++Each SATA PHY controller should have its own node.
++
++Required properties:
++- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
++- reg: offset and length of the SATA PHY register set;
++- #phy-cells: must be zero
++- clocks: must be exactly one entry
++- clock-names: must be "cfg"
++
++Example:
++ sata_phy: sata-phy@1b400000 {
++ compatible = "qcom,ipq806x-sata-phy";
++ reg = <0x1b400000 0x200>;
++
++ clocks = <&gcc SATA_PHY_CFG_CLK>;
++ clock-names = "cfg";
++
++ #phy-cells = <0>;
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch
new file mode 100644
index 0000000..0d196ad
--- /dev/null
+++ b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch
@@ -0,0 +1,72 @@
+From 6992cf3e8900d042a845eafc11e7841f32fec0a6 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Thu, 12 Jun 2014 10:56:54 -0500
+Subject: [PATCH 146/182] ARM: dts: qcom: Add SATA support for IPQ8064 and
+ AP148 board
+
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 8 ++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 30 ++++++++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index 11f7a77..c752889 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -118,5 +118,13 @@
+ 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */
+ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
+ };
++
++ sata-phy@1b400000 {
++ status = "ok";
++ };
++
++ sata@29000000 {
++ status = "ok";
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 42a651f..93c0315 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -339,5 +339,35 @@
+ clock-names = "core", "iface", "phy";
+ status = "disabled";
+ };
++
++ sata_phy: sata-phy@1b400000 {
++ compatible = "qcom,ipq806x-sata-phy";
++ reg = <0x1b400000 0x200>;
++
++ clocks = <&gcc SATA_PHY_CFG_CLK>;
++ clock-names = "cfg";
++
++ #phy-cells = <0>;
++ status = "disabled";
++ };
++
++ sata@29000000 {
++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci";
++ reg = <0x29000000 0x180>;
++
++ interrupts = <0 209 0x0>;
++
++ clocks = <&gcc SFAB_SATA_S_H_CLK>,
++ <&gcc SATA_H_CLK>,
++ <&gcc SATA_A_CLK>,
++ <&gcc SATA_RXOOB_CLK>,
++ <&gcc SATA_PMALIVE_CLK>;
++ clock-names = "slave_face", "iface", "core",
++ "rxoob", "pmalive";
++
++ phys = <&sata_phy>;
++ phy-names = "sata-phy";
++ status = "disabled";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch
new file mode 100644
index 0000000..43e9dff
--- /dev/null
+++ b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch
@@ -0,0 +1,42 @@
+From 54a945eca8381fa8cda03b20eda3b2f23a88d289 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Tue, 3 Jun 2014 10:36:41 -0500
+Subject: [PATCH 147/182] ARM: qcom: Enable SATA/SATA-PHY drivers in defconfig
+
+---
+ arch/arm/configs/qcom_defconfig | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 4866dec..84c6639 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -57,7 +57,6 @@ CONFIG_MTD_BLOCK=y
+ CONFIG_MTD_M25P80=y
+ CONFIG_BLK_DEV_LOOP=y
+ CONFIG_BLK_DEV_RAM=y
+-CONFIG_SCSI=y
+ CONFIG_SCSI_TGT=y
+ CONFIG_BLK_DEV_SD=y
+ CONFIG_CHR_DEV_SG=y
+@@ -66,6 +65,8 @@ CONFIG_SCSI_MULTI_LUN=y
+ CONFIG_SCSI_CONSTANTS=y
+ CONFIG_SCSI_LOGGING=y
+ CONFIG_SCSI_SCAN_ASYNC=y
++CONFIG_ATA=y
++CONFIG_AHCI_QCOM=y
+ CONFIG_NETDEVICES=y
+ CONFIG_DUMMY=y
+ CONFIG_MDIO_BITBANG=y
+@@ -141,7 +142,7 @@ CONFIG_MSM_GCC_8660=y
+ CONFIG_MSM_MMCC_8960=y
+ CONFIG_MSM_MMCC_8974=y
+ CONFIG_MSM_IOMMU=y
+-CONFIG_GENERIC_PHY=y
++CONFIG_PHY_QCOM_IPQ806X_SATA=y
+ CONFIG_EXT2_FS=y
+ CONFIG_EXT2_FS_XATTR=y
+ CONFIG_EXT3_FS=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch
new file mode 100644
index 0000000..29d7126
--- /dev/null
+++ b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch
@@ -0,0 +1,26 @@
+From 8c9156c67c3e3e37812bb0e4d24263be0e564881 Mon Sep 17 00:00:00 2001
+From: Kumar Gala <galak@codeaurora.org>
+Date: Mon, 16 Jun 2014 14:40:29 -0500
+Subject: [PATCH 148/182] ARM: qcom: enable default CPU_IDLE to get wfi
+ support on IPQ806x
+
+Signed-off-by: Kumar Gala <galak@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 84c6639..85a35af 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -31,6 +31,7 @@ CONFIG_HIGHPTE=y
+ CONFIG_CLEANCACHE=y
+ CONFIG_ARM_APPENDED_DTB=y
+ CONFIG_ARM_ATAG_DTB_COMPAT=y
++CONFIG_CPU_IDLE=y
+ CONFIG_VFP=y
+ CONFIG_NEON=y
+ # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch
new file mode 100644
index 0000000..8e21ee8
--- /dev/null
+++ b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch
@@ -0,0 +1,55 @@
+From b1325bfad3ad0e543ce90a6b08d74500dc96f4b9 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 16 Jun 2014 16:52:55 -0500
+Subject: [PATCH 149/182] pinctrl: qcom: Add BUS_HOLD/Keeper bias
+
+This patch adds the bus_hold bias option for pins.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/pinctrl/pinctrl-msm.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
+index 7d67d34..f054b25 100644
+--- a/drivers/pinctrl/pinctrl-msm.c
++++ b/drivers/pinctrl/pinctrl-msm.c
+@@ -207,6 +207,7 @@ static int msm_config_reg(struct msm_pinctrl *pctrl,
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
++ case PIN_CONFIG_BIAS_BUS_HOLD:
+ *bit = g->pull_bit;
+ *mask = 3;
+ break;
+@@ -243,6 +244,7 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+
+ #define MSM_NO_PULL 0
+ #define MSM_PULL_DOWN 1
++#define MSM_KEEPER 2
+ #define MSM_PULL_UP 3
+
+ static unsigned msm_regval_to_drive(u32 val)
+@@ -280,6 +282,9 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev,
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ arg = arg == MSM_PULL_DOWN;
+ break;
++ case PIN_CONFIG_BIAS_BUS_HOLD:
++ arg = arg == MSM_KEEPER;
++ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ arg = arg == MSM_PULL_UP;
+ break;
+@@ -339,6 +344,9 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev,
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ arg = MSM_PULL_DOWN;
+ break;
++ case PIN_CONFIG_BIAS_BUS_HOLD:
++ arg = MSM_KEEPER;
++ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ arg = MSM_PULL_UP;
+ break;
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch
new file mode 100644
index 0000000..5a9fb51
--- /dev/null
+++ b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch
@@ -0,0 +1,8803 @@
+From d2981ca1343b837fc574c4e46806d041b258720d Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 16 Jun 2014 17:13:22 -0500
+Subject: [PATCH 150/182] mtd: nand: Add Qualcomm NAND controller
+
+This patch adds the Qualcomm NAND controller and required infrastructure.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/mtd/nand/Kconfig | 18 +
+ drivers/mtd/nand/Makefile | 2 +
+ drivers/mtd/nand/qcom_adm_dma.c | 797 +++++
+ drivers/mtd/nand/qcom_adm_dma.h | 268 ++
+ drivers/mtd/nand/qcom_nand.c | 7455 +++++++++++++++++++++++++++++++++++++++
+ drivers/mtd/nand/qcom_nand.h | 196 +
+ 6 files changed, 8736 insertions(+)
+ create mode 100644 drivers/mtd/nand/qcom_adm_dma.c
+ create mode 100644 drivers/mtd/nand/qcom_adm_dma.h
+ create mode 100644 drivers/mtd/nand/qcom_nand.c
+ create mode 100644 drivers/mtd/nand/qcom_nand.h
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index 90ff447..6e3842f 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -510,4 +510,22 @@ config MTD_NAND_XWAY
+ Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ to the External Bus Unit (EBU).
+
++config MTD_QCOM_DMA
++ tristate "QCMO NAND DMA Support"
++ depends on ARCH_QCOM && MTD_QCOM_NAND
++ default n
++ help
++ DMA support for QCOM NAND
++
++config MTD_QCOM_NAND
++ tristate "QCOM NAND Device Support"
++ depends on MTD && ARCH_QCOM
++ select CRC16
++ select BITREVERSE
++ select MTD_NAND_IDS
++ select MTD_QCOM_DMA
++ default y
++ help
++ Support for some NAND chips connected to the QCOM NAND controller.
++
+ endif # MTD_NAND
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index 542b568..6ef3c02 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -49,5 +49,7 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
++obj-$(CONFIG_MTD_QCOM_NAND) += qcom_nand.o
++obj-$(CONFIG_MTD_QCOM_DMA) += qcom_adm_dma.o
+
+ nand-objs := nand_base.o nand_bbt.o
+diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c
+new file mode 100644
+index 0000000..46d8473
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_adm_dma.c
+@@ -0,0 +1,797 @@
++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */
++/* linux/arch/arm/mach-msm/dma.c
++ *
++ * Copyright (C) 2007 Google, Inc.
++ * Copyright (c) 2008-2010, 2012 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/err.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/pm_runtime.h>
++#include <linux/reset.h>
++#include <linux/reset-controller.h>
++#include "qcom_adm_dma.h"
++
++#define MODULE_NAME "msm_dmov"
++
++#define MSM_DMOV_CHANNEL_COUNT 16
++#define MSM_DMOV_CRCI_COUNT 16
++
++enum {
++ CLK_DIS,
++ CLK_TO_BE_DIS,
++ CLK_EN
++};
++
++struct msm_dmov_ci_conf {
++ int start;
++ int end;
++ int burst;
++};
++
++struct msm_dmov_crci_conf {
++ int sd;
++ int blk_size;
++};
++
++struct msm_dmov_chan_conf {
++ int sd;
++ int block;
++ int priority;
++};
++
++struct msm_dmov_conf {
++ void *base;
++ struct msm_dmov_crci_conf *crci_conf;
++ struct msm_dmov_chan_conf *chan_conf;
++ int channel_active;
++ int sd;
++ size_t sd_size;
++ struct list_head staged_commands[MSM_DMOV_CHANNEL_COUNT];
++ struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT];
++ struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT];
++ struct mutex lock;
++ spinlock_t list_lock;
++ unsigned int irq;
++ struct clk *clk;
++ struct clk *pclk;
++ struct clk *ebiclk;
++ unsigned int clk_ctl;
++ struct delayed_work work;
++ struct workqueue_struct *cmd_wq;
++
++ struct reset_control *adm_reset;
++ struct reset_control *pbus_reset;
++ struct reset_control *c0_reset;
++ struct reset_control *c1_reset;
++ struct reset_control *c2_reset;
++
++};
++
++static void msm_dmov_clock_work(struct work_struct *);
++
++#define DMOV_CRCI_DEFAULT_CONF { .sd = 0, .blk_size = 0 }
++#define DMOV_CRCI_CONF(secd, blk) { .sd = secd, .blk_size = blk }
++
++static struct msm_dmov_crci_conf adm_crci_conf[] = {
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_CONF(0, 1),
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++ DMOV_CRCI_DEFAULT_CONF,
++};
++
++#define DMOV_CHANNEL_DEFAULT_CONF { .sd = 0, .block = 0, .priority = 1 }
++
++static struct msm_dmov_chan_conf adm_chan_conf[] = {
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++ DMOV_CHANNEL_DEFAULT_CONF,
++};
++
++#define DMOV_IRQ_TO_ADM(irq) 0
++
++static struct msm_dmov_conf dmov_conf[] = {
++ {
++ .crci_conf = adm_crci_conf,
++ .chan_conf = adm_chan_conf,
++ .lock = __MUTEX_INITIALIZER(dmov_conf[0].lock),
++ .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock),
++ .clk_ctl = CLK_EN,
++ .work = __DELAYED_WORK_INITIALIZER(dmov_conf[0].work,
++ msm_dmov_clock_work,0),
++ }
++};
++
++#define MSM_DMOV_ID_COUNT (MSM_DMOV_CHANNEL_COUNT * ARRAY_SIZE(dmov_conf))
++#define DMOV_REG(name, adm) ((name) + (dmov_conf[adm].base) +\
++ (dmov_conf[adm].sd * dmov_conf[adm].sd_size))
++#define DMOV_ID_TO_ADM(id) ((id) / MSM_DMOV_CHANNEL_COUNT)
++#define DMOV_ID_TO_CHAN(id) ((id) % MSM_DMOV_CHANNEL_COUNT)
++#define DMOV_CHAN_ADM_TO_ID(ch, adm) ((ch) + (adm) * MSM_DMOV_CHANNEL_COUNT)
++
++enum {
++ MSM_DMOV_PRINT_ERRORS = 1,
++ MSM_DMOV_PRINT_IO = 2,
++ MSM_DMOV_PRINT_FLOW = 4
++};
++
++unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS;
++
++#define MSM_DMOV_DPRINTF(mask, format, args...) \
++ do { \
++ if ((mask) & msm_dmov_print_mask) \
++ printk(KERN_ERR format, args); \
++ } while (0)
++#define PRINT_ERROR(format, args...) \
++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_ERRORS, format, args);
++#define PRINT_IO(format, args...) \
++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_IO, format, args);
++#define PRINT_FLOW(format, args...) \
++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_FLOW, format, args);
++
++static int msm_dmov_clk_on(int adm)
++{
++ int ret;
++
++return 0;
++ ret = clk_prepare_enable(dmov_conf[adm].clk);
++ if (ret)
++ return ret;
++ if (dmov_conf[adm].pclk) {
++ ret = clk_prepare_enable(dmov_conf[adm].pclk);
++ if (ret) {
++ clk_disable_unprepare(dmov_conf[adm].clk);
++ return ret;
++ }
++ }
++ if (dmov_conf[adm].ebiclk) {
++ ret = clk_prepare_enable(dmov_conf[adm].ebiclk);
++ if (ret) {
++ if (dmov_conf[adm].pclk)
++ clk_disable_unprepare(dmov_conf[adm].pclk);
++ clk_disable_unprepare(dmov_conf[adm].clk);
++ }
++ }
++ return ret;
++}
++
++static void msm_dmov_clk_off(int adm)
++{
++#if 0
++ if (dmov_conf[adm].ebiclk)
++ clk_disable_unprepare(dmov_conf[adm].ebiclk);
++ if (dmov_conf[adm].pclk)
++ clk_disable_unprepare(dmov_conf[adm].pclk);
++ clk_disable_unprepare(dmov_conf[adm].clk);
++#endif
++}
++
++static void msm_dmov_clock_work(struct work_struct *work)
++{
++ struct msm_dmov_conf *conf =
++ container_of(to_delayed_work(work), struct msm_dmov_conf, work);
++ int adm = DMOV_IRQ_TO_ADM(conf->irq);
++ mutex_lock(&conf->lock);
++ if (conf->clk_ctl == CLK_TO_BE_DIS) {
++ BUG_ON(conf->channel_active);
++ msm_dmov_clk_off(adm);
++ conf->clk_ctl = CLK_DIS;
++ }
++ mutex_unlock(&conf->lock);
++}
++
++enum {
++ NOFLUSH = 0,
++ GRACEFUL,
++ NONGRACEFUL,
++};
++
++/* Caller must hold the list lock */
++static struct msm_dmov_cmd *start_ready_cmd(unsigned ch, int adm)
++{
++ struct msm_dmov_cmd *cmd;
++
++ if (list_empty(&dmov_conf[adm].ready_commands[ch])) {
++ return NULL;
++ }
++
++ cmd = list_entry(dmov_conf[adm].ready_commands[ch].next, typeof(*cmd),
++ list);
++ list_del(&cmd->list);
++ if (cmd->exec_func)
++ cmd->exec_func(cmd);
++ list_add_tail(&cmd->list, &dmov_conf[adm].active_commands[ch]);
++ if (!dmov_conf[adm].channel_active) {
++ enable_irq(dmov_conf[adm].irq);
++ }
++ dmov_conf[adm].channel_active |= BIT(ch);
++ PRINT_IO("msm dmov enqueue command, %x, ch %d\n", cmd->cmdptr, ch);
++ writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm));
++
++ return cmd;
++}
++
++static void msm_dmov_enqueue_cmd_ext_work(struct work_struct *work)
++{
++ struct msm_dmov_cmd *cmd =
++ container_of(work, struct msm_dmov_cmd, work);
++ unsigned id = cmd->id;
++ unsigned status;
++ unsigned long flags;
++ int adm = DMOV_ID_TO_ADM(id);
++ int ch = DMOV_ID_TO_CHAN(id);
++
++ mutex_lock(&dmov_conf[adm].lock);
++ if (dmov_conf[adm].clk_ctl == CLK_DIS) {
++ status = msm_dmov_clk_on(adm);
++ if (status != 0)
++ goto error;
++ }
++ dmov_conf[adm].clk_ctl = CLK_EN;
++
++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags);
++
++ cmd = list_entry(dmov_conf[adm].staged_commands[ch].next, typeof(*cmd),
++ list);
++ list_del(&cmd->list);
++ list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]);
++ status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm));
++ if (status & DMOV_STATUS_CMD_PTR_RDY) {
++ PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n",
++ id, status);
++ cmd = start_ready_cmd(ch, adm);
++ /*
++ * We added something to the ready list, and still hold the
++ * list lock. Thus, no need to check for cmd == NULL
++ */
++ if (cmd->toflush) {
++ int flush = (cmd->toflush == GRACEFUL) ? 1 << 31 : 0;
++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm));
++ }
++ } else {
++ cmd->toflush = 0;
++ if (list_empty(&dmov_conf[adm].active_commands[ch]) &&
++ !list_empty(&dmov_conf[adm].ready_commands[ch]))
++ PRINT_ERROR("msm_dmov_enqueue_cmd_ext(%d), stalled, "
++ "status %x\n", id, status);
++ PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status "
++ "%x\n", id, status);
++ }
++ if (!dmov_conf[adm].channel_active) {
++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS;
++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10));
++ }
++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags);
++error:
++ mutex_unlock(&dmov_conf[adm].lock);
++}
++
++static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd)
++{
++ int adm = DMOV_ID_TO_ADM(id);
++ int ch = DMOV_ID_TO_CHAN(id);
++ unsigned long flags;
++ cmd->id = id;
++ cmd->toflush = 0;
++
++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags);
++ list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]);
++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags);
++
++ queue_work(dmov_conf[adm].cmd_wq, &cmd->work);
++}
++
++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd)
++{
++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work);
++ __msm_dmov_enqueue_cmd_ext(id, cmd);
++}
++EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext);
++
++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
++{
++ /* Disable callback function (for backwards compatibility) */
++ cmd->exec_func = NULL;
++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work);
++ __msm_dmov_enqueue_cmd_ext(id, cmd);
++}
++EXPORT_SYMBOL(msm_dmov_enqueue_cmd);
++
++void msm_dmov_flush(unsigned int id, int graceful)
++{
++ unsigned long irq_flags;
++ int ch = DMOV_ID_TO_CHAN(id);
++ int adm = DMOV_ID_TO_ADM(id);
++ int flush = graceful ? DMOV_FLUSH_TYPE : 0;
++ struct msm_dmov_cmd *cmd;
++
++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags);
++ /* XXX not checking if flush cmd sent already */
++ if (!list_empty(&dmov_conf[adm].active_commands[ch])) {
++ PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id);
++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm));
++ }
++ list_for_each_entry(cmd, &dmov_conf[adm].staged_commands[ch], list)
++ cmd->toflush = graceful ? GRACEFUL : NONGRACEFUL;
++ /* spin_unlock_irqrestore has the necessary barrier */
++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags);
++}
++EXPORT_SYMBOL(msm_dmov_flush);
++
++struct msm_dmov_exec_cmdptr_cmd {
++ struct msm_dmov_cmd dmov_cmd;
++ struct completion complete;
++ unsigned id;
++ unsigned int result;
++ struct msm_dmov_errdata err;
++};
++
++static void
++dmov_exec_cmdptr_complete_func(struct msm_dmov_cmd *_cmd,
++ unsigned int result,
++ struct msm_dmov_errdata *err)
++{
++ struct msm_dmov_exec_cmdptr_cmd *cmd = container_of(_cmd, struct msm_dmov_exec_cmdptr_cmd, dmov_cmd);
++ cmd->result = result;
++ if (result != 0x80000002 && err)
++ memcpy(&cmd->err, err, sizeof(struct msm_dmov_errdata));
++
++ complete(&cmd->complete);
++}
++
++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr)
++{
++ struct msm_dmov_exec_cmdptr_cmd cmd;
++
++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x)\n", id, cmdptr);
++
++ cmd.dmov_cmd.cmdptr = cmdptr;
++ cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func;
++ cmd.dmov_cmd.exec_func = NULL;
++ cmd.id = id;
++ cmd.result = 0;
++ INIT_WORK_ONSTACK(&cmd.dmov_cmd.work, msm_dmov_enqueue_cmd_ext_work);
++ init_completion(&cmd.complete);
++
++ __msm_dmov_enqueue_cmd_ext(id, &cmd.dmov_cmd);
++ wait_for_completion_timeout(&cmd.complete, msecs_to_jiffies(1000));
++
++ if (cmd.result != 0x80000002) {
++ PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result);
++ PRINT_ERROR("dmov_exec_cmdptr(%d): flush: %x %x %x %x\n",
++ id, cmd.err.flush[0], cmd.err.flush[1], cmd.err.flush[2], cmd.err.flush[3]);
++ return -EIO;
++ }
++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr);
++ return 0;
++}
++EXPORT_SYMBOL(msm_dmov_exec_cmd);
++
++static void fill_errdata(struct msm_dmov_errdata *errdata, int ch, int adm)
++{
++ errdata->flush[0] = readl_relaxed(DMOV_REG(DMOV_FLUSH0(ch), adm));
++ errdata->flush[1] = readl_relaxed(DMOV_REG(DMOV_FLUSH1(ch), adm));
++ errdata->flush[2] = 0;
++ errdata->flush[3] = readl_relaxed(DMOV_REG(DMOV_FLUSH3(ch), adm));
++ errdata->flush[4] = readl_relaxed(DMOV_REG(DMOV_FLUSH4(ch), adm));
++ errdata->flush[5] = readl_relaxed(DMOV_REG(DMOV_FLUSH5(ch), adm));
++}
++
++static irqreturn_t msm_dmov_isr(int irq, void *dev_id)
++{
++ unsigned int int_status;
++ unsigned int mask;
++ unsigned int id;
++ unsigned int ch;
++ unsigned long irq_flags;
++ unsigned int ch_status;
++ unsigned int ch_result;
++ unsigned int valid = 0;
++ struct msm_dmov_cmd *cmd;
++ int adm = DMOV_IRQ_TO_ADM(irq);
++
++ mutex_lock(&dmov_conf[adm].lock);
++ /* read and clear isr */
++ int_status = readl_relaxed(DMOV_REG(DMOV_ISR, adm));
++ PRINT_FLOW("msm_datamover_irq_handler: DMOV_ISR %x\n", int_status);
++
++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags);
++ while (int_status) {
++ mask = int_status & -int_status;
++ ch = fls(mask) - 1;
++ id = DMOV_CHAN_ADM_TO_ID(ch, adm);
++ PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", int_status, mask, id);
++ int_status &= ~mask;
++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm));
++ if (!(ch_status & DMOV_STATUS_RSLT_VALID)) {
++ PRINT_FLOW("msm_datamover_irq_handler id %d, "
++ "result not valid %x\n", id, ch_status);
++ continue;
++ }
++ do {
++ valid = 1;
++ ch_result = readl_relaxed(DMOV_REG(DMOV_RSLT(ch), adm));
++ if (list_empty(&dmov_conf[adm].active_commands[ch])) {
++ PRINT_ERROR("msm_datamover_irq_handler id %d, got result "
++ "with no active command, status %x, result %x\n",
++ id, ch_status, ch_result);
++ cmd = NULL;
++ } else {
++ cmd = list_entry(dmov_conf[adm].
++ active_commands[ch].next, typeof(*cmd),
++ list);
++ }
++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x, result %x\n", id, ch_status, ch_result);
++ if (ch_result & DMOV_RSLT_DONE) {
++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n",
++ id, ch_status);
++ PRINT_IO("msm_datamover_irq_handler id %d, got result "
++ "for %p, result %x\n", id, cmd, ch_result);
++ if (cmd) {
++ list_del(&cmd->list);
++ cmd->complete_func(cmd, ch_result, NULL);
++ }
++ }
++ if (ch_result & DMOV_RSLT_FLUSH) {
++ struct msm_dmov_errdata errdata;
++
++ fill_errdata(&errdata, ch, adm);
++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status);
++ PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]);
++ if (cmd) {
++ list_del(&cmd->list);
++ cmd->complete_func(cmd, ch_result, &errdata);
++ }
++ }
++ if (ch_result & DMOV_RSLT_ERROR) {
++ struct msm_dmov_errdata errdata;
++
++ fill_errdata(&errdata, ch, adm);
++
++ PRINT_ERROR("msm_datamover_irq_handler id %d, status %x\n", id, ch_status);
++ PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]);
++ if (cmd) {
++ list_del(&cmd->list);
++ cmd->complete_func(cmd, ch_result, &errdata);
++ }
++ /* this does not seem to work, once we get an error */
++ /* the datamover will no longer accept commands */
++ writel_relaxed(0, DMOV_REG(DMOV_FLUSH0(ch),
++ adm));
++ }
++ rmb();
++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch),
++ adm));
++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status);
++ if (ch_status & DMOV_STATUS_CMD_PTR_RDY)
++ start_ready_cmd(ch, adm);
++ } while (ch_status & DMOV_STATUS_RSLT_VALID);
++ if (list_empty(&dmov_conf[adm].active_commands[ch]) &&
++ list_empty(&dmov_conf[adm].ready_commands[ch]))
++ dmov_conf[adm].channel_active &= ~(1U << ch);
++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status);
++ }
++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags);
++
++ if (!dmov_conf[adm].channel_active && valid) {
++ disable_irq_nosync(dmov_conf[adm].irq);
++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS;
++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10));
++ }
++
++ mutex_unlock(&dmov_conf[adm].lock);
++
++ return valid ? IRQ_HANDLED : IRQ_NONE;
++}
++
++static int msm_dmov_suspend_late(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ int adm = (pdev->id >= 0) ? pdev->id : 0;
++ mutex_lock(&dmov_conf[adm].lock);
++ if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) {
++ BUG_ON(dmov_conf[adm].channel_active);
++ msm_dmov_clk_off(adm);
++ dmov_conf[adm].clk_ctl = CLK_DIS;
++ }
++ mutex_unlock(&dmov_conf[adm].lock);
++ return 0;
++}
++
++static int msm_dmov_runtime_suspend(struct device *dev)
++{
++ dev_dbg(dev, "pm_runtime: suspending...\n");
++ return 0;
++}
++
++static int msm_dmov_runtime_resume(struct device *dev)
++{
++ dev_dbg(dev, "pm_runtime: resuming...\n");
++ return 0;
++}
++
++static int msm_dmov_runtime_idle(struct device *dev)
++{
++ dev_dbg(dev, "pm_runtime: idling...\n");
++ return 0;
++}
++
++static struct dev_pm_ops msm_dmov_dev_pm_ops = {
++ .runtime_suspend = msm_dmov_runtime_suspend,
++ .runtime_resume = msm_dmov_runtime_resume,
++ .runtime_idle = msm_dmov_runtime_idle,
++ .suspend = msm_dmov_suspend_late,
++};
++
++static int msm_dmov_init_clocks(struct platform_device *pdev)
++{
++ int adm = (pdev->id >= 0) ? pdev->id : 0;
++ int ret;
++
++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk");
++ if (IS_ERR(dmov_conf[adm].clk)) {
++ printk(KERN_ERR "%s: Error getting adm_clk\n", __func__);
++ dmov_conf[adm].clk = NULL;
++ return -ENOENT;
++ }
++
++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk");
++ if (IS_ERR(dmov_conf[adm].pclk)) {
++ dmov_conf[adm].pclk = NULL;
++ /* pclk not present on all SoCs, don't bail on failure */
++ }
++
++ dmov_conf[adm].ebiclk = devm_clk_get(&pdev->dev, "mem_clk");
++ if (IS_ERR(dmov_conf[adm].ebiclk)) {
++ dmov_conf[adm].ebiclk = NULL;
++ /* ebiclk not present on all SoCs, don't bail on failure */
++ } else {
++ ret = clk_set_rate(dmov_conf[adm].ebiclk, 27000000);
++ if (ret)
++ return -ENOENT;
++ }
++
++ return 0;
++}
++
++static void config_datamover(int adm)
++{
++ int i;
++
++ /* Reset the ADM */
++ reset_control_assert(dmov_conf[adm].adm_reset);
++ reset_control_assert(dmov_conf[adm].c0_reset);
++ reset_control_assert(dmov_conf[adm].c1_reset);
++ reset_control_assert(dmov_conf[adm].c2_reset);
++
++ reset_control_deassert(dmov_conf[adm].c2_reset);
++ reset_control_deassert(dmov_conf[adm].c1_reset);
++ reset_control_deassert(dmov_conf[adm].c0_reset);
++ reset_control_deassert(dmov_conf[adm].adm_reset);
++
++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) {
++ struct msm_dmov_chan_conf *chan_conf =
++ dmov_conf[adm].chan_conf;
++ unsigned conf;
++ /* Only configure scorpion channels */
++ if (chan_conf[i].sd <= 1) {
++ conf = readl_relaxed(DMOV_REG(DMOV_CONF(i), adm));
++ conf |= DMOV_CONF_MPU_DISABLE |
++ DMOV_CONF_PERM_MPU_CONF |
++ DMOV_CONF_FLUSH_RSLT_EN |
++ DMOV_CONF_FORCE_RSLT_EN |
++ DMOV_CONF_IRQ_EN |
++ DMOV_CONF_PRIORITY(chan_conf[i].priority);
++
++ conf &= ~DMOV_CONF_SD(7);
++ conf |= DMOV_CONF_SD(chan_conf[i].sd);
++ writel_relaxed(conf, DMOV_REG(DMOV_CONF(i), adm));
++ }
++ }
++
++ for (i = 0; i < MSM_DMOV_CRCI_COUNT; i++) {
++ writel_relaxed(DMOV_CRCI_CTL_RST,
++ DMOV_REG(DMOV_CRCI_CTL(i), adm));
++ }
++
++ /* NAND CRCI Enable */
++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_DATA), adm));
++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_CMD), adm));
++
++ /* GSBI5 CRCI Enable */
++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_RX_CRCI), adm));
++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_TX_CRCI), adm));
++
++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x40) | /* EBI1 */
++ DMOV_CI_CONF_RANGE_END(0xb0) |
++ DMOV_CI_CONF_MAX_BURST(0x8),
++ DMOV_REG(DMOV_CI_CONF(0), adm));
++
++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x2a) | /* IMEM */
++ DMOV_CI_CONF_RANGE_END(0x2c) |
++ DMOV_CI_CONF_MAX_BURST(0x8),
++ DMOV_REG(DMOV_CI_CONF(1), adm));
++
++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x12) | /* CPSS/SPS */
++ DMOV_CI_CONF_RANGE_END(0x28) |
++ DMOV_CI_CONF_MAX_BURST(0x8),
++ DMOV_REG(DMOV_CI_CONF(2), adm));
++
++ writel_relaxed(DMOV_HI_GP_CTL_CORE_CLK_LP_EN | /* will disable LP */
++ DMOV_HI_GP_CTL_LP_CNT(0xf),
++ DMOV_REG(DMOV_HI_GP_CTL, adm));
++
++}
++
++static int msm_dmov_probe(struct platform_device *pdev)
++{
++
++ int adm = (pdev->id >= 0) ? pdev->id : 0;
++ int i;
++ int ret;
++ struct resource *irqres =
++ platform_get_resource(pdev, IORESOURCE_IRQ, 0);
++ struct resource *mres =
++ platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ dmov_conf[adm].sd=0;
++ dmov_conf[adm].sd_size=0x800;
++
++ dmov_conf[adm].irq = irqres->start;
++
++ dmov_conf[adm].base = devm_ioremap_resource(&pdev->dev, mres);
++ if (!dmov_conf[adm].base)
++ return -ENOMEM;
++
++ dmov_conf[adm].cmd_wq = alloc_ordered_workqueue("dmov%d_wq", 0, adm);
++ if (!dmov_conf[adm].cmd_wq) {
++ PRINT_ERROR("Couldn't allocate ADM%d workqueue.\n", adm);
++ return -ENOMEM;
++ }
++
++ /* get resets */
++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm");
++ if (IS_ERR(dmov_conf[adm].adm_reset)) {
++ dev_err(&pdev->dev, "failed to get adm reset\n");
++ ret = PTR_ERR(dmov_conf[adm].adm_reset);
++ goto out_wq;
++ }
++
++ dmov_conf[adm].pbus_reset = devm_reset_control_get(&pdev->dev, "pbus");
++ if (IS_ERR(dmov_conf[adm].pbus_reset)) {
++ dev_err(&pdev->dev, "failed to get pbus reset\n");
++ ret = PTR_ERR(dmov_conf[adm].pbus_reset);
++ goto out_wq;
++ }
++
++ dmov_conf[adm].c0_reset = devm_reset_control_get(&pdev->dev, "c0");
++ if (IS_ERR(dmov_conf[adm].c0_reset)) {
++ dev_err(&pdev->dev, "failed to get c0 reset\n");
++ ret = PTR_ERR(dmov_conf[adm].c0_reset);
++ goto out_wq;
++ }
++
++ dmov_conf[adm].c1_reset = devm_reset_control_get(&pdev->dev, "c1");
++ if (IS_ERR(dmov_conf[adm].c1_reset)) {
++ dev_err(&pdev->dev, "failed to get c1 reset\n");
++ ret = PTR_ERR(dmov_conf[adm].c1_reset);
++ goto out_wq;
++ }
++
++ dmov_conf[adm].c2_reset = devm_reset_control_get(&pdev->dev, "c2");
++ if (IS_ERR(dmov_conf[adm].c2_reset)) {
++ dev_err(&pdev->dev, "failed to get c2 reset\n");
++ ret = PTR_ERR(dmov_conf[adm].c2_reset);
++ goto out_wq;
++ }
++
++ ret = devm_request_threaded_irq(&pdev->dev, dmov_conf[adm].irq, NULL,
++ msm_dmov_isr, IRQF_ONESHOT, "msmdatamover", NULL);
++
++ if (ret) {
++ PRINT_ERROR("Requesting ADM%d irq %d failed\n", adm,
++ dmov_conf[adm].irq);
++ goto out_wq;
++ }
++
++ disable_irq(dmov_conf[adm].irq);
++ ret = msm_dmov_init_clocks(pdev);
++ if (ret) {
++ PRINT_ERROR("Requesting ADM%d clocks failed\n", adm);
++ goto out_wq;
++ }
++ clk_prepare_enable(dmov_conf[adm].clk);
++ clk_prepare_enable(dmov_conf[adm].pclk);
++
++// ret = msm_dmov_clk_on(adm);
++// if (ret) {
++// PRINT_ERROR("Enabling ADM%d clocks failed\n", adm);
++// goto out_wq;
++// }
++
++ config_datamover(adm);
++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) {
++ INIT_LIST_HEAD(&dmov_conf[adm].staged_commands[i]);
++ INIT_LIST_HEAD(&dmov_conf[adm].ready_commands[i]);
++ INIT_LIST_HEAD(&dmov_conf[adm].active_commands[i]);
++
++ writel_relaxed(DMOV_RSLT_CONF_IRQ_EN
++ | DMOV_RSLT_CONF_FORCE_FLUSH_RSLT,
++ DMOV_REG(DMOV_RSLT_CONF(i), adm));
++ }
++ wmb();
++// msm_dmov_clk_off(adm);
++ return ret;
++out_wq:
++ destroy_workqueue(dmov_conf[adm].cmd_wq);
++ return ret;
++}
++
++#ifdef CONFIG_OF
++static const struct of_device_id adm_of_match[] = {
++ { .compatible = "qcom,adm", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++#endif
++
++static struct platform_driver msm_dmov_driver = {
++ .probe = msm_dmov_probe,
++ .driver = {
++ .name = MODULE_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = adm_of_match,
++ .pm = &msm_dmov_dev_pm_ops,
++ },
++};
++
++/* static int __init */
++static int __init msm_init_datamover(void)
++{
++ int ret;
++ ret = platform_driver_register(&msm_dmov_driver);
++ if (ret)
++ return ret;
++ return 0;
++}
++arch_initcall(msm_init_datamover);
+diff --git a/drivers/mtd/nand/qcom_adm_dma.h b/drivers/mtd/nand/qcom_adm_dma.h
+new file mode 100644
+index 0000000..1014d57
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_adm_dma.h
+@@ -0,0 +1,268 @@
++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */
++/* linux/include/asm-arm/arch-msm/dma.h
++ *
++ * Copyright (C) 2007 Google, Inc.
++ * Copyright (c) 2008-2012, 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.
++ *
++ */
++
++#ifndef __ASM_ARCH_MSM_DMA_H
++#define __ASM_ARCH_MSM_DMA_H
++#include <linux/list.h>
++
++struct msm_dmov_errdata {
++ uint32_t flush[6];
++};
++
++struct msm_dmov_cmd {
++ struct list_head list;
++ unsigned int cmdptr;
++ void (*complete_func)(struct msm_dmov_cmd *cmd,
++ unsigned int result,
++ struct msm_dmov_errdata *err);
++ void (*exec_func)(struct msm_dmov_cmd *cmd);
++ struct work_struct work;
++ unsigned id; /* For internal use */
++ void *user; /* Pointer for caller's reference */
++ u8 toflush;
++};
++
++struct msm_dmov_pdata {
++ int sd;
++ size_t sd_size;
++};
++
++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd);
++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd);
++void msm_dmov_flush(unsigned int id, int graceful);
++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr);
++
++#define DMOV_CRCIS_PER_CONF 10
++
++#define DMOV_ADDR(off, ch) ((off) + ((ch) << 2))
++
++#define DMOV_CMD_PTR(ch) DMOV_ADDR(0x000, ch)
++#define DMOV_CMD_LIST (0 << 29) /* does not work */
++#define DMOV_CMD_PTR_LIST (1 << 29) /* works */
++#define DMOV_CMD_INPUT_CFG (2 << 29) /* untested */
++#define DMOV_CMD_OUTPUT_CFG (3 << 29) /* untested */
++#define DMOV_CMD_ADDR(addr) ((addr) >> 3)
++
++#define DMOV_RSLT(ch) DMOV_ADDR(0x040, ch)
++#define DMOV_RSLT_VALID (1 << 31) /* 0 == host has empties result fifo */
++#define DMOV_RSLT_ERROR (1 << 3)
++#define DMOV_RSLT_FLUSH (1 << 2)
++#define DMOV_RSLT_DONE (1 << 1) /* top pointer done */
++#define DMOV_RSLT_USER (1 << 0) /* command with FR force result */
++
++#define DMOV_FLUSH0(ch) DMOV_ADDR(0x080, ch)
++#define DMOV_FLUSH1(ch) DMOV_ADDR(0x0C0, ch)
++#define DMOV_FLUSH2(ch) DMOV_ADDR(0x100, ch)
++#define DMOV_FLUSH3(ch) DMOV_ADDR(0x140, ch)
++#define DMOV_FLUSH4(ch) DMOV_ADDR(0x180, ch)
++#define DMOV_FLUSH5(ch) DMOV_ADDR(0x1C0, ch)
++#define DMOV_FLUSH_TYPE (1 << 31)
++
++#define DMOV_STATUS(ch) DMOV_ADDR(0x200, ch)
++#define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29))
++#define DMOV_STATUS_CMD_COUNT(n) (((n) >> 27) & 3)
++#define DMOV_STATUS_RSLT_VALID (1 << 1)
++#define DMOV_STATUS_CMD_PTR_RDY (1 << 0)
++
++#define DMOV_CONF(ch) DMOV_ADDR(0x240, ch)
++#define DMOV_CONF_SD(sd) (((sd & 4) << 11) | ((sd & 3) << 4))
++#define DMOV_CONF_OTHER_CH_BLK_MASK(m) ((m << 0x10) & 0xffff0000)
++#define DMOV_CONF_SHADOW_EN (1 << 12)
++#define DMOV_CONF_MPU_DISABLE (1 << 11)
++#define DMOV_CONF_PERM_MPU_CONF (1 << 9)
++#define DMOV_CONF_FLUSH_RSLT_EN (1 << 8)
++#define DMOV_CONF_IRQ_EN (1 << 6)
++#define DMOV_CONF_FORCE_RSLT_EN (1 << 7)
++#define DMOV_CONF_PRIORITY(n) (n << 0)
++
++#define DMOV_DBG_ERR(ci) DMOV_ADDR(0x280, ci)
++
++#define DMOV_RSLT_CONF(ch) DMOV_ADDR(0x300, ch)
++#define DMOV_RSLT_CONF_FORCE_TOP_PTR_RSLT (1 << 2)
++#define DMOV_RSLT_CONF_FORCE_FLUSH_RSLT (1 << 1)
++#define DMOV_RSLT_CONF_IRQ_EN (1 << 0)
++
++#define DMOV_ISR DMOV_ADDR(0x380, 0)
++
++#define DMOV_CI_CONF(ci) DMOV_ADDR(0x390, ci)
++#define DMOV_CI_CONF_RANGE_END(n) ((n) << 24)
++#define DMOV_CI_CONF_RANGE_START(n) ((n) << 16)
++#define DMOV_CI_CONF_MAX_BURST(n) ((n) << 0)
++
++#define DMOV_CI_DBG_ERR(ci) DMOV_ADDR(0x3B0, ci)
++
++#define DMOV_CRCI_CONF0 DMOV_ADDR(0x3D0, 0)
++#define DMOV_CRCI_CONF0_CRCI9_SD (2 << 0x1b)
++
++#define DMOV_CRCI_CONF1 DMOV_ADDR(0x3D4, 0)
++#define DMOV_CRCI_CONF0_SD(crci, sd) (sd << (crci*3))
++#define DMOV_CRCI_CONF1_SD(crci, sd) (sd << ((crci-DMOV_CRCIS_PER_CONF)*3))
++
++#define DMOV_HI_GP_CTL DMOV_ADDR(0x3D8, 0)
++#define DMOV_HI_GP_CTL_CORE_CLK_LP_EN (1 << 12)
++#define DMOV_HI_GP_CTL_LP_CNT(x) (((x) & 0xf) << 8)
++#define DMOV_HI_GP_CTL_CI3_CLK_LP_EN (1 << 7)
++#define DMOV_HI_GP_CTL_CI2_CLK_LP_EN (1 << 6)
++#define DMOV_HI_GP_CTL_CI1_CLK_LP_EN (1 << 5)
++#define DMOV_HI_GP_CTL_CI0_CLK_LP_EN (1 << 4)
++
++#define DMOV_CRCI_CTL(crci) DMOV_ADDR(0x400, crci)
++#define DMOV_CRCI_CTL_BLK_SZ(n) ((n) << 0)
++#define DMOV_CRCI_CTL_RST (1 << 17)
++#define DMOV_CRCI_MUX (1 << 18)
++
++/* channel assignments */
++
++/*
++ * Format of CRCI numbers: crci number + (muxsel << 4)
++ */
++
++#define DMOV_GP_CHAN 9
++
++#define DMOV_CE_IN_CHAN 0
++#define DMOV_CE_IN_CRCI 2
++
++#define DMOV_CE_OUT_CHAN 1
++#define DMOV_CE_OUT_CRCI 3
++
++#define DMOV_TSIF_CHAN 2
++#define DMOV_TSIF_CRCI 11
++
++#define DMOV_HSUART_GSBI6_TX_CHAN 7
++#define DMOV_HSUART_GSBI6_TX_CRCI 6
++
++#define DMOV_HSUART_GSBI6_RX_CHAN 8
++#define DMOV_HSUART_GSBI6_RX_CRCI 11
++
++#define DMOV_HSUART_GSBI8_TX_CHAN 7
++#define DMOV_HSUART_GSBI8_TX_CRCI 10
++
++#define DMOV_HSUART_GSBI8_RX_CHAN 8
++#define DMOV_HSUART_GSBI8_RX_CRCI 9
++
++#define DMOV_HSUART_GSBI9_TX_CHAN 4
++#define DMOV_HSUART_GSBI9_TX_CRCI 13
++
++#define DMOV_HSUART_GSBI9_RX_CHAN 3
++#define DMOV_HSUART_GSBI9_RX_CRCI 12
++
++#define DMOV_NAND_CHAN 3
++#define DMOV_NAND_CRCI_CMD 15
++#define DMOV_NAND_CRCI_DATA 3
++
++#define DMOV_SPI_GSBI5_RX_CRCI 9
++#define DMOV_SPI_GSBI5_TX_CRCI 10
++#define DMOV_SPI_GSBI5_RX_CHAN 6
++#define DMOV_SPI_GSBI5_TX_CHAN 5
++
++/* channels for APQ8064 */
++#define DMOV8064_CE_IN_CHAN 0
++#define DMOV8064_CE_IN_CRCI 14
++
++#define DMOV8064_CE_OUT_CHAN 1
++#define DMOV8064_CE_OUT_CRCI 15
++
++#define DMOV8064_TSIF_CHAN 2
++#define DMOV8064_TSIF_CRCI 1
++
++/* channels for APQ8064 SGLTE*/
++#define DMOV_APQ8064_HSUART_GSBI4_TX_CHAN 11
++#define DMOV_APQ8064_HSUART_GSBI4_TX_CRCI 8
++
++#define DMOV_APQ8064_HSUART_GSBI4_RX_CHAN 10
++#define DMOV_APQ8064_HSUART_GSBI4_RX_CRCI 7
++
++/* channels for MPQ8064 */
++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN 7
++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI 6
++
++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN 6
++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI 11
++
++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CHAN DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN
++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CRCI DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI
++
++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CHAN DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN
++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CRCI DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI
++
++/* no client rate control ifc (eg, ram) */
++#define DMOV_NONE_CRCI 0
++
++
++/* If the CMD_PTR register has CMD_PTR_LIST selected, the data mover
++ * is going to walk a list of 32bit pointers as described below. Each
++ * pointer points to a *array* of dmov_s, etc structs. The last pointer
++ * in the list is marked with CMD_PTR_LP. The last struct in each array
++ * is marked with CMD_LC (see below).
++ */
++#define CMD_PTR_ADDR(addr) ((addr) >> 3)
++#define CMD_PTR_LP (1 << 31) /* last pointer */
++#define CMD_PTR_PT (3 << 29) /* ? */
++
++/* Single Item Mode */
++typedef struct {
++ unsigned cmd;
++ unsigned src;
++ unsigned dst;
++ unsigned len;
++} dmov_s;
++
++/* Scatter/Gather Mode */
++typedef struct {
++ unsigned cmd;
++ unsigned src_dscr;
++ unsigned dst_dscr;
++ unsigned _reserved;
++} dmov_sg;
++
++/* Box mode */
++typedef struct {
++ uint32_t cmd;
++ uint32_t src_row_addr;
++ uint32_t dst_row_addr;
++ uint32_t src_dst_len;
++ uint32_t num_rows;
++ uint32_t row_offset;
++} dmov_box;
++
++/* bits for the cmd field of the above structures */
++
++#define CMD_LC (1 << 31) /* last command */
++#define CMD_FR (1 << 22) /* force result -- does not work? */
++#define CMD_OCU (1 << 21) /* other channel unblock */
++#define CMD_OCB (1 << 20) /* other channel block */
++#define CMD_TCB (1 << 19) /* ? */
++#define CMD_DAH (1 << 18) /* destination address hold -- does not work?*/
++#define CMD_SAH (1 << 17) /* source address hold -- does not work? */
++
++#define CMD_MODE_SINGLE (0 << 0) /* dmov_s structure used */
++#define CMD_MODE_SG (1 << 0) /* untested */
++#define CMD_MODE_IND_SG (2 << 0) /* untested */
++#define CMD_MODE_BOX (3 << 0) /* untested */
++
++#define CMD_DST_SWAP_BYTES (1 << 14) /* exchange each byte n with byte n+1 */
++#define CMD_DST_SWAP_SHORTS (1 << 15) /* exchange each short n with short n+1 */
++#define CMD_DST_SWAP_WORDS (1 << 16) /* exchange each word n with word n+1 */
++
++#define CMD_SRC_SWAP_BYTES (1 << 11) /* exchange each byte n with byte n+1 */
++#define CMD_SRC_SWAP_SHORTS (1 << 12) /* exchange each short n with short n+1 */
++#define CMD_SRC_SWAP_WORDS (1 << 13) /* exchange each word n with word n+1 */
++
++#define CMD_DST_CRCI(n) (((n) & 15) << 7)
++#define CMD_SRC_CRCI(n) (((n) & 15) << 3)
++
++#endif
+diff --git a/drivers/mtd/nand/qcom_nand.c b/drivers/mtd/nand/qcom_nand.c
+new file mode 100644
+index 0000000..9314132
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nand.c
+@@ -0,0 +1,7455 @@
++/*
++ * Copyright (C) 2007 Google, Inc.
++ * Copyright (c) 2008-2012, 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/slab.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/platform_device.h>
++#include <linux/sched.h>
++#include <linux/dma-mapping.h>
++#include <linux/io.h>
++#include <linux/crc16.h>
++#include <linux/bitrev.h>
++#include <linux/clk.h>
++
++#include <asm/dma.h>
++#include <asm/mach/flash.h>
++
++#include "qcom_adm_dma.h"
++
++#include "qcom_nand.h"
++unsigned long msm_nand_phys = 0;
++unsigned long msm_nandc01_phys = 0;
++unsigned long msm_nandc10_phys = 0;
++unsigned long msm_nandc11_phys = 0;
++unsigned long ebi2_register_base = 0;
++static uint32_t dual_nand_ctlr_present;
++static uint32_t interleave_enable;
++static uint32_t enable_bch_ecc;
++static uint32_t boot_layout;
++
++
++#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K
++#define MSM_NAND_DMA_BUFFER_SLOTS \
++ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8))
++
++#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800
++#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000
++#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d
++#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d
++
++#define ONFI_IDENTIFIER_LENGTH 0x0004
++#define ONFI_PARAM_INFO_LENGTH 0x0200
++#define ONFI_PARAM_PAGE_LENGTH 0x0100
++
++#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F
++
++#define FLASH_READ_ONFI_IDENTIFIER_COMMAND 0x90
++#define FLASH_READ_ONFI_IDENTIFIER_ADDRESS 0x20
++#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC
++#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00
++
++#define UD_SIZE_BYTES_MASK (0x3FF << 9)
++#define SPARE_SIZE_BYTES_MASK (0xF << 23)
++#define ECC_NUM_DATA_BYTES_MASK (0x3FF << 16)
++
++#define VERBOSE 0
++
++struct msm_nand_chip {
++ struct device *dev;
++ wait_queue_head_t wait_queue;
++ atomic_t dma_buffer_busy;
++ unsigned dma_channel;
++ uint8_t *dma_buffer;
++ dma_addr_t dma_addr;
++ unsigned CFG0, CFG1, CFG0_RAW, CFG1_RAW;
++ uint32_t ecc_buf_cfg;
++ uint32_t ecc_bch_cfg;
++ uint32_t ecc_parity_bytes;
++ unsigned cw_size;
++ unsigned int uncorrectable_bit_mask;
++ unsigned int num_err_mask;
++};
++
++#define CFG1_WIDE_FLASH (1U << 1)
++
++/* TODO: move datamover code out */
++
++#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
++#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
++#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
++#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
++
++#define msm_virt_to_dma(chip, vaddr) \
++ ((chip)->dma_addr + \
++ ((uint8_t *)(vaddr) - (chip)->dma_buffer))
++
++/**
++ * msm_nand_oob_64 - oob info for 2KB page
++ */
++static struct nand_ecclayout msm_nand_oob_64 = {
++ .eccbytes = 40,
++ .eccpos = {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
++ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
++ },
++ .oobavail = 16,
++ .oobfree = {
++ {30, 16},
++ }
++};
++
++/**
++ * msm_nand_oob_128 - oob info for 4KB page
++ */
++static struct nand_ecclayout msm_nand_oob_128 = {
++ .eccbytes = 80,
++ .eccpos = {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
++ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
++ },
++ .oobavail = 32,
++ .oobfree = {
++ {70, 32},
++ }
++};
++
++/**
++ * msm_nand_oob_224 - oob info for 4KB page 8Bit interface
++ */
++static struct nand_ecclayout msm_nand_oob_224_x8 = {
++ .eccbytes = 104,
++ .eccpos = {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
++ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
++ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
++ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
++ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
++ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
++ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
++ },
++ .oobavail = 32,
++ .oobfree = {
++ {91, 32},
++ }
++};
++
++/**
++ * msm_nand_oob_224 - oob info for 4KB page 16Bit interface
++ */
++static struct nand_ecclayout msm_nand_oob_224_x16 = {
++ .eccbytes = 112,
++ .eccpos = {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
++ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
++ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
++ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
++ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
++ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
++ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
++ },
++ .oobavail = 32,
++ .oobfree = {
++ {98, 32},
++ }
++};
++
++/**
++ * msm_nand_oob_256 - oob info for 8KB page
++ */
++static struct nand_ecclayout msm_nand_oob_256 = {
++ .eccbytes = 160,
++ .eccpos = {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
++ 90, 91, 92, 93, 94, 96, 97, 98 , 99, 100,
++ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
++ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
++ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
++ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
++ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
++ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
++ },
++ .oobavail = 64,
++ .oobfree = {
++ {151, 64},
++ }
++};
++
++/**
++ * msm_onenand_oob_64 - oob info for large (2KB) page
++ */
++static struct nand_ecclayout msm_onenand_oob_64 = {
++ .eccbytes = 20,
++ .eccpos = {
++ 8, 9, 10, 11, 12,
++ 24, 25, 26, 27, 28,
++ 40, 41, 42, 43, 44,
++ 56, 57, 58, 59, 60,
++ },
++ .oobavail = 20,
++ .oobfree = {
++ {2, 3}, {14, 2}, {18, 3}, {30, 2},
++ {34, 3}, {46, 2}, {50, 3}, {62, 2}
++ }
++};
++
++static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size)
++{
++ unsigned int bitmask, free_bitmask, old_bitmask;
++ unsigned int need_mask, current_need_mask;
++ int free_index;
++
++ need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
++ bitmask = atomic_read(&chip->dma_buffer_busy);
++ free_bitmask = ~bitmask;
++ while (free_bitmask) {
++ free_index = __ffs(free_bitmask);
++ current_need_mask = need_mask << free_index;
++
++ if (size + free_index * MSM_NAND_DMA_BUFFER_SLOTS >=
++ MSM_NAND_DMA_BUFFER_SIZE)
++ return NULL;
++
++ if ((bitmask & current_need_mask) == 0) {
++ old_bitmask =
++ atomic_cmpxchg(&chip->dma_buffer_busy,
++ bitmask,
++ bitmask | current_need_mask);
++ if (old_bitmask == bitmask)
++ return chip->dma_buffer +
++ free_index * MSM_NAND_DMA_BUFFER_SLOTS;
++ free_bitmask = 0; /* force return */
++ }
++ /* current free range was too small, clear all free bits */
++ /* below the top busy bit within current_need_mask */
++ free_bitmask &=
++ ~(~0U >> (32 - fls(bitmask & current_need_mask)));
++ }
++
++ return NULL;
++}
++
++static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip,
++ void *buffer, size_t size)
++{
++ int index;
++ unsigned int used_mask;
++
++ used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
++ index = ((uint8_t *)buffer - chip->dma_buffer) /
++ MSM_NAND_DMA_BUFFER_SLOTS;
++ atomic_sub(used_mask << index, &chip->dma_buffer_busy);
++
++ wake_up(&chip->wait_queue);
++}
++
++
++unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr)
++{
++ struct {
++ dmov_s cmd;
++ unsigned cmdptr;
++ unsigned data;
++ } *dma_buffer;
++ unsigned rv;
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU;
++ dma_buffer->cmd.src = addr;
++ dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data);
++ dma_buffer->cmd.len = 4;
++
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++ dma_buffer->data = 0xeeeeeeee;
++
++ mb();
++ msm_dmov_exec_cmd(
++ chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ rv = dma_buffer->data;
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ return rv;
++}
++
++void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val)
++{
++ struct {
++ dmov_s cmd;
++ unsigned cmdptr;
++ unsigned data;
++ } *dma_buffer;
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU;
++ dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data);
++ dma_buffer->cmd.dst = addr;
++ dma_buffer->cmd.len = 4;
++
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++ dma_buffer->data = val;
++
++ mb();
++ msm_dmov_exec_cmd(
++ chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++}
++
++/*
++ * Allocates a bounce buffer, and stores the buffer address in
++ * variable pointed to by bounce_buf. bounce_buf should point to a
++ * stack variable, to avoid SMP issues.
++ */
++static int msm_nand_alloc_bounce(void *addr, size_t size,
++ enum dma_data_direction dir,
++ uint8_t **bounce_buf)
++{
++ if (bounce_buf == NULL) {
++ printk(KERN_ERR "not allocating bounce buffer\n");
++ return -EINVAL;
++ }
++
++ *bounce_buf = kmalloc(size, GFP_KERNEL | GFP_NOFS | GFP_DMA);
++ if (*bounce_buf == NULL) {
++ printk(KERN_ERR "error alloc bounce buffer %zu\n", size);
++ return -ENOMEM;
++ }
++
++ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)
++ memcpy(*bounce_buf, addr, size);
++
++ return 0;
++}
++
++/*
++ * Maps the user buffer for DMA. If the buffer is vmalloced and the
++ * buffer crosses a page boundary, then we kmalloc a bounce buffer and
++ * copy the data into it. The bounce buffer is stored in the variable
++ * pointed to by bounce_buf, for freeing up later on. The bounce_buf
++ * should point to a stack variable, to avoid SMP issues.
++ */
++static dma_addr_t
++msm_nand_dma_map(struct device *dev, void *addr, size_t size,
++ enum dma_data_direction dir, uint8_t **bounce_buf)
++{
++ int ret;
++ struct page *page;
++ unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
++
++ if (virt_addr_valid(addr)) {
++ page = virt_to_page(addr);
++ } else {
++ if (size + offset > PAGE_SIZE) {
++ ret = msm_nand_alloc_bounce(addr, size, dir, bounce_buf);
++ if (ret < 0)
++ return DMA_ERROR_CODE;
++
++ offset = (unsigned long)*bounce_buf & ~PAGE_MASK;
++ page = virt_to_page(*bounce_buf);
++ } else {
++ page = vmalloc_to_page(addr);
++ }
++ }
++
++ return dma_map_page(dev, page, offset, size, dir);
++}
++
++static void msm_nand_dma_unmap(struct device *dev, dma_addr_t addr, size_t size,
++ enum dma_data_direction dir,
++ void *orig_buf, void *bounce_buf)
++{
++ dma_unmap_page(dev, addr, size, dir);
++
++ if (bounce_buf != NULL) {
++ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
++ memcpy(orig_buf, bounce_buf, size);
++
++ kfree(bounce_buf);
++ }
++}
++
++uint32_t flash_read_id(struct msm_nand_chip *chip)
++{
++ struct {
++ dmov_s cmd[9];
++ unsigned cmdptr;
++ unsigned data[7];
++ } *dma_buffer;
++ uint32_t rv;
++ dmov_s *cmd;
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ dma_buffer->data[0] = 0 | 4;
++ dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID;
++ dma_buffer->data[2] = 1;
++ dma_buffer->data[3] = 0xeeeeeeee;
++ dma_buffer->data[4] = 0xeeeeeeee;
++ dma_buffer->data[5] = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG);
++ dma_buffer->data[6] = 0x00000000;
++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->data) - 1);
++
++ cmd = dma_buffer->cmd;
++
++ cmd->cmd = 0 | CMD_OCB;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
++ cmd->dst = MSM_NAND_ADDR1;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[1]);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[2]);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[3]);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_READ_ID;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[4]);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = CMD_OCU | CMD_LC;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[5]);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->cmd) - 1);
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3
++ ) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ pr_info("status: %x\n", dma_buffer->data[3]);
++ pr_info("nandid: %x maker %02x device %02x\n",
++ dma_buffer->data[4], dma_buffer->data[4] & 0xff,
++ (dma_buffer->data[4] >> 8) & 0xff);
++ rv = dma_buffer->data[4];
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++ return rv;
++}
++
++struct flash_identification {
++ uint32_t flash_id;
++ uint32_t density;
++ uint32_t widebus;
++ uint32_t pagesize;
++ uint32_t blksize;
++ uint32_t oobsize;
++ uint32_t ecc_correctability;
++} supported_flash;
++
++uint16_t flash_onfi_crc_check(uint8_t *buffer, uint16_t count)
++{
++ int i;
++ uint16_t result;
++
++ for (i = 0; i < count; i++)
++ buffer[i] = bitrev8(buffer[i]);
++
++ result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count));
++
++ for (i = 0; i < count; i++)
++ buffer[i] = bitrev8(buffer[i]);
++
++ return result;
++}
++
++static void flash_reset(struct msm_nand_chip *chip)
++{
++ struct {
++ dmov_s cmd[6];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t exec;
++ uint32_t flash_status;
++ uint32_t sflash_bcfg_orig;
++ uint32_t sflash_bcfg_mod;
++ uint32_t chip_select;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ dma_addr_t dma_cmd;
++ dma_addr_t dma_cmdptr;
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ dma_buffer->data.sflash_bcfg_orig
++ = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG);
++ dma_buffer->data.sflash_bcfg_mod = 0x00000000;
++ dma_buffer->data.chip_select = 4;
++ dma_buffer->data.cmd = MSM_NAND_CMD_RESET;
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.flash_status = 0xeeeeeeee;
++
++ cmd = dma_buffer->cmd;
++
++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_mod);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select);
++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready, & write Reset command */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status);
++ cmd->len = 4;
++ cmd++;
++
++ /* Restore the SFLASH_BURST_CONFIG register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_orig);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->cmd));
++
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_cmd = msm_virt_to_dma(chip, dma_buffer->cmd);
++ dma_buffer->cmdptr = (dma_cmd >> 3) | CMD_PTR_LP;
++
++ mb();
++ dma_cmdptr = msm_virt_to_dma(chip, &dma_buffer->cmdptr);
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(dma_cmdptr));
++ mb();
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++}
++
++uint32_t flash_onfi_probe(struct msm_nand_chip *chip)
++{
++
++
++ struct onfi_param_page {
++ uint32_t parameter_page_signature;
++ uint16_t revision_number;
++ uint16_t features_supported;
++ uint16_t optional_commands_supported;
++ uint8_t reserved0[22];
++ uint8_t device_manufacturer[12];
++ uint8_t device_model[20];
++ uint8_t jedec_manufacturer_id;
++ uint16_t date_code;
++ uint8_t reserved1[13];
++ uint32_t number_of_data_bytes_per_page;
++ uint16_t number_of_spare_bytes_per_page;
++ uint32_t number_of_data_bytes_per_partial_page;
++ uint16_t number_of_spare_bytes_per_partial_page;
++ uint32_t number_of_pages_per_block;
++ uint32_t number_of_blocks_per_logical_unit;
++ uint8_t number_of_logical_units;
++ uint8_t number_of_address_cycles;
++ uint8_t number_of_bits_per_cell;
++ uint16_t maximum_bad_blocks_per_logical_unit;
++ uint16_t block_endurance;
++ uint8_t guaranteed_valid_begin_blocks;
++ uint16_t guaranteed_valid_begin_blocks_endurance;
++ uint8_t number_of_programs_per_page;
++ uint8_t partial_program_attributes;
++ uint8_t number_of_bits_ecc_correctability;
++ uint8_t number_of_interleaved_address_bits;
++ uint8_t interleaved_operation_attributes;
++ uint8_t reserved2[13];
++ uint8_t io_pin_capacitance;
++ uint16_t timing_mode_support;
++ uint16_t program_cache_timing_mode_support;
++ uint16_t maximum_page_programming_time;
++ uint16_t maximum_block_erase_time;
++ uint16_t maximum_page_read_time;
++ uint16_t maximum_change_column_setup_time;
++ uint8_t reserved3[23];
++ uint16_t vendor_specific_revision_number;
++ uint8_t vendor_specific[88];
++ uint16_t integrity_crc;
++
++ } __attribute__((__packed__));
++
++ struct onfi_param_page *onfi_param_page_ptr;
++ uint8_t *onfi_identifier_buf = NULL;
++ uint8_t *onfi_param_info_buf = NULL;
++
++ struct {
++ dmov_s cmd[12];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t exec;
++ uint32_t flash_status;
++ uint32_t devcmd1_orig;
++ uint32_t devcmdvld_orig;
++ uint32_t devcmd1_mod;
++ uint32_t devcmdvld_mod;
++ uint32_t sflash_bcfg_orig;
++ uint32_t sflash_bcfg_mod;
++ uint32_t chip_select;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ unsigned page_address = 0;
++ int err = 0;
++ dma_addr_t dma_addr_param_info = 0;
++ dma_addr_t dma_addr_identifier = 0;
++ unsigned cmd_set_count = 2;
++ unsigned crc_chk_count = 0;
++
++ /*if (msm_nand_data.nr_parts) {
++ page_address = ((msm_nand_data.parts[0]).offset << 6);
++ } else {
++ pr_err("flash_onfi_probe: "
++ "No partition info available\n");
++ err = -EIO;
++ return err;
++ }*/
++
++ wait_event(chip->wait_queue, (onfi_identifier_buf =
++ msm_nand_get_dma_buffer(chip, ONFI_IDENTIFIER_LENGTH)));
++ dma_addr_identifier = msm_virt_to_dma(chip, onfi_identifier_buf);
++
++ wait_event(chip->wait_queue, (onfi_param_info_buf =
++ msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH)));
++ dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf);
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ dma_buffer->data.sflash_bcfg_orig = flash_rd_reg
++ (chip, MSM_NAND_SFLASHC_BURST_CFG);
++ dma_buffer->data.devcmd1_orig = flash_rd_reg(chip, MSM_NAND_DEV_CMD1);
++ dma_buffer->data.devcmdvld_orig = flash_rd_reg(chip,
++ MSM_NAND_DEV_CMD_VLD);
++ dma_buffer->data.chip_select = 4;
++
++ while (cmd_set_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.devcmd1_mod = (dma_buffer->data.devcmd1_orig &
++ 0xFFFFFF00) | (cmd_set_count
++ ? FLASH_READ_ONFI_IDENTIFIER_COMMAND
++ : FLASH_READ_ONFI_PARAMETERS_COMMAND);
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
++ dma_buffer->data.addr0 = (page_address << 16) | (cmd_set_count
++ ? FLASH_READ_ONFI_IDENTIFIER_ADDRESS
++ : FLASH_READ_ONFI_PARAMETERS_ADDRESS);
++ dma_buffer->data.addr1 = (page_address >> 16) & 0xFF;
++ dma_buffer->data.cfg0 = (cmd_set_count
++ ? MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER
++ : MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO);
++ dma_buffer->data.cfg1 = (cmd_set_count
++ ? MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER
++ : MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO);
++ dma_buffer->data.sflash_bcfg_mod = 0x00000000;
++ dma_buffer->data.devcmdvld_mod = (dma_buffer->
++ data.devcmdvld_orig & 0xFFFFFFFE);
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.flash_status = 0xeeeeeeee;
++
++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sflash_bcfg_mod);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select);
++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready, & write CMD,ADDR0,ADDR1,CHIPSEL regs */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ cmd->len = 12;
++ cmd++;
++
++ /* Configure the CFG0 and CFG1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = MSM_NAND_DEV0_CFG0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Configure the DEV_CMD_VLD register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.devcmdvld_mod);
++ cmd->dst = MSM_NAND_DEV_CMD_VLD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Configure the DEV_CMD1 register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.devcmd1_mod);
++ cmd->dst = MSM_NAND_DEV_CMD1;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the two status registers */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.flash_status);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read data block - valid only if status says success */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER;
++ cmd->dst = (cmd_set_count ? dma_addr_identifier :
++ dma_addr_param_info);
++ cmd->len = (cmd_set_count ? ONFI_IDENTIFIER_LENGTH :
++ ONFI_PARAM_INFO_LENGTH);
++ cmd++;
++
++ /* Restore the DEV_CMD1 register */
++ cmd->cmd = 0 ;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.devcmd1_orig);
++ cmd->dst = MSM_NAND_DEV_CMD1;
++ cmd->len = 4;
++ cmd++;
++
++ /* Restore the DEV_CMD_VLD register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.devcmdvld_orig);
++ cmd->dst = MSM_NAND_DEV_CMD_VLD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Restore the SFLASH_BURST_CONFIG register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sflash_bcfg_orig);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(12 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ /* Check for errors, protection violations etc */
++ if (dma_buffer->data.flash_status & 0x110) {
++ pr_info("MPU/OP error (0x%x) during "
++ "ONFI probe\n",
++ dma_buffer->data.flash_status);
++ err = -EIO;
++ break;
++ }
++
++ if (cmd_set_count) {
++ onfi_param_page_ptr = (struct onfi_param_page *)
++ (&(onfi_identifier_buf[0]));
++ if (onfi_param_page_ptr->parameter_page_signature !=
++ ONFI_PARAMETER_PAGE_SIGNATURE) {
++ pr_info("ONFI probe : Found a non"
++ "ONFI Compliant device \n");
++ err = -EIO;
++ break;
++ }
++ } else {
++ for (crc_chk_count = 0; crc_chk_count <
++ ONFI_PARAM_INFO_LENGTH
++ / ONFI_PARAM_PAGE_LENGTH;
++ crc_chk_count++) {
++ onfi_param_page_ptr =
++ (struct onfi_param_page *)
++ (&(onfi_param_info_buf
++ [ONFI_PARAM_PAGE_LENGTH *
++ crc_chk_count]));
++ if (flash_onfi_crc_check(
++ (uint8_t *)onfi_param_page_ptr,
++ ONFI_PARAM_PAGE_LENGTH - 2) ==
++ onfi_param_page_ptr->integrity_crc) {
++ break;
++ }
++ }
++ if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH
++ / ONFI_PARAM_PAGE_LENGTH) {
++ pr_info("ONFI probe : CRC Check "
++ "failed on ONFI Parameter "
++ "data \n");
++ err = -EIO;
++ break;
++ } else {
++ supported_flash.flash_id =
++ flash_read_id(chip);
++ supported_flash.widebus =
++ onfi_param_page_ptr->
++ features_supported & 0x01;
++ supported_flash.pagesize =
++ onfi_param_page_ptr->
++ number_of_data_bytes_per_page;
++ supported_flash.blksize =
++ onfi_param_page_ptr->
++ number_of_pages_per_block *
++ supported_flash.pagesize;
++ supported_flash.oobsize =
++ onfi_param_page_ptr->
++ number_of_spare_bytes_per_page;
++ supported_flash.density =
++ onfi_param_page_ptr->
++ number_of_blocks_per_logical_unit
++ * supported_flash.blksize;
++ supported_flash.ecc_correctability =
++ onfi_param_page_ptr->
++ number_of_bits_ecc_correctability;
++
++ pr_info("ONFI probe : Found an ONFI "
++ "compliant device %s\n",
++ onfi_param_page_ptr->device_model);
++
++ /* Temporary hack for MT29F4G08ABC device.
++ * Since the device is not properly adhering
++ * to ONFi specification it is reporting
++ * as 16 bit device though it is 8 bit device!!!
++ */
++ if (!strncmp(onfi_param_page_ptr->device_model,
++ "MT29F4G08ABC", 12))
++ supported_flash.widebus = 0;
++ }
++ }
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++ msm_nand_release_dma_buffer(chip, onfi_param_info_buf,
++ ONFI_PARAM_INFO_LENGTH);
++ msm_nand_release_dma_buffer(chip, onfi_identifier_buf,
++ ONFI_IDENTIFIER_LENGTH);
++
++ return err;
++}
++
++static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from,
++ struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[8 * 5 + 2];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t eccbchcfg;
++ uint32_t exec;
++ uint32_t ecccfg;
++ struct {
++ uint32_t flash_status;
++ uint32_t buffer_status;
++ } result[8];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned n;
++ unsigned page = 0;
++ uint32_t oob_len;
++ uint32_t sectordatasize;
++ uint32_t sectoroobsize;
++ int err, pageerr, rawerr;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++ uint8_t *dat_bounce_buf = NULL;
++ uint8_t *oob_bounce_buf = NULL;
++ uint32_t oob_col = 0;
++ unsigned page_count;
++ unsigned pages_read = 0;
++ unsigned start_sector = 0;
++ uint32_t ecc_errors;
++ uint32_t total_ecc_errors = 0;
++ unsigned cwperpage;
++#if VERBOSE
++ pr_info("================================================="
++ "================\n");
++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n",
++ __func__, from, ops->mode, ops->datbuf, ops->len,
++ ops->oobbuf, ops->ooblen);
++#endif
++
++ if (mtd->writesize == 2048)
++ page = from >> 11;
++
++ if (mtd->writesize == 4096)
++ page = from >> 12;
++
++ oob_len = ops->ooblen;
++ cwperpage = (mtd->writesize >> 9);
++
++ if (from & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported from, 0x%llx\n",
++ __func__, from);
++ return -EINVAL;
++ }
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
++ /* when ops->datbuf is NULL, ops->len can be ooblen */
++ pr_err("%s: unsupported ops->len, %d\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if (ops->datbuf != NULL &&
++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len,"
++ " %d for MTD_OPS_RAW\n", __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n",
++ __func__, ops->ooboffs);
++ return -EINVAL;
++ }
++
++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB)
++ start_sector = cwperpage - 1;
++
++ if (ops->oobbuf && !ops->datbuf) {
++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
++ mtd->oobavail : mtd->oobsize);
++ if ((page_count == 0) && (ops->ooblen))
++ page_count = 1;
++ } else if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ if (ops->datbuf) {
++ data_dma_addr_curr = data_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
++ DMA_FROM_DEVICE, &dat_bounce_buf);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("msm_nand_read_oob: failed to get dma addr "
++ "for %p\n", ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ memset(ops->oobbuf, 0xff, ops->ooblen);
++ oob_dma_addr_curr = oob_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->oobbuf,
++ ops->ooblen, DMA_BIDIRECTIONAL,
++ &oob_bounce_buf);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("msm_nand_read_oob: failed to get dma addr "
++ "for %p\n", ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ oob_col = start_sector * chip->cw_size;
++ if (chip->CFG1 & CFG1_WIDE_FLASH)
++ oob_col >>= 1;
++
++ err = 0;
++ while (page_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
++ if (ops->mode != MTD_OPS_RAW) {
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC;
++ dma_buffer->data.cfg0 =
++ (chip->CFG0 & ~(7U << 6))
++ | (((cwperpage-1) - start_sector) << 6);
++ dma_buffer->data.cfg1 = chip->CFG1;
++ if (enable_bch_ecc)
++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
++ } else {
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
++ dma_buffer->data.cfg0 = (chip->CFG0_RAW
++ & ~(7U << 6)) | ((cwperpage-1) << 6);
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++ }
++
++ dma_buffer->data.addr0 = (page << 16) | oob_col;
++ dma_buffer->data.addr1 = (page >> 16) & 0xff;
++ /* chipsel_0 + enable DM interface */
++ dma_buffer->data.chipsel = 0 | 4;
++
++
++ /* GO bit for the EXEC register */
++ dma_buffer->data.exec = 1;
++
++
++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result));
++
++ for (n = start_sector; n < cwperpage; n++) {
++ /* flash + buffer status return words */
++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee;
++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee;
++
++ /* block on cmd ready, then
++ * write CMD / ADDR0 / ADDR1 / CHIPSEL
++ * regs in a burst
++ */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ if (n == start_sector)
++ cmd->len = 16;
++ else
++ cmd->len = 4;
++ cmd++;
++
++ if (n == start_sector) {
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = MSM_NAND_DEV0_CFG0;
++ if (enable_bch_ecc)
++ cmd->len = 12;
++ else
++ cmd->len = 8;
++ cmd++;
++
++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ecccfg);
++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
++ cmd->len = 4;
++ cmd++;
++ }
++
++ /* kick the execute register */
++ cmd->cmd = 0;
++ cmd->src =
++ msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.result[n]);
++ /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */
++ cmd->len = 8;
++ cmd++;
++
++ /* read data block
++ * (only valid if status says success)
++ */
++ if (ops->datbuf) {
++ if (ops->mode != MTD_OPS_RAW) {
++ if (!boot_layout)
++ sectordatasize = (n < (cwperpage - 1))
++ ? 516 : (512 - ((cwperpage - 1) << 2));
++ else
++ sectordatasize = 512;
++ } else {
++ sectordatasize = chip->cw_size;
++ }
++
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER;
++ cmd->dst = data_dma_addr_curr;
++ data_dma_addr_curr += sectordatasize;
++ cmd->len = sectordatasize;
++ cmd++;
++ }
++
++ if (ops->oobbuf && (n == (cwperpage - 1)
++ || ops->mode != MTD_OPS_AUTO_OOB)) {
++ cmd->cmd = 0;
++ if (n == (cwperpage - 1)) {
++ cmd->src = MSM_NAND_FLASH_BUFFER +
++ (512 - ((cwperpage - 1) << 2));
++ sectoroobsize = (cwperpage << 2);
++ if (ops->mode != MTD_OPS_AUTO_OOB)
++ sectoroobsize +=
++ chip->ecc_parity_bytes;
++ } else {
++ cmd->src = MSM_NAND_FLASH_BUFFER + 516;
++ sectoroobsize = chip->ecc_parity_bytes;
++ }
++
++ cmd->dst = oob_dma_addr_curr;
++ if (sectoroobsize < oob_len)
++ cmd->len = sectoroobsize;
++ else
++ cmd->len = oob_len;
++ oob_dma_addr_curr += cmd->len;
++ oob_len -= cmd->len;
++ if (cmd->len > 0)
++ cmd++;
++ }
++ }
++
++ BUILD_BUG_ON(8 * 5 + 2 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
++ | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ /* if any of the writes failed (0x10), or there
++ * was a protection violation (0x100), we lose
++ */
++ pageerr = rawerr = 0;
++ for (n = start_sector; n < cwperpage; n++) {
++ if (dma_buffer->data.result[n].flash_status & 0x110) {
++ rawerr = -EIO;
++ break;
++ }
++ }
++ if (rawerr) {
++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) {
++ uint8_t *datbuf = ops->datbuf +
++ pages_read * mtd->writesize;
++
++ dma_sync_single_for_cpu(chip->dev,
++ data_dma_addr_curr-mtd->writesize,
++ mtd->writesize, DMA_BIDIRECTIONAL);
++
++ for (n = 0; n < mtd->writesize; n++) {
++ /* empty blocks read 0x54 at
++ * these offsets
++ */
++ if ((n % 516 == 3 || n % 516 == 175)
++ && datbuf[n] == 0x54)
++ datbuf[n] = 0xff;
++ if (datbuf[n] != 0xff) {
++ pageerr = rawerr;
++ break;
++ }
++ }
++
++ dma_sync_single_for_device(chip->dev,
++ data_dma_addr_curr-mtd->writesize,
++ mtd->writesize, DMA_BIDIRECTIONAL);
++
++ }
++ if (ops->oobbuf) {
++ dma_sync_single_for_cpu(chip->dev,
++ oob_dma_addr_curr - (ops->ooblen - oob_len),
++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
++
++ for (n = 0; n < ops->ooblen; n++) {
++ if (ops->oobbuf[n] != 0xff) {
++ pageerr = rawerr;
++ break;
++ }
++ }
++
++ dma_sync_single_for_device(chip->dev,
++ oob_dma_addr_curr - (ops->ooblen - oob_len),
++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
++ }
++ }
++ if (pageerr) {
++ for (n = start_sector; n < cwperpage; n++) {
++ if (dma_buffer->data.result[n].buffer_status &
++ chip->uncorrectable_bit_mask) {
++ /* not thread safe */
++ mtd->ecc_stats.failed++;
++ pageerr = -EBADMSG;
++ break;
++ }
++ }
++ }
++ if (!rawerr) { /* check for corretable errors */
++ for (n = start_sector; n < cwperpage; n++) {
++ ecc_errors =
++ (dma_buffer->data.result[n].buffer_status
++ & chip->num_err_mask);
++ if (ecc_errors) {
++ total_ecc_errors += ecc_errors;
++ /* not thread safe */
++ mtd->ecc_stats.corrected += ecc_errors;
++ if (ecc_errors > 1)
++ pageerr = -EUCLEAN;
++ }
++ }
++ }
++ if (pageerr && (pageerr != -EUCLEAN || err == 0))
++ err = pageerr;
++
++#if VERBOSE
++ if (rawerr && !pageerr) {
++ pr_err("msm_nand_read_oob %llx %x %x empty page\n",
++ (loff_t)page * mtd->writesize, ops->len,
++ ops->ooblen);
++ } else {
++ for (n = start_sector; n < cwperpage; n++)
++ pr_info("flash_status[%d] = %x,\
++ buffr_status[%d] = %x\n",
++ n, dma_buffer->data.result[n].flash_status,
++ n, dma_buffer->data.result[n].buffer_status);
++ }
++#endif
++ if (err && err != -EUCLEAN && err != -EBADMSG)
++ break;
++ pages_read++;
++ page++;
++ }
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (ops->oobbuf) {
++ msm_nand_dma_unmap(chip->dev, oob_dma_addr,
++ ops->ooblen, DMA_FROM_DEVICE,
++ ops->oobbuf, oob_bounce_buf);
++ }
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf) {
++ msm_nand_dma_unmap(chip->dev, data_dma_addr,
++ ops->len, DMA_BIDIRECTIONAL,
++ ops->datbuf, dat_bounce_buf);
++ }
++
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_read;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize) *
++ pages_read;
++ ops->oobretlen = ops->ooblen - oob_len;
++ if (err)
++ pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n",
++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
++ total_ecc_errors);
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("==================================================="
++ "==============\n");
++#endif
++ return err;
++}
++
++static int msm_nand_read_oob_dualnandc(struct mtd_info *mtd, loff_t from,
++ struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[16 * 6 + 20];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t nandc01_addr0;
++ uint32_t nandc10_addr0;
++ uint32_t nandc11_addr1;
++ uint32_t chipsel_cs0;
++ uint32_t chipsel_cs1;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t eccbchcfg;
++ uint32_t exec;
++ uint32_t ecccfg;
++ uint32_t ebi2_chip_select_cfg0;
++ uint32_t adm_mux_data_ack_req_nc01;
++ uint32_t adm_mux_cmd_ack_req_nc01;
++ uint32_t adm_mux_data_ack_req_nc10;
++ uint32_t adm_mux_cmd_ack_req_nc10;
++ uint32_t adm_default_mux;
++ uint32_t default_ebi2_chip_select_cfg0;
++ uint32_t nc10_flash_dev_cmd_vld;
++ uint32_t nc10_flash_dev_cmd1;
++ uint32_t nc10_flash_dev_cmd_vld_default;
++ uint32_t nc10_flash_dev_cmd1_default;
++ struct {
++ uint32_t flash_status;
++ uint32_t buffer_status;
++ } result[16];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned n;
++ unsigned page = 0;
++ uint32_t oob_len;
++ uint32_t sectordatasize;
++ uint32_t sectoroobsize;
++ int err, pageerr, rawerr;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++ uint32_t oob_col = 0;
++ unsigned page_count;
++ unsigned pages_read = 0;
++ unsigned start_sector = 0;
++ uint32_t ecc_errors;
++ uint32_t total_ecc_errors = 0;
++ unsigned cwperpage;
++ unsigned cw_offset = chip->cw_size;
++#if VERBOSE
++ pr_info("================================================="
++ "============\n");
++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n\n",
++ __func__, from, ops->mode, ops->datbuf,
++ ops->len, ops->oobbuf, ops->ooblen);
++#endif
++
++ if (mtd->writesize == 2048)
++ page = from >> 11;
++
++ if (mtd->writesize == 4096)
++ page = from >> 12;
++
++ if (interleave_enable)
++ page = (from >> 1) >> 12;
++
++ oob_len = ops->ooblen;
++ cwperpage = (mtd->writesize >> 9);
++
++ if (from & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported from, 0x%llx\n",
++ __func__, from);
++ return -EINVAL;
++ }
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
++ pr_err("%s: unsupported ops->len, %d\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if (ops->datbuf != NULL &&
++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len,"
++ " %d for MTD_OPS_RAW\n", __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n",
++ __func__, ops->ooboffs);
++ return -EINVAL;
++ }
++
++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB)
++ start_sector = cwperpage - 1;
++
++ if (ops->oobbuf && !ops->datbuf) {
++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
++ mtd->oobavail : mtd->oobsize);
++ if ((page_count == 0) && (ops->ooblen))
++ page_count = 1;
++ } else if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ if (ops->datbuf) {
++ data_dma_addr_curr = data_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
++ DMA_FROM_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("msm_nand_read_oob_dualnandc: "
++ "failed to get dma addr for %p\n",
++ ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ memset(ops->oobbuf, 0xff, ops->ooblen);
++ oob_dma_addr_curr = oob_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->oobbuf,
++ ops->ooblen, DMA_BIDIRECTIONAL, NULL);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("msm_nand_read_oob_dualnandc: "
++ "failed to get dma addr for %p\n",
++ ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ oob_col = start_sector * chip->cw_size;
++ if (chip->CFG1 & CFG1_WIDE_FLASH) {
++ oob_col >>= 1;
++ cw_offset >>= 1;
++ }
++
++ err = 0;
++ while (page_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ if (ops->mode != MTD_OPS_RAW) {
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC;
++ if (start_sector == (cwperpage - 1)) {
++ dma_buffer->data.cfg0 = (chip->CFG0 &
++ ~(7U << 6));
++ } else {
++ dma_buffer->data.cfg0 = (chip->CFG0 &
++ ~(7U << 6))
++ | (((cwperpage >> 1)-1) << 6);
++ }
++ dma_buffer->data.cfg1 = chip->CFG1;
++ if (enable_bch_ecc)
++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
++ } else {
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW &
++ ~(7U << 6)) | ((((cwperpage >> 1)-1) << 6)));
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++ }
++
++ if (!interleave_enable) {
++ if (start_sector == (cwperpage - 1)) {
++ dma_buffer->data.nandc10_addr0 =
++ (page << 16) | oob_col;
++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0xD;
++ dma_buffer->data.nc10_flash_dev_cmd1 =
++ 0xF00F3000;
++ } else {
++ dma_buffer->data.nandc01_addr0 = page << 16;
++ /* NC10 ADDR0 points to the next code word */
++ dma_buffer->data.nandc10_addr0 = (page << 16) |
++ cw_offset;
++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0x1D;
++ dma_buffer->data.nc10_flash_dev_cmd1 =
++ 0xF00FE005;
++ }
++ } else {
++ dma_buffer->data.nandc01_addr0 =
++ dma_buffer->data.nandc10_addr0 =
++ (page << 16) | oob_col;
++ }
++ /* ADDR1 */
++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff;
++
++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C;
++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14;
++ dma_buffer->data.adm_default_mux = 0x00000FC0;
++ dma_buffer->data.nc10_flash_dev_cmd_vld_default = 0x1D;
++ dma_buffer->data.nc10_flash_dev_cmd1_default = 0xF00F3000;
++
++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
++
++ /* chipsel_0 + enable DM interface */
++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
++ /* chipsel_1 + enable DM interface */
++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
++
++ /* GO bit for the EXEC register */
++ dma_buffer->data.exec = 1;
++
++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.result));
++
++ for (n = start_sector; n < cwperpage; n++) {
++ /* flash + buffer status return words */
++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee;
++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee;
++
++ if (n == start_sector) {
++ if (!interleave_enable) {
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.nc10_flash_dev_cmd_vld);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc10_flash_dev_cmd1);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD1);
++ cmd->len = 4;
++ cmd++;
++
++ /* NC01, NC10 --> ADDR1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc11_addr1);
++ cmd->dst = NC11(MSM_NAND_ADDR1);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC11(MSM_NAND_DEV0_CFG0);
++ if (enable_bch_ecc)
++ cmd->len = 12;
++ else
++ cmd->len = 8;
++ cmd++;
++ } else {
++ /* enable CS0 & CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ /* NC01, NC10 --> ADDR1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc11_addr1);
++ cmd->dst = NC11(MSM_NAND_ADDR1);
++ cmd->len = 4;
++ cmd++;
++
++ /* Enable CS0 for NC01 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.chipsel_cs0);
++ cmd->dst =
++ NC01(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ /* Enable CS1 for NC10 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.chipsel_cs1);
++ cmd->dst =
++ NC10(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ /* config DEV0_CFG0 & CFG1 for CS0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
++ cmd->len = 8;
++ cmd++;
++
++ /* config DEV1_CFG0 & CFG1 for CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
++ cmd->len = 8;
++ cmd++;
++ }
++
++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ecccfg);
++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG);
++ cmd->len = 4;
++ cmd++;
++
++ /* if 'only' the last code word */
++ if (n == cwperpage - 1) {
++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_cmd_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst = NC10(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* NC10 --> ADDR0 ( 0x0 ) */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc10_addr0);
++ cmd->dst = NC10(MSM_NAND_ADDR0);
++ cmd->len = 4;
++ cmd++;
++
++ /* kick the execute reg for NC10 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst = NC10(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready from NC10, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.result[n]);
++ /* MSM_NAND_FLASH_STATUS +
++ * MSM_NAND_BUFFER_STATUS
++ */
++ cmd->len = 8;
++ cmd++;
++ } else {
++ /* NC01 --> ADDR0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc01_addr0);
++ cmd->dst = NC01(MSM_NAND_ADDR0);
++ cmd->len = 4;
++ cmd++;
++
++ /* NC10 --> ADDR1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc10_addr0);
++ cmd->dst = NC10(MSM_NAND_ADDR0);
++ cmd->len = 4;
++ cmd++;
++
++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_cmd_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst = NC01(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* kick the execute register for NC01*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst = NC01(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++ }
++ }
++
++ /* read data block
++ * (only valid if status says success)
++ */
++ if (ops->datbuf || (ops->oobbuf &&
++ ops->mode != MTD_OPS_AUTO_OOB)) {
++ if (ops->mode != MTD_OPS_RAW)
++ sectordatasize = (n < (cwperpage - 1))
++ ? 516 : (512 - ((cwperpage - 1) << 2));
++ else
++ sectordatasize = chip->cw_size;
++
++ if (n % 2 == 0) {
++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_data_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready from NC01, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC01(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.result[n]);
++ /* MSM_NAND_FLASH_STATUS +
++ * MSM_NAND_BUFFER_STATUS
++ */
++ cmd->len = 8;
++ cmd++;
++
++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_cmd_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst = NC10(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* kick the execute register for NC10 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst = NC10(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read only when there is data
++ * buffer
++ */
++ if (ops->datbuf) {
++ cmd->cmd = 0;
++ cmd->src =
++ NC01(MSM_NAND_FLASH_BUFFER);
++ cmd->dst = data_dma_addr_curr;
++ data_dma_addr_curr +=
++ sectordatasize;
++ cmd->len = sectordatasize;
++ cmd++;
++ }
++ } else {
++ /* MASK DATA ACK/REQ -->
++ * NC01 (0xA3C)
++ */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready from NC10
++ * then read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src =
++ NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.result[n]);
++ /* MSM_NAND_FLASH_STATUS +
++ * MSM_NAND_BUFFER_STATUS
++ */
++ cmd->len = 8;
++ cmd++;
++ if (n != cwperpage - 1) {
++ /* MASK CMD ACK/REQ -->
++ * NC10 (0xF14)
++ */
++ cmd->cmd = 0;
++ cmd->src =
++ msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_cmd_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst =
++ NC01(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* EXEC */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst =
++ NC01(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++ }
++
++ /* Read only when there is data
++ * buffer
++ */
++ if (ops->datbuf) {
++ cmd->cmd = 0;
++ cmd->src =
++ NC10(MSM_NAND_FLASH_BUFFER);
++ cmd->dst = data_dma_addr_curr;
++ data_dma_addr_curr +=
++ sectordatasize;
++ cmd->len = sectordatasize;
++ cmd++;
++ }
++ }
++ }
++
++ if (ops->oobbuf && (n == (cwperpage - 1)
++ || ops->mode != MTD_OPS_AUTO_OOB)) {
++ cmd->cmd = 0;
++ if (n == (cwperpage - 1)) {
++ /* Use NC10 for reading the
++ * last codeword!!!
++ */
++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) +
++ (512 - ((cwperpage - 1) << 2));
++ sectoroobsize = (cwperpage << 2);
++ if (ops->mode != MTD_OPS_AUTO_OOB)
++ sectoroobsize +=
++ chip->ecc_parity_bytes;
++ } else {
++ if (n % 2 == 0)
++ cmd->src =
++ NC01(MSM_NAND_FLASH_BUFFER)
++ + 516;
++ else
++ cmd->src =
++ NC10(MSM_NAND_FLASH_BUFFER)
++ + 516;
++ sectoroobsize = chip->ecc_parity_bytes;
++ }
++ cmd->dst = oob_dma_addr_curr;
++ if (sectoroobsize < oob_len)
++ cmd->len = sectoroobsize;
++ else
++ cmd->len = oob_len;
++ oob_dma_addr_curr += cmd->len;
++ oob_len -= cmd->len;
++ if (cmd->len > 0)
++ cmd++;
++ }
++ }
++ /* ADM --> Default mux state (0xFC0) */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_default_mux);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ if (!interleave_enable) {
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc10_flash_dev_cmd_vld_default);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc10_flash_dev_cmd1_default);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD1);
++ cmd->len = 4;
++ cmd++;
++ } else {
++ /* disable CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.default_ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++ }
++
++ BUILD_BUG_ON(16 * 6 + 20 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
++ | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ /* if any of the writes failed (0x10), or there
++ * was a protection violation (0x100), we lose
++ */
++ pageerr = rawerr = 0;
++ for (n = start_sector; n < cwperpage; n++) {
++ if (dma_buffer->data.result[n].flash_status & 0x110) {
++ rawerr = -EIO;
++ break;
++ }
++ }
++ if (rawerr) {
++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) {
++ uint8_t *datbuf = ops->datbuf +
++ pages_read * mtd->writesize;
++
++ dma_sync_single_for_cpu(chip->dev,
++ data_dma_addr_curr-mtd->writesize,
++ mtd->writesize, DMA_BIDIRECTIONAL);
++
++ for (n = 0; n < mtd->writesize; n++) {
++ /* empty blocks read 0x54 at
++ * these offsets
++ */
++ if ((n % 516 == 3 || n % 516 == 175)
++ && datbuf[n] == 0x54)
++ datbuf[n] = 0xff;
++ if (datbuf[n] != 0xff) {
++ pageerr = rawerr;
++ break;
++ }
++ }
++
++ dma_sync_single_for_device(chip->dev,
++ data_dma_addr_curr-mtd->writesize,
++ mtd->writesize, DMA_BIDIRECTIONAL);
++
++ }
++ if (ops->oobbuf) {
++ dma_sync_single_for_cpu(chip->dev,
++ oob_dma_addr_curr - (ops->ooblen - oob_len),
++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
++
++ for (n = 0; n < ops->ooblen; n++) {
++ if (ops->oobbuf[n] != 0xff) {
++ pageerr = rawerr;
++ break;
++ }
++ }
++
++ dma_sync_single_for_device(chip->dev,
++ oob_dma_addr_curr - (ops->ooblen - oob_len),
++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL);
++ }
++ }
++ if (pageerr) {
++ for (n = start_sector; n < cwperpage; n++) {
++ if (dma_buffer->data.result[n].buffer_status
++ & chip->uncorrectable_bit_mask) {
++ /* not thread safe */
++ mtd->ecc_stats.failed++;
++ pageerr = -EBADMSG;
++ break;
++ }
++ }
++ }
++ if (!rawerr) { /* check for corretable errors */
++ for (n = start_sector; n < cwperpage; n++) {
++ ecc_errors = dma_buffer->data.
++ result[n].buffer_status
++ & chip->num_err_mask;
++ if (ecc_errors) {
++ total_ecc_errors += ecc_errors;
++ /* not thread safe */
++ mtd->ecc_stats.corrected += ecc_errors;
++ if (ecc_errors > 1)
++ pageerr = -EUCLEAN;
++ }
++ }
++ }
++ if (pageerr && (pageerr != -EUCLEAN || err == 0))
++ err = pageerr;
++
++#if VERBOSE
++ if (rawerr && !pageerr) {
++ pr_err("msm_nand_read_oob_dualnandc "
++ "%llx %x %x empty page\n",
++ (loff_t)page * mtd->writesize, ops->len,
++ ops->ooblen);
++ } else {
++ for (n = start_sector; n < cwperpage; n++) {
++ if (n%2) {
++ pr_info("NC10: flash_status[%d] = %x, "
++ "buffr_status[%d] = %x\n",
++ n, dma_buffer->
++ data.result[n].flash_status,
++ n, dma_buffer->
++ data.result[n].buffer_status);
++ } else {
++ pr_info("NC01: flash_status[%d] = %x, "
++ "buffr_status[%d] = %x\n",
++ n, dma_buffer->
++ data.result[n].flash_status,
++ n, dma_buffer->
++ data.result[n].buffer_status);
++ }
++ }
++ }
++#endif
++ if (err && err != -EUCLEAN && err != -EBADMSG)
++ break;
++ pages_read++;
++ page++;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (ops->oobbuf) {
++ dma_unmap_page(chip->dev, oob_dma_addr,
++ ops->ooblen, DMA_FROM_DEVICE);
++ }
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf) {
++ dma_unmap_page(chip->dev, data_dma_addr,
++ ops->len, DMA_BIDIRECTIONAL);
++ }
++
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_read;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize) *
++ pages_read;
++ ops->oobretlen = ops->ooblen - oob_len;
++ if (err)
++ pr_err("msm_nand_read_oob_dualnandc "
++ "%llx %x %x failed %d, corrected %d\n",
++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
++ total_ecc_errors);
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("==================================================="
++ "==========\n");
++#endif
++ return err;
++}
++
++static int
++msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ int ret;
++ struct mtd_ecc_stats stats;
++ struct mtd_oob_ops ops;
++ int (*read_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *);
++
++ if (!dual_nand_ctlr_present)
++ read_oob = msm_nand_read_oob;
++ else
++ read_oob = msm_nand_read_oob_dualnandc;
++
++ ops.mode = MTD_OPS_PLACE_OOB;
++ ops.retlen = 0;
++ ops.ooblen = 0;
++ ops.oobbuf = NULL;
++ ret = 0;
++ *retlen = 0;
++ stats = mtd->ecc_stats;
++
++ if ((from & (mtd->writesize - 1)) == 0 && len == mtd->writesize) {
++ /* reading a page on page boundary */
++ ops.len = len;
++ ops.datbuf = buf;
++ ret = read_oob(mtd, from, &ops);
++ *retlen = ops.retlen;
++ } else if (len > 0) {
++ /* reading any size on any offset. partial page is supported */
++ u8 *bounce_buf;
++ loff_t aligned_from;
++ loff_t offset;
++ size_t actual_len;
++
++ bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
++ if (!bounce_buf) {
++ pr_err("%s: could not allocate memory\n", __func__);
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ops.len = mtd->writesize;
++ offset = from & (mtd->writesize - 1);
++ aligned_from = from - offset;
++
++ for (;;) {
++ int no_copy;
++
++ actual_len = mtd->writesize - offset;
++ if (actual_len > len)
++ actual_len = len;
++
++ no_copy = (offset == 0 && actual_len == mtd->writesize);
++ ops.datbuf = (no_copy) ? buf : bounce_buf;
++
++ /*
++ * MTD API requires that all the pages are to
++ * be read even if uncorrectable or
++ * correctable ECC errors occur.
++ */
++ ret = read_oob(mtd, aligned_from, &ops);
++ if (ret == -EBADMSG || ret == -EUCLEAN)
++ ret = 0;
++
++ if (ret < 0)
++ break;
++
++ if (!no_copy)
++ memcpy(buf, bounce_buf + offset, actual_len);
++
++ len -= actual_len;
++ *retlen += actual_len;
++ if (len == 0)
++ break;
++
++ buf += actual_len;
++ offset = 0;
++ aligned_from += mtd->writesize;
++ }
++
++ kfree(bounce_buf);
++ }
++
++out:
++ if (ret)
++ return ret;
++
++ if (mtd->ecc_stats.failed - stats.failed)
++ return -EBADMSG;
++
++ if (mtd->ecc_stats.corrected - stats.corrected)
++ return -EUCLEAN;
++
++ return 0;
++}
++
++static int
++msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++ struct {
++ dmov_s cmd[8 * 7 + 2];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t eccbchcfg;
++ uint32_t exec;
++ uint32_t ecccfg;
++ uint32_t clrfstatus;
++ uint32_t clrrstatus;
++ uint32_t flash_status[8];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned n;
++ unsigned page = 0;
++ uint32_t oob_len;
++ uint32_t sectordatawritesize;
++ int err = 0;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++ uint8_t *dat_bounce_buf = NULL;
++ uint8_t *oob_bounce_buf = NULL;
++ unsigned page_count;
++ unsigned pages_written = 0;
++ unsigned cwperpage;
++#if VERBOSE
++ pr_info("================================================="
++ "================\n");
++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n",
++ __func__, to, ops->mode, ops->datbuf, ops->len,
++ ops->oobbuf, ops->ooblen);
++#endif
++
++ if (mtd->writesize == 2048)
++ page = to >> 11;
++
++ if (mtd->writesize == 4096)
++ page = to >> 12;
++
++ oob_len = ops->ooblen;
++ cwperpage = (mtd->writesize >> 9);
++
++ if (to & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
++ return -EINVAL;
++ }
++
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) {
++ pr_err("%s: unsupported ops->mode,%d\n",
++ __func__, ops->mode);
++ return -EINVAL;
++ }
++ if ((ops->len % mtd->writesize) != 0) {
++ pr_err("%s: unsupported ops->len, %d\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len, "
++ "%d for MTD_OPS_RAW mode\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if (ops->datbuf == NULL) {
++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
++ return -EINVAL;
++ }
++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n",
++ __func__, ops->ooboffs);
++ return -EINVAL;
++ }
++
++ if (ops->datbuf) {
++ data_dma_addr_curr = data_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->datbuf,
++ ops->len, DMA_TO_DEVICE,
++ &dat_bounce_buf);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("msm_nand_write_oob: failed to get dma addr "
++ "for %p\n", ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ oob_dma_addr_curr = oob_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->oobbuf,
++ ops->ooblen, DMA_TO_DEVICE,
++ &oob_bounce_buf);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("msm_nand_write_oob: failed to get dma addr "
++ "for %p\n", ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++ if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ wait_event(chip->wait_queue, (dma_buffer =
++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
++
++ while (page_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ if (ops->mode != MTD_OPS_RAW) {
++ dma_buffer->data.cfg0 = chip->CFG0;
++ dma_buffer->data.cfg1 = chip->CFG1;
++ if (enable_bch_ecc)
++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
++ } else {
++ dma_buffer->data.cfg0 = (chip->CFG0_RAW &
++ ~(7U << 6)) | ((cwperpage-1) << 6);
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++ }
++
++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
++ dma_buffer->data.addr0 = page << 16;
++ dma_buffer->data.addr1 = (page >> 16) & 0xff;
++ /* chipsel_0 + enable DM interface */
++ dma_buffer->data.chipsel = 0 | 4;
++
++
++ /* GO bit for the EXEC register */
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.clrfstatus = 0x00000020;
++ dma_buffer->data.clrrstatus = 0x000000C0;
++
++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status));
++
++ for (n = 0; n < cwperpage ; n++) {
++ /* status return words */
++ dma_buffer->data.flash_status[n] = 0xeeeeeeee;
++ /* block on cmd ready, then
++ * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst
++ */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src =
++ msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ if (n == 0)
++ cmd->len = 16;
++ else
++ cmd->len = 4;
++ cmd++;
++
++ if (n == 0) {
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = MSM_NAND_DEV0_CFG0;
++ if (enable_bch_ecc)
++ cmd->len = 12;
++ else
++ cmd->len = 8;
++ cmd++;
++
++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ecccfg);
++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
++ cmd->len = 4;
++ cmd++;
++ }
++
++ /* write data block */
++ if (ops->mode != MTD_OPS_RAW) {
++ if (!boot_layout)
++ sectordatawritesize = (n < (cwperpage - 1)) ?
++ 516 : (512 - ((cwperpage - 1) << 2));
++ else
++ sectordatawritesize = 512;
++ } else {
++ sectordatawritesize = chip->cw_size;
++ }
++
++ cmd->cmd = 0;
++ cmd->src = data_dma_addr_curr;
++ data_dma_addr_curr += sectordatawritesize;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = sectordatawritesize;
++ cmd++;
++
++ if (ops->oobbuf) {
++ if (n == (cwperpage - 1)) {
++ cmd->cmd = 0;
++ cmd->src = oob_dma_addr_curr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER +
++ (512 - ((cwperpage - 1) << 2));
++ if ((cwperpage << 2) < oob_len)
++ cmd->len = (cwperpage << 2);
++ else
++ cmd->len = oob_len;
++ oob_dma_addr_curr += cmd->len;
++ oob_len -= cmd->len;
++ if (cmd->len > 0)
++ cmd++;
++ }
++ if (ops->mode != MTD_OPS_AUTO_OOB) {
++ /* skip ecc bytes in oobbuf */
++ if (oob_len < chip->ecc_parity_bytes) {
++ oob_dma_addr_curr +=
++ chip->ecc_parity_bytes;
++ oob_len -=
++ chip->ecc_parity_bytes;
++ } else {
++ oob_dma_addr_curr += oob_len;
++ oob_len = 0;
++ }
++ }
++ }
++
++ /* kick the execute register */
++ cmd->cmd = 0;
++ cmd->src =
++ msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.flash_status[n]);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.clrfstatus);
++ cmd->dst = MSM_NAND_FLASH_STATUS;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.clrrstatus);
++ cmd->dst = MSM_NAND_READ_STATUS;
++ cmd->len = 4;
++ cmd++;
++
++ }
++
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++ BUILD_BUG_ON(8 * 7 + 2 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) |
++ CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
++ msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ /* if any of the writes failed (0x10), or there was a
++ * protection violation (0x100), or the program success
++ * bit (0x80) is unset, we lose
++ */
++ err = 0;
++ for (n = 0; n < cwperpage; n++) {
++ if (dma_buffer->data.flash_status[n] & 0x110) {
++ err = -EIO;
++ break;
++ }
++ if (!(dma_buffer->data.flash_status[n] & 0x80)) {
++ err = -EIO;
++ break;
++ }
++ }
++
++#if VERBOSE
++ for (n = 0; n < cwperpage; n++)
++ pr_info("write pg %d: flash_status[%d] = %x\n", page,
++ n, dma_buffer->data.flash_status[n]);
++
++#endif
++ if (err)
++ break;
++ pages_written++;
++ page++;
++ }
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_written;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
++
++ ops->oobretlen = ops->ooblen - oob_len;
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (ops->oobbuf) {
++ msm_nand_dma_unmap(chip->dev, oob_dma_addr,
++ ops->ooblen, DMA_TO_DEVICE,
++ ops->oobbuf, oob_bounce_buf);
++ }
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf) {
++ msm_nand_dma_unmap(chip->dev, data_dma_addr, ops->len,
++ DMA_TO_DEVICE, ops->datbuf,
++ dat_bounce_buf);
++ }
++ if (err)
++ pr_err("msm_nand_write_oob %llx %x %x failed %d\n",
++ to, ops->len, ops->ooblen, err);
++
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("==================================================="
++ "==============\n");
++#endif
++ return err;
++}
++
++static int
++msm_nand_write_oob_dualnandc(struct mtd_info *mtd, loff_t to,
++ struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++ struct {
++ dmov_s cmd[16 * 6 + 18];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t nandc01_addr0;
++ uint32_t nandc10_addr0;
++ uint32_t nandc11_addr1;
++ uint32_t chipsel_cs0;
++ uint32_t chipsel_cs1;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t eccbchcfg;
++ uint32_t exec;
++ uint32_t ecccfg;
++ uint32_t cfg0_nc01;
++ uint32_t ebi2_chip_select_cfg0;
++ uint32_t adm_mux_data_ack_req_nc01;
++ uint32_t adm_mux_cmd_ack_req_nc01;
++ uint32_t adm_mux_data_ack_req_nc10;
++ uint32_t adm_mux_cmd_ack_req_nc10;
++ uint32_t adm_default_mux;
++ uint32_t default_ebi2_chip_select_cfg0;
++ uint32_t nc01_flash_dev_cmd_vld;
++ uint32_t nc10_flash_dev_cmd0;
++ uint32_t nc01_flash_dev_cmd_vld_default;
++ uint32_t nc10_flash_dev_cmd0_default;
++ uint32_t flash_status[16];
++ uint32_t clrfstatus;
++ uint32_t clrrstatus;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned n;
++ unsigned page = 0;
++ uint32_t oob_len;
++ uint32_t sectordatawritesize;
++ int err = 0;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++ unsigned page_count;
++ unsigned pages_written = 0;
++ unsigned cwperpage;
++ unsigned cw_offset = chip->cw_size;
++#if VERBOSE
++ pr_info("================================================="
++ "============\n");
++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n\n",
++ __func__, to, ops->mode, ops->datbuf, ops->len,
++ ops->oobbuf, ops->ooblen);
++#endif
++
++ if (mtd->writesize == 2048)
++ page = to >> 11;
++
++ if (mtd->writesize == 4096)
++ page = to >> 12;
++
++ if (interleave_enable)
++ page = (to >> 1) >> 12;
++
++ oob_len = ops->ooblen;
++ cwperpage = (mtd->writesize >> 9);
++
++ if (to & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
++ return -EINVAL;
++ }
++
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) {
++ pr_err("%s: unsupported ops->mode,%d\n",
++ __func__, ops->mode);
++ return -EINVAL;
++ }
++ if ((ops->len % mtd->writesize) != 0) {
++ pr_err("%s: unsupported ops->len, %d\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len, "
++ "%d for MTD_OPS_RAW mode\n",
++ __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if (ops->datbuf == NULL) {
++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
++ return -EINVAL;
++ }
++
++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n",
++ __func__, ops->ooboffs);
++ return -EINVAL;
++ }
++
++ if (ops->datbuf) {
++ data_dma_addr_curr = data_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->datbuf,
++ ops->len, DMA_TO_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("msm_nand_write_oob_dualnandc:"
++ "failed to get dma addr "
++ "for %p\n", ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ oob_dma_addr_curr = oob_dma_addr =
++ msm_nand_dma_map(chip->dev, ops->oobbuf,
++ ops->ooblen, DMA_TO_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("msm_nand_write_oob_dualnandc:"
++ "failed to get dma addr "
++ "for %p\n", ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++ if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ wait_event(chip->wait_queue, (dma_buffer =
++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
++
++ if (chip->CFG1 & CFG1_WIDE_FLASH)
++ cw_offset >>= 1;
++
++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C;
++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14;
++ dma_buffer->data.adm_default_mux = 0x00000FC0;
++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
++ dma_buffer->data.nc01_flash_dev_cmd_vld = 0x9;
++ dma_buffer->data.nc10_flash_dev_cmd0 = 0x1085D060;
++ dma_buffer->data.nc01_flash_dev_cmd_vld_default = 0x1D;
++ dma_buffer->data.nc10_flash_dev_cmd0_default = 0x1080D060;
++ dma_buffer->data.clrfstatus = 0x00000020;
++ dma_buffer->data.clrrstatus = 0x000000C0;
++
++ while (page_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ if (ops->mode != MTD_OPS_RAW) {
++ dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6))
++ & ~(1 << 4)) | ((((cwperpage >> 1)-1)) << 6);
++ dma_buffer->data.cfg1 = chip->CFG1;
++ if (enable_bch_ecc)
++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
++ } else {
++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW &
++ ~(7U << 6)) & ~(1 << 4)) | (((cwperpage >> 1)-1) << 6);
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++ }
++
++ /* Disables the automatic issuing of the read
++ * status command for first NAND controller.
++ */
++ if (!interleave_enable)
++ dma_buffer->data.cfg0_nc01 = dma_buffer->data.cfg0
++ | (1 << 4);
++ else
++ dma_buffer->data.cfg0 |= (1 << 4);
++
++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
++
++ /* GO bit for the EXEC register */
++ dma_buffer->data.exec = 1;
++
++ if (!interleave_enable) {
++ dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0;
++ /* NC10 ADDR0 points to the next code word */
++ dma_buffer->data.nandc10_addr0 =
++ (page << 16) | cw_offset;
++ } else {
++ dma_buffer->data.nandc01_addr0 =
++ dma_buffer->data.nandc10_addr0 = (page << 16) | 0x0;
++ }
++ /* ADDR1 */
++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff;
++
++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.flash_status));
++
++ for (n = 0; n < cwperpage; n++) {
++ /* status return words */
++ dma_buffer->data.flash_status[n] = 0xeeeeeeee;
++
++ if (n == 0) {
++ if (!interleave_enable) {
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.nc01_flash_dev_cmd_vld);
++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc10_flash_dev_cmd0);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD0);
++ cmd->len = 4;
++ cmd++;
++
++ /* common settings for both NC01 & NC10
++ * NC01, NC10 --> ADDR1 / CHIPSEL
++ */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc11_addr1);
++ cmd->dst = NC11(MSM_NAND_ADDR1);
++ cmd->len = 8;
++ cmd++;
++
++ /* Disables the automatic issue of the
++ * read status command after the write
++ * operation.
++ */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0_nc01);
++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC10(MSM_NAND_DEV0_CFG0);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg1);
++ cmd->dst = NC11(MSM_NAND_DEV0_CFG1);
++ if (enable_bch_ecc)
++ cmd->len = 8;
++ else
++ cmd->len = 4;
++ cmd++;
++ } else {
++ /* enable CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ /* NC11 --> ADDR1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc11_addr1);
++ cmd->dst = NC11(MSM_NAND_ADDR1);
++ cmd->len = 4;
++ cmd++;
++
++ /* Enable CS0 for NC01 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.chipsel_cs0);
++ cmd->dst =
++ NC01(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ /* Enable CS1 for NC10 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.chipsel_cs1);
++ cmd->dst =
++ NC10(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ /* config DEV0_CFG0 & CFG1 for CS0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
++ cmd->len = 8;
++ cmd++;
++
++ /* config DEV1_CFG0 & CFG1 for CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cfg0);
++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
++ cmd->len = 8;
++ cmd++;
++ }
++
++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg;
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ecccfg);
++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG);
++ cmd->len = 4;
++ cmd++;
++
++ /* NC01 --> ADDR0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc01_addr0);
++ cmd->dst = NC01(MSM_NAND_ADDR0);
++ cmd->len = 4;
++ cmd++;
++
++ /* NC10 --> ADDR0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nandc10_addr0);
++ cmd->dst = NC10(MSM_NAND_ADDR0);
++ cmd->len = 4;
++ cmd++;
++ }
++
++ if (n % 2 == 0) {
++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst = NC01(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++ } else {
++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* CMD */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.cmd);
++ cmd->dst = NC10(MSM_NAND_FLASH_CMD);
++ cmd->len = 4;
++ cmd++;
++ }
++
++ if (ops->mode != MTD_OPS_RAW)
++ sectordatawritesize = (n < (cwperpage - 1)) ?
++ 516 : (512 - ((cwperpage - 1) << 2));
++ else
++ sectordatawritesize = chip->cw_size;
++
++ cmd->cmd = 0;
++ cmd->src = data_dma_addr_curr;
++ data_dma_addr_curr += sectordatawritesize;
++
++ if (n % 2 == 0)
++ cmd->dst = NC01(MSM_NAND_FLASH_BUFFER);
++ else
++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER);
++ cmd->len = sectordatawritesize;
++ cmd++;
++
++ if (ops->oobbuf) {
++ if (n == (cwperpage - 1)) {
++ cmd->cmd = 0;
++ cmd->src = oob_dma_addr_curr;
++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER) +
++ (512 - ((cwperpage - 1) << 2));
++ if ((cwperpage << 2) < oob_len)
++ cmd->len = (cwperpage << 2);
++ else
++ cmd->len = oob_len;
++ oob_dma_addr_curr += cmd->len;
++ oob_len -= cmd->len;
++ if (cmd->len > 0)
++ cmd++;
++ }
++ if (ops->mode != MTD_OPS_AUTO_OOB) {
++ /* skip ecc bytes in oobbuf */
++ if (oob_len < chip->ecc_parity_bytes) {
++ oob_dma_addr_curr +=
++ chip->ecc_parity_bytes;
++ oob_len -=
++ chip->ecc_parity_bytes;
++ } else {
++ oob_dma_addr_curr += oob_len;
++ oob_len = 0;
++ }
++ }
++ }
++
++ if (n % 2 == 0) {
++ if (n != 0) {
++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->
++ data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready from NC10, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.flash_status[n-1]);
++ cmd->len = 4;
++ cmd++;
++ }
++ /* kick the NC01 execute register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.exec);
++ cmd->dst = NC01(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++ } else {
++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* block on data ready from NC01, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC01(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.flash_status[n-1]);
++ cmd->len = 4;
++ cmd++;
++
++ /* kick the execute register */
++ cmd->cmd = 0;
++ cmd->src =
++ msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = NC10(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++ }
++ }
++
++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* we should process outstanding request */
++ /* block on data ready, then
++ * read the status register
++ */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.flash_status[n-1]);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
++ cmd->dst = NC11(MSM_NAND_READ_STATUS);
++ cmd->len = 4;
++ cmd++;
++
++ /* MASK DATA ACK/REQ --> NC01 (0xFC0)*/
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_default_mux);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ if (!interleave_enable) {
++ /* setting to defalut values back */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc01_flash_dev_cmd_vld_default);
++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.nc10_flash_dev_cmd0_default);
++ cmd->dst = NC10(MSM_NAND_DEV_CMD0);
++ cmd->len = 4;
++ cmd++;
++ } else {
++ /* disable CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.default_ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++ }
++
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++ BUILD_BUG_ON(16 * 6 + 18 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmdptr =
++ ((msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP);
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
++ msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ /* if any of the writes failed (0x10), or there was a
++ * protection violation (0x100), or the program success
++ * bit (0x80) is unset, we lose
++ */
++ err = 0;
++ for (n = 0; n < cwperpage; n++) {
++ if (dma_buffer->data.flash_status[n] & 0x110) {
++ err = -EIO;
++ break;
++ }
++ if (!(dma_buffer->data.flash_status[n] & 0x80)) {
++ err = -EIO;
++ break;
++ }
++ }
++ /* check for flash status busy for the last codeword */
++ if (!interleave_enable)
++ if (!(dma_buffer->data.flash_status[cwperpage - 1]
++ & 0x20)) {
++ err = -EIO;
++ break;
++ }
++#if VERBOSE
++ for (n = 0; n < cwperpage; n++) {
++ if (n%2) {
++ pr_info("NC10: write pg %d: flash_status[%d] = %x\n",
++ page, n, dma_buffer->data.flash_status[n]);
++ } else {
++ pr_info("NC01: write pg %d: flash_status[%d] = %x\n",
++ page, n, dma_buffer->data.flash_status[n]);
++ }
++ }
++#endif
++ if (err)
++ break;
++ pages_written++;
++ page++;
++ }
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_written;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
++
++ ops->oobretlen = ops->ooblen - oob_len;
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (ops->oobbuf)
++ dma_unmap_page(chip->dev, oob_dma_addr,
++ ops->ooblen, DMA_TO_DEVICE);
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf)
++ dma_unmap_page(chip->dev, data_dma_addr, ops->len,
++ DMA_TO_DEVICE);
++ if (err)
++ pr_err("msm_nand_write_oob_dualnandc %llx %x %x failed %d\n",
++ to, ops->len, ops->ooblen, err);
++
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("==================================================="
++ "==========\n");
++#endif
++ return err;
++}
++
++static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ int ret;
++ struct mtd_oob_ops ops;
++ int (*write_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *);
++
++ if (!dual_nand_ctlr_present)
++ write_oob = msm_nand_write_oob;
++ else
++ write_oob = msm_nand_write_oob_dualnandc;
++
++ ops.mode = MTD_OPS_PLACE_OOB;
++ ops.retlen = 0;
++ ops.ooblen = 0;
++ ops.oobbuf = NULL;
++ ret = 0;
++ *retlen = 0;
++
++ if (!virt_addr_valid(buf) &&
++ ((to | len) & (mtd->writesize - 1)) == 0 &&
++ ((unsigned long) buf & ~PAGE_MASK) + len > PAGE_SIZE) {
++ /*
++ * Handle writing of large size write buffer in vmalloc
++ * address space that does not fit in an MMU page.
++ * The destination address must be on page boundary,
++ * and the size must be multiple of NAND page size.
++ * Writing partial page is not supported.
++ */
++ ops.len = mtd->writesize;
++
++ for (;;) {
++ ops.datbuf = (uint8_t *) buf;
++
++ ret = write_oob(mtd, to, &ops);
++ if (ret < 0)
++ break;
++
++ len -= mtd->writesize;
++ *retlen += mtd->writesize;
++ if (len == 0)
++ break;
++
++ buf += mtd->writesize;
++ to += mtd->writesize;
++ }
++ } else {
++ ops.len = len;
++ ops.datbuf = (uint8_t *) buf;
++ ret = write_oob(mtd, to, &ops);
++ *retlen = ops.retlen;
++ }
++
++ return ret;
++}
++
++static int
++msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ int err;
++ struct msm_nand_chip *chip = mtd->priv;
++ struct {
++ dmov_s cmd[6];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t exec;
++ uint32_t flash_status;
++ uint32_t clrfstatus;
++ uint32_t clrrstatus;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned page = 0;
++
++ if (mtd->writesize == 2048)
++ page = instr->addr >> 11;
++
++ if (mtd->writesize == 4096)
++ page = instr->addr >> 12;
++
++ if (instr->addr & (mtd->erasesize - 1)) {
++ pr_err("%s: unsupported erase address, 0x%llx\n",
++ __func__, instr->addr);
++ return -EINVAL;
++ }
++ if (instr->len != mtd->erasesize) {
++ pr_err("%s: unsupported erase len, %lld\n",
++ __func__, instr->len);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE;
++ dma_buffer->data.addr0 = page;
++ dma_buffer->data.addr1 = 0;
++ dma_buffer->data.chipsel = 0 | 4;
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.flash_status = 0xeeeeeeee;
++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
++ dma_buffer->data.cfg1 = chip->CFG1;
++ dma_buffer->data.clrfstatus = 0x00000020;
++ dma_buffer->data.clrrstatus = 0x000000C0;
++
++ cmd->cmd = DST_CRCI_NAND_CMD | CMD_OCB;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ cmd->len = 16;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = MSM_NAND_DEV0_CFG0;
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
++ cmd->dst = MSM_NAND_FLASH_STATUS;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = CMD_OCU | CMD_LC;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
++ cmd->dst = MSM_NAND_READ_STATUS;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1);
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(
++ chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ /* we fail if there was an operation error, a mpu error, or the
++ * erase success bit was not set.
++ */
++
++ if (dma_buffer->data.flash_status & 0x110 ||
++ !(dma_buffer->data.flash_status & 0x80))
++ err = -EIO;
++ else
++ err = 0;
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++ if (err) {
++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr);
++ instr->fail_addr = instr->addr;
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ instr->fail_addr = 0xffffffff;
++ mtd_erase_callback(instr);
++ }
++ return err;
++}
++
++static int
++msm_nand_erase_dualnandc(struct mtd_info *mtd, struct erase_info *instr)
++{
++ int err;
++ struct msm_nand_chip *chip = mtd->priv;
++ struct {
++ dmov_s cmd[18];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel_cs0;
++ uint32_t chipsel_cs1;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t exec;
++ uint32_t ecccfg;
++ uint32_t ebi2_chip_select_cfg0;
++ uint32_t adm_mux_data_ack_req_nc01;
++ uint32_t adm_mux_cmd_ack_req_nc01;
++ uint32_t adm_mux_data_ack_req_nc10;
++ uint32_t adm_mux_cmd_ack_req_nc10;
++ uint32_t adm_default_mux;
++ uint32_t default_ebi2_chip_select_cfg0;
++ uint32_t nc01_flash_dev_cmd0;
++ uint32_t nc01_flash_dev_cmd0_default;
++ uint32_t flash_status[2];
++ uint32_t clrfstatus;
++ uint32_t clrrstatus;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ unsigned page = 0;
++
++ if (mtd->writesize == 2048)
++ page = instr->addr >> 11;
++
++ if (mtd->writesize == 4096)
++ page = instr->addr >> 12;
++
++ if (mtd->writesize == 8192)
++ page = (instr->addr >> 1) >> 12;
++
++ if (instr->addr & (mtd->erasesize - 1)) {
++ pr_err("%s: unsupported erase address, 0x%llx\n",
++ __func__, instr->addr);
++ return -EINVAL;
++ }
++ if (instr->len != mtd->erasesize) {
++ pr_err("%s: unsupported erase len, %lld\n",
++ __func__, instr->len);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE;
++ dma_buffer->data.addr0 = page;
++ dma_buffer->data.addr1 = 0;
++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.flash_status[0] = 0xeeeeeeee;
++ dma_buffer->data.flash_status[1] = 0xeeeeeeee;
++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
++ dma_buffer->data.cfg1 = chip->CFG1;
++ dma_buffer->data.clrfstatus = 0x00000020;
++ dma_buffer->data.clrrstatus = 0x000000C0;
++
++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C;
++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14;
++ dma_buffer->data.adm_default_mux = 0x00000FC0;
++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
++
++ /* enable CS1 */
++ cmd->cmd = 0 | CMD_OCB;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ /* erase CS0 block now !!! */
++ /* 0xF14 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = NC01(MSM_NAND_FLASH_CMD);
++ cmd->len = 16;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = NC01(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* 0xF28 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC01(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /* erase CS1 block now !!! */
++ /* 0x53C */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = NC10(MSM_NAND_FLASH_CMD);
++ cmd->len = 12;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1);
++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
++ cmd->len = 8;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = NC10(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* 0xA3C */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[1]);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus);
++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus);
++ cmd->dst = NC11(MSM_NAND_READ_STATUS);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_default_mux);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* disable CS1 */
++ cmd->cmd = CMD_OCU | CMD_LC;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.default_ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1);
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++
++ dma_buffer->cmdptr =
++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(
++ chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ /* we fail if there was an operation error, a mpu error, or the
++ * erase success bit was not set.
++ */
++
++ if (dma_buffer->data.flash_status[0] & 0x110 ||
++ !(dma_buffer->data.flash_status[0] & 0x80) ||
++ dma_buffer->data.flash_status[1] & 0x110 ||
++ !(dma_buffer->data.flash_status[1] & 0x80))
++ err = -EIO;
++ else
++ err = 0;
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++ if (err) {
++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr);
++ instr->fail_addr = instr->addr;
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ instr->fail_addr = 0xffffffff;
++ mtd_erase_callback(instr);
++ }
++ return err;
++}
++
++static int
++msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++ int ret;
++ struct {
++ dmov_s cmd[5];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t eccbchcfg;
++ uint32_t exec;
++ uint32_t ecccfg;
++ struct {
++ uint32_t flash_status;
++ uint32_t buffer_status;
++ } result;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ uint8_t *buf;
++ unsigned page = 0;
++ unsigned cwperpage;
++
++ if (mtd->writesize == 2048)
++ page = ofs >> 11;
++
++ if (mtd->writesize == 4096)
++ page = ofs >> 12;
++
++ cwperpage = (mtd->writesize >> 9);
++
++ /* Check for invalid offset */
++ if (ofs > mtd->size)
++ return -EINVAL;
++ if (ofs & (mtd->erasesize - 1)) {
++ pr_err("%s: unsupported block address, 0x%x\n",
++ __func__, (uint32_t)ofs);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(chip ,
++ sizeof(*dma_buffer) + 4)));
++ buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
++
++ /* Read 4 bytes starting from the bad block marker location
++ * in the last code word of the page
++ */
++
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6);
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++ if (enable_bch_ecc)
++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg;
++
++ if (chip->CFG1 & CFG1_WIDE_FLASH)
++ dma_buffer->data.addr0 = (page << 16) |
++ ((chip->cw_size * (cwperpage-1)) >> 1);
++ else
++ dma_buffer->data.addr0 = (page << 16) |
++ (chip->cw_size * (cwperpage-1));
++
++ dma_buffer->data.addr1 = (page >> 16) & 0xff;
++ dma_buffer->data.chipsel = 0 | 4;
++
++ dma_buffer->data.exec = 1;
++
++ dma_buffer->data.result.flash_status = 0xeeeeeeee;
++ dma_buffer->data.result.buffer_status = 0xeeeeeeee;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_FLASH_CMD;
++ cmd->len = 16;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = MSM_NAND_DEV0_CFG0;
++ if (enable_bch_ecc)
++ cmd->len = 12;
++ else
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_FLASH_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER +
++ (mtd->writesize - (chip->cw_size * (cwperpage-1)));
++ cmd->dst = msm_virt_to_dma(chip, buf);
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip,
++ dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ ret = 0;
++ if (dma_buffer->data.result.flash_status & 0x110)
++ ret = -EIO;
++
++ if (!ret) {
++ /* Check for bad block marker byte */
++ if (chip->CFG1 & CFG1_WIDE_FLASH) {
++ if (buf[0] != 0xFF || buf[1] != 0xFF)
++ ret = 1;
++ } else {
++ if (buf[0] != 0xFF)
++ ret = 1;
++ }
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4);
++ return ret;
++}
++
++static int
++msm_nand_block_isbad_dualnandc(struct mtd_info *mtd, loff_t ofs)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++ int ret;
++ struct {
++ dmov_s cmd[18];
++ unsigned cmdptr;
++ struct {
++ uint32_t cmd;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t chipsel_cs0;
++ uint32_t chipsel_cs1;
++ uint32_t cfg0;
++ uint32_t cfg1;
++ uint32_t exec;
++ uint32_t ecccfg;
++ uint32_t ebi2_chip_select_cfg0;
++ uint32_t adm_mux_data_ack_req_nc01;
++ uint32_t adm_mux_cmd_ack_req_nc01;
++ uint32_t adm_mux_data_ack_req_nc10;
++ uint32_t adm_mux_cmd_ack_req_nc10;
++ uint32_t adm_default_mux;
++ uint32_t default_ebi2_chip_select_cfg0;
++ struct {
++ uint32_t flash_status;
++ uint32_t buffer_status;
++ } result[2];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++ uint8_t *buf01;
++ uint8_t *buf10;
++ unsigned page = 0;
++ unsigned cwperpage;
++
++ if (mtd->writesize == 2048)
++ page = ofs >> 11;
++
++ if (mtd->writesize == 4096)
++ page = ofs >> 12;
++
++ if (mtd->writesize == 8192)
++ page = (ofs >> 1) >> 12;
++
++ cwperpage = ((mtd->writesize >> 1) >> 9);
++
++ /* Check for invalid offset */
++ if (ofs > mtd->size)
++ return -EINVAL;
++ if (ofs & (mtd->erasesize - 1)) {
++ pr_err("%s: unsupported block address, 0x%x\n",
++ __func__, (uint32_t)ofs);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(chip ,
++ sizeof(*dma_buffer) + 8)));
++ buf01 = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
++ buf10 = buf01 + 4;
++
++ /* Read 4 bytes starting from the bad block marker location
++ * in the last code word of the page
++ */
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6);
++ dma_buffer->data.cfg1 = chip->CFG1_RAW |
++ (chip->CFG1 & CFG1_WIDE_FLASH);
++
++ if (chip->CFG1 & CFG1_WIDE_FLASH)
++ dma_buffer->data.addr0 = (page << 16) |
++ ((528*(cwperpage-1)) >> 1);
++ else
++ dma_buffer->data.addr0 = (page << 16) |
++ (528*(cwperpage-1));
++
++ dma_buffer->data.addr1 = (page >> 16) & 0xff;
++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4;
++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5;
++
++ dma_buffer->data.exec = 1;
++
++ dma_buffer->data.result[0].flash_status = 0xeeeeeeee;
++ dma_buffer->data.result[0].buffer_status = 0xeeeeeeee;
++ dma_buffer->data.result[1].flash_status = 0xeeeeeeee;
++ dma_buffer->data.result[1].buffer_status = 0xeeeeeeee;
++
++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C;
++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28;
++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14;
++ dma_buffer->data.adm_default_mux = 0x00000FC0;
++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
++
++ /* Reading last code word from NC01 */
++ /* enable CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ /* 0xF14 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = NC01(MSM_NAND_FLASH_CMD);
++ cmd->len = 16;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = NC01(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* 0xF28 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc10);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC01(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[0]);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = NC01(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) -
++ (528*(cwperpage-1)));
++ cmd->dst = msm_virt_to_dma(chip, buf01);
++ cmd->len = 4;
++ cmd++;
++
++ /* Reading last code word from NC10 */
++ /* 0x53C */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = NC10(MSM_NAND_FLASH_CMD);
++ cmd->len = 12;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1);
++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT);
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = NC10(MSM_NAND_EXEC_CMD);
++ cmd->len = 4;
++ cmd++;
++
++ /* A3C */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_mux_data_ack_req_nc01);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = NC10(MSM_NAND_FLASH_STATUS);
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[1]);
++ cmd->len = 8;
++ cmd++;
++
++ cmd->cmd = 0;
++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) -
++ (528*(cwperpage-1)));
++ cmd->dst = msm_virt_to_dma(chip, buf10);
++ cmd->len = 4;
++ cmd++;
++
++ /* FC0 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.adm_default_mux);
++ cmd->dst = EBI2_NAND_ADM_MUX;
++ cmd->len = 4;
++ cmd++;
++
++ /* disble CS1 */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.ebi2_chip_select_cfg0);
++ cmd->dst = EBI2_CHIP_SELECT_CFG0;
++ cmd->len = 4;
++ cmd++;
++
++ BUILD_BUG_ON(18 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip,
++ dma_buffer->cmd) >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
++ mb();
++
++ ret = 0;
++ if ((dma_buffer->data.result[0].flash_status & 0x110) ||
++ (dma_buffer->data.result[1].flash_status & 0x110))
++ ret = -EIO;
++
++ if (!ret) {
++ /* Check for bad block marker byte for NC01 & NC10 */
++ if (chip->CFG1 & CFG1_WIDE_FLASH) {
++ if ((buf01[0] != 0xFF || buf01[1] != 0xFF) ||
++ (buf10[0] != 0xFF || buf10[1] != 0xFF))
++ ret = 1;
++ } else {
++ if (buf01[0] != 0xFF || buf10[0] != 0xFF)
++ ret = 1;
++ }
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 8);
++ return ret;
++}
++
++static int
++msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++ struct mtd_oob_ops ops;
++ int ret;
++ uint8_t *buf;
++
++ /* Check for invalid offset */
++ if (ofs > mtd->size)
++ return -EINVAL;
++ if (ofs & (mtd->erasesize - 1)) {
++ pr_err("%s: unsupported block address, 0x%x\n",
++ __func__, (uint32_t)ofs);
++ return -EINVAL;
++ }
++
++ /*
++ Write all 0s to the first page
++ This will set the BB marker to 0
++ */
++ buf = page_address(ZERO_PAGE());
++
++ ops.mode = MTD_OPS_RAW;
++ ops.len = mtd->writesize + mtd->oobsize;
++ ops.retlen = 0;
++ ops.ooblen = 0;
++ ops.datbuf = buf;
++ ops.oobbuf = NULL;
++ if (!interleave_enable)
++ ret = msm_nand_write_oob(mtd, ofs, &ops);
++ else
++ ret = msm_nand_write_oob_dualnandc(mtd, ofs, &ops);
++
++ return ret;
++}
++
++/**
++ * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash
++ * @param mtd MTD device structure
++ */
++static int msm_nand_suspend(struct mtd_info *mtd)
++{
++ return 0;
++}
++
++/**
++ * msm_nand_resume - [MTD Interface] Resume the msm_nand flash
++ * @param mtd MTD device structure
++ */
++static void msm_nand_resume(struct mtd_info *mtd)
++{
++}
++
++struct onenand_information {
++ uint16_t manufacturer_id;
++ uint16_t device_id;
++ uint16_t version_id;
++ uint16_t data_buf_size;
++ uint16_t boot_buf_size;
++ uint16_t num_of_buffers;
++ uint16_t technology;
++};
++
++static struct onenand_information onenand_info;
++static uint32_t nand_sfcmd_mode;
++
++uint32_t flash_onenand_probe(struct msm_nand_chip *chip)
++{
++ struct {
++ dmov_s cmd[7];
++ unsigned cmdptr;
++ struct {
++ uint32_t bcfg;
++ uint32_t cmd;
++ uint32_t exec;
++ uint32_t status;
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++ uint32_t initialsflashcmd = 0;
++
++ initialsflashcmd = flash_rd_reg(chip, MSM_NAND_SFLASHC_CMD);
++
++ if ((initialsflashcmd & 0x10) == 0x10)
++ nand_sfcmd_mode = MSM_NAND_SFCMD_ASYNC;
++ else
++ nand_sfcmd_mode = MSM_NAND_SFCMD_BURST;
++
++ printk(KERN_INFO "SFLASHC Async Mode bit: %x \n", nand_sfcmd_mode);
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ cmd = dma_buffer->cmd;
++
++ dma_buffer->data.bcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.cmd = SFLASH_PREPCMD(7, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.exec = 1;
++ dma_buffer->data.status = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_DEVICE_ID << 16) |
++ (ONENAND_MANUFACTURER_ID);
++ dma_buffer->data.addr1 = (ONENAND_DATA_BUFFER_SIZE << 16) |
++ (ONENAND_VERSION_ID);
++ dma_buffer->data.addr2 = (ONENAND_AMOUNT_OF_BUFFERS << 16) |
++ (ONENAND_BOOT_BUFFER_SIZE);
++ dma_buffer->data.addr3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_TECHNOLOGY << 0);
++ dma_buffer->data.data0 = CLEAN_DATA_32;
++ dma_buffer->data.data1 = CLEAN_DATA_32;
++ dma_buffer->data.data2 = CLEAN_DATA_32;
++ dma_buffer->data.data3 = CLEAN_DATA_32;
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.bcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Configure the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Configure the ADDR2 and ADDR3 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 8;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the two status registers */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.status);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read data registers - valid only if status says success */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG0;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->len = 16;
++ cmd++;
++
++ BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ /* Check for errors, protection violations etc */
++ if (dma_buffer->data.status & 0x110) {
++ pr_info("%s: MPU/OP error"
++ "(0x%x) during Onenand probe\n",
++ __func__, dma_buffer->data.status);
++ err = -EIO;
++ } else {
++
++ onenand_info.manufacturer_id =
++ (dma_buffer->data.data0 >> 0) & 0x0000FFFF;
++ onenand_info.device_id =
++ (dma_buffer->data.data0 >> 16) & 0x0000FFFF;
++ onenand_info.version_id =
++ (dma_buffer->data.data1 >> 0) & 0x0000FFFF;
++ onenand_info.data_buf_size =
++ (dma_buffer->data.data1 >> 16) & 0x0000FFFF;
++ onenand_info.boot_buf_size =
++ (dma_buffer->data.data2 >> 0) & 0x0000FFFF;
++ onenand_info.num_of_buffers =
++ (dma_buffer->data.data2 >> 16) & 0x0000FFFF;
++ onenand_info.technology =
++ (dma_buffer->data.data3 >> 0) & 0x0000FFFF;
++
++
++ pr_info("======================================="
++ "==========================\n");
++
++ pr_info("%s: manufacturer_id = 0x%x\n"
++ , __func__, onenand_info.manufacturer_id);
++ pr_info("%s: device_id = 0x%x\n"
++ , __func__, onenand_info.device_id);
++ pr_info("%s: version_id = 0x%x\n"
++ , __func__, onenand_info.version_id);
++ pr_info("%s: data_buf_size = 0x%x\n"
++ , __func__, onenand_info.data_buf_size);
++ pr_info("%s: boot_buf_size = 0x%x\n"
++ , __func__, onenand_info.boot_buf_size);
++ pr_info("%s: num_of_buffers = 0x%x\n"
++ , __func__, onenand_info.num_of_buffers);
++ pr_info("%s: technology = 0x%x\n"
++ , __func__, onenand_info.technology);
++
++ pr_info("======================================="
++ "==========================\n");
++
++ if ((onenand_info.manufacturer_id != 0x00EC)
++ || ((onenand_info.device_id & 0x0040) != 0x0040)
++ || (onenand_info.data_buf_size != 0x0800)
++ || (onenand_info.boot_buf_size != 0x0200)
++ || (onenand_info.num_of_buffers != 0x0201)
++ || (onenand_info.technology != 0)) {
++
++ pr_info("%s: Detected an unsupported device\n"
++ , __func__);
++ err = -EIO;
++ }
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ return err;
++}
++
++int msm_onenand_read_oob(struct mtd_info *mtd,
++ loff_t from, struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[53];
++ unsigned cmdptr;
++ struct {
++ uint32_t sfbcfg;
++ uint32_t sfcmd[9];
++ uint32_t sfexec;
++ uint32_t sfstat[9];
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ uint32_t macro[5];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++ int i;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++
++ loff_t from_curr = 0;
++ unsigned page_count;
++ unsigned pages_read = 0;
++
++ uint16_t onenand_startaddr1;
++ uint16_t onenand_startaddr8;
++ uint16_t onenand_startaddr2;
++ uint16_t onenand_startbuffer;
++ uint16_t onenand_sysconfig1;
++ uint16_t controller_status;
++ uint16_t interrupt_status;
++ uint16_t ecc_status;
++#if VERBOSE
++ pr_info("================================================="
++ "================\n");
++ pr_info("%s: from 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n",
++ __func__, from, ops->mode, ops->datbuf, ops->len,
++ ops->oobbuf, ops->ooblen);
++#endif
++ if (!mtd) {
++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__,
++ (uint32_t)mtd);
++ return -EINVAL;
++ }
++ if (from & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported from, 0x%llx\n", __func__,
++ from);
++ return -EINVAL;
++ }
++
++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) &&
++ (ops->mode != MTD_OPS_RAW)) {
++ pr_err("%s: unsupported ops->mode, %d\n", __func__,
++ ops->mode);
++ return -EINVAL;
++ }
++
++ if (((ops->datbuf == NULL) || (ops->len == 0)) &&
++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) {
++ pr_err("%s: incorrect ops fields - nothing to do\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if ((ops->datbuf != NULL) && (ops->len == 0)) {
++ pr_err("%s: data buffer passed but length 0\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) {
++ pr_err("%s: oob buffer passed but length 0\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
++ /* when ops->datbuf is NULL, ops->len can be ooblen */
++ pr_err("%s: unsupported ops->len, %d\n", __func__,
++ ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if (ops->datbuf != NULL &&
++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len,"
++ " %d for MTD_OPS_RAW\n", __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) {
++ pr_err("%s: unsupported operation, oobbuf pointer "
++ "passed in for RAW mode, %x\n", __func__,
++ (uint32_t)ops->oobbuf);
++ return -EINVAL;
++ }
++
++ if (ops->oobbuf && !ops->datbuf) {
++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
++ mtd->oobavail : mtd->oobsize);
++ if ((page_count == 0) && (ops->ooblen))
++ page_count = 1;
++ } else if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) {
++ if (page_count * mtd->oobsize > ops->ooblen) {
++ pr_err("%s: unsupported ops->ooblen for "
++ "PLACE, %d\n", __func__, ops->ooblen);
++ return -EINVAL;
++ }
++ }
++
++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) &&
++ (ops->ooboffs != 0)) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n", __func__,
++ ops->ooboffs);
++ return -EINVAL;
++ }
++
++ if (ops->datbuf) {
++ memset(ops->datbuf, 0x55, ops->len);
++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev,
++ ops->datbuf, ops->len, DMA_FROM_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("%s: failed to get dma addr for %p\n",
++ __func__, ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ memset(ops->oobbuf, 0x55, ops->ooblen);
++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev,
++ ops->oobbuf, ops->ooblen, DMA_FROM_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("%s: failed to get dma addr for %p\n",
++ __func__, ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ from_curr = from;
++
++ while (page_count-- > 0) {
++
++ cmd = dma_buffer->cmd;
++
++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
++ && (from_curr >= (mtd->size>>1))) { /* DDP Device */
++ onenand_startaddr1 = DEVICE_FLASHCORE_1 |
++ (((uint32_t)(from_curr-(mtd->size>>1))
++ / mtd->erasesize));
++ onenand_startaddr2 = DEVICE_BUFFERRAM_1;
++ } else {
++ onenand_startaddr1 = DEVICE_FLASHCORE_0 |
++ ((uint32_t)from_curr / mtd->erasesize) ;
++ onenand_startaddr2 = DEVICE_BUFFERRAM_0;
++ }
++
++ onenand_startaddr8 = (((uint32_t)from_curr &
++ (mtd->erasesize - 1)) / mtd->writesize) << 2;
++ onenand_startbuffer = DATARAM0_0 << 8;
++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ?
++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) :
++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode);
++
++ dma_buffer->data.sfbcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_INTHI);
++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATRD);
++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATRD);
++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATRD);
++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATRD);
++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(32, 0, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATRD);
++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(4, 10, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfexec = 1;
++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
++ (ONENAND_START_ADDRESS_2);
++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
++ (ONENAND_COMMAND);
++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
++ (ONENAND_INTERRUPT_STATUS);
++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
++ (onenand_sysconfig1);
++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
++ (onenand_startaddr1);
++ dma_buffer->data.data2 = (onenand_startbuffer << 16) |
++ (onenand_startaddr2);
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMDLOADSPARE);
++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
++ (CLEAN_DATA_16);
++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
++ (ONENAND_STARTADDR1_RES);
++ dma_buffer->data.macro[0] = 0x0200;
++ dma_buffer->data.macro[1] = 0x0300;
++ dma_buffer->data.macro[2] = 0x0400;
++ dma_buffer->data.macro[3] = 0x0500;
++ dma_buffer->data.macro[4] = 0x8010;
++
++ /*************************************************************/
++ /* Write necessary address registers in the onenand device */
++ /*************************************************************/
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the ADDR6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
++ cmd->dst = MSM_NAND_ADDR6;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->dst = MSM_NAND_GENP_REG0;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the FLASH_DEV_CMD4,5,6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->dst = MSM_NAND_DEV_CMD4;
++ cmd->len = 12;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Wait for the interrupt from the Onenand device controller */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Read necessary status registers from the onenand device */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the GENP3 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG3;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the DEVCMD4 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_DEV_CMD4;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Read the data ram area from the onenand buffer ram */
++ /*************************************************************/
++
++ if (ops->datbuf) {
++
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMDLOAD);
++
++ for (i = 0; i < 4; i++) {
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfcmd[3+i]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the MACRO1 register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.macro[i]);
++ cmd->dst = MSM_NAND_MACRO1_REG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data rdy, & read status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfstat[3+i]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Transfer nand ctlr buf contents to usr buf */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER;
++ cmd->dst = data_dma_addr_curr;
++ cmd->len = 512;
++ data_dma_addr_curr += 512;
++ cmd++;
++ }
++ }
++
++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) {
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfcmd[7]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the MACRO1 register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.macro[4]);
++ cmd->dst = MSM_NAND_MACRO1_REG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfstat[7]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Transfer nand ctlr buffer contents into usr buf */
++ if (ops->mode == MTD_OPS_AUTO_OOB) {
++ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER +
++ mtd->ecclayout->oobfree[i].offset;
++ cmd->dst = oob_dma_addr_curr;
++ cmd->len =
++ mtd->ecclayout->oobfree[i].length;
++ oob_dma_addr_curr +=
++ mtd->ecclayout->oobfree[i].length;
++ cmd++;
++ }
++ }
++ if (ops->mode == MTD_OPS_PLACE_OOB) {
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER;
++ cmd->dst = oob_dma_addr_curr;
++ cmd->len = mtd->oobsize;
++ oob_dma_addr_curr += mtd->oobsize;
++ cmd++;
++ }
++ if (ops->mode == MTD_OPS_RAW) {
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_FLASH_BUFFER;
++ cmd->dst = data_dma_addr_curr;
++ cmd->len = mtd->oobsize;
++ data_dma_addr_curr += mtd->oobsize;
++ cmd++;
++ }
++ }
++
++ /*************************************************************/
++ /* Restore the necessary registers to proper values */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]);
++ cmd->len = 4;
++ cmd++;
++
++
++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ ecc_status = (dma_buffer->data.data3 >> 16) &
++ 0x0000FFFF;
++ interrupt_status = (dma_buffer->data.data4 >> 0) &
++ 0x0000FFFF;
++ controller_status = (dma_buffer->data.data4 >> 16) &
++ 0x0000FFFF;
++
++#if VERBOSE
++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x"
++ "%x %x\n", __func__,
++ dma_buffer->data.sfstat[0],
++ dma_buffer->data.sfstat[1],
++ dma_buffer->data.sfstat[2],
++ dma_buffer->data.sfstat[3],
++ dma_buffer->data.sfstat[4],
++ dma_buffer->data.sfstat[5],
++ dma_buffer->data.sfstat[6],
++ dma_buffer->data.sfstat[7],
++ dma_buffer->data.sfstat[8]);
++
++ pr_info("%s: controller_status = %x\n", __func__,
++ controller_status);
++ pr_info("%s: interrupt_status = %x\n", __func__,
++ interrupt_status);
++ pr_info("%s: ecc_status = %x\n", __func__,
++ ecc_status);
++#endif
++ /* Check for errors, protection violations etc */
++ if ((controller_status != 0)
++ || (dma_buffer->data.sfstat[0] & 0x110)
++ || (dma_buffer->data.sfstat[1] & 0x110)
++ || (dma_buffer->data.sfstat[2] & 0x110)
++ || (dma_buffer->data.sfstat[8] & 0x110)
++ || ((dma_buffer->data.sfstat[3] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[4] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[5] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[6] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[7] & 0x110) &&
++ ((ops->oobbuf)
++ || (ops->mode == MTD_OPS_RAW)))) {
++ pr_info("%s: ECC/MPU/OP error\n", __func__);
++ err = -EIO;
++ }
++
++ if (err)
++ break;
++ pages_read++;
++ from_curr += mtd->writesize;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (ops->oobbuf) {
++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen,
++ DMA_FROM_DEVICE);
++ }
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf) {
++ dma_unmap_page(chip->dev, data_dma_addr, ops->len,
++ DMA_FROM_DEVICE);
++ }
++
++ if (err) {
++ pr_err("%s: %llx %x %x failed\n", __func__, from_curr,
++ ops->datbuf ? ops->len : 0, ops->ooblen);
++ } else {
++ ops->retlen = ops->oobretlen = 0;
++ if (ops->datbuf != NULL) {
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_read;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize)
++ * pages_read;
++ }
++ if (ops->oobbuf != NULL) {
++ if (ops->mode == MTD_OPS_AUTO_OOB)
++ ops->oobretlen = mtd->oobavail * pages_read;
++ else
++ ops->oobretlen = mtd->oobsize * pages_read;
++ }
++ }
++
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("==================================================="
++ "==============\n");
++#endif
++ return err;
++}
++
++int msm_onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ int ret;
++ struct mtd_oob_ops ops;
++
++ ops.mode = MTD_OPS_PLACE_OOB;
++ ops.datbuf = buf;
++ ops.len = len;
++ ops.retlen = 0;
++ ops.oobbuf = NULL;
++ ops.ooblen = 0;
++ ops.oobretlen = 0;
++ ret = msm_onenand_read_oob(mtd, from, &ops);
++ *retlen = ops.retlen;
++
++ return ret;
++}
++
++static int msm_onenand_write_oob(struct mtd_info *mtd, loff_t to,
++ struct mtd_oob_ops *ops)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[53];
++ unsigned cmdptr;
++ struct {
++ uint32_t sfbcfg;
++ uint32_t sfcmd[10];
++ uint32_t sfexec;
++ uint32_t sfstat[10];
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ uint32_t macro[5];
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++ int i, j, k;
++ dma_addr_t data_dma_addr = 0;
++ dma_addr_t oob_dma_addr = 0;
++ dma_addr_t init_dma_addr = 0;
++ dma_addr_t data_dma_addr_curr = 0;
++ dma_addr_t oob_dma_addr_curr = 0;
++ uint8_t *init_spare_bytes;
++
++ loff_t to_curr = 0;
++ unsigned page_count;
++ unsigned pages_written = 0;
++
++ uint16_t onenand_startaddr1;
++ uint16_t onenand_startaddr8;
++ uint16_t onenand_startaddr2;
++ uint16_t onenand_startbuffer;
++ uint16_t onenand_sysconfig1;
++
++ uint16_t controller_status;
++ uint16_t interrupt_status;
++ uint16_t ecc_status;
++
++#if VERBOSE
++ pr_info("================================================="
++ "================\n");
++ pr_info("%s: to 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x"
++ "\noobbuf 0x%p ooblen 0x%x\n",
++ __func__, to, ops->mode, ops->datbuf, ops->len,
++ ops->oobbuf, ops->ooblen);
++#endif
++ if (!mtd) {
++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__,
++ (uint32_t)mtd);
++ return -EINVAL;
++ }
++ if (to & (mtd->writesize - 1)) {
++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
++ return -EINVAL;
++ }
++
++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) &&
++ (ops->mode != MTD_OPS_RAW)) {
++ pr_err("%s: unsupported ops->mode, %d\n", __func__,
++ ops->mode);
++ return -EINVAL;
++ }
++
++ if (((ops->datbuf == NULL) || (ops->len == 0)) &&
++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) {
++ pr_err("%s: incorrect ops fields - nothing to do\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if ((ops->datbuf != NULL) && (ops->len == 0)) {
++ pr_err("%s: data buffer passed but length 0\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) {
++ pr_err("%s: oob buffer passed but length 0\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ if (ops->mode != MTD_OPS_RAW) {
++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
++ /* when ops->datbuf is NULL, ops->len can be ooblen */
++ pr_err("%s: unsupported ops->len, %d\n", __func__,
++ ops->len);
++ return -EINVAL;
++ }
++ } else {
++ if (ops->datbuf != NULL &&
++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
++ pr_err("%s: unsupported ops->len,"
++ " %d for MTD_OPS_RAW\n", __func__, ops->len);
++ return -EINVAL;
++ }
++ }
++
++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) {
++ pr_err("%s: unsupported operation, oobbuf pointer "
++ "passed in for RAW mode, %x\n", __func__,
++ (uint32_t)ops->oobbuf);
++ return -EINVAL;
++ }
++
++ if (ops->oobbuf && !ops->datbuf) {
++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ?
++ mtd->oobavail : mtd->oobsize);
++ if ((page_count == 0) && (ops->ooblen))
++ page_count = 1;
++ } else if (ops->mode != MTD_OPS_RAW)
++ page_count = ops->len / mtd->writesize;
++ else
++ page_count = ops->len / (mtd->writesize + mtd->oobsize);
++
++ if ((ops->mode == MTD_OPS_AUTO_OOB) && (ops->oobbuf != NULL)) {
++ if (page_count > 1) {
++ pr_err("%s: unsupported ops->ooblen for"
++ "AUTO, %d\n", __func__, ops->ooblen);
++ return -EINVAL;
++ }
++ }
++
++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) {
++ if (page_count * mtd->oobsize > ops->ooblen) {
++ pr_err("%s: unsupported ops->ooblen for"
++ "PLACE, %d\n", __func__, ops->ooblen);
++ return -EINVAL;
++ }
++ }
++
++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) &&
++ (ops->ooboffs != 0)) {
++ pr_err("%s: unsupported ops->ooboffs, %d\n",
++ __func__, ops->ooboffs);
++ return -EINVAL;
++ }
++
++ init_spare_bytes = kmalloc(64, GFP_KERNEL);
++ if (!init_spare_bytes) {
++ pr_err("%s: failed to alloc init_spare_bytes buffer\n",
++ __func__);
++ return -ENOMEM;
++ }
++ for (i = 0; i < 64; i++)
++ init_spare_bytes[i] = 0xFF;
++
++ if ((ops->oobbuf) && (ops->mode == MTD_OPS_AUTO_OOB)) {
++ for (i = 0, k = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++)
++ for (j = 0; j < mtd->ecclayout->oobfree[i].length;
++ j++) {
++ init_spare_bytes[j +
++ mtd->ecclayout->oobfree[i].offset]
++ = (ops->oobbuf)[k];
++ k++;
++ }
++ }
++
++ if (ops->datbuf) {
++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev,
++ ops->datbuf, ops->len, DMA_TO_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, data_dma_addr)) {
++ pr_err("%s: failed to get dma addr for %p\n",
++ __func__, ops->datbuf);
++ return -EIO;
++ }
++ }
++ if (ops->oobbuf) {
++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev,
++ ops->oobbuf, ops->ooblen, DMA_TO_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
++ pr_err("%s: failed to get dma addr for %p\n",
++ __func__, ops->oobbuf);
++ err = -EIO;
++ goto err_dma_map_oobbuf_failed;
++ }
++ }
++
++ init_dma_addr = msm_nand_dma_map(chip->dev, init_spare_bytes, 64,
++ DMA_TO_DEVICE, NULL);
++ if (dma_mapping_error(chip->dev, init_dma_addr)) {
++ pr_err("%s: failed to get dma addr for %p\n",
++ __func__, init_spare_bytes);
++ err = -EIO;
++ goto err_dma_map_initbuf_failed;
++ }
++
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ to_curr = to;
++
++ while (page_count-- > 0) {
++ cmd = dma_buffer->cmd;
++
++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
++ && (to_curr >= (mtd->size>>1))) { /* DDP Device */
++ onenand_startaddr1 = DEVICE_FLASHCORE_1 |
++ (((uint32_t)(to_curr-(mtd->size>>1))
++ / mtd->erasesize));
++ onenand_startaddr2 = DEVICE_BUFFERRAM_1;
++ } else {
++ onenand_startaddr1 = DEVICE_FLASHCORE_0 |
++ ((uint32_t)to_curr / mtd->erasesize) ;
++ onenand_startaddr2 = DEVICE_BUFFERRAM_0;
++ }
++
++ onenand_startaddr8 = (((uint32_t)to_curr &
++ (mtd->erasesize - 1)) / mtd->writesize) << 2;
++ onenand_startbuffer = DATARAM0_0 << 8;
++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ?
++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) :
++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode);
++
++ dma_buffer->data.sfbcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(6, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATWR);
++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATWR);
++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATWR);
++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATWR);
++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(32, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_DATWR);
++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(1, 6, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(0, 0, 32,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_INTHI);
++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(3, 7, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(4, 10, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfexec = 1;
++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[9] = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
++ (ONENAND_START_ADDRESS_2);
++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
++ (ONENAND_COMMAND);
++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
++ (ONENAND_INTERRUPT_STATUS);
++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
++ (onenand_sysconfig1);
++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
++ (onenand_startaddr1);
++ dma_buffer->data.data2 = (onenand_startbuffer << 16) |
++ (onenand_startaddr2);
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMDPROGSPARE);
++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
++ (CLEAN_DATA_16);
++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
++ (ONENAND_STARTADDR1_RES);
++ dma_buffer->data.macro[0] = 0x0200;
++ dma_buffer->data.macro[1] = 0x0300;
++ dma_buffer->data.macro[2] = 0x0400;
++ dma_buffer->data.macro[3] = 0x0500;
++ dma_buffer->data.macro[4] = 0x8010;
++
++
++ /*************************************************************/
++ /* Write necessary address registers in the onenand device */
++ /*************************************************************/
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the ADDR6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
++ cmd->dst = MSM_NAND_ADDR6;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->dst = MSM_NAND_GENP_REG0;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the FLASH_DEV_CMD4,5,6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->dst = MSM_NAND_DEV_CMD4;
++ cmd->len = 12;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Write the data ram area in the onenand buffer ram */
++ /*************************************************************/
++
++ if (ops->datbuf) {
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMDPROG);
++
++ for (i = 0; i < 4; i++) {
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfcmd[1+i]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Trnsfr usr buf contents to nand ctlr buf */
++ cmd->cmd = 0;
++ cmd->src = data_dma_addr_curr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = 512;
++ data_dma_addr_curr += 512;
++ cmd++;
++
++ /* Write the MACRO1 register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.macro[i]);
++ cmd->dst = MSM_NAND_MACRO1_REG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data rdy, & read status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip,
++ &dma_buffer->data.sfstat[1+i]);
++ cmd->len = 4;
++ cmd++;
++
++ }
++ }
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[5]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) {
++
++ /* Transfer user buf contents into nand ctlr buffer */
++ if (ops->mode == MTD_OPS_AUTO_OOB) {
++ cmd->cmd = 0;
++ cmd->src = init_dma_addr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = mtd->oobsize;
++ cmd++;
++ }
++ if (ops->mode == MTD_OPS_PLACE_OOB) {
++ cmd->cmd = 0;
++ cmd->src = oob_dma_addr_curr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = mtd->oobsize;
++ oob_dma_addr_curr += mtd->oobsize;
++ cmd++;
++ }
++ if (ops->mode == MTD_OPS_RAW) {
++ cmd->cmd = 0;
++ cmd->src = data_dma_addr_curr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = mtd->oobsize;
++ data_dma_addr_curr += mtd->oobsize;
++ cmd++;
++ }
++ } else {
++ cmd->cmd = 0;
++ cmd->src = init_dma_addr;
++ cmd->dst = MSM_NAND_FLASH_BUFFER;
++ cmd->len = mtd->oobsize;
++ cmd++;
++ }
++
++ /* Write the MACRO1 register */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[4]);
++ cmd->dst = MSM_NAND_MACRO1_REG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[5]);
++ cmd->len = 4;
++ cmd++;
++
++ /*********************************************************/
++ /* Issuing write command */
++ /*********************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[6]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[6]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Wait for the interrupt from the Onenand device controller */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[7]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[7]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Read necessary status registers from the onenand device */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the GENP3 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG3;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the DEVCMD4 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_DEV_CMD4;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Restore the necessary registers to proper values */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[9]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[9]);
++ cmd->len = 4;
++ cmd++;
++
++
++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
++ interrupt_status = (dma_buffer->data.data4 >> 0)&0x0000FFFF;
++ controller_status = (dma_buffer->data.data4 >> 16)&0x0000FFFF;
++
++#if VERBOSE
++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x"
++ " %x %x %x\n", __func__,
++ dma_buffer->data.sfstat[0],
++ dma_buffer->data.sfstat[1],
++ dma_buffer->data.sfstat[2],
++ dma_buffer->data.sfstat[3],
++ dma_buffer->data.sfstat[4],
++ dma_buffer->data.sfstat[5],
++ dma_buffer->data.sfstat[6],
++ dma_buffer->data.sfstat[7],
++ dma_buffer->data.sfstat[8],
++ dma_buffer->data.sfstat[9]);
++
++ pr_info("%s: controller_status = %x\n", __func__,
++ controller_status);
++ pr_info("%s: interrupt_status = %x\n", __func__,
++ interrupt_status);
++ pr_info("%s: ecc_status = %x\n", __func__,
++ ecc_status);
++#endif
++ /* Check for errors, protection violations etc */
++ if ((controller_status != 0)
++ || (dma_buffer->data.sfstat[0] & 0x110)
++ || (dma_buffer->data.sfstat[6] & 0x110)
++ || (dma_buffer->data.sfstat[7] & 0x110)
++ || (dma_buffer->data.sfstat[8] & 0x110)
++ || (dma_buffer->data.sfstat[9] & 0x110)
++ || ((dma_buffer->data.sfstat[1] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[2] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[3] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[4] & 0x110) &&
++ (ops->datbuf))
++ || ((dma_buffer->data.sfstat[5] & 0x110) &&
++ ((ops->oobbuf)
++ || (ops->mode == MTD_OPS_RAW)))) {
++ pr_info("%s: ECC/MPU/OP error\n", __func__);
++ err = -EIO;
++ }
++
++ if (err)
++ break;
++ pages_written++;
++ to_curr += mtd->writesize;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ dma_unmap_page(chip->dev, init_dma_addr, 64, DMA_TO_DEVICE);
++
++err_dma_map_initbuf_failed:
++ if (ops->oobbuf) {
++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen,
++ DMA_TO_DEVICE);
++ }
++err_dma_map_oobbuf_failed:
++ if (ops->datbuf) {
++ dma_unmap_page(chip->dev, data_dma_addr, ops->len,
++ DMA_TO_DEVICE);
++ }
++
++ if (err) {
++ pr_err("%s: %llx %x %x failed\n", __func__, to_curr,
++ ops->datbuf ? ops->len : 0, ops->ooblen);
++ } else {
++ ops->retlen = ops->oobretlen = 0;
++ if (ops->datbuf != NULL) {
++ if (ops->mode != MTD_OPS_RAW)
++ ops->retlen = mtd->writesize * pages_written;
++ else
++ ops->retlen = (mtd->writesize + mtd->oobsize)
++ * pages_written;
++ }
++ if (ops->oobbuf != NULL) {
++ if (ops->mode == MTD_OPS_AUTO_OOB)
++ ops->oobretlen = mtd->oobavail * pages_written;
++ else
++ ops->oobretlen = mtd->oobsize * pages_written;
++ }
++ }
++
++#if VERBOSE
++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n",
++ __func__, err, ops->retlen, ops->oobretlen);
++
++ pr_info("================================================="
++ "================\n");
++#endif
++ kfree(init_spare_bytes);
++ return err;
++}
++
++static int msm_onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ int ret;
++ struct mtd_oob_ops ops;
++
++ ops.mode = MTD_OPS_PLACE_OOB;
++ ops.datbuf = (uint8_t *)buf;
++ ops.len = len;
++ ops.retlen = 0;
++ ops.oobbuf = NULL;
++ ops.ooblen = 0;
++ ops.oobretlen = 0;
++ ret = msm_onenand_write_oob(mtd, to, &ops);
++ *retlen = ops.retlen;
++
++ return ret;
++}
++
++static int msm_onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[20];
++ unsigned cmdptr;
++ struct {
++ uint32_t sfbcfg;
++ uint32_t sfcmd[4];
++ uint32_t sfexec;
++ uint32_t sfstat[4];
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++
++ uint16_t onenand_startaddr1;
++ uint16_t onenand_startaddr8;
++ uint16_t onenand_startaddr2;
++ uint16_t onenand_startbuffer;
++
++ uint16_t controller_status;
++ uint16_t interrupt_status;
++ uint16_t ecc_status;
++
++ uint64_t temp;
++
++#if VERBOSE
++ pr_info("================================================="
++ "================\n");
++ pr_info("%s: addr 0x%llx len 0x%llx\n",
++ __func__, instr->addr, instr->len);
++#endif
++ if (instr->addr & (mtd->erasesize - 1)) {
++ pr_err("%s: Unsupported erase address, 0x%llx\n",
++ __func__, instr->addr);
++ return -EINVAL;
++ }
++ if (instr->len != mtd->erasesize) {
++ pr_err("%s: Unsupported erase len, %lld\n",
++ __func__, instr->len);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ cmd = dma_buffer->cmd;
++
++ temp = instr->addr;
++
++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
++ && (temp >= (mtd->size>>1))) { /* DDP Device */
++ onenand_startaddr1 = DEVICE_FLASHCORE_1 |
++ (((uint32_t)(temp-(mtd->size>>1))
++ / mtd->erasesize));
++ onenand_startaddr2 = DEVICE_BUFFERRAM_1;
++ } else {
++ onenand_startaddr1 = DEVICE_FLASHCORE_0 |
++ ((uint32_t)temp / mtd->erasesize) ;
++ onenand_startaddr2 = DEVICE_BUFFERRAM_0;
++ }
++
++ onenand_startaddr8 = 0x0000;
++ onenand_startbuffer = DATARAM0_0 << 8;
++
++ dma_buffer->data.sfbcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_INTHI);
++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfexec = 1;
++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) |
++ (ONENAND_START_ADDRESS_2);
++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) |
++ (ONENAND_COMMAND);
++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
++ (ONENAND_INTERRUPT_STATUS);
++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
++ (onenand_startaddr1);
++ dma_buffer->data.data2 = (onenand_startbuffer << 16) |
++ (onenand_startaddr2);
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMDERAS);
++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
++ (CLEAN_DATA_16);
++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
++ (ONENAND_STARTADDR1_RES);
++
++ /***************************************************************/
++ /* Write the necessary address registers in the onenand device */
++ /***************************************************************/
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the ADDR6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
++ cmd->dst = MSM_NAND_ADDR6;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->dst = MSM_NAND_GENP_REG0;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the FLASH_DEV_CMD4,5,6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->dst = MSM_NAND_DEV_CMD4;
++ cmd->len = 12;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /***************************************************************/
++ /* Wait for the interrupt from the Onenand device controller */
++ /***************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
++ cmd->len = 4;
++ cmd++;
++
++ /***************************************************************/
++ /* Read the necessary status registers from the onenand device */
++ /***************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the GENP3 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG3;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the DEVCMD4 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_DEV_CMD4;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->len = 4;
++ cmd++;
++
++ /***************************************************************/
++ /* Restore the necessary registers to proper values */
++ /***************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
++ cmd->len = 4;
++ cmd++;
++
++
++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
++
++#if VERBOSE
++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
++ dma_buffer->data.sfstat[0],
++ dma_buffer->data.sfstat[1],
++ dma_buffer->data.sfstat[2],
++ dma_buffer->data.sfstat[3]);
++
++ pr_info("%s: controller_status = %x\n", __func__,
++ controller_status);
++ pr_info("%s: interrupt_status = %x\n", __func__,
++ interrupt_status);
++ pr_info("%s: ecc_status = %x\n", __func__,
++ ecc_status);
++#endif
++ /* Check for errors, protection violations etc */
++ if ((controller_status != 0)
++ || (dma_buffer->data.sfstat[0] & 0x110)
++ || (dma_buffer->data.sfstat[1] & 0x110)
++ || (dma_buffer->data.sfstat[2] & 0x110)
++ || (dma_buffer->data.sfstat[3] & 0x110)) {
++ pr_err("%s: ECC/MPU/OP error\n", __func__);
++ err = -EIO;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++ if (err) {
++ pr_err("%s: Erase failed, 0x%llx\n", __func__,
++ instr->addr);
++ instr->fail_addr = instr->addr;
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ instr->fail_addr = 0xffffffff;
++ mtd_erase_callback(instr);
++ }
++
++#if VERBOSE
++ pr_info("\n%s: ret %d\n", __func__, err);
++ pr_info("===================================================="
++ "=============\n");
++#endif
++ return err;
++}
++
++static int msm_onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
++{
++ struct mtd_oob_ops ops;
++ int rval, i;
++ int ret = 0;
++ uint8_t *buffer;
++ uint8_t *oobptr;
++
++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) {
++ pr_err("%s: unsupported block address, 0x%x\n",
++ __func__, (uint32_t)ofs);
++ return -EINVAL;
++ }
++
++ buffer = kmalloc(2112, GFP_KERNEL|GFP_DMA);
++ if (buffer == 0) {
++ pr_err("%s: Could not kmalloc for buffer\n",
++ __func__);
++ return -ENOMEM;
++ }
++
++ memset(buffer, 0x00, 2112);
++ oobptr = &(buffer[2048]);
++
++ ops.mode = MTD_OPS_RAW;
++ ops.len = 2112;
++ ops.retlen = 0;
++ ops.ooblen = 0;
++ ops.oobretlen = 0;
++ ops.ooboffs = 0;
++ ops.datbuf = buffer;
++ ops.oobbuf = NULL;
++
++ for (i = 0; i < 2; i++) {
++ ofs = ofs + i*mtd->writesize;
++ rval = msm_onenand_read_oob(mtd, ofs, &ops);
++ if (rval) {
++ pr_err("%s: Error in reading bad blk info\n",
++ __func__);
++ ret = rval;
++ break;
++ }
++ if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) ||
++ (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) ||
++ (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) ||
++ (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF)
++ ) {
++ ret = 1;
++ break;
++ }
++ }
++
++ kfree(buffer);
++
++#if VERBOSE
++ if (ret == 1)
++ pr_info("%s : Block containing 0x%x is bad\n",
++ __func__, (unsigned int)ofs);
++#endif
++ return ret;
++}
++
++static int msm_onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++ struct mtd_oob_ops ops;
++ int rval, i;
++ int ret = 0;
++ uint8_t *buffer;
++
++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) {
++ pr_err("%s: unsupported block address, 0x%x\n",
++ __func__, (uint32_t)ofs);
++ return -EINVAL;
++ }
++
++ buffer = page_address(ZERO_PAGE());
++
++ ops.mode = MTD_OPS_RAW;
++ ops.len = 2112;
++ ops.retlen = 0;
++ ops.ooblen = 0;
++ ops.oobretlen = 0;
++ ops.ooboffs = 0;
++ ops.datbuf = buffer;
++ ops.oobbuf = NULL;
++
++ for (i = 0; i < 2; i++) {
++ ofs = ofs + i*mtd->writesize;
++ rval = msm_onenand_write_oob(mtd, ofs, &ops);
++ if (rval) {
++ pr_err("%s: Error in writing bad blk info\n",
++ __func__);
++ ret = rval;
++ break;
++ }
++ }
++
++ return ret;
++}
++
++static int msm_onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[20];
++ unsigned cmdptr;
++ struct {
++ uint32_t sfbcfg;
++ uint32_t sfcmd[4];
++ uint32_t sfexec;
++ uint32_t sfstat[4];
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++
++ uint16_t onenand_startaddr1;
++ uint16_t onenand_startaddr8;
++ uint16_t onenand_startaddr2;
++ uint16_t onenand_startblock;
++
++ uint16_t controller_status;
++ uint16_t interrupt_status;
++ uint16_t write_prot_status;
++
++ uint64_t start_ofs;
++
++#if VERBOSE
++ pr_info("===================================================="
++ "=============\n");
++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
++#endif
++ /* 'ofs' & 'len' should align to block size */
++ if (ofs&(mtd->erasesize - 1)) {
++ pr_err("%s: Unsupported ofs address, 0x%llx\n",
++ __func__, ofs);
++ return -EINVAL;
++ }
++
++ if (len&(mtd->erasesize - 1)) {
++ pr_err("%s: Unsupported len, %lld\n",
++ __func__, len);
++ return -EINVAL;
++ }
++
++ if (ofs+len > mtd->size) {
++ pr_err("%s: Maximum chip size exceeded\n", __func__);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) {
++#if VERBOSE
++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
++#endif
++
++ cmd = dma_buffer->cmd;
++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
++ && (ofs >= (mtd->size>>1))) { /* DDP Device */
++ onenand_startaddr1 = DEVICE_FLASHCORE_1 |
++ (((uint32_t)(ofs - (mtd->size>>1))
++ / mtd->erasesize));
++ onenand_startaddr2 = DEVICE_BUFFERRAM_1;
++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1))
++ / mtd->erasesize);
++ } else {
++ onenand_startaddr1 = DEVICE_FLASHCORE_0 |
++ ((uint32_t)ofs / mtd->erasesize) ;
++ onenand_startaddr2 = DEVICE_BUFFERRAM_0;
++ onenand_startblock = ((uint32_t)ofs
++ / mtd->erasesize);
++ }
++
++ onenand_startaddr8 = 0x0000;
++ dma_buffer->data.sfbcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_INTHI);
++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfexec = 1;
++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) |
++ (ONENAND_START_ADDRESS_2);
++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) |
++ (ONENAND_COMMAND);
++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
++ (ONENAND_INTERRUPT_STATUS);
++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
++ (onenand_startaddr1);
++ dma_buffer->data.data2 = (onenand_startblock << 16) |
++ (onenand_startaddr2);
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMD_UNLOCK);
++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
++ (CLEAN_DATA_16);
++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
++ (ONENAND_STARTADDR1_RES);
++
++ /*************************************************************/
++ /* Write the necessary address reg in the onenand device */
++ /*************************************************************/
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the ADDR6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
++ cmd->dst = MSM_NAND_ADDR6;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->dst = MSM_NAND_GENP_REG0;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the FLASH_DEV_CMD4,5,6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->dst = MSM_NAND_DEV_CMD4;
++ cmd->len = 12;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Wait for the interrupt from the Onenand device controller */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
++ cmd->len = 4;
++ cmd++;
++
++ /*********************************************************/
++ /* Read the necessary status reg from the onenand device */
++ /*********************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the GENP3 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG3;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the DEVCMD4 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_DEV_CMD4;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->len = 4;
++ cmd++;
++
++ /************************************************************/
++ /* Restore the necessary registers to proper values */
++ /************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
++ cmd->len = 4;
++ cmd++;
++
++
++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
++
++#if VERBOSE
++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
++ dma_buffer->data.sfstat[0],
++ dma_buffer->data.sfstat[1],
++ dma_buffer->data.sfstat[2],
++ dma_buffer->data.sfstat[3]);
++
++ pr_info("%s: controller_status = %x\n", __func__,
++ controller_status);
++ pr_info("%s: interrupt_status = %x\n", __func__,
++ interrupt_status);
++ pr_info("%s: write_prot_status = %x\n", __func__,
++ write_prot_status);
++#endif
++ /* Check for errors, protection violations etc */
++ if ((controller_status != 0)
++ || (dma_buffer->data.sfstat[0] & 0x110)
++ || (dma_buffer->data.sfstat[1] & 0x110)
++ || (dma_buffer->data.sfstat[2] & 0x110)
++ || (dma_buffer->data.sfstat[3] & 0x110)) {
++ pr_err("%s: ECC/MPU/OP error\n", __func__);
++ err = -EIO;
++ }
++
++ if (!(write_prot_status & ONENAND_WP_US)) {
++ pr_err("%s: Unexpected status ofs = 0x%llx,"
++ "wp_status = %x\n",
++ __func__, ofs, write_prot_status);
++ err = -EIO;
++ }
++
++ if (err)
++ break;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++#if VERBOSE
++ pr_info("\n%s: ret %d\n", __func__, err);
++ pr_info("===================================================="
++ "=============\n");
++#endif
++ return err;
++}
++
++static int msm_onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[20];
++ unsigned cmdptr;
++ struct {
++ uint32_t sfbcfg;
++ uint32_t sfcmd[4];
++ uint32_t sfexec;
++ uint32_t sfstat[4];
++ uint32_t addr0;
++ uint32_t addr1;
++ uint32_t addr2;
++ uint32_t addr3;
++ uint32_t addr4;
++ uint32_t addr5;
++ uint32_t addr6;
++ uint32_t data0;
++ uint32_t data1;
++ uint32_t data2;
++ uint32_t data3;
++ uint32_t data4;
++ uint32_t data5;
++ uint32_t data6;
++ } data;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ int err = 0;
++
++ uint16_t onenand_startaddr1;
++ uint16_t onenand_startaddr8;
++ uint16_t onenand_startaddr2;
++ uint16_t onenand_startblock;
++
++ uint16_t controller_status;
++ uint16_t interrupt_status;
++ uint16_t write_prot_status;
++
++ uint64_t start_ofs;
++
++#if VERBOSE
++ pr_info("===================================================="
++ "=============\n");
++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
++#endif
++ /* 'ofs' & 'len' should align to block size */
++ if (ofs&(mtd->erasesize - 1)) {
++ pr_err("%s: Unsupported ofs address, 0x%llx\n",
++ __func__, ofs);
++ return -EINVAL;
++ }
++
++ if (len&(mtd->erasesize - 1)) {
++ pr_err("%s: Unsupported len, %lld\n",
++ __func__, len);
++ return -EINVAL;
++ }
++
++ if (ofs+len > mtd->size) {
++ pr_err("%s: Maximum chip size exceeded\n", __func__);
++ return -EINVAL;
++ }
++
++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer
++ (chip, sizeof(*dma_buffer))));
++
++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) {
++#if VERBOSE
++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len);
++#endif
++
++ cmd = dma_buffer->cmd;
++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP)
++ && (ofs >= (mtd->size>>1))) { /* DDP Device */
++ onenand_startaddr1 = DEVICE_FLASHCORE_1 |
++ (((uint32_t)(ofs - (mtd->size>>1))
++ / mtd->erasesize));
++ onenand_startaddr2 = DEVICE_BUFFERRAM_1;
++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1))
++ / mtd->erasesize);
++ } else {
++ onenand_startaddr1 = DEVICE_FLASHCORE_0 |
++ ((uint32_t)ofs / mtd->erasesize) ;
++ onenand_startaddr2 = DEVICE_BUFFERRAM_0;
++ onenand_startblock = ((uint32_t)ofs
++ / mtd->erasesize);
++ }
++
++ onenand_startaddr8 = 0x0000;
++ dma_buffer->data.sfbcfg = SFLASH_BCFG |
++ (nand_sfcmd_mode ? 0 : (1 << 24));
++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_INTHI);
++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
++ MSM_NAND_SFCMD_DATXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGRD);
++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
++ MSM_NAND_SFCMD_CMDXS,
++ nand_sfcmd_mode,
++ MSM_NAND_SFCMD_REGWR);
++ dma_buffer->data.sfexec = 1;
++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32;
++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32;
++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) |
++ (ONENAND_START_ADDRESS_2);
++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) |
++ (ONENAND_COMMAND);
++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
++ (ONENAND_INTERRUPT_STATUS);
++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) |
++ (ONENAND_SYSTEM_CONFIG_1);
++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) |
++ (ONENAND_START_ADDRESS_1);
++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) |
++ (onenand_startaddr1);
++ dma_buffer->data.data2 = (onenand_startblock << 16) |
++ (onenand_startaddr2);
++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) |
++ (ONENAND_CMD_LOCK);
++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) |
++ (CLEAN_DATA_16);
++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) |
++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode));
++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) |
++ (ONENAND_STARTADDR1_RES);
++
++ /*************************************************************/
++ /* Write the necessary address reg in the onenand device */
++ /*************************************************************/
++
++ /* Enable and configure the SFlash controller */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg);
++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the ADDR0 and ADDR1 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0);
++ cmd->dst = MSM_NAND_ADDR0;
++ cmd->len = 8;
++ cmd++;
++
++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2);
++ cmd->dst = MSM_NAND_ADDR2;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the ADDR6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6);
++ cmd->dst = MSM_NAND_ADDR6;
++ cmd->len = 4;
++ cmd++;
++
++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0);
++ cmd->dst = MSM_NAND_GENP_REG0;
++ cmd->len = 16;
++ cmd++;
++
++ /* Write the FLASH_DEV_CMD4,5,6 registers */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->dst = MSM_NAND_DEV_CMD4;
++ cmd->len = 12;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]);
++ cmd->len = 4;
++ cmd++;
++
++ /*************************************************************/
++ /* Wait for the interrupt from the Onenand device controller */
++ /*************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]);
++ cmd->len = 4;
++ cmd++;
++
++ /*********************************************************/
++ /* Read the necessary status reg from the onenand device */
++ /*********************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the GENP3 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_GENP_REG3;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3);
++ cmd->len = 4;
++ cmd++;
++
++ /* Read the DEVCMD4 register */
++ cmd->cmd = 0;
++ cmd->src = MSM_NAND_DEV_CMD4;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4);
++ cmd->len = 4;
++ cmd++;
++
++ /************************************************************/
++ /* Restore the necessary registers to proper values */
++ /************************************************************/
++
++ /* Block on cmd ready and write CMD register */
++ cmd->cmd = DST_CRCI_NAND_CMD;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]);
++ cmd->dst = MSM_NAND_SFLASHC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Kick the execute command */
++ cmd->cmd = 0;
++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec);
++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD;
++ cmd->len = 4;
++ cmd++;
++
++ /* Block on data ready, and read the status register */
++ cmd->cmd = SRC_CRCI_NAND_DATA;
++ cmd->src = MSM_NAND_SFLASHC_STATUS;
++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]);
++ cmd->len = 4;
++ cmd++;
++
++
++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd)
++ >> 3) | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel,
++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++
++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF;
++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF;
++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF;
++
++#if VERBOSE
++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__,
++ dma_buffer->data.sfstat[0],
++ dma_buffer->data.sfstat[1],
++ dma_buffer->data.sfstat[2],
++ dma_buffer->data.sfstat[3]);
++
++ pr_info("%s: controller_status = %x\n", __func__,
++ controller_status);
++ pr_info("%s: interrupt_status = %x\n", __func__,
++ interrupt_status);
++ pr_info("%s: write_prot_status = %x\n", __func__,
++ write_prot_status);
++#endif
++ /* Check for errors, protection violations etc */
++ if ((controller_status != 0)
++ || (dma_buffer->data.sfstat[0] & 0x110)
++ || (dma_buffer->data.sfstat[1] & 0x110)
++ || (dma_buffer->data.sfstat[2] & 0x110)
++ || (dma_buffer->data.sfstat[3] & 0x110)) {
++ pr_err("%s: ECC/MPU/OP error\n", __func__);
++ err = -EIO;
++ }
++
++ if (!(write_prot_status & ONENAND_WP_LS)) {
++ pr_err("%s: Unexpected status ofs = 0x%llx,"
++ "wp_status = %x\n",
++ __func__, ofs, write_prot_status);
++ err = -EIO;
++ }
++
++ if (err)
++ break;
++ }
++
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++
++#if VERBOSE
++ pr_info("\n%s: ret %d\n", __func__, err);
++ pr_info("===================================================="
++ "=============\n");
++#endif
++ return err;
++}
++
++static int msm_onenand_suspend(struct mtd_info *mtd)
++{
++ return 0;
++}
++
++static void msm_onenand_resume(struct mtd_info *mtd)
++{
++}
++
++int msm_onenand_scan(struct mtd_info *mtd, int maxchips)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ /* Probe and check whether onenand device is present */
++ if (flash_onenand_probe(chip))
++ return -ENODEV;
++
++ mtd->size = 0x1000000 << ((onenand_info.device_id & 0xF0) >> 4);
++ mtd->writesize = onenand_info.data_buf_size;
++ mtd->oobsize = mtd->writesize >> 5;
++ mtd->erasesize = mtd->writesize << 6;
++ mtd->oobavail = msm_onenand_oob_64.oobavail;
++ mtd->ecclayout = &msm_onenand_oob_64;
++
++ mtd->type = MTD_NANDFLASH;
++ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->_erase = msm_onenand_erase;
++ mtd->_point = NULL;
++ mtd->_unpoint = NULL;
++ mtd->_read = msm_onenand_read;
++ mtd->_write = msm_onenand_write;
++ mtd->_read_oob = msm_onenand_read_oob;
++ mtd->_write_oob = msm_onenand_write_oob;
++ mtd->_lock = msm_onenand_lock;
++ mtd->_unlock = msm_onenand_unlock;
++ mtd->_suspend = msm_onenand_suspend;
++ mtd->_resume = msm_onenand_resume;
++ mtd->_block_isbad = msm_onenand_block_isbad;
++ mtd->_block_markbad = msm_onenand_block_markbad;
++ mtd->owner = THIS_MODULE;
++
++ pr_info("Found a supported onenand device\n");
++
++ return 0;
++}
++
++static const unsigned int bch_sup_cntrl[] = {
++ 0x307, /* MSM7x2xA */
++ 0x4030, /* MDM 9x15 */
++};
++
++static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) {
++ if (hw_id == bch_sup_cntrl[i])
++ return true;
++ }
++
++ return false;
++}
++
++/**
++ * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
++ * @param mtd MTD device structure
++ * @param maxchips Number of chips to scan for
++ *
++ * This fills out all the not initialized function pointers
++ * with the defaults.
++ * The flash ID is read and the mtd/chip structures are
++ * filled with the appropriate values.
++ */
++int msm_nand_scan(struct mtd_info *mtd, int maxchips)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++ uint32_t flash_id = 0, i, mtd_writesize;
++ uint8_t dev_found = 0;
++ uint8_t wide_bus;
++ uint32_t manid;
++ uint32_t devid;
++ uint32_t devcfg;
++ struct nand_flash_dev *flashdev = NULL;
++ struct nand_manufacturers *flashman = NULL;
++ unsigned int hw_id;
++
++ /*
++ * Some Spansion parts, like the S34MS04G2, requires that the
++ * NAND Flash be reset before issuing an ONFI probe.
++ */
++ flash_reset(chip);
++
++ /* Probe the Flash device for ONFI compliance */
++ if (!flash_onfi_probe(chip)) {
++ dev_found = 1;
++ } else {
++ /* Read the Flash ID from the Nand Flash Device */
++ flash_id = flash_read_id(chip);
++ manid = flash_id & 0xFF;
++ devid = (flash_id >> 8) & 0xFF;
++ devcfg = (flash_id >> 24) & 0xFF;
++
++ for (i = 0; !flashman && nand_manuf_ids[i].id; ++i)
++ if (nand_manuf_ids[i].id == manid)
++ flashman = &nand_manuf_ids[i];
++ for (i = 0; !flashdev && nand_flash_ids[i].id; ++i)
++ if (nand_flash_ids[i].id == devid)
++ flashdev = &nand_flash_ids[i];
++ if (!flashdev || !flashman) {
++ pr_err("ERROR: unknown nand device manuf=%x devid=%x\n",
++ manid, devid);
++ return -ENOENT;
++ } else
++ dev_found = 1;
++
++ if (!flashdev->pagesize) {
++ supported_flash.flash_id = flash_id;
++ supported_flash.density = flashdev->chipsize << 20;
++ supported_flash.widebus = devcfg & (1 << 6) ? 1 : 0;
++ supported_flash.pagesize = 1024 << (devcfg & 0x3);
++ supported_flash.blksize = (64 * 1024) <<
++ ((devcfg >> 4) & 0x3);
++ supported_flash.oobsize = (8 << ((devcfg >> 2) & 0x3)) *
++ (supported_flash.pagesize >> 9);
++
++ if ((supported_flash.oobsize > 64) &&
++ (supported_flash.pagesize == 2048)) {
++ pr_info("msm_nand: Found a 2K page device with"
++ " %d oobsize - changing oobsize to 64 "
++ "bytes.\n", supported_flash.oobsize);
++ supported_flash.oobsize = 64;
++ }
++ } else {
++ supported_flash.flash_id = flash_id;
++ supported_flash.density = flashdev->chipsize << 20;
++ supported_flash.widebus = flashdev->options &
++ NAND_BUSWIDTH_16 ? 1 : 0;
++ supported_flash.pagesize = flashdev->pagesize;
++ supported_flash.blksize = flashdev->erasesize;
++ supported_flash.oobsize = flashdev->pagesize >> 5;
++ }
++ }
++
++ if (dev_found) {
++ (!interleave_enable) ? (i = 1) : (i = 2);
++ wide_bus = supported_flash.widebus;
++ mtd->size = supported_flash.density * i;
++ mtd->writesize = supported_flash.pagesize * i;
++ mtd->oobsize = supported_flash.oobsize * i;
++ mtd->erasesize = supported_flash.blksize * i;
++ mtd->writebufsize = mtd->writesize;
++
++ if (!interleave_enable)
++ mtd_writesize = mtd->writesize;
++ else
++ mtd_writesize = mtd->writesize >> 1;
++
++ /* Check whether controller and NAND device support 8bit ECC*/
++ hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO);
++ if (msm_nand_has_bch_ecc_engine(hw_id)
++ && (supported_flash.ecc_correctability >= 8)) {
++ pr_info("Found supported NAND device for %dbit ECC\n",
++ supported_flash.ecc_correctability);
++ enable_bch_ecc = 1;
++ } else {
++ pr_info("Found a supported NAND device\n");
++ }
++ pr_info("NAND Controller ID : 0x%x\n", hw_id);
++ pr_info("NAND Device ID : 0x%x\n", supported_flash.flash_id);
++ pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8);
++ pr_info("Density : %lld MByte\n", (mtd->size>>20));
++ pr_info("Pagesize : %d Bytes\n", mtd->writesize);
++ pr_info("Erasesize: %d Bytes\n", mtd->erasesize);
++ pr_info("Oobsize : %d Bytes\n", mtd->oobsize);
++ } else {
++ pr_err("Unsupported Nand,Id: 0x%x \n", flash_id);
++ return -ENODEV;
++ }
++
++ /* Size of each codeword is 532Bytes incase of 8bit BCH ECC*/
++ chip->cw_size = enable_bch_ecc ? 532 : 528;
++ chip->CFG0 = (((mtd_writesize >> 9)-1) << 6) /* 4/8 cw/pg for 2/4k */
++ | (516 << 9) /* 516 user data bytes */
++ | (10 << 19) /* 10 parity bytes */
++ | (5 << 27) /* 5 address cycles */
++ | (0 << 30) /* Do not read status before data */
++ | (1 << 31) /* Send read cmd */
++ /* 0 spare bytes for 16 bit nand or 1/2 spare bytes for 8 bit */
++ | (wide_bus ? 0 << 23 : (enable_bch_ecc ? 2 << 23 : 1 << 23));
++
++ chip->CFG1 = (0 << 0) /* Enable ecc */
++ | (7 << 2) /* 8 recovery cycles */
++ | (0 << 5) /* Allow CS deassertion */
++ /* Bad block marker location */
++ | ((mtd_writesize - (chip->cw_size * (
++ (mtd_writesize >> 9) - 1)) + 1) << 6)
++ | (0 << 16) /* Bad block in user data area */
++ | (2 << 17) /* 6 cycle tWB/tRB */
++ | ((wide_bus) ? CFG1_WIDE_FLASH : 0); /* Wide flash bit */
++
++ chip->ecc_buf_cfg = 0x203;
++ chip->CFG0_RAW = 0xA80420C0;
++ chip->CFG1_RAW = 0x5045D;
++
++ if (enable_bch_ecc) {
++ chip->CFG1 |= (1 << 27); /* Enable BCH engine */
++ chip->ecc_bch_cfg = (0 << 0) /* Enable ECC*/
++ | (0 << 1) /* Enable/Disable SW reset of ECC engine */
++ | (1 << 4) /* 8bit ecc*/
++ | ((wide_bus) ? (14 << 8) : (13 << 8))/*parity bytes*/
++ | (516 << 16) /* 516 user data bytes */
++ | (1 << 30); /* Turn on ECC engine clocks always */
++ chip->CFG0_RAW = 0xA80428C0; /* CW size is increased to 532B */
++ }
++
++ /*
++ * For 4bit RS ECC (default ECC), parity bytes = 10 (for x8 and x16 I/O)
++ * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O).
++ */
++ chip->ecc_parity_bytes = enable_bch_ecc ? (wide_bus ? 14 : 13) : 10;
++
++ pr_info("CFG0 Init : 0x%08x\n", chip->CFG0);
++ pr_info("CFG1 Init : 0x%08x\n", chip->CFG1);
++ pr_info("ECCBUFCFG : 0x%08x\n", chip->ecc_buf_cfg);
++
++ if (mtd->oobsize == 64) {
++ mtd->oobavail = msm_nand_oob_64.oobavail;
++ mtd->ecclayout = &msm_nand_oob_64;
++ } else if (mtd->oobsize == 128) {
++ mtd->oobavail = msm_nand_oob_128.oobavail;
++ mtd->ecclayout = &msm_nand_oob_128;
++ } else if (mtd->oobsize == 224) {
++ mtd->oobavail = wide_bus ? msm_nand_oob_224_x16.oobavail :
++ msm_nand_oob_224_x8.oobavail;
++ mtd->ecclayout = wide_bus ? &msm_nand_oob_224_x16 :
++ &msm_nand_oob_224_x8;
++ } else if (mtd->oobsize == 256) {
++ mtd->oobavail = msm_nand_oob_256.oobavail;
++ mtd->ecclayout = &msm_nand_oob_256;
++ } else {
++ pr_err("Unsupported Nand, oobsize: 0x%x \n",
++ mtd->oobsize);
++ return -ENODEV;
++ }
++
++ /* Fill in remaining MTD driver data */
++ mtd->type = MTD_NANDFLASH;
++ mtd->flags = MTD_CAP_NANDFLASH;
++ /* mtd->ecctype = MTD_ECC_SW; */
++ mtd->_erase = msm_nand_erase;
++ mtd->_block_isbad = msm_nand_block_isbad;
++ mtd->_block_markbad = msm_nand_block_markbad;
++ mtd->_point = NULL;
++ mtd->_unpoint = NULL;
++ mtd->_read = msm_nand_read;
++ mtd->_write = msm_nand_write;
++ mtd->_read_oob = msm_nand_read_oob;
++ mtd->_write_oob = msm_nand_write_oob;
++ if (dual_nand_ctlr_present) {
++ mtd->_read_oob = msm_nand_read_oob_dualnandc;
++ mtd->_write_oob = msm_nand_write_oob_dualnandc;
++ if (interleave_enable) {
++ mtd->_erase = msm_nand_erase_dualnandc;
++ mtd->_block_isbad = msm_nand_block_isbad_dualnandc;
++ }
++ }
++
++ /* mtd->sync = msm_nand_sync; */
++ mtd->_lock = NULL;
++ /* mtd->_unlock = msm_nand_unlock; */
++ mtd->_suspend = msm_nand_suspend;
++ mtd->_resume = msm_nand_resume;
++ mtd->owner = THIS_MODULE;
++
++ /* Unlock whole block */
++ /* msm_nand_unlock_all(mtd); */
++
++ /* return this->scan_bbt(mtd); */
++ return 0;
++}
++EXPORT_SYMBOL_GPL(msm_nand_scan);
++
++/**
++ * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device
++ * @param mtd MTD device structure
++ */
++void msm_nand_release(struct mtd_info *mtd)
++{
++ /* struct msm_nand_chip *this = mtd->priv; */
++
++ /* Deregister the device */
++ mtd_device_unregister(mtd);
++}
++EXPORT_SYMBOL_GPL(msm_nand_release);
++
++struct msm_nand_info {
++ struct mtd_info mtd;
++ struct mtd_partition *parts;
++ struct msm_nand_chip msm_nand;
++};
++
++/* duplicating the NC01 XFR contents to NC10 */
++static int msm_nand_nc10_xfr_settings(struct mtd_info *mtd)
++{
++ struct msm_nand_chip *chip = mtd->priv;
++
++ struct {
++ dmov_s cmd[2];
++ unsigned cmdptr;
++ } *dma_buffer;
++ dmov_s *cmd;
++
++ wait_event(chip->wait_queue,
++ (dma_buffer = msm_nand_get_dma_buffer(
++ chip, sizeof(*dma_buffer))));
++
++ cmd = dma_buffer->cmd;
++
++ /* Copying XFR register contents from NC01 --> NC10 */
++ cmd->cmd = 0;
++ cmd->src = NC01(MSM_NAND_XFR_STEP1);
++ cmd->dst = NC10(MSM_NAND_XFR_STEP1);
++ cmd->len = 28;
++ cmd++;
++
++ BUILD_BUG_ON(2 != ARRAY_SIZE(dma_buffer->cmd));
++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
++ dma_buffer->cmd[0].cmd |= CMD_OCB;
++ cmd[-1].cmd |= CMD_OCU | CMD_LC;
++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
++ | CMD_PTR_LP;
++
++ mb();
++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST
++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
++ &dma_buffer->cmdptr)));
++ mb();
++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
++ return 0;
++}
++
++static ssize_t boot_layout_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return sprintf(buf, "%d\n", boot_layout);
++}
++
++static ssize_t boot_layout_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t n)
++{
++ struct msm_nand_info *info = dev_get_drvdata(dev);
++ struct msm_nand_chip *chip = info->mtd.priv;
++ unsigned int ud_size;
++ unsigned int spare_size;
++ unsigned int ecc_num_data_bytes;
++
++ sscanf(buf, "%d", &boot_layout);
++
++ ud_size = boot_layout? 512: 516;
++ spare_size = boot_layout? (chip->cw_size -
++ (chip->ecc_parity_bytes+ 1+ ud_size)):
++ (enable_bch_ecc ? 2 : 1);
++ ecc_num_data_bytes = boot_layout? 512: 516;
++
++ chip->CFG0 = (chip->CFG0 & ~SPARE_SIZE_BYTES_MASK);
++ chip->CFG0 |= (spare_size << 23);
++
++ chip->CFG0 = (chip->CFG0 & ~UD_SIZE_BYTES_MASK);
++ chip->CFG0 |= (ud_size << 9);
++
++ chip->ecc_buf_cfg = (chip->ecc_buf_cfg & ~ECC_NUM_DATA_BYTES_MASK)
++ | (ecc_num_data_bytes << 16);
++
++ return n;
++}
++
++static const DEVICE_ATTR(boot_layout, 0644, boot_layout_show, boot_layout_store);
++
++static int msm_nand_probe(struct platform_device *pdev)
++
++{
++ struct msm_nand_info *info;
++ struct resource *res;
++ int err;
++ struct mtd_part_parser_data ppdata = {};
++
++
++ res = platform_get_resource(pdev,
++ IORESOURCE_MEM, 0);
++ if (!res || !res->start) {
++ pr_err("%s: msm_nand_phys resource invalid/absent\n",
++ __func__);
++ return -ENODEV;
++ }
++ msm_nand_phys = res->start;
++
++ info = devm_kzalloc(&pdev->dev, sizeof(struct msm_nand_info), GFP_KERNEL);
++ if (!info) {
++ pr_err("%s: No memory for msm_nand_info\n", __func__);
++ return -ENOMEM;
++ }
++
++ info->msm_nand.dev = &pdev->dev;
++
++ init_waitqueue_head(&info->msm_nand.wait_queue);
++
++ info->msm_nand.dma_channel = 3;
++ pr_info("%s: dmac 0x%x\n", __func__, info->msm_nand.dma_channel);
++
++ /* this currently fails if dev is passed in */
++ info->msm_nand.dma_buffer =
++ dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE,
++ &info->msm_nand.dma_addr, GFP_KERNEL);
++ if (info->msm_nand.dma_buffer == NULL) {
++ pr_err("%s: No memory for msm_nand.dma_buffer\n", __func__);
++ err = -ENOMEM;
++ goto out_free_info;
++ }
++
++ pr_info("%s: allocated dma buffer at %p, dma_addr %x\n",
++ __func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr);
++
++ /* Let default be VERSION_1 for backward compatibility */
++ info->msm_nand.uncorrectable_bit_mask = BIT(8);
++ info->msm_nand.num_err_mask = 0x1F;
++
++ info->mtd.name = dev_name(&pdev->dev);
++ info->mtd.priv = &info->msm_nand;
++ info->mtd.owner = THIS_MODULE;
++
++ /* config ebi2_cfg register only for ping pong mode!!! */
++ if (!interleave_enable && dual_nand_ctlr_present)
++ flash_wr_reg(&info->msm_nand, EBI2_CFG_REG, 0x4010080);
++
++ if (dual_nand_ctlr_present)
++ msm_nand_nc10_xfr_settings(&info->mtd);
++
++ if (msm_nand_scan(&info->mtd, 1))
++ if (msm_onenand_scan(&info->mtd, 1)) {
++ pr_err("%s: No nand device found\n", __func__);
++ err = -ENXIO;
++ goto out_free_dma_buffer;
++ }
++
++ flash_wr_reg(&info->msm_nand, MSM_NAND_DEV_CMD_VLD,
++ DEV_CMD_VLD_SEQ_READ_START_VLD |
++ DEV_CMD_VLD_ERASE_START_VLD |
++ DEV_CMD_VLD_WRITE_START_VLD |
++ DEV_CMD_VLD_READ_START_VLD);
++
++ ppdata.of_node = pdev->dev.of_node;
++ err = mtd_device_parse_register(&info->mtd, NULL, &ppdata, NULL, 0);
++
++ if (err < 0) {
++ pr_err("%s: mtd_device_parse_register failed with err=%d\n",
++ __func__, err);
++ goto out_free_dma_buffer;
++ }
++
++ err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr);
++ if (err)
++ goto out_free_dma_buffer;
++
++ dev_set_drvdata(&pdev->dev, info);
++
++ return 0;
++
++out_free_dma_buffer:
++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE,
++ info->msm_nand.dma_buffer,
++ info->msm_nand.dma_addr);
++out_free_info:
++ return err;
++}
++
++static int msm_nand_remove(struct platform_device *pdev)
++{
++ struct msm_nand_info *info = dev_get_drvdata(&pdev->dev);
++
++ dev_set_drvdata(&pdev->dev, NULL);
++
++ if (info) {
++ msm_nand_release(&info->mtd);
++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE,
++ info->msm_nand.dma_buffer,
++ info->msm_nand.dma_addr);
++ }
++
++ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr);
++
++ return 0;
++}
++
++
++#ifdef CONFIG_OF
++static const struct of_device_id msm_nand_of_match[] = {
++ { .compatible = "qcom,qcom_nand", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, msm_nand_of_match);
++#endif
++
++
++static struct platform_driver msm_nand_driver = {
++ .probe = msm_nand_probe,
++ .remove = msm_nand_remove,
++ .driver = {
++ .name = "qcom_nand",
++ .owner = THIS_MODULE,
++ .of_match_table = msm_nand_of_match,
++ }
++};
++
++
++module_platform_driver(msm_nand_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("msm_nand flash driver code");
+diff --git a/drivers/mtd/nand/qcom_nand.h b/drivers/mtd/nand/qcom_nand.h
+new file mode 100644
+index 0000000..468186c
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nand.h
+@@ -0,0 +1,196 @@
++/* drivers/mtd/devices/msm_nand.h
++ *
++ * Copyright (c) 2008-2011, The Linux Foundation. All rights reserved.
++ * Copyright (C) 2007 Google, Inc.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H
++#define __DRIVERS_MTD_DEVICES_MSM_NAND_H
++
++extern unsigned long msm_nand_phys;
++extern unsigned long msm_nandc01_phys;
++extern unsigned long msm_nandc10_phys;
++extern unsigned long msm_nandc11_phys;
++extern unsigned long ebi2_register_base;
++
++#define NC01(X) ((X) + msm_nandc01_phys - msm_nand_phys)
++#define NC10(X) ((X) + msm_nandc10_phys - msm_nand_phys)
++#define NC11(X) ((X) + msm_nandc11_phys - msm_nand_phys)
++
++#define MSM_NAND_REG(off) (msm_nand_phys + (off))
++
++#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000)
++#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004)
++#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008)
++#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C)
++#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010)
++#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014)
++#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018)
++#define MSM_NAND_SFLASHC_STATUS MSM_NAND_REG(0x001C)
++#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020)
++#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024)
++#define MSM_NAND_DEV0_ECC_CFG MSM_NAND_REG(0x0028)
++#define MSM_NAND_DEV1_ECC_CFG MSM_NAND_REG(0x002C)
++#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030)
++#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034)
++#define MSM_NAND_SFLASHC_CMD MSM_NAND_REG(0x0038)
++#define MSM_NAND_SFLASHC_EXEC_CMD MSM_NAND_REG(0x003C)
++#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040)
++#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044)
++#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050)
++#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054)
++#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058)
++#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060)
++#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064)
++#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070)
++#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074)
++#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078)
++#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C)
++#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080)
++#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084)
++#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088)
++#define MSM_NAND_GENP_REG0 MSM_NAND_REG(0x0090)
++#define MSM_NAND_GENP_REG1 MSM_NAND_REG(0x0094)
++#define MSM_NAND_GENP_REG2 MSM_NAND_REG(0x0098)
++#define MSM_NAND_GENP_REG3 MSM_NAND_REG(0x009C)
++#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0)
++#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4)
++#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8)
++#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC)
++#define DEV_CMD_VLD_SEQ_READ_START_VLD 0x10
++#define DEV_CMD_VLD_ERASE_START_VLD 0x8
++#define DEV_CMD_VLD_WRITE_START_VLD 0x4
++#define DEV_CMD_VLD_READ_STOP_VLD 0x2
++#define DEV_CMD_VLD_READ_START_VLD 0x1
++
++#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0)
++#define MSM_NAND_ADDR2 MSM_NAND_REG(0x00C0)
++#define MSM_NAND_ADDR3 MSM_NAND_REG(0x00C4)
++#define MSM_NAND_ADDR4 MSM_NAND_REG(0x00C8)
++#define MSM_NAND_ADDR5 MSM_NAND_REG(0x00CC)
++#define MSM_NAND_DEV_CMD3 MSM_NAND_REG(0x00D0)
++#define MSM_NAND_DEV_CMD4 MSM_NAND_REG(0x00D4)
++#define MSM_NAND_DEV_CMD5 MSM_NAND_REG(0x00D8)
++#define MSM_NAND_DEV_CMD6 MSM_NAND_REG(0x00DC)
++#define MSM_NAND_SFLASHC_BURST_CFG MSM_NAND_REG(0x00E0)
++#define MSM_NAND_ADDR6 MSM_NAND_REG(0x00E4)
++#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0)
++#define MSM_NAND_HW_INFO MSM_NAND_REG(0x00FC)
++#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100)
++
++/* device commands */
++
++#define MSM_NAND_CMD_SOFT_RESET 0x01
++#define MSM_NAND_CMD_PAGE_READ 0x32
++#define MSM_NAND_CMD_PAGE_READ_ECC 0x33
++#define MSM_NAND_CMD_PAGE_READ_ALL 0x34
++#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15
++#define MSM_NAND_CMD_PRG_PAGE 0x36
++#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37
++#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39
++#define MSM_NAND_CMD_BLOCK_ERASE 0x3A
++#define MSM_NAND_CMD_FETCH_ID 0x0B
++#define MSM_NAND_CMD_STATUS 0x0C
++#define MSM_NAND_CMD_RESET 0x0D
++
++/* Sflash Commands */
++
++#define MSM_NAND_SFCMD_DATXS 0x0
++#define MSM_NAND_SFCMD_CMDXS 0x1
++#define MSM_NAND_SFCMD_BURST 0x0
++#define MSM_NAND_SFCMD_ASYNC 0x1
++#define MSM_NAND_SFCMD_ABORT 0x1
++#define MSM_NAND_SFCMD_REGRD 0x2
++#define MSM_NAND_SFCMD_REGWR 0x3
++#define MSM_NAND_SFCMD_INTLO 0x4
++#define MSM_NAND_SFCMD_INTHI 0x5
++#define MSM_NAND_SFCMD_DATRD 0x6
++#define MSM_NAND_SFCMD_DATWR 0x7
++
++#define SFLASH_PREPCMD(numxfr, offval, delval, trnstp, mode, opcode) \
++ ((numxfr<<20)|(offval<<12)|(delval<<6)|(trnstp<<5)|(mode<<4)|opcode)
++
++#define SFLASH_BCFG 0x20100327
++
++/* Onenand addresses */
++
++#define ONENAND_MANUFACTURER_ID 0xF000
++#define ONENAND_DEVICE_ID 0xF001
++#define ONENAND_VERSION_ID 0xF002
++#define ONENAND_DATA_BUFFER_SIZE 0xF003
++#define ONENAND_BOOT_BUFFER_SIZE 0xF004
++#define ONENAND_AMOUNT_OF_BUFFERS 0xF005
++#define ONENAND_TECHNOLOGY 0xF006
++#define ONENAND_START_ADDRESS_1 0xF100
++#define ONENAND_START_ADDRESS_2 0xF101
++#define ONENAND_START_ADDRESS_3 0xF102
++#define ONENAND_START_ADDRESS_4 0xF103
++#define ONENAND_START_ADDRESS_5 0xF104
++#define ONENAND_START_ADDRESS_6 0xF105
++#define ONENAND_START_ADDRESS_7 0xF106
++#define ONENAND_START_ADDRESS_8 0xF107
++#define ONENAND_START_BUFFER 0xF200
++#define ONENAND_COMMAND 0xF220
++#define ONENAND_SYSTEM_CONFIG_1 0xF221
++#define ONENAND_SYSTEM_CONFIG_2 0xF222
++#define ONENAND_CONTROLLER_STATUS 0xF240
++#define ONENAND_INTERRUPT_STATUS 0xF241
++#define ONENAND_START_BLOCK_ADDRESS 0xF24C
++#define ONENAND_WRITE_PROT_STATUS 0xF24E
++#define ONENAND_ECC_STATUS 0xFF00
++#define ONENAND_ECC_ERRPOS_MAIN0 0xFF01
++#define ONENAND_ECC_ERRPOS_SPARE0 0xFF02
++#define ONENAND_ECC_ERRPOS_MAIN1 0xFF03
++#define ONENAND_ECC_ERRPOS_SPARE1 0xFF04
++#define ONENAND_ECC_ERRPOS_MAIN2 0xFF05
++#define ONENAND_ECC_ERRPOS_SPARE2 0xFF06
++#define ONENAND_ECC_ERRPOS_MAIN3 0xFF07
++#define ONENAND_ECC_ERRPOS_SPARE3 0xFF08
++
++/* Onenand commands */
++#define ONENAND_WP_US (1 << 2)
++#define ONENAND_WP_LS (1 << 1)
++
++#define ONENAND_CMDLOAD 0x0000
++#define ONENAND_CMDLOADSPARE 0x0013
++#define ONENAND_CMDPROG 0x0080
++#define ONENAND_CMDPROGSPARE 0x001A
++#define ONENAND_CMDERAS 0x0094
++#define ONENAND_CMD_UNLOCK 0x0023
++#define ONENAND_CMD_LOCK 0x002A
++
++#define ONENAND_SYSCFG1_ECCENA(mode) (0x40E0 | (mode ? 0 : 0x8002))
++#define ONENAND_SYSCFG1_ECCDIS(mode) (0x41E0 | (mode ? 0 : 0x8002))
++
++#define ONENAND_CLRINTR 0x0000
++#define ONENAND_STARTADDR1_RES 0x07FF
++#define ONENAND_STARTADDR3_RES 0x07FF
++
++#define DATARAM0_0 0x8
++#define DEVICE_FLASHCORE_0 (0 << 15)
++#define DEVICE_FLASHCORE_1 (1 << 15)
++#define DEVICE_BUFFERRAM_0 (0 << 15)
++#define DEVICE_BUFFERRAM_1 (1 << 15)
++#define ONENAND_DEVICE_IS_DDP (1 << 3)
++
++#define CLEAN_DATA_16 0xFFFF
++#define CLEAN_DATA_32 0xFFFFFFFF
++
++#define EBI2_REG(off) (ebi2_register_base + (off))
++#define EBI2_CHIP_SELECT_CFG0 EBI2_REG(0x0000)
++#define EBI2_CFG_REG EBI2_REG(0x0004)
++#define EBI2_NAND_ADM_MUX EBI2_REG(0x005C)
++
++extern struct flash_platform_data msm_nand_data;
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch
new file mode 100644
index 0000000..b79c16f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch
@@ -0,0 +1,115 @@
+From 4490cfa66379909cdddc3518c8e75b7cd26d8f69 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 16 Jun 2014 16:53:49 -0500
+Subject: [PATCH 151/182] ARM: ipq8064: Add nand device info
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 34 ++++++++++++++++++++++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++
+ 2 files changed, 67 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index c752889..4062eb6 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -45,6 +45,29 @@
+ bias-none;
+ };
+ };
++ nand_pins: nand_pins {
++ mux {
++ pins = "gpio34", "gpio35", "gpio36",
++ "gpio37", "gpio38", "gpio39",
++ "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ function = "nand";
++ drive-strength = <10>;
++ bias-disable;
++ };
++ pullups {
++ pins = "gpio39";
++ bias-pull-up;
++ };
++ hold {
++ pins = "gpio40", "gpio41", "gpio42",
++ "gpio43", "gpio44", "gpio45",
++ "gpio46", "gpio47";
++ bias-bus-hold;
++ };
++ };
++
+ };
+
+ gsbi@16300000 {
+@@ -126,5 +149,16 @@
+ sata@29000000 {
+ status = "ok";
+ };
++
++ dma@18300000 {
++ status = "ok";
++ };
++
++ nand@0x1ac00000 {
++ status = "ok";
++
++ pinctrl-0 = <&nand_pins>;
++ pinctrl-names = "default";
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 93c0315..d9fce15 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -76,6 +76,7 @@
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 32 0x4>;
++
+ };
+
+ intc: interrupt-controller@2000000 {
+@@ -369,5 +370,37 @@
+ phy-names = "sata-phy";
+ status = "disabled";
+ };
++
++ adm_dma: dma@18300000 {
++ compatible = "qcom,adm";
++ reg = <0x18300000 0x100000>;
++ interrupts = <0 170 0>;
++
++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
++ clock-names = "core_clk", "iface_clk";
++
++ resets = <&gcc ADM0_RESET>,
++ <&gcc ADM0_PBUS_RESET>,
++ <&gcc ADM0_C0_RESET>,
++ <&gcc ADM0_C1_RESET>,
++ <&gcc ADM0_C2_RESET>;
++
++ reset-names = "adm", "pbus", "c0", "c1", "c2";
++
++ status = "disabled";
++ };
++
++ nand@0x1ac00000 {
++ compatible = "qcom,qcom_nand";
++ reg = <0x1ac00000 0x800>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ clocks = <&gcc EBI2_CLK>;
++ clock-names = "core_clk";
++
++
++ status = "disabled";
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch
new file mode 100644
index 0000000..97f012d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch
@@ -0,0 +1,27 @@
+From d04f697bf7cfe756de6187be1d0e5669d4d5bca0 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 16 Jun 2014 17:30:04 -0500
+Subject: [PATCH 152/182] ARM: qcom: config: Add NAND config options
+
+Add NAND config options to enable QCOM NAND controller
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 85a35af..e3f9013 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -56,6 +56,7 @@ CONFIG_DEVTMPFS_MOUNT=y
+ CONFIG_MTD=y
+ CONFIG_MTD_BLOCK=y
+ CONFIG_MTD_M25P80=y
++CONFIG_MTD_NAND=y
+ CONFIG_BLK_DEV_LOOP=y
+ CONFIG_BLK_DEV_RAM=y
+ CONFIG_SCSI_TGT=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch
new file mode 100644
index 0000000..03b65e6
--- /dev/null
+++ b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch
@@ -0,0 +1,169 @@
+From 0b469f3f4c0e6a0e0b1b60a059600e325a03b6f5 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 12 Jun 2014 00:52:06 -0500
+Subject: [PATCH 153/182] soc: qcom: tcsr: Add TCSR driver
+
+This patch adds support for the TCSR IP block present in Qualcomm processors.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ .../devicetree/bindings/soc/qcom/qcom,tcsr.txt | 25 ++++++++
+ drivers/soc/qcom/Kconfig | 6 ++
+ drivers/soc/qcom/Makefile | 1 +
+ drivers/soc/qcom/qcom_tcsr.c | 64 ++++++++++++++++++++
+ include/dt-bindings/soc/qcom,tcsr.h | 19 ++++++
+ 5 files changed, 115 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt
+ create mode 100644 drivers/soc/qcom/qcom_tcsr.c
+ create mode 100644 include/dt-bindings/soc/qcom,tcsr.h
+
+diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt
+new file mode 100644
+index 0000000..6ea74c1
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt
+@@ -0,0 +1,25 @@
++QCOM TCSR (Top Control and Status Register) Driver
++
++The TCSR provides miscellaneous control functions and status registers for
++Qualcomm processors.
++
++Required properties:
++- compatible: must contain "qcom,tcsr" for IPQ806x
++- reg: Address range for TCSR registers
++
++Optional properties:
++- qcom,usb-ctrl-select : indicates USB port type selection. Please reference
++ dt-bindings/soc/qcom,tcsr.h for valid USB port selection values.
++
++Example for IPQ8064:
++
++#include <dt-bindings/soc/qcom,tcsr.h>
++
++ tcsr: tcsr@1a400000 {
++ compatible = "qcom,tcsr";
++ reg = <0x1a400000 0x100>;
++
++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>;
++ };
++
++
+diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
+index 7bd2c94..3e4486a 100644
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -9,3 +9,9 @@ config QCOM_GSBI
+ functions for connecting the underlying serial UART, SPI, and I2C
+ devices to the output pins.
+
++config QCOM_TCSR
++ tristate "QCOM Top Control and Status Registers"
++ depends on ARCH_QCOM
++ help
++ Say y here to enable TCSR support. The TCSR provides control
++ functions for various peripherals.
+diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
+index 4389012..d299492 100644
+--- a/drivers/soc/qcom/Makefile
++++ b/drivers/soc/qcom/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
++obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o
+diff --git a/drivers/soc/qcom/qcom_tcsr.c b/drivers/soc/qcom/qcom_tcsr.c
+new file mode 100644
+index 0000000..dd33153
+--- /dev/null
++++ b/drivers/soc/qcom/qcom_tcsr.c
+@@ -0,0 +1,64 @@
++/*
++ * 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 rev 2 and
++ * only rev 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/of_platform.h>
++#include <linux/platform_device.h>
++
++#define TCSR_USB_PORT_SEL 0xb0
++
++static int tcsr_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ const struct device_node *node = pdev->dev.of_node;
++ void __iomem *base;
++ u32 val;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) {
++ dev_err(&pdev->dev, "setting usb port select = %d\n", val);
++ writel(val, base + TCSR_USB_PORT_SEL);
++ }
++
++ return 0;
++}
++
++static const struct of_device_id tcsr_dt_match[] = {
++ { .compatible = "qcom,tcsr", },
++ { },
++};
++
++MODULE_DEVICE_TABLE(of, tcsr_dt_match);
++
++static struct platform_driver tcsr_driver = {
++ .driver = {
++ .name = "tcsr",
++ .owner = THIS_MODULE,
++ .of_match_table = tcsr_dt_match,
++ },
++ .probe = tcsr_probe,
++};
++
++module_platform_driver(tcsr_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM TCSR driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/include/dt-bindings/soc/qcom,tcsr.h b/include/dt-bindings/soc/qcom,tcsr.h
+new file mode 100644
+index 0000000..c9d497a
+--- /dev/null
++++ b/include/dt-bindings/soc/qcom,tcsr.h
+@@ -0,0 +1,19 @@
++/* 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.
++ */
++#ifndef __DT_BINDINGS_QCOM_TCSR_H
++#define __DT_BINDINGS_QCOM_TCSR_H
++
++#define TCSR_USB_SELECT_USB3_P0 0x1
++#define TCSR_USB_SELECT_USB3_P1 0x2
++#define TCSR_USB_SELECT_USB3_DUAL 0x3
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch
new file mode 100644
index 0000000..28951ab
--- /dev/null
+++ b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch
@@ -0,0 +1,29 @@
+From 2561c49dcf5cb35ad72df34bf225a51fe2fa87e5 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 12 Jun 2014 00:53:19 -0500
+Subject: [PATCH 154/182] clk: qcom: Correct UTMI clock frequency table
+
+This patch changes the UTMI clock frequency from 20MHz to the correct value of
+60MHz.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
+index f7916be..d80dc69 100644
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -1992,7 +1992,7 @@ static struct clk_branch usb30_1_branch_clk = {
+ };
+
+ static const struct freq_tbl clk_tbl_usb30_utmi[] = {
+- { 60000000, P_PLL0, 1, 1, 40 },
++ { 60000000, P_PLL8, 1, 5, 32 },
+ { }
+ };
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch
new file mode 100644
index 0000000..7bfbbcb
--- /dev/null
+++ b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch
@@ -0,0 +1,63 @@
+From 9ab5cb48696dca02bf43170b50d1034a96fb9e85 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Sun, 15 Jun 2014 00:39:57 -0500
+Subject: [PATCH 155/182] clk: qcom: Fix incorrect UTMI DT include values
+
+Corrected values for UTMI clock definitions.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 38 +++++++++++++-------------
+ 1 file changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+index 0fd3e8a..163ba85 100644
+--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -273,24 +273,24 @@
+ #define USB30_SLEEP_CLK 262
+ #define USB30_UTMI_SRC 263
+ #define USB30_0_UTMI_CLK 264
+-#define USB30_1_UTMI_CLK 264
+-#define USB30_MASTER_SRC 265
+-#define USB30_0_MASTER_CLK 266
+-#define USB30_1_MASTER_CLK 267
+-#define GMAC_CORE1_CLK_SRC 268
+-#define GMAC_CORE2_CLK_SRC 269
+-#define GMAC_CORE3_CLK_SRC 270
+-#define GMAC_CORE4_CLK_SRC 271
+-#define GMAC_CORE1_CLK 272
+-#define GMAC_CORE2_CLK 273
+-#define GMAC_CORE3_CLK 274
+-#define GMAC_CORE4_CLK 275
+-#define UBI32_CORE1_CLK_SRC 276
+-#define UBI32_CORE2_CLK_SRC 277
+-#define UBI32_CORE1_CLK 278
+-#define UBI32_CORE2_CLK 279
+-#define NSSTCM_CLK_SRC 280
+-#define NSSTCM_CLK 281
+-#define NSS_CORE_CLK 282 /* Virtual */
++#define USB30_1_UTMI_CLK 265
++#define USB30_MASTER_SRC 266
++#define USB30_0_MASTER_CLK 267
++#define USB30_1_MASTER_CLK 268
++#define GMAC_CORE1_CLK_SRC 269
++#define GMAC_CORE2_CLK_SRC 270
++#define GMAC_CORE3_CLK_SRC 271
++#define GMAC_CORE4_CLK_SRC 272
++#define GMAC_CORE1_CLK 273
++#define GMAC_CORE2_CLK 274
++#define GMAC_CORE3_CLK 275
++#define GMAC_CORE4_CLK 276
++#define UBI32_CORE1_CLK_SRC 277
++#define UBI32_CORE2_CLK_SRC 278
++#define UBI32_CORE1_CLK 279
++#define UBI32_CORE2_CLK 280
++#define NSSTCM_CLK_SRC 281
++#define NSSTCM_CLK 282
++#define NSS_CORE_CLK 283 /* Virtual */
+
+ #endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch
new file mode 100644
index 0000000..f8ad3cd
--- /dev/null
+++ b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch
@@ -0,0 +1,212 @@
+From 364532aeb9024d0ff7b88121f9a953f559b1c136 Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Mon, 7 Oct 2013 10:44:57 +0300
+Subject: [PATCH 156/182] usb: dwc3: Add Qualcomm DWC3 glue layer driver
+
+DWC3 glue layer is hardware layer around Synopsys DesignWare
+USB3 core. Its purpose is to supply Synopsys IP with required
+clocks, voltages and interface it with the rest of the SoC.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+---
+ drivers/usb/dwc3/Kconfig | 8 +++
+ drivers/usb/dwc3/Makefile | 1 +
+ drivers/usb/dwc3/dwc3-qcom.c | 156 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 165 insertions(+)
+ create mode 100644 drivers/usb/dwc3/dwc3-qcom.c
+
+diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
+index e2c730f..2d01983 100644
+--- a/drivers/usb/dwc3/Kconfig
++++ b/drivers/usb/dwc3/Kconfig
+@@ -59,6 +59,14 @@ config USB_DWC3_EXYNOS
+ Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
+ say 'Y' or 'M' if you have one such device.
+
++config USB_DWC3_QCOM
++ tristate "Qualcomm Platforms"
++ default USB_DWC3
++ select USB_QCOM_DWC3_PHYS
++ help
++ Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
++ say 'Y' or 'M' if you have one such device.
++
+ config USB_DWC3_PCI
+ tristate "PCIe-based Platforms"
+ depends on PCI
+diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
+index 10ac3e7..4066e4e 100644
+--- a/drivers/usb/dwc3/Makefile
++++ b/drivers/usb/dwc3/Makefile
+@@ -31,5 +31,6 @@ endif
+
+ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
+ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
++obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+ obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+ obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+new file mode 100644
+index 0000000..8d17360
+--- /dev/null
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -0,0 +1,156 @@
++/* 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/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++
++#include "core.h"
++
++
++struct dwc3_qcom {
++ struct device *dev;
++
++ struct clk *core_clk;
++ struct clk *iface_clk;
++ struct clk *sleep_clk;
++
++ struct regulator *gdsc;
++};
++
++static int dwc3_qcom_probe(struct platform_device *pdev)
++{
++ struct device_node *node = pdev->dev.of_node;
++ struct dwc3_qcom *mdwc;
++ int ret = 0;
++
++ mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL);
++ if (!mdwc)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, mdwc);
++
++ mdwc->dev = &pdev->dev;
++
++ mdwc->gdsc = devm_regulator_get(mdwc->dev, "gdsc");
++
++ mdwc->core_clk = devm_clk_get(mdwc->dev, "core");
++ if (IS_ERR(mdwc->core_clk)) {
++ dev_dbg(mdwc->dev, "failed to get core clock\n");
++ return PTR_ERR(mdwc->core_clk);
++ }
++
++ mdwc->iface_clk = devm_clk_get(mdwc->dev, "iface");
++ if (IS_ERR(mdwc->iface_clk)) {
++ dev_dbg(mdwc->dev, "failed to get iface clock, skipping\n");
++ mdwc->iface_clk = NULL;
++ }
++
++ mdwc->sleep_clk = devm_clk_get(mdwc->dev, "sleep");
++ if (IS_ERR(mdwc->sleep_clk)) {
++ dev_dbg(mdwc->dev, "failed to get sleep clock, skipping\n");
++ mdwc->sleep_clk = NULL;
++ }
++
++ if (!IS_ERR(mdwc->gdsc)) {
++ ret = regulator_enable(mdwc->gdsc);
++ if (ret)
++ dev_err(mdwc->dev, "cannot enable gdsc\n");
++ }
++
++ clk_prepare_enable(mdwc->core_clk);
++
++ if (mdwc->iface_clk)
++ clk_prepare_enable(mdwc->iface_clk);
++
++ if (mdwc->sleep_clk)
++ clk_prepare_enable(mdwc->sleep_clk);
++
++ ret = of_platform_populate(node, NULL, NULL, mdwc->dev);
++ if (ret) {
++ dev_err(mdwc->dev, "failed to register core - %d\n", ret);
++ dev_dbg(mdwc->dev, "failed to add create dwc3 core\n");
++ goto dis_clks;
++ }
++
++ return 0;
++
++dis_clks:
++
++ dev_err(mdwc->dev, "disabling clocks\n");
++
++ if (mdwc->sleep_clk)
++ clk_disable_unprepare(mdwc->sleep_clk);
++
++ if (mdwc->iface_clk)
++ clk_disable_unprepare(mdwc->iface_clk);
++
++ clk_disable_unprepare(mdwc->core_clk);
++
++ if (!IS_ERR(mdwc->gdsc)) {
++ ret = regulator_disable(mdwc->gdsc);
++ if (ret)
++ dev_dbg(mdwc->dev, "cannot disable gdsc\n");
++ }
++
++ return ret;
++}
++
++static int dwc3_qcom_remove(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ struct dwc3_qcom *mdwc = platform_get_drvdata(pdev);
++
++ if (mdwc->sleep_clk)
++ clk_disable_unprepare(mdwc->sleep_clk);
++
++ if (mdwc->iface_clk)
++ clk_disable_unprepare(mdwc->iface_clk);
++
++ clk_disable_unprepare(mdwc->core_clk);
++
++ if (!IS_ERR(mdwc->gdsc)) {
++ ret = regulator_disable(mdwc->gdsc);
++ if (ret)
++ dev_dbg(mdwc->dev, "cannot disable gdsc\n");
++ }
++ return ret;
++}
++
++static const struct of_device_id of_dwc3_match[] = {
++ { .compatible = "qcom,dwc3" },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, of_dwc3_match);
++
++static struct platform_driver dwc3_qcom_driver = {
++ .probe = dwc3_qcom_probe,
++ .remove = dwc3_qcom_remove,
++ .driver = {
++ .name = "qcom-dwc3",
++ .owner = THIS_MODULE,
++ .of_match_table = of_dwc3_match,
++ },
++};
++
++module_platform_driver(dwc3_qcom_driver);
++
++MODULE_ALIAS("platform:qcom-dwc3");
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
new file mode 100644
index 0000000..332a32c
--- /dev/null
+++ b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
@@ -0,0 +1,874 @@
+From d968f8c82db73141c6bc145148642391cb698442 Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Mon, 7 Oct 2013 10:44:56 +0300
+Subject: [PATCH 157/182] usb: phy: Add Qualcomm DWC3 HS/SS PHY drivers
+
+These drivers handles control and configuration of the HS
+and SS USB PHY transceivers. They are part of the driver
+which manage Synopsys DesignWare USB3 controller stack
+inside Qualcomm SoC's.
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+---
+ drivers/usb/phy/Kconfig | 13 +-
+ drivers/usb/phy/Makefile | 2 +
+ drivers/usb/phy/phy-qcom-hsusb.c | 340 ++++++++++++++++++++++++++++
+ drivers/usb/phy/phy-qcom-ssusb.c | 455 ++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 809 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/usb/phy/phy-qcom-hsusb.c
+ create mode 100644 drivers/usb/phy/phy-qcom-ssusb.c
+
+diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
+index 7d1451d..ddb65be 100644
+--- a/drivers/usb/phy/Kconfig
++++ b/drivers/usb/phy/Kconfig
+@@ -193,7 +193,7 @@ config USB_ISP1301
+
+ config USB_MSM_OTG
+ tristate "OTG support for Qualcomm on-chip USB controller"
+- depends on (USB || USB_GADGET) && ARCH_MSM
++ depends on (USB || USB_GADGET) && ARCH_QCOM
+ select USB_PHY
+ help
+ Enable this to support the USB OTG transceiver on MSM chips. It
+@@ -251,6 +251,17 @@ config USB_RCAR_GEN2_PHY
+ To compile this driver as a module, choose M here: the
+ module will be called phy-rcar-gen2-usb.
+
++config USB_QCOM_DWC3_PHY
++ tristate "Qualcomm USB controller DWC3 PHY wrappers support"
++ depends on (USB || USB_GADGET) && ARCH_QCOM
++ select USB_PHY
++ help
++ Enable this to support the DWC3 USB PHY transceivers on QCOM chips
++ with DWC3 USB core. It handles PHY initialization, clock
++ management required after resetting the hardware and power
++ management. This driver is required even for peripheral only or
++ host only mode configurations.
++
+ config USB_ULPI
+ bool "Generic ULPI Transceiver Driver"
+ depends on ARM
+diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
+index be58ada..857f04e 100644
+--- a/drivers/usb/phy/Makefile
++++ b/drivers/usb/phy/Makefile
+@@ -26,6 +26,8 @@ obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o
+ obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
+ obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
+ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-hsusb.o
++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-ssusb.o
+ obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
+ obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
+ obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
+diff --git a/drivers/usb/phy/phy-qcom-hsusb.c b/drivers/usb/phy/phy-qcom-hsusb.c
+new file mode 100644
+index 0000000..f96b2b9
+--- /dev/null
++++ b/drivers/usb/phy/phy-qcom-hsusb.c
+@@ -0,0 +1,340 @@
++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++
++/**
++ * USB QSCRATCH Hardware registers
++ */
++#define QSCRATCH_CTRL_REG (0x04)
++#define QSCRATCH_GENERAL_CFG (0x08)
++#define PHY_CTRL_REG (0x10)
++#define PARAMETER_OVERRIDE_X_REG (0x14)
++#define CHARGING_DET_CTRL_REG (0x18)
++#define CHARGING_DET_OUTPUT_REG (0x1c)
++#define ALT_INTERRUPT_EN_REG (0x20)
++#define PHY_IRQ_STAT_REG (0x24)
++#define CGCTL_REG (0x28)
++
++#define PHY_3P3_VOL_MIN 3050000 /* uV */
++#define PHY_3P3_VOL_MAX 3300000 /* uV */
++#define PHY_3P3_HPM_LOAD 16000 /* uA */
++
++#define PHY_1P8_VOL_MIN 1800000 /* uV */
++#define PHY_1P8_VOL_MAX 1800000 /* uV */
++#define PHY_1P8_HPM_LOAD 19000 /* uA */
++
++/* TODO: these are suspicious */
++#define USB_VDDCX_NO 1 /* index */
++#define USB_VDDCX_MIN 5 /* index */
++#define USB_VDDCX_MAX 7 /* index */
++
++struct qcom_dwc3_hs_phy {
++ struct usb_phy phy;
++ void __iomem *base;
++ struct device *dev;
++
++ struct clk *xo_clk;
++ struct clk *utmi_clk;
++
++ struct regulator *v3p3;
++ struct regulator *v1p8;
++ struct regulator *vddcx;
++ struct regulator *vbus;
++};
++
++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_hs_phy, phy)
++
++
++/**
++ * Write register.
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @offset - register offset.
++ * @val - value to write.
++ */
++static inline void qcom_dwc3_hs_write(void __iomem *base, u32 offset, u32 val)
++{
++ writel(val, base + offset);
++}
++
++/**
++ * 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_hs_write_readback(void __iomem *base, u32 offset,
++ const u32 mask, u32 val)
++{
++ u32 write_val, tmp = readl(base + offset);
++
++ tmp &= ~mask; /* retain other bits */
++ write_val = tmp | val;
++
++ writel(write_val, base + offset);
++
++ /* Read back to see if val was written */
++ tmp = readl(base + offset);
++ tmp &= mask; /* clear other bits */
++
++ if (tmp != val)
++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
++}
++
++static int qcom_dwc3_hs_notify_connect(struct usb_phy *x,
++ enum usb_device_speed speed)
++{
++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
++
++ dev_err(phy->dev, "notify connect\n");
++ return 0;
++}
++
++static int qcom_dwc3_hs_notify_disconnect(struct usb_phy *x,
++ enum usb_device_speed speed)
++{
++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
++
++ dev_err(phy->dev, "notify disconnect\n");
++ return 0;
++}
++
++
++static void qcom_dwc3_hs_phy_shutdown(struct usb_phy *x)
++{
++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
++ int ret;
++
++ ret = regulator_set_voltage(phy->v3p3, 0, PHY_3P3_VOL_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for v3p3\n");
++
++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for v1p8\n");
++
++ ret = regulator_disable(phy->v1p8);
++ if (ret)
++ dev_err(phy->dev, "cannot disable v1p8\n");
++
++ ret = regulator_disable(phy->v3p3);
++ if (ret)
++ dev_err(phy->dev, "cannot disable v3p3\n");
++
++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for vddcx\n");
++
++ ret = regulator_disable(phy->vddcx);
++ if (ret)
++ dev_err(phy->dev, "cannot enable vddcx\n");
++
++ clk_disable_unprepare(phy->utmi_clk);
++}
++
++static int qcom_dwc3_hs_phy_init(struct usb_phy *x)
++{
++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
++ int ret;
++ u32 val;
++
++ clk_prepare_enable(phy->utmi_clk);
++
++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for vddcx\n");
++
++ ret = regulator_enable(phy->vddcx);
++ if (ret)
++ dev_err(phy->dev, "cannot enable vddcx\n");
++
++ ret = regulator_set_voltage(phy->v3p3, PHY_3P3_VOL_MIN,
++ PHY_3P3_VOL_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for v3p3\n");
++
++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
++ PHY_1P8_VOL_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for v1p8\n");
++
++ ret = regulator_set_optimum_mode(phy->v1p8, PHY_1P8_HPM_LOAD);
++ if (ret < 0)
++ dev_err(phy->dev, "cannot set HPM of regulator v1p8\n");
++
++ ret = regulator_enable(phy->v1p8);
++ if (ret)
++ dev_err(phy->dev, "cannot enable v1p8\n");
++
++ ret = regulator_set_optimum_mode(phy->v3p3, PHY_3P3_HPM_LOAD);
++ if (ret < 0)
++ dev_err(phy->dev, "cannot set HPM of regulator v3p3\n");
++
++ ret = regulator_enable(phy->v3p3);
++ if (ret)
++ dev_err(phy->dev, "cannot enable v3p3\n");
++
++ /*
++ * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
++ * and disable RETENTION (power-on default is ENABLED)
++ */
++ val = readl(phy->base + PHY_CTRL_REG);
++ val |= BIT(18) | BIT(20) | BIT(11) | 0x70;
++ qcom_dwc3_hs_write(phy->base, PHY_CTRL_REG, val);
++ usleep_range(2000, 2200);
++
++ /*
++ * write HSPHY init value to QSCRATCH reg to set HSPHY parameters like
++ * VBUS valid threshold, disconnect valid threshold, DC voltage level,
++ * preempasis and rise/fall time.
++ */
++ qcom_dwc3_hs_write_readback(phy->base, PARAMETER_OVERRIDE_X_REG,
++ 0x03ffffff, 0x00d191a4);
++
++ /* Disable (bypass) VBUS and ID filters */
++ qcom_dwc3_hs_write(phy->base, QSCRATCH_GENERAL_CFG, BIT(2));
++
++ return 0;
++}
++
++static int qcom_dwc3_hs_phy_set_vbus(struct usb_phy *x, int on)
++{
++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
++ int ret = 0;
++
++ if (IS_ERR(phy->vbus))
++ return on ? PTR_ERR(phy->vbus) : 0;
++
++ if (on)
++ ret = regulator_enable(phy->vbus);
++ else
++ ret = regulator_disable(phy->vbus);
++
++ if (ret)
++ dev_err(x->dev, "Cannot %s Vbus\n", on ? "set" : "off");
++ return ret;
++}
++
++static int qcom_dwc3_hs_probe(struct platform_device *pdev)
++{
++ struct qcom_dwc3_hs_phy *phy;
++ struct resource *res;
++ void __iomem *base;
++
++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++ if (!phy)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, phy);
++
++ phy->dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(phy->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
++ if (IS_ERR(phy->vddcx)) {
++ dev_dbg(phy->dev, "cannot get vddcx\n");
++ return PTR_ERR(phy->vddcx);
++ }
++
++ phy->v3p3 = devm_regulator_get(phy->dev, "v3p3");
++ if (IS_ERR(phy->v3p3)) {
++ dev_dbg(phy->dev, "cannot get v3p3\n");
++ return PTR_ERR(phy->v3p3);
++ }
++
++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
++ if (IS_ERR(phy->v1p8)) {
++ dev_dbg(phy->dev, "cannot get v1p8\n");
++ return PTR_ERR(phy->v1p8);
++ }
++
++ phy->vbus = devm_regulator_get(phy->dev, "vbus");
++ if (IS_ERR(phy->vbus))
++ dev_dbg(phy->dev, "Failed to get vbus\n");
++
++ phy->utmi_clk = devm_clk_get(phy->dev, "utmi");
++ if (IS_ERR(phy->utmi_clk)) {
++ dev_dbg(phy->dev, "cannot get UTMI handle\n");
++ return PTR_ERR(phy->utmi_clk);
++ }
++
++ phy->xo_clk = devm_clk_get(phy->dev, "xo");
++ if (IS_ERR(phy->xo_clk)) {
++ dev_dbg(phy->dev, "cannot get TCXO buffer handle\n");
++ phy->xo_clk = NULL;
++ }
++
++ clk_set_rate(phy->utmi_clk, 60000000);
++
++ if (phy->xo_clk)
++ clk_prepare_enable(phy->xo_clk);
++
++ phy->base = base;
++ phy->phy.dev = phy->dev;
++ phy->phy.label = "qcom-dwc3-hsphy";
++ phy->phy.init = qcom_dwc3_hs_phy_init;
++ phy->phy.notify_connect = qcom_dwc3_hs_notify_connect;
++ phy->phy.notify_disconnect = qcom_dwc3_hs_notify_disconnect;
++ phy->phy.shutdown = qcom_dwc3_hs_phy_shutdown;
++ phy->phy.set_vbus = qcom_dwc3_hs_phy_set_vbus;
++ phy->phy.type = USB_PHY_TYPE_USB2;
++ phy->phy.state = OTG_STATE_UNDEFINED;
++
++ usb_add_phy_dev(&phy->phy);
++ return 0;
++}
++
++static int qcom_dwc3_hs_remove(struct platform_device *pdev)
++{
++ struct qcom_dwc3_hs_phy *phy = platform_get_drvdata(pdev);
++
++ if (phy->xo_clk)
++ clk_disable_unprepare(phy->xo_clk);
++ usb_remove_phy(&phy->phy);
++ return 0;
++}
++
++static const struct of_device_id qcom_dwc3_hs_id_table[] = {
++ { .compatible = "qcom,dwc3-hsphy" },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_dwc3_hs_id_table);
++
++static struct platform_driver qcom_dwc3_hs_driver = {
++ .probe = qcom_dwc3_hs_probe,
++ .remove = qcom_dwc3_hs_remove,
++ .driver = {
++ .name = "qcom-dwc3-hsphy",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_dwc3_hs_id_table,
++ },
++};
++
++module_platform_driver(qcom_dwc3_hs_driver);
++
++MODULE_ALIAS("platform:qcom-dwc3-hsphy");
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM HSPHY driver");
+diff --git a/drivers/usb/phy/phy-qcom-ssusb.c b/drivers/usb/phy/phy-qcom-ssusb.c
+new file mode 100644
+index 0000000..3da778f
+--- /dev/null
++++ b/drivers/usb/phy/phy-qcom-ssusb.c
+@@ -0,0 +1,455 @@
++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/usb/phy.h>
++
++/**
++ * USB QSCRATCH Hardware registers
++ */
++#define PHY_CTRL_REG (0x00)
++#define PHY_PARAM_CTRL_1 (0x04)
++#define 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)
++
++#define PHY_1P8_VOL_MIN 1800000 /* uV */
++#define PHY_1P8_VOL_MAX 1800000 /* uV */
++#define PHY_1P8_HPM_LOAD 23000 /* uA */
++
++/* TODO: these are suspicious */
++#define USB_VDDCX_NO 1 /* index */
++#define USB_VDDCX_MIN 5 /* index */
++#define USB_VDDCX_MAX 7 /* index */
++
++struct qcom_dwc3_ss_phy {
++ struct usb_phy phy;
++ void __iomem *base;
++ struct device *dev;
++
++ struct regulator *v1p8;
++ struct regulator *vddcx;
++
++ struct clk *xo_clk;
++ struct clk *ref_clk;
++};
++
++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_ss_phy, phy)
++
++
++/**
++ * Write register
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @offset - register offset.
++ * @val - value to write.
++ */
++static inline void qcom_dwc3_ss_write(void __iomem *base, u32 offset, u32 val)
++{
++ writel(val, base + offset);
++}
++
++/**
++ * 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_ss_write_readback(void __iomem *base, u32 offset,
++ const u32 mask, u32 val)
++{
++ u32 write_val, tmp = readl(base + offset);
++
++ tmp &= ~mask; /* retain other bits */
++ write_val = tmp | val;
++
++ writel(write_val, base + offset);
++
++ /* Read back to see if val was written */
++ tmp = readl(base + offset);
++ tmp &= mask; /* clear other bits */
++
++ if (tmp != val)
++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
++}
++
++/**
++ * Write SSPHY register
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to write.
++ * @val - value to write.
++ */
++static void qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
++{
++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
++ cpu_relax();
++
++ writel(val, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
++ while (readl(base + CR_PROTOCOL_CAP_DATA_REG))
++ cpu_relax();
++
++ writel(0x1, base + CR_PROTOCOL_WRITE_REG);
++ while (readl(base + CR_PROTOCOL_WRITE_REG))
++ cpu_relax();
++}
++
++/**
++ * Read SSPHY register.
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to read.
++ */
++static u32 qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr)
++{
++ bool first_read = true;
++
++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
++ cpu_relax();
++
++ /*
++ * Due to hardware bug, first read of SSPHY register might be
++ * incorrect. Hence as workaround, SW should perform SSPHY register
++ * read twice, but use only second read and ignore first read.
++ */
++retry:
++ writel(0x1, base + CR_PROTOCOL_READ_REG);
++ while (readl(base + CR_PROTOCOL_READ_REG))
++ cpu_relax();
++
++ if (first_read) {
++ readl(base + CR_PROTOCOL_DATA_OUT_REG);
++ first_read = false;
++ goto retry;
++ }
++
++ return readl(base + CR_PROTOCOL_DATA_OUT_REG);
++}
++
++static void qcom_dwc3_ss_phy_shutdown(struct usb_phy *x)
++{
++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
++ int ret;
++
++ /* Sequence to put SSPHY in low power state:
++ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
++ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
++ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
++ * 4. Disable SSPHY ref clk
++ */
++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 8), 0x0);
++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 28), 0x0);
++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG,
++ (1 << 26), (1 << 26));
++
++ usleep_range(1000, 1200);
++ clk_disable_unprepare(phy->ref_clk);
++
++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set voltage for vddcx\n");
++
++ regulator_disable(phy->vddcx);
++
++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
++ if (ret)
++ dev_err(phy->dev, "cannot set v1p8\n");
++
++ regulator_disable(phy->v1p8);
++}
++
++static int qcom_dwc3_ss_phy_init(struct usb_phy *x)
++{
++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
++ u32 data = 0;
++ int ret;
++
++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
++ if (ret) {
++ dev_err(phy->dev, "cannot set voltage for vddcx\n");
++ return ret;
++ }
++
++ ret = regulator_enable(phy->vddcx);
++ if (ret) {
++ dev_err(phy->dev, "cannot enable vddcx\n");
++ return ret;
++ }
++
++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
++ PHY_1P8_VOL_MAX);
++ if (ret) {
++ regulator_disable(phy->vddcx);
++ dev_err(phy->dev, "cannot set v1p8\n");
++ return ret;
++ }
++
++ ret = regulator_enable(phy->v1p8);
++ if (ret) {
++ regulator_disable(phy->vddcx);
++ dev_err(phy->dev, "cannot enable v1p8\n");
++ return ret;
++ }
++
++ clk_prepare_enable(phy->ref_clk);
++ usleep_range(1000, 1200);
++
++ /* reset phy */
++ data = readl_relaxed(phy->base + PHY_CTRL_REG);
++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
++ usleep_range(2000, 2200);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++
++ /* clear REF_PAD, we don't have XO clk */
++ data &= ~BIT(28);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++ msleep(30);
++
++ data |= BIT(8) | BIT(24);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++
++ /*
++ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
++ * in HS mode instead of SS mode. Workaround it by asserting
++ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
++ data |= (1 << 7);
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
++
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
++ data &= ~0xff0;
++ data |= 0x20;
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
++
++ /*
++ * 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
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
++ data &= ~(1 << 6);
++ data |= (1 << 7);
++ data &= ~(0x7 << 8);
++ data |= (0x3 << 8);
++ data |= (0x1 << 11);
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
++
++ /*
++ * 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.
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
++ data &= ~0x3f80;
++ data |= (0x16 << 7);
++ data &= ~0x7f;
++ data |= (0x7f | (1 << 14));
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
++
++ /*
++ * 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_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
++ 0x07f03f07, 0x07f01605);
++ return 0;
++}
++
++static int qcom_dwc3_ss_set_suspend(struct usb_phy *x, int suspend)
++{
++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
++ u32 data;
++
++ if (!suspend) {
++ /* reset phy */
++ data = readl_relaxed(phy->base + PHY_CTRL_REG);
++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
++ usleep_range(2000, 2200);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++
++ /* clear REF_PAD, we don't have XO clk */
++ data &= ~BIT(28);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++ msleep(30);
++
++ data |= BIT(8) | BIT(24);
++ writel_relaxed(data, phy->base + PHY_CTRL_REG);
++
++ /*
++ * WORKAROUND: There is SSPHY suspend bug due to which USB
++ * enumerates in HS mode instead of SS mode. Workaround it by
++ * asserting LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use
++ * alt bus mode
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
++ data |= (1 << 7);
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
++
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
++ data &= ~0xff0;
++ data |= 0x20;
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
++
++ /*
++ * 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
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
++ data &= ~(1 << 6);
++ data |= (1 << 7);
++ data &= ~(0x7 << 8);
++ data |= (0x3 << 8);
++ data |= (0x1 << 11);
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
++
++ /*
++ * 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.
++ */
++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
++ data &= ~0x3f80;
++ data |= (0x16 << 7);
++ data &= ~0x7f;
++ data |= (0x7f | (1 << 14));
++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
++
++ /*
++ * 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_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
++ 0x07f03f07, 0x07f01605);
++ }
++ return 0;
++}
++
++static int qcom_dwc3_ss_probe(struct platform_device *pdev)
++{
++ struct qcom_dwc3_ss_phy *phy;
++ struct resource *res;
++ void __iomem *base;
++ int ret;
++
++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++ if (!phy)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, phy);
++
++ phy->dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(phy->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
++ if (IS_ERR(phy->vddcx)) {
++ dev_dbg(phy->dev, "cannot get vddcx\n");
++ return PTR_ERR(phy->vddcx);
++ }
++
++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
++ if (IS_ERR(phy->v1p8)) {
++ dev_dbg(phy->dev, "cannot get v1p8\n");
++ return PTR_ERR(phy->v1p8);
++ }
++
++ phy->xo_clk = devm_clk_get(phy->dev, "xo");
++ if (IS_ERR(phy->xo_clk)) {
++ dev_dbg(phy->dev, "cannot get XO clk, assuming not present\n");
++ phy->xo_clk = NULL;
++ }
++
++ phy->ref_clk = devm_clk_get(phy->dev, "ref");
++ if (IS_ERR(phy->ref_clk)) {
++ dev_dbg(phy->dev, "cannot get ref clock handle\n");
++ return PTR_ERR(phy->ref_clk);
++ }
++
++ clk_set_rate(phy->ref_clk, 125000000);
++ if (phy->xo_clk)
++ clk_prepare_enable(phy->xo_clk);
++
++ phy->base = base;
++ phy->phy.dev = phy->dev;
++ phy->phy.label = "qcom-dwc3-ssphy";
++ phy->phy.init = qcom_dwc3_ss_phy_init;
++ phy->phy.shutdown = qcom_dwc3_ss_phy_shutdown;
++ phy->phy.set_suspend = qcom_dwc3_ss_set_suspend;
++ phy->phy.type = USB_PHY_TYPE_USB3;
++
++ ret = usb_add_phy_dev(&phy->phy);
++ return ret;
++}
++
++static int qcom_dwc3_ss_remove(struct platform_device *pdev)
++{
++ struct qcom_dwc3_ss_phy *phy = platform_get_drvdata(pdev);
++
++ if (phy->xo_clk)
++ clk_disable_unprepare(phy->xo_clk);
++ usb_remove_phy(&phy->phy);
++ return 0;
++}
++
++static const struct of_device_id qcom_dwc3_ss_id_table[] = {
++ { .compatible = "qcom,dwc3-ssphy" },
++ { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_dwc3_ss_id_table);
++
++static struct platform_driver qcom_dwc3_ss_driver = {
++ .probe = qcom_dwc3_ss_probe,
++ .remove = qcom_dwc3_ss_remove,
++ .driver = {
++ .name = "qcom-dwc3-ssphy",
++ .owner = THIS_MODULE,
++ .of_match_table = qcom_dwc3_ss_id_table,
++ },
++};
++
++module_platform_driver(qcom_dwc3_ss_driver);
++
++MODULE_ALIAS("platform:qcom-dwc3-ssphy");
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM SSPHY driver");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch
new file mode 100644
index 0000000..cf1b300
--- /dev/null
+++ b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch
@@ -0,0 +1,131 @@
+From c7045330c5976eb31bd79bc57c5db684588d595e Mon Sep 17 00:00:00 2001
+From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
+Date: Mon, 7 Oct 2013 10:44:55 +0300
+Subject: [PATCH 158/182] usb: dwc3: qcom: Add device tree binding
+
+QCOM USB3.0 core wrapper consist of USB3.0 IP from Synopsys
+(SNPS) and HS, SS PHY's control and configuration registers.
+
+It could operate in device mode (SS, HS, FS) and host
+mode (SS, HS, FS, LS).
+
+Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
+Acked-by: Stephen Warren <swarren@nvidia.com>
+---
+ .../devicetree/bindings/usb/qcom,dwc3.txt | 104 ++++++++++++++++++++
+ 1 file changed, 104 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/usb/qcom,dwc3.txt
+
+diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
+new file mode 100644
+index 0000000..105b6b7
+--- /dev/null
++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
+@@ -0,0 +1,104 @@
++Qualcomm SuperSpeed DWC3 USB SoC controller
++
++
++QCOM DWC3 Highspeed USB PHY
++========================
++Required properities:
++- compatible: should contain "qcom,dwc3-hsphy";
++- reg: offset and length of the register set in the memory map
++- clocks: A list of phandle + clock-specifier pairs for the
++ clocks listed in clock-names
++- clock-names: Should contain the following:
++ "utmi" UTMI clock
++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY.
++- v3p3-supply: phandle to the regulator for the 3.3v supply to HSPHY.
++- vbus-supply: phandle to the regulator for the vbus supply for host
++ mode.
++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY
++ digital circuit operation.
++
++Optional clocks:
++ "xo" External reference clock
++
++
++QCOM DWC3 Superspeed USB PHY
++=========================
++Required properities:
++- compatible: should contain "qcom,dwc3-ssphy";
++- reg: offset and length of the register set in the memory map
++- clocks: A list of phandle + clock-specifier pairs for the
++ clocks listed in clock-names
++- clock-names: Should contain the following:
++ "ref" Reference clock used in host mode.
++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY.
++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY
++ digital circuit operation.
++
++Optional clocks:
++ "xo" External reference clock
++
++QCOM DWC3 controller wrapper
++===========================
++Required properties:
++- compatible: should contain "qcom,dwc3"
++- clocks: A list of phandle + clock-specifier pairs for the
++ clocks listed in clock-names
++- clock-names: Should contain the following:
++ "core" Master/Core clock, have to be >= 125 MHz for SS
++ operation and >= 60MHz for HS operation
++
++Optional clocks:
++ "iface" System bus AXI clock. Not present on all platforms
++ "sleep" Sleep clock, used when USB3 core goes into low
++ power mode (U3).
++
++Optional regulator:
++- gdsc-supply: phandle to the regulator from globally distributed
++ switch controller
++
++Required child node:
++A child node must exist to represent the core DWC3 IP block. The name of
++the node is not important. The content of the node is defined in dwc3.txt.
++
++Example device nodes:
++
++ hs_phy_0: phy@110f8800 {
++ compatible = "qcom,dwc3-hsphy";
++ reg = <0x110f8800 0x30>;
++ clocks = <&gcc USB30_0_UTMI_CLK>;
++ clock-names = "utmi";
++
++ status = "disabled";
++ };
++
++ ss_phy_0: phy@110f8830 {
++ compatible = "qcom,dwc3-ssphy";
++ reg = <0x110f8830 0x30>;
++
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "ref";
++
++ status = "disabled";
++ };
++
++ usb3_0: usb30@0 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "core";
++
++ ranges;
++
++ status = "disabled";
++
++ dwc3@11000000 {
++ compatible = "snps,dwc3";
++ reg = <0x11000000 0xcd00>;
++ interrupts = <0 110 0x4>;
++ usb-phy = <&hs_phy_0>, <&ss_phy_0>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch
new file mode 100644
index 0000000..5a0d099
--- /dev/null
+++ b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch
@@ -0,0 +1,162 @@
+From 269a71c81438604d27f01ec703daa7f5e3f39e8b Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Sun, 15 Jun 2014 00:48:18 -0500
+Subject: [PATCH 159/182] arm: ipq8064: Add USB3 DT information
+
+This patch fleshes out the USB3 specific information for the IPQ8064 platform.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 29 ++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 90 ++++++++++++++++++++++++++++++
+ 2 files changed, 119 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index 4062eb6..2b2d63c 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -160,5 +160,34 @@
+ pinctrl-0 = <&nand_pins>;
+ pinctrl-names = "default";
+ };
++
++ tcsr@1a400000 {
++ status = "ok";
++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>;
++ };
++
++ phy@100f8800 { /* USB3 port 1 HS phy */
++ status = "ok";
++ };
++
++ phy@100f8830 { /* USB3 port 1 SS phy */
++ status = "ok";
++ };
++
++ phy@110f8800 { /* USB3 port 0 HS phy */
++ status = "ok";
++ };
++
++ phy@110f8830 { /* USB3 port 0 SS phy */
++ status = "ok";
++ };
++
++ usb30@0 {
++ status = "ok";
++ };
++
++ usb30@1 {
++ status = "ok";
++ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index d9fce15..6be6ac9 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -4,6 +4,7 @@
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/soc/qcom,tcsr.h>
+
+ / {
+ model = "Qualcomm IPQ8064";
+@@ -402,5 +403,94 @@
+
+ status = "disabled";
+ };
++
++ tcsr: tcsr@1a400000 {
++ compatible = "qcom,tcsr";
++ reg = <0x1a400000 0x100>;
++
++ status = "disabled";
++ };
++
++ hs_phy_1: phy@100f8800 {
++ compatible = "qcom,dwc3-hsphy";
++ reg = <0x100f8800 0x30>;
++ clocks = <&gcc USB30_1_UTMI_CLK>;
++ clock-names = "utmi";
++
++ status = "disabled";
++ };
++
++ ss_phy_1: phy@100f8830 {
++ compatible = "qcom,dwc3-ssphy";
++ reg = <0x100f8830 0x30>;
++
++ clocks = <&gcc USB30_1_MASTER_CLK>;
++ clock-names = "ref";
++
++ status = "disabled";
++ };
++
++ hs_phy_0: phy@110f8800 {
++ compatible = "qcom,dwc3-hsphy";
++ reg = <0x110f8800 0x30>;
++ clocks = <&gcc USB30_0_UTMI_CLK>;
++ clock-names = "utmi";
++
++ status = "disabled";
++ };
++
++ ss_phy_0: phy@110f8830 {
++ compatible = "qcom,dwc3-ssphy";
++ reg = <0x110f8830 0x30>;
++
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "ref";
++
++ status = "disabled";
++ };
++
++ usb3_0: usb30@0 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_0_MASTER_CLK>;
++ clock-names = "core";
++
++ ranges;
++
++ status = "disabled";
++
++ dwc3@11000000 {
++ compatible = "snps,dwc3";
++ reg = <0x11000000 0xcd00>;
++ interrupts = <0 110 0x4>;
++ usb-phy = <&hs_phy_0>, <&ss_phy_0>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
++
++ usb3_1: usb30@1 {
++ compatible = "qcom,dwc3";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&gcc USB30_1_MASTER_CLK>;
++ clock-names = "core";
++
++ ranges;
++
++ status = "disabled";
++
++ dwc3@10000000 {
++ compatible = "snps,dwc3";
++ reg = <0x10000000 0xcd00>;
++ interrupts = <0 205 0x4>;
++ usb-phy = <&hs_phy_1>, <&ss_phy_1>;
++ phy-names = "usb2-phy", "usb3-phy";
++ tx-fifo-resize;
++ dr_mode = "host";
++ };
++ };
+ };
+ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch
new file mode 100644
index 0000000..b74db3e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch
@@ -0,0 +1,42 @@
+From 8e69ee9a592f363476f18e91f57d871662e9a393 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 16 Jun 2014 16:37:22 -0500
+Subject: [PATCH 160/182] ARM: qcom: config: Add TCSR and USB3 options
+
+Enable TCSR and USB3 options.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index e3f9013..3d55d79 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -122,9 +122,14 @@ CONFIG_HID_BATTERY_STRENGTH=y
+ CONFIG_USB=y
+ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+ CONFIG_USB_MON=y
++CONFIG_USB_XHCI_HCD=y
+ CONFIG_USB_EHCI_HCD=y
+ CONFIG_USB_ACM=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_DWC3=y
++CONFIG_USB_DWC3_HOST=y
+ CONFIG_USB_SERIAL=y
++CONFIG_USB_QCOM_DWC3_PHY=y
+ CONFIG_USB_GADGET=y
+ CONFIG_USB_GADGET_DEBUG_FILES=y
+ CONFIG_USB_GADGET_VBUS_DRAW=500
+@@ -138,6 +143,7 @@ CONFIG_DMADEVICES=y
+ CONFIG_QCOM_BAM_DMA=y
+ CONFIG_STAGING=y
+ CONFIG_QCOM_GSBI=y
++CONFIG_QCOM_TCSR=y
+ CONFIG_COMMON_CLK_QCOM=y
+ CONFIG_IPQ_GCC_806X=y
+ CONFIG_MSM_GCC_8660=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch
new file mode 100644
index 0000000..41dae6e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch
@@ -0,0 +1,259 @@
+From 5d102a45ee224fa32c775deb75bf7eb9d2ee2cf0 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Tue, 3 Jun 2014 11:24:23 -0700
+Subject: [PATCH 161/182] ARM: Remove ARCH_HAS_CPUFREQ config option
+
+This config exists entirely to hide the cpufreq menu from the
+kernel configuration unless a platform has selected it. Nothing
+is actually built if this config is 'Y' and it just leads to more
+patches that add a select under a platform Kconfig so that some
+other CPUfreq option can be chosen. Let's remove the option so
+that we can always enable CPUfreq drivers on ARM platforms.
+
+Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm/Kconfig | 17 -----------------
+ arch/arm/mach-davinci/Kconfig | 1 -
+ arch/arm/mach-highbank/Kconfig | 1 -
+ arch/arm/mach-imx/Kconfig | 2 --
+ arch/arm/mach-omap2/Kconfig | 1 -
+ arch/arm/mach-shmobile/Kconfig | 2 --
+ arch/arm/mach-spear/Kconfig | 1 -
+ arch/arm/mach-tegra/Kconfig | 1 -
+ arch/arm/mach-ux500/Kconfig | 1 -
+ arch/arm/mach-vexpress/Kconfig | 1 -
+ arch/arm/mach-vt8500/Kconfig | 1 -
+ 11 files changed, 29 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 4332e8d..1a61d4a 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -180,13 +180,6 @@ config ARCH_HAS_ILOG2_U32
+ config ARCH_HAS_ILOG2_U64
+ bool
+
+-config ARCH_HAS_CPUFREQ
+- bool
+- help
+- Internal node to signify that the ARCH has CPUFREQ support
+- and that the relevant menu configurations are displayed for
+- it.
+-
+ config ARCH_HAS_BANDGAP
+ bool
+
+@@ -315,7 +308,6 @@ config ARCH_MULTIPLATFORM
+
+ config ARCH_INTEGRATOR
+ bool "ARM Ltd. Integrator family"
+- select ARCH_HAS_CPUFREQ
+ select ARM_AMBA
+ select ARM_PATCH_PHYS_VIRT
+ select AUTO_ZRELADDR
+@@ -540,7 +532,6 @@ config ARCH_DOVE
+
+ config ARCH_KIRKWOOD
+ bool "Marvell Kirkwood"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select CPU_FEROCEON
+ select GENERIC_CLOCKEVENTS
+@@ -641,7 +632,6 @@ config ARCH_LPC32XX
+ config ARCH_PXA
+ bool "PXA2xx/PXA3xx-based"
+ depends on MMU
+- select ARCH_HAS_CPUFREQ
+ select ARCH_MTD_XIP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_CPU_SUSPEND if PM
+@@ -710,7 +700,6 @@ config ARCH_RPC
+
+ config ARCH_SA1100
+ bool "SA1100-based"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_MTD_XIP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARCH_SPARSEMEM_ENABLE
+@@ -728,7 +717,6 @@ config ARCH_SA1100
+
+ config ARCH_S3C24XX
+ bool "Samsung S3C24XX SoCs"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select CLKDEV_LOOKUP
+ select CLKSRC_SAMSUNG_PWM
+@@ -748,7 +736,6 @@ config ARCH_S3C24XX
+
+ config ARCH_S3C64XX
+ bool "Samsung S3C64XX"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_AMBA
+ select ARM_VIC
+@@ -809,7 +796,6 @@ config ARCH_S5PC100
+
+ config ARCH_S5PV210
+ bool "Samsung S5PV210/S5PC110"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_HOLES_MEMORYMODEL
+ select ARCH_SPARSEMEM_ENABLE
+ select CLKDEV_LOOKUP
+@@ -863,7 +849,6 @@ config ARCH_DAVINCI
+ config ARCH_OMAP1
+ bool "TI OMAP1"
+ depends on MMU
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_HOLES_MEMORYMODEL
+ select ARCH_OMAP
+ select ARCH_REQUIRE_GPIOLIB
+@@ -2170,9 +2155,7 @@ endmenu
+
+ menu "CPU Power Management"
+
+-if ARCH_HAS_CPUFREQ
+ source "drivers/cpufreq/Kconfig"
+-endif
+
+ source "drivers/cpuidle/Kconfig"
+
+diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig
+index a075b3e..34cfecc 100644
+--- a/arch/arm/mach-davinci/Kconfig
++++ b/arch/arm/mach-davinci/Kconfig
+@@ -39,7 +39,6 @@ config ARCH_DAVINCI_DA830
+ config ARCH_DAVINCI_DA850
+ bool "DA850/OMAP-L138/AM18x based system"
+ select ARCH_DAVINCI_DA8XX
+- select ARCH_HAS_CPUFREQ
+ select CP_INTC
+
+ config ARCH_DAVINCI_DA8XX
+diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig
+index 0aded64..9f74755 100644
+--- a/arch/arm/mach-highbank/Kconfig
++++ b/arch/arm/mach-highbank/Kconfig
+@@ -1,7 +1,6 @@
+ config ARCH_HIGHBANK
+ bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_HOLES_MEMORYMODEL
+ select ARCH_HAS_OPP
+ select ARCH_SUPPORTS_BIG_ENDIAN
+diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
+index 33567aa..e7ae0ee 100644
+--- a/arch/arm/mach-imx/Kconfig
++++ b/arch/arm/mach-imx/Kconfig
+@@ -103,7 +103,6 @@ config SOC_IMX25
+
+ config SOC_IMX27
+ bool
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+ select CPU_ARM926T
+ select IMX_HAVE_IOMUX_V1
+@@ -129,7 +128,6 @@ config SOC_IMX35
+
+ config SOC_IMX5
+ bool
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+ select ARCH_MXC_IOMUX_V3
+ select CPU_V7
+diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
+index 0af7ca0..0674cb7 100644
+--- a/arch/arm/mach-omap2/Kconfig
++++ b/arch/arm/mach-omap2/Kconfig
+@@ -93,7 +93,6 @@ config SOC_DRA7XX
+ config ARCH_OMAP2PLUS
+ bool
+ select ARCH_HAS_BANDGAP
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_HOLES_MEMORYMODEL
+ select ARCH_OMAP
+ select ARCH_REQUIRE_GPIOLIB
+diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
+index 05fa505..61d4d31 100644
+--- a/arch/arm/mach-shmobile/Kconfig
++++ b/arch/arm/mach-shmobile/Kconfig
+@@ -85,7 +85,6 @@ config ARCH_R8A73A4
+ select CPU_V7
+ select SH_CLK_CPG
+ select RENESAS_IRQC
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+
+ config ARCH_R8A7740
+@@ -271,7 +270,6 @@ config MACH_KOELSCH
+ config MACH_KZM9G
+ bool "KZM-A9-GT board"
+ depends on ARCH_SH73A0
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+ select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
+diff --git a/arch/arm/mach-spear/Kconfig b/arch/arm/mach-spear/Kconfig
+index ac1710e6..811ba13 100644
+--- a/arch/arm/mach-spear/Kconfig
++++ b/arch/arm/mach-spear/Kconfig
+@@ -16,7 +16,6 @@ if PLAT_SPEAR
+ config ARCH_SPEAR13XX
+ bool "ST SPEAr13xx"
+ depends on ARCH_MULTI_V7 || PLAT_SPEAR_SINGLE
+- select ARCH_HAS_CPUFREQ
+ select ARM_GIC
+ select CPU_V7
+ select GPIO_SPEAR_SPICS
+diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
+index b1232d8..52bfc9e 100644
+--- a/arch/arm/mach-tegra/Kconfig
++++ b/arch/arm/mach-tegra/Kconfig
+@@ -1,6 +1,5 @@
+ config ARCH_TEGRA
+ bool "NVIDIA Tegra" if ARCH_MULTI_V7
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
+ select ARM_GIC
+diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
+index 0034d2c..cb1176e 100644
+--- a/arch/arm/mach-ux500/Kconfig
++++ b/arch/arm/mach-ux500/Kconfig
+@@ -3,7 +3,6 @@ config ARCH_U8500
+ depends on MMU
+ select AB8500_CORE
+ select ABX500_CORE
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_AMBA
+ select ARM_ERRATA_754322
+diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
+index 4a70be4..ca5b7e5 100644
+--- a/arch/arm/mach-vexpress/Kconfig
++++ b/arch/arm/mach-vexpress/Kconfig
+@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB
+
+ config ARCH_VEXPRESS_SPC
+ bool "Versatile Express Serial Power Controller (SPC)"
+- select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+ select PM_OPP
+ help
+diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
+index 927be93..788d0b4 100644
+--- a/arch/arm/mach-vt8500/Kconfig
++++ b/arch/arm/mach-vt8500/Kconfig
+@@ -1,6 +1,5 @@
+ config ARCH_VT8500
+ bool
+- select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select CLKDEV_LOOKUP
+ select CLKSRC_OF
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch
new file mode 100644
index 0000000..979212f
--- /dev/null
+++ b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch
@@ -0,0 +1,134 @@
+From 18e4542f7b02f586d46594977818cd6b24d9cbcb Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@linaro.org>
+Date: Fri, 6 Jun 2014 11:36:56 +0100
+Subject: [PATCH 162/182] PM / OPP: Remove ARCH_HAS_OPP
+
+Since the OPP layer is a kernel library which has been converted to be
+directly selectable by its callers rather than user selectable and
+requiring architectures to enable it explicitly the ARCH_HAS_OPP symbol
+has become redundant and can be removed. Do so.
+
+Signed-off-by: Mark Brown <broonie@linaro.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ Documentation/power/opp.txt | 3 ---
+ arch/arm/mach-exynos/Kconfig | 1 -
+ arch/arm/mach-highbank/Kconfig | 1 -
+ arch/arm/mach-omap2/Kconfig | 1 -
+ arch/arm/mach-shmobile/Kconfig | 2 --
+ arch/arm/mach-vexpress/Kconfig | 1 -
+ drivers/devfreq/Kconfig | 1 -
+ kernel/power/Kconfig | 3 ---
+ 8 files changed, 13 deletions(-)
+
+diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt
+index b8a907d..7b6a021 100644
+--- a/Documentation/power/opp.txt
++++ b/Documentation/power/opp.txt
+@@ -52,9 +52,6 @@ Typical usage of the OPP library is as follows:
+ SoC framework -> modifies on required cases certain OPPs -> OPP layer
+ -> queries to search/retrieve information ->
+
+-Architectures that provide a SoC framework for OPP should select ARCH_HAS_OPP
+-to make the OPP layer available.
+-
+ OPP layer expects each domain to be represented by a unique device pointer. SoC
+ framework registers a set of initial OPPs per device with the OPP layer. This
+ list is expected to be an optimally small number typically around 5 per device.
+diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
+index 4c414af..67e69a8 100644
+--- a/arch/arm/mach-exynos/Kconfig
++++ b/arch/arm/mach-exynos/Kconfig
+@@ -107,7 +107,6 @@ config SOC_EXYNOS5440
+ depends on ARCH_EXYNOS5
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+ select ARCH_HAS_BANDGAP
+- select ARCH_HAS_OPP
+ select HAVE_ARM_ARCH_TIMER
+ select AUTO_ZRELADDR
+ select MIGHT_HAVE_PCI
+diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig
+index 9f74755..43bd782 100644
+--- a/arch/arm/mach-highbank/Kconfig
++++ b/arch/arm/mach-highbank/Kconfig
+@@ -2,7 +2,6 @@ config ARCH_HIGHBANK
+ bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+ select ARCH_HAS_HOLES_MEMORYMODEL
+- select ARCH_HAS_OPP
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_AMBA
+diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
+index 0674cb7..3e6596c 100644
+--- a/arch/arm/mach-omap2/Kconfig
++++ b/arch/arm/mach-omap2/Kconfig
+@@ -13,7 +13,6 @@ config ARCH_OMAP3
+ bool "TI OMAP3"
+ depends on ARCH_MULTI_V7
+ select ARCH_OMAP2PLUS
+- select ARCH_HAS_OPP
+ select ARM_CPU_SUSPEND if PM
+ select CPU_V7
+ select MULTI_IRQ_HANDLER
+diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
+index 61d4d31..9fc2dd2 100644
+--- a/arch/arm/mach-shmobile/Kconfig
++++ b/arch/arm/mach-shmobile/Kconfig
+@@ -85,7 +85,6 @@ config ARCH_R8A73A4
+ select CPU_V7
+ select SH_CLK_CPG
+ select RENESAS_IRQC
+- select ARCH_HAS_OPP
+
+ config ARCH_R8A7740
+ bool "R-Mobile A1 (R8A77400)"
+@@ -270,7 +269,6 @@ config MACH_KOELSCH
+ config MACH_KZM9G
+ bool "KZM-A9-GT board"
+ depends on ARCH_SH73A0
+- select ARCH_HAS_OPP
+ select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
+ select SND_SOC_AK4642 if SND_SIMPLE_CARD
+diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
+index ca5b7e5..4ed6e97 100644
+--- a/arch/arm/mach-vexpress/Kconfig
++++ b/arch/arm/mach-vexpress/Kconfig
+@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB
+
+ config ARCH_VEXPRESS_SPC
+ bool "Versatile Express Serial Power Controller (SPC)"
+- select ARCH_HAS_OPP
+ select PM_OPP
+ help
+ The TC2 (A15x2 A7x3) versatile express core tile integrates a logic
+diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
+index 7d2f435..d416754 100644
+--- a/drivers/devfreq/Kconfig
++++ b/drivers/devfreq/Kconfig
+@@ -68,7 +68,6 @@ comment "DEVFREQ Drivers"
+ config ARM_EXYNOS4_BUS_DEVFREQ
+ bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver"
+ depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
+- select ARCH_HAS_OPP
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ help
+ This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)
+diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
+index 2fac9cc..caa040c 100644
+--- a/kernel/power/Kconfig
++++ b/kernel/power/Kconfig
+@@ -253,9 +253,6 @@ config APM_EMULATION
+ anything, try disabling/enabling this option (or disabling/enabling
+ APM in your BIOS).
+
+-config ARCH_HAS_OPP
+- bool
+-
+ config PM_OPP
+ bool "Operating Performance Point (OPP) Layer library"
+ depends on ARCH_HAS_OPP
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch
new file mode 100644
index 0000000..7ea69f3
--- /dev/null
+++ b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch
@@ -0,0 +1,49 @@
+From 1ce8667062bc7b8813adf6103ad2374d9dd52fb0 Mon Sep 17 00:00:00 2001
+From: Jean-Francois Moine <moinejf@free.fr>
+Date: Mon, 25 Nov 2013 19:47:04 +0100
+Subject: [PATCH 163/182] clk: return probe defer when DT clock not yet ready
+
+At probe time, a clock device may not be ready when some other device
+wants to use it.
+
+This patch lets the functions clk_get/devm_clk_get return a probe defer
+when the clock is defined in the DT but not yet available.
+
+Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
+Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
+Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
+Signed-off-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/clk/clk.c | 2 +-
+ drivers/clk/clkdev.c | 2 ++
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
+index 0582068..a3facad 100644
+--- a/drivers/clk/clk.c
++++ b/drivers/clk/clk.c
+@@ -2502,7 +2502,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider);
+ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
+ {
+ struct of_clk_provider *provider;
+- struct clk *clk = ERR_PTR(-ENOENT);
++ struct clk *clk = ERR_PTR(-EPROBE_DEFER);
+
+ /* Check if we have such a provider in our array */
+ list_for_each_entry(provider, &of_clk_providers, link) {
+diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
+index 48f6721..a360b2e 100644
+--- a/drivers/clk/clkdev.c
++++ b/drivers/clk/clkdev.c
+@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
+ clk = of_clk_get_by_name(dev->of_node, con_id);
+ if (!IS_ERR(clk))
+ return clk;
++ if (PTR_ERR(clk) == -EPROBE_DEFER)
++ return clk;
+ }
+
+ return clk_get_sys(dev_id, con_id);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch
new file mode 100644
index 0000000..652590e
--- /dev/null
+++ b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch
@@ -0,0 +1,146 @@
+From d8bf5e13683e027c52476b89b874d50e5281c130 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 18 Oct 2013 16:45:05 -0700
+Subject: [PATCH 164/182] ARM: Add Krait L2 register accessor functions
+
+Krait CPUs have a handful of L2 cache controller registers that
+live behind a cp15 based indirection register. First you program
+the indirection register (l2cpselr) to point the L2 'window'
+register (l2cpdr) at what you want to read/write. Then you
+read/write the 'window' register to do what you want. The
+l2cpselr register is not banked per-cpu so we must lock around
+accesses to it to prevent other CPUs from re-pointing l2cpdr
+underneath us.
+
+Cc: Mark Rutland <mark.rutland@arm.com>
+Cc: Russell King <linux@arm.linux.org.uk>
+Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm/common/Kconfig | 3 ++
+ arch/arm/common/Makefile | 1 +
+ arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++
+ arch/arm/include/asm/krait-l2-accessors.h | 20 ++++++++++
+ 4 files changed, 82 insertions(+)
+ create mode 100644 arch/arm/common/krait-l2-accessors.c
+ create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
+
+diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
+index c3a4e9c..9da52dc 100644
+--- a/arch/arm/common/Kconfig
++++ b/arch/arm/common/Kconfig
+@@ -9,6 +9,9 @@ config DMABOUNCE
+ bool
+ select ZONE_DMA
+
++config KRAIT_L2_ACCESSORS
++ bool
++
+ config SHARP_LOCOMO
+ bool
+
+diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
+index 4bdc4162..2836f99 100644
+--- a/arch/arm/common/Makefile
++++ b/arch/arm/common/Makefile
+@@ -7,6 +7,7 @@ obj-y += firmware.o
+ obj-$(CONFIG_ICST) += icst.o
+ obj-$(CONFIG_SA1111) += sa1111.o
+ obj-$(CONFIG_DMABOUNCE) += dmabounce.o
++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
+ obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
+ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c
+new file mode 100644
+index 0000000..5d514bb
+--- /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);
+diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h
+new file mode 100644
+index 0000000..48fe552
+--- /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
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch
new file mode 100644
index 0000000..0964106
--- /dev/null
+++ b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch
@@ -0,0 +1,663 @@
+From 151d7e91baaa4016ba687b80e8f7ccead62d6c72 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Tue, 25 Mar 2014 13:37:55 -0700
+Subject: [PATCH 165/182] clk: qcom: Add support for muxes, dividers, and mux
+ dividers
+
+The Krait CPU clocks are made up of muxes and dividers with a
+handful of sources. Add a set of clk_ops that allow us to
+configure these clocks so we can support CPU frequency scaling on
+Krait CPUs.
+
+Based on code originally written by Saravana Kannan.
+
+Cc: Saravana Kannan <skannan@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-generic.c | 405 +++++++++++++++++++++++++++++++++++
+ include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++
+ 3 files changed, 614 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-generic.c
+ create mode 100644 include/linux/clk/msm-clk-generic.h
+
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index df2a1b3..2cc6039 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
+ clk-qcom-y += clk-rcg.o
+ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
++clk-qcom-y += clk-generic.o
+ clk-qcom-y += reset.o
+
+ obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
+diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c
+new file mode 100644
+index 0000000..a0d778b
+--- /dev/null
++++ b/drivers/clk/qcom/clk-generic.c
+@@ -0,0 +1,405 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/export.h>
++#include <linux/bug.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/clk/msm-clk-generic.h>
++
++
++/* ==================== Mux clock ==================== */
++
++static int mux_set_parent(struct clk_hw *hw, u8 sel)
++{
++ struct mux_clk *mux = to_mux_clk(hw);
++
++ if (mux->parent_map)
++ sel = mux->parent_map[sel];
++
++ return mux->ops->set_mux_sel(mux, sel);
++}
++
++static u8 mux_get_parent(struct clk_hw *hw)
++{
++ struct mux_clk *mux = to_mux_clk(hw);
++ int num_parents = __clk_get_num_parents(hw->clk);
++ int i;
++ u8 sel;
++
++ sel = mux->ops->get_mux_sel(mux);
++ if (mux->parent_map) {
++ for (i = 0; i < num_parents; i++)
++ if (sel == mux->parent_map[i])
++ return i;
++ WARN(1, "Can't find parent\n");
++ return -EINVAL;
++ }
++
++ return sel;
++}
++
++static int mux_enable(struct clk_hw *hw)
++{
++ struct mux_clk *mux = to_mux_clk(hw);
++ if (mux->ops->enable)
++ return mux->ops->enable(mux);
++ return 0;
++}
++
++static void mux_disable(struct clk_hw *hw)
++{
++ struct mux_clk *mux = to_mux_clk(hw);
++ if (mux->ops->disable)
++ return mux->ops->disable(mux);
++}
++
++static struct clk *mux_get_safe_parent(struct clk_hw *hw)
++{
++ int i;
++ struct mux_clk *mux = to_mux_clk(hw);
++ int num_parents = __clk_get_num_parents(hw->clk);
++
++ if (!mux->has_safe_parent)
++ return NULL;
++
++ i = mux->safe_sel;
++ if (mux->parent_map)
++ for (i = 0; i < num_parents; i++)
++ if (mux->safe_sel == mux->parent_map[i])
++ break;
++
++ return clk_get_parent_by_index(hw->clk, i);
++}
++
++const struct clk_ops clk_ops_gen_mux = {
++ .enable = mux_enable,
++ .disable = mux_disable,
++ .set_parent = mux_set_parent,
++ .get_parent = mux_get_parent,
++ .determine_rate = __clk_mux_determine_rate,
++ .get_safe_parent = mux_get_safe_parent,
++};
++EXPORT_SYMBOL_GPL(clk_ops_gen_mux);
++
++/* ==================== Divider clock ==================== */
++
++static long __div_round_rate(struct div_data *data, unsigned long rate,
++ struct clk *parent, unsigned int *best_div, unsigned long *best_prate,
++ bool set_parent)
++{
++ unsigned int div, min_div, max_div, _best_div = 1;
++ unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate;
++ unsigned int numer;
++
++ rate = max(rate, 1UL);
++
++ min_div = max(data->min_div, 1U);
++ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate));
++
++ /*
++ * div values are doubled for half dividers.
++ * Adjust for that by picking a numer of 2.
++ */
++ numer = data->is_half_divider ? 2 : 1;
++
++ if (!set_parent) {
++ prate = *best_prate * numer;
++ div = DIV_ROUND_UP(prate, rate);
++ div = clamp(1U, div, max_div);
++ if (best_div)
++ *best_div = div;
++ return mult_frac(*best_prate, numer, div);
++ }
++
++ for (div = min_div; div <= max_div; div++) {
++ req_prate = mult_frac(rate, div, numer);
++ prate = __clk_round_rate(parent, req_prate);
++ if (IS_ERR_VALUE(prate))
++ break;
++
++ actual_rate = mult_frac(prate, numer, div);
++ if (is_better_rate(rate, rrate, actual_rate)) {
++ rrate = actual_rate;
++ _best_div = div;
++ _best_prate = prate;
++ }
++
++ /*
++ * Trying higher dividers is only going to ask the parent for
++ * a higher rate. If it can't even output a rate higher than
++ * the one we request for this divider, the parent is not
++ * going to be able to output an even higher rate required
++ * for a higher divider. So, stop trying higher dividers.
++ */
++ if (actual_rate < rate)
++ break;
++
++ if (rrate <= rate)
++ break;
++ }
++
++ if (!rrate)
++ return -EINVAL;
++ if (best_div)
++ *best_div = _best_div;
++ if (best_prate)
++ *best_prate = _best_prate;
++
++ return rrate;
++}
++
++static long div_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ struct div_clk *d = to_div_clk(hw);
++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
++
++ return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk),
++ NULL, parent_rate, set_parent);
++}
++
++static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long
++ parent_rate)
++{
++ struct div_clk *d = to_div_clk(hw);
++ int div, rc = 0;
++ struct div_data *data = &d->data;
++
++ div = parent_rate / rate;
++ if (div != data->div)
++ rc = d->ops->set_div(d, div);
++ data->div = div;
++
++ return rc;
++}
++
++static int div_enable(struct clk_hw *hw)
++{
++ struct div_clk *d = to_div_clk(hw);
++ if (d->ops && d->ops->enable)
++ return d->ops->enable(d);
++ return 0;
++}
++
++static void div_disable(struct clk_hw *hw)
++{
++ struct div_clk *d = to_div_clk(hw);
++ if (d->ops && d->ops->disable)
++ return d->ops->disable(d);
++}
++
++static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate)
++{
++ struct div_clk *d = to_div_clk(hw);
++ unsigned int div = d->data.div;
++
++ if (d->ops && d->ops->get_div)
++ div = max(d->ops->get_div(d), 1);
++ div = max(div, 1U);
++
++ if (!d->ops || !d->ops->set_div)
++ d->data.min_div = d->data.max_div = div;
++ d->data.div = div;
++
++ return prate / div;
++}
++
++const struct clk_ops clk_ops_div = {
++ .enable = div_enable,
++ .disable = div_disable,
++ .round_rate = div_round_rate,
++ .set_rate = div_set_rate,
++ .recalc_rate = div_recalc_rate,
++};
++EXPORT_SYMBOL_GPL(clk_ops_div);
++
++/* ==================== Mux_div clock ==================== */
++
++static int mux_div_clk_enable(struct clk_hw *hw)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++
++ if (md->ops->enable)
++ return md->ops->enable(md);
++ return 0;
++}
++
++static void mux_div_clk_disable(struct clk_hw *hw)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++
++ if (md->ops->disable)
++ return md->ops->disable(md);
++}
++
++static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate,
++ struct clk **best_parent, int *best_div, unsigned long *best_prate)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ unsigned int i;
++ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0;
++ struct clk *_best_parent = 0;
++ int num_parents = __clk_get_num_parents(hw->clk);
++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
++
++ for (i = 0; i < num_parents; i++) {
++ int div;
++ unsigned long prate;
++ struct clk *p = clk_get_parent_by_index(hw->clk, i);
++
++ rrate = __div_round_rate(&md->data, rate, p, &div, &prate,
++ set_parent);
++
++ if (is_better_rate(rate, best, rrate)) {
++ best = rrate;
++ _best_div = div;
++ _best_prate = prate;
++ _best_parent = p;
++ }
++
++ if (rate <= rrate)
++ break;
++ }
++
++ if (best_div)
++ *best_div = _best_div;
++ if (best_prate)
++ *best_prate = _best_prate;
++ if (best_parent)
++ *best_parent = _best_parent;
++
++ if (best)
++ return best;
++ return -EINVAL;
++}
++
++static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate);
++}
++
++/* requires enable lock to be held */
++static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div)
++{
++ int rc;
++
++ rc = md->ops->set_src_div(md, src_sel, div);
++ if (!rc) {
++ md->data.div = div;
++ md->src_sel = src_sel;
++ }
++
++ return rc;
++}
++
++/* Must be called after handoff to ensure parent clock rates are initialized */
++static int safe_parent_init_once(struct clk_hw *hw)
++{
++ unsigned long rrate;
++ u32 best_div;
++ struct clk *best_parent;
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++
++ if (IS_ERR(md->safe_parent))
++ return -EINVAL;
++ if (!md->safe_freq || md->safe_parent)
++ return 0;
++
++ rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent,
++ &best_div, NULL);
++
++ if (rrate == md->safe_freq) {
++ md->safe_div = best_div;
++ md->safe_parent = best_parent;
++ } else {
++ md->safe_parent = ERR_PTR(-EINVAL);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int
++__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ int rc;
++
++ rc = safe_parent_init_once(hw);
++ if (rc)
++ return rc;
++
++ return __set_src_div(md, index, div);
++}
++
++static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate, u8 index)
++{
++ return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate);
++}
++
++static int mux_div_clk_set_rate(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ return __mux_div_clk_set_rate_and_parent(hw, md->src_sel,
++ parent_rate / rate);
++}
++
++static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index],
++ md->data.div);
++}
++
++static u8 mux_div_clk_get_parent(struct clk_hw *hw)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ int num_parents = __clk_get_num_parents(hw->clk);
++ u32 i, div, sel;
++
++ md->ops->get_src_div(md, &sel, &div);
++ md->src_sel = sel;
++
++ for (i = 0; i < num_parents; i++)
++ if (sel == md->parent_map[i])
++ return i;
++ WARN(1, "Can't find parent\n");
++ return -EINVAL;
++}
++
++static unsigned long
++mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
++{
++ struct mux_div_clk *md = to_mux_div_clk(hw);
++ u32 div, sel;
++
++ md->ops->get_src_div(md, &sel, &div);
++
++ return prate / div;
++}
++
++const struct clk_ops clk_ops_mux_div_clk = {
++ .enable = mux_div_clk_enable,
++ .disable = mux_div_clk_disable,
++ .set_rate_and_parent = mux_div_clk_set_rate_and_parent,
++ .set_rate = mux_div_clk_set_rate,
++ .set_parent = mux_div_clk_set_parent,
++ .round_rate = mux_div_clk_round_rate,
++ .get_parent = mux_div_clk_get_parent,
++ .recalc_rate = mux_div_clk_recalc_rate,
++};
++EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk);
+diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h
+new file mode 100644
+index 0000000..cee3863
+--- /dev/null
++++ b/include/linux/clk/msm-clk-generic.h
+@@ -0,0 +1,208 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __QCOM_CLK_GENERIC_H__
++#define __QCOM_CLK_GENERIC_H__
++
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++
++static inline bool is_better_rate(unsigned long req, unsigned long best,
++ unsigned long new)
++{
++ if (IS_ERR_VALUE(new))
++ return false;
++
++ return (req <= new && new < best) || (best < req && best < new);
++}
++
++/* ==================== Mux clock ==================== */
++
++struct mux_clk;
++
++struct clk_mux_ops {
++ int (*set_mux_sel)(struct mux_clk *clk, int sel);
++ int (*get_mux_sel)(struct mux_clk *clk);
++
++ /* Optional */
++ bool (*is_enabled)(struct mux_clk *clk);
++ int (*enable)(struct mux_clk *clk);
++ void (*disable)(struct mux_clk *clk);
++};
++
++struct mux_clk {
++ /* Parents in decreasing order of preference for obtaining rates. */
++ u8 *parent_map;
++ bool has_safe_parent;
++ u8 safe_sel;
++ const struct clk_mux_ops *ops;
++
++ /* Fields not used by helper function. */
++ void __iomem *base;
++ u32 offset;
++ u32 en_offset;
++ int en_reg;
++ u32 mask;
++ u32 shift;
++ u32 en_mask;
++ void *priv;
++
++ struct clk_hw hw;
++};
++
++static inline struct mux_clk *to_mux_clk(struct clk_hw *hw)
++{
++ return container_of(hw, struct mux_clk, hw);
++}
++
++extern const struct clk_ops clk_ops_gen_mux;
++
++/* ==================== Divider clock ==================== */
++
++struct div_clk;
++
++struct clk_div_ops {
++ int (*set_div)(struct div_clk *clk, int div);
++ int (*get_div)(struct div_clk *clk);
++ bool (*is_enabled)(struct div_clk *clk);
++ int (*enable)(struct div_clk *clk);
++ void (*disable)(struct div_clk *clk);
++};
++
++struct div_data {
++ unsigned int div;
++ unsigned int min_div;
++ unsigned int max_div;
++ /*
++ * Indicate whether this divider clock supports half-interger divider.
++ * If it is, all the min_div and max_div have been doubled. It means
++ * they are 2*N.
++ */
++ bool is_half_divider;
++};
++
++struct div_clk {
++ struct div_data data;
++
++ /* Optional */
++ const struct clk_div_ops *ops;
++
++ /* Fields not used by helper function. */
++ void __iomem *base;
++ u32 offset;
++ u32 mask;
++ u32 shift;
++ u32 en_mask;
++ void *priv;
++ struct clk_hw hw;
++};
++
++static inline struct div_clk *to_div_clk(struct clk_hw *hw)
++{
++ return container_of(hw, struct div_clk, hw);
++}
++
++extern const struct clk_ops clk_ops_div;
++
++#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
++static struct div_clk clk_name = { \
++ .data = { \
++ .max_div = _div, \
++ .min_div = _div, \
++ .div = _div, \
++ }, \
++ .hw.init = &(struct clk_init_data){ \
++ .parent_names = (const char *[]){ _parent }, \
++ .num_parents = 1, \
++ .name = #clk_name, \
++ .ops = &clk_ops_div, \
++ .flags = CLK_SET_RATE_PARENT, \
++ } \
++}
++
++/* ==================== Mux Div clock ==================== */
++
++struct mux_div_clk;
++
++/*
++ * struct mux_div_ops
++ * the enable and disable ops are optional.
++ */
++
++struct mux_div_ops {
++ int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div);
++ void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div);
++ int (*enable)(struct mux_div_clk *);
++ void (*disable)(struct mux_div_clk *);
++ bool (*is_enabled)(struct mux_div_clk *);
++};
++
++/*
++ * struct mux_div_clk - combined mux/divider clock
++ * @priv
++ parameters needed by ops
++ * @safe_freq
++ when switching rates from A to B, the mux div clock will
++ instead switch from A -> safe_freq -> B. This allows the
++ mux_div clock to change rates while enabled, even if this
++ behavior is not supported by the parent clocks.
++
++ If changing the rate of parent A also causes the rate of
++ parent B to change, then safe_freq must be defined.
++
++ safe_freq is expected to have a source clock which is always
++ on and runs at only one rate.
++ * @parents
++ list of parents and mux indicies
++ * @ops
++ function pointers for hw specific operations
++ * @src_sel
++ the mux index which will be used if the clock is enabled.
++ */
++
++struct mux_div_clk {
++ /* Required parameters */
++ const struct mux_div_ops *ops;
++ struct div_data data;
++ u8 *parent_map;
++
++ struct clk_hw hw;
++
++ /* Internal */
++ u32 src_sel;
++
++ /* Optional parameters */
++ void *priv;
++ void __iomem *base;
++ u32 div_mask;
++ u32 div_offset;
++ u32 div_shift;
++ u32 src_mask;
++ u32 src_offset;
++ u32 src_shift;
++ u32 en_mask;
++ u32 en_offset;
++
++ u32 safe_div;
++ struct clk *safe_parent;
++ unsigned long safe_freq;
++};
++
++static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw)
++{
++ return container_of(hw, struct mux_div_clk, hw);
++}
++
++extern const struct clk_ops clk_ops_mux_div_clk;
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
new file mode 100644
index 0000000..3ff1a95
--- /dev/null
+++ b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
@@ -0,0 +1,359 @@
+From 8a51ac2a4e36505e1ff82b0e49fd8b2edd0b7695 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 14:15:58 -0700
+Subject: [PATCH 166/182] clk: qcom: Add support for High-Frequency PLLs
+ (HFPLLs)
+
+HFPLLs are the main frequency source for Krait CPU clocks. Add
+support for changing the rate of these PLLs.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-hfpll.c | 260 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
+ 3 files changed, 315 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-hfpll.c
+ create mode 100644 drivers/clk/qcom/clk-hfpll.h
+
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 2cc6039..93fd03f 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o
+ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-generic.o
++clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+
+ obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
+diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c
+new file mode 100644
+index 0000000..f8a40a7
+--- /dev/null
++++ b/drivers/clk/qcom/clk-hfpll.c
+@@ -0,0 +1,260 @@
++/*
++ * 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/regmap.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/clk-provider.h>
++#include <linux/spinlock.h>
++
++#include "clk-regmap.h"
++#include "clk-hfpll.h"
++
++#define PLL_OUTCTRL BIT(0)
++#define PLL_BYPASSNL BIT(1)
++#define PLL_RESET_N BIT(2)
++
++/* Initialize a HFPLL at a given rate and enable it. */
++static void __clk_hfpll_init_once(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++
++ if (likely(h->init_done))
++ return;
++
++ /* Configure PLL parameters for integer mode. */
++ if (hd->config_val)
++ regmap_write(regmap, hd->config_reg, hd->config_val);
++ regmap_write(regmap, hd->m_reg, 0);
++ regmap_write(regmap, hd->n_reg, 1);
++
++ if (hd->user_reg) {
++ u32 regval = hd->user_val;
++ unsigned long rate;
++
++ rate = __clk_get_rate(hw->clk);
++
++ /* Pick the right VCO. */
++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
++ regval |= hd->user_vco_mask;
++ regmap_write(regmap, hd->user_reg, regval);
++ }
++
++ if (hd->droop_reg)
++ regmap_write(regmap, hd->droop_reg, hd->droop_val);
++
++ h->init_done = true;
++}
++
++static void __clk_hfpll_enable(struct clk_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ struct hfpll_data const *hd = h->d;
++ struct regmap *regmap = h->clkr.regmap;
++ u32 val;
++
++ __clk_hfpll_init_once(hw);
++
++ /* Disable PLL bypass mode. */
++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
++
++ /*
++ * H/W requires a 5us delay between disabling the bypass and
++ * de-asserting the reset. Delay 10us just to be safe.
++ */
++ mb();
++ 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 {
++ mb();
++ udelay(60);
++ }
++
++ /* Enable PLL output. */
++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
++
++ /* Make sure the enable is done before returning. */
++ mb();
++}
++
++/* 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_hw *hw)
++{
++ struct clk_hfpll *h = to_clk_hfpll(hw);
++ 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(hw);
++ 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(hw);
++
++ /* 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);
+diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h
+new file mode 100644
+index 0000000..48c18d6
+--- /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
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch
new file mode 100644
index 0000000..c2c1680
--- /dev/null
+++ b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch
@@ -0,0 +1,162 @@
+From 49134da893bc11e833e3d87139c57e3b84e65219 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Thu, 19 Jun 2014 18:46:31 -0700
+Subject: [PATCH 167/182] clk: qcom: Add HFPLL driver
+
+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.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig | 8 ++++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/hfpll.c | 110 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 119 insertions(+)
+ create mode 100644 drivers/clk/qcom/hfpll.c
+
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index cfaa54c..de8ba31 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -53,3 +53,11 @@ config MSM_MMCC_8974
+ Support for the multimedia clock controller on msm8974 devices.
+ Say Y if you want to support multimedia devices such as display,
+ graphics, video encode/decode, camera, etc.
++
++config QCOM_HFPLL
++ tristate "High-Frequency PLL (HFPLL) Clock Controller"
++ depends on COMMON_CLK_QCOM
++ help
++ Support for the high-frequency PLLs present on Qualcomm devices.
++ Say Y if you want to support CPU frequency scaling on devices
++ such as MSM8974, APQ8084, etc.
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 93fd03f..d0d8e3d 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -16,3 +16,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
+ 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
+diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c
+new file mode 100644
+index 0000000..701a377
+--- /dev/null
++++ b/drivers/clk/qcom/hfpll.c
+@@ -0,0 +1,110 @@
++/*
++ * 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 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",
++ .owner = THIS_MODULE,
++ .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");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch
new file mode 100644
index 0000000..68fda61
--- /dev/null
+++ b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch
@@ -0,0 +1,122 @@
+From 0a38d7a21ef0e851d025e4e16f096d5579226299 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 16 Jun 2014 17:44:08 -0700
+Subject: [PATCH 168/182] clk: qcom: Add MSM8960's HFPLLs
+
+Describe the HFPLLs present on MSM8960 devices.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 82 insertions(+)
+
+diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
+index f4ffd91..d04fc99 100644
+--- a/drivers/clk/qcom/gcc-msm8960.c
++++ b/drivers/clk/qcom/gcc-msm8960.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 pll3 = {
+@@ -75,6 +76,84 @@ static struct clk_regmap pll8_vote = {
+ },
+ };
+
++static struct hfpll_data hfpll0_data = {
++ .mode_reg = 0x3200,
++ .l_reg = 0x3208,
++ .m_reg = 0x320c,
++ .n_reg = 0x3210,
++ .config_reg = 0x3204,
++ .status_reg = 0x321c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3214,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll0 = {
++ .d = &hfpll0_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll0",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
++};
++
++static struct hfpll_data hfpll1_data = {
++ .mode_reg = 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 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 = 0x3400,
++ .l_reg = 0x3408,
++ .m_reg = 0x340c,
++ .n_reg = 0x3410,
++ .config_reg = 0x3404,
++ .status_reg = 0x341c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3414,
++ .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,
+@@ -2763,6 +2842,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = {
+ [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
+ [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
+ [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
++ [PLL9] = &hfpll0.clkr,
++ [PLL10] = &hfpll1.clkr,
++ [PLL12] = &hfpll_l2.clkr,
+ };
+
+ static const struct qcom_reset_map gcc_msm8960_resets[] = {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch
new file mode 100644
index 0000000..9b76f59
--- /dev/null
+++ b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch
@@ -0,0 +1,203 @@
+From 63ecfef8560631a15ee13129b2778cd4dffbcfe2 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 14:18:31 -0700
+Subject: [PATCH 169/182] clk: qcom: Add support for Krait clocks
+
+The Krait clocks are made up of a series of muxes and a divider
+that choose between a fixed rate clock and dedicated HFPLLs for
+each CPU. Instead of using mmio accesses to remux parents, the
+Krait implementation exposes the remux control via cp15
+registers. Support these clocks.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig | 4 ++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/clk-krait.c | 121 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-krait.h | 22 ++++++++
+ 4 files changed, 148 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-krait.c
+ create mode 100644 drivers/clk/qcom/clk-krait.h
+
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index de8ba31..70b6a7c 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -61,3 +61,7 @@ config QCOM_HFPLL
+ Support for the high-frequency PLLs present on Qualcomm devices.
+ Say Y if you want to support CPU frequency scaling on devices
+ such as MSM8974, APQ8084, etc.
++
++config KRAIT_CLOCKS
++ bool
++ select KRAIT_L2_ACCESSORS
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index d0d8e3d..6482165 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o
+ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-generic.o
++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
+ clk-qcom-y += clk-hfpll.o
+ clk-qcom-y += reset.o
+
+diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
+new file mode 100644
+index 0000000..4283426
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.c
+@@ -0,0 +1,121 @@
++/*
++ * 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(kpss_clock_reg_lock);
++
++#define LPL_SHIFT 8
++static void __kpss_mux_set_sel(struct mux_clk *mux, int sel)
++{
++ unsigned long flags;
++ u32 regval;
++
++ spin_lock_irqsave(&kpss_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->priv) {
++ 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(&kpss_clock_reg_lock, flags);
++
++ /* Wait for switch to complete. */
++ mb();
++ udelay(1);
++}
++
++static int kpss_mux_set_sel(struct mux_clk *mux, int sel)
++{
++ mux->en_mask = sel;
++ /* Don't touch mux if CPU is off as it won't work */
++ if (__clk_is_enabled(mux->hw.clk))
++ __kpss_mux_set_sel(mux, sel);
++ return 0;
++}
++
++static int kpss_mux_get_sel(struct mux_clk *mux)
++{
++ u32 sel;
++
++ sel = krait_get_l2_indirect_reg(mux->offset);
++ sel >>= mux->shift;
++ sel &= mux->mask;
++ mux->en_mask = sel;
++
++ return sel;
++}
++
++static int kpss_mux_enable(struct mux_clk *mux)
++{
++ __kpss_mux_set_sel(mux, mux->en_mask);
++ return 0;
++}
++
++static void kpss_mux_disable(struct mux_clk *mux)
++{
++ __kpss_mux_set_sel(mux, mux->safe_sel);
++}
++
++const struct clk_mux_ops clk_mux_ops_kpss = {
++ .enable = kpss_mux_enable,
++ .disable = kpss_mux_disable,
++ .set_mux_sel = kpss_mux_set_sel,
++ .get_mux_sel = kpss_mux_get_sel,
++};
++EXPORT_SYMBOL_GPL(clk_mux_ops_kpss);
++
++/*
++ * The divider can divide by 2, 4, 6 and 8. But we only really need div-2. So
++ * force it to div-2 during handoff and treat it like a fixed div-2 clock.
++ */
++static int kpss_div2_get_div(struct div_clk *div)
++{
++ unsigned long flags;
++ u32 regval;
++ int val;
++
++ spin_lock_irqsave(&kpss_clock_reg_lock, flags);
++ regval = krait_get_l2_indirect_reg(div->offset);
++ val = (regval >> div->shift) & div->mask;
++ regval &= ~(div->mask << div->shift);
++ if (div->priv)
++ regval &= ~(div->mask << (div->shift + LPL_SHIFT));
++ krait_set_l2_indirect_reg(div->offset, regval);
++ spin_unlock_irqrestore(&kpss_clock_reg_lock, flags);
++
++ val = (val + 1) * 2;
++ WARN(val != 2, "Divider %s was configured to div-%d instead of 2!\n",
++ __clk_get_name(div->hw.clk), val);
++
++ return 2;
++}
++
++const struct clk_div_ops clk_div_ops_kpss_div2 = {
++ .get_div = kpss_div2_get_div,
++};
++EXPORT_SYMBOL_GPL(clk_div_ops_kpss_div2);
+diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
+new file mode 100644
+index 0000000..9c3eb38
+--- /dev/null
++++ b/drivers/clk/qcom/clk-krait.h
+@@ -0,0 +1,22 @@
++/*
++ * 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 __SOC_QCOM_CLOCK_KRAIT_H
++#define __SOC_QCOM_CLOCK_KRAIT_H
++
++#include <linux/clk/msm-clk-generic.h>
++
++extern const struct clk_mux_ops clk_mux_ops_kpss;
++extern const struct clk_div_ops clk_div_ops_kpss_div2;
++
++#endif
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
new file mode 100644
index 0000000..8817bbb
--- /dev/null
+++ b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch
@@ -0,0 +1,171 @@
+From 67a9d5a02b178644da624ef9c32b4e6abb2c4f6e Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 14:25:41 -0700
+Subject: [PATCH 170/182] clk: qcom: Add KPSS ACC/GCC driver
+
+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.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig | 8 +++
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/kpss-xcc.c | 115 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 124 insertions(+)
+ create mode 100644 drivers/clk/qcom/kpss-xcc.c
+
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index 70b6a7c..e9e5360 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -62,6 +62,14 @@ config QCOM_HFPLL
+ Say Y if you want to support CPU frequency scaling on devices
+ such as MSM8974, APQ8084, etc.
+
++config KPSS_XCC
++ tristate "KPSS Clock Controller"
++ depends on COMMON_CLK_QCOM
++ help
++ Support for the Krait ACC and GCC clock controllers. Say Y
++ if you want to support CPU frequency scaling on devices such
++ as MSM8960, APQ8064, etc.
++
+ config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 6482165..29b2a45 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -17,4 +17,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
+ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
+ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c
+new file mode 100644
+index 0000000..1061668
+--- /dev/null
++++ b/drivers/clk/qcom/kpss-xcc.c
+@@ -0,0 +1,115 @@
++/* 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/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/clk/msm-clk-generic.h>
++
++static int kpss_xcc_set_mux_sel(struct mux_clk *clk, int sel)
++{
++ writel_relaxed(sel, clk->base + clk->offset);
++ return 0;
++}
++
++static int kpss_xcc_get_mux_sel(struct mux_clk *clk)
++{
++ return readl_relaxed(clk->base + clk->offset);
++}
++
++static const struct clk_mux_ops kpss_xcc_ops = {
++ .set_mux_sel = kpss_xcc_set_mux_sel,
++ .get_mux_sel = kpss_xcc_get_mux_sel,
++};
++
++static const char *aux_parents[] = {
++ "pll8_vote",
++ "pxo",
++};
++
++static u8 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;
++ struct mux_clk *mux_clk;
++ struct clk_init_data init = {
++ .parent_names = aux_parents,
++ .num_parents = 2,
++ .ops = &clk_ops_gen_mux,
++ };
++
++ 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);
++
++ mux_clk = devm_kzalloc(&pdev->dev, sizeof(*mux_clk), GFP_KERNEL);
++ if (!mux_clk)
++ return -ENOMEM;
++
++ mux_clk->mask = 0x3;
++ mux_clk->parent_map = aux_parent_map;
++ mux_clk->ops = &kpss_xcc_ops;
++ mux_clk->base = base;
++ mux_clk->hw.init = &init;
++
++ if (id->data) {
++ if (of_property_read_string_index(pdev->dev.of_node,
++ "clock-output-names", 0, &init.name))
++ return -ENODEV;
++ mux_clk->offset = 0x14;
++ } else {
++ init.name = "acpu_l2_aux";
++ mux_clk->offset = 0x28;
++ }
++
++ clk = devm_clk_register(&pdev->dev, &mux_clk->hw);
++
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct platform_driver kpss_xcc_driver = {
++ .probe = kpss_xcc_driver_probe,
++ .driver = {
++ .name = "kpss-xcc",
++ .of_match_table = kpss_xcc_match_table,
++ .owner = THIS_MODULE,
++ },
++};
++module_platform_driver(kpss_xcc_driver);
++
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644
index 0000000..8fa83df
--- /dev/null
+++ b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch
@@ -0,0 +1,420 @@
+From 6912e27d97ba5671e8c2434bed0ebd23fde5e13d Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 14:29:29 -0700
+Subject: [PATCH 171/182] clk: qcom: Add Krait clock controller driver
+
+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.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig | 8 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/krait-cc.c | 364 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 373 insertions(+)
+ create mode 100644 drivers/clk/qcom/krait-cc.c
+
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index e9e5360..7418108 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -70,6 +70,14 @@ config KPSS_XCC
+ if you want to support CPU frequency scaling on devices such
+ as MSM8960, APQ8064, etc.
+
++config KRAITCC
++ tristate "Krait Clock Controller"
++ depends on COMMON_CLK_QCOM && ARM
++ select KRAIT_CLOCKS
++ help
++ Support for the Krait CPU clocks on Qualcomm devices.
++ Say Y if you want to support CPU frequency scaling.
++
+ config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 29b2a45..1b88abe 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -19,3 +19,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
++obj-$(CONFIG_KRAITCC) += krait-cc.o
+diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
+new file mode 100644
+index 0000000..90985ea
+--- /dev/null
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -0,0 +1,364 @@
++/* 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/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 <asm/smp_plat.h>
++
++#include "clk-krait.h"
++
++DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote");
++
++static u8 sec_mux_map[] = {
++ 2,
++ 0,
++};
++
++static u8 pri_mux_map[] = {
++ 1,
++ 2,
++ 0,
++};
++
++static int
++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
++{
++ struct div_clk *div;
++ struct clk_init_data init = {
++ .num_parents = 1,
++ .ops = &clk_ops_div,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ const char *p_names[1];
++ struct clk *clk;
++
++ div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL);
++ if (!div)
++ return -ENOMEM;
++
++ div->data.div = 2;
++ div->data.min_div = 2;
++ div->data.max_div = 2;
++ div->ops = &clk_div_ops_kpss_div2;
++ div->mask = 0x3;
++ div->shift = 6;
++ div->priv = (void *)(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 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 = &clk_ops_gen_mux,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ struct clk *clk;
++
++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++ if (!mux)
++ return -ENOMEM;
++
++ mux->offset = offset;
++ mux->priv = (void *)(id >= 0);
++ mux->has_safe_parent = true;
++ mux->safe_sel = 2;
++ mux->ops = &clk_mux_ops_kpss;
++ 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 mux_clk *mux;
++ const char *p_names[3];
++ struct clk_init_data init = {
++ .parent_names = p_names,
++ .num_parents = ARRAY_SIZE(p_names),
++ .ops = &clk_ops_gen_mux,
++ .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->ops = &clk_mux_ops_kpss;
++ mux->mask = 0x3;
++ mux->shift = 0;
++ mux->offset = offset;
++ mux->priv = (void *)(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 i, cpu;
++ struct clk *clk;
++ struct clk **clks;
++ struct clk *l2_pri_mux_clk;
++
++ id = of_match_device(krait_cc_match_table, &pdev->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 = devm_clk_register(dev, &acpu_aux.hw);
++ 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(i) {
++ cpu = cpu_logical_map(i);
++ 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(i) {
++ cpu = cpu_logical_map(i);
++ 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(i) {
++ cpu = cpu_logical_map(i);
++ clk = clks[cpu];
++ cur_rate = clk_get_rate(clk);
++ if (cur_rate == 1) {
++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", i);
++ 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", i, 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 = "clock-krait",
++ .of_match_table = krait_cc_match_table,
++ .owner = THIS_MODULE,
++ },
++};
++module_platform_driver(krait_cc_driver);
++
++MODULE_DESCRIPTION("Krait CPU Clock Driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch
new file mode 100644
index 0000000..3150a4d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch
@@ -0,0 +1,255 @@
+From 5cf343c60d7557aefb468603065cac5062f11b8d Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 30 May 2014 16:36:11 -0700
+Subject: [PATCH 172/182] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
+
+Krait processors have individual clocks for each CPU that can
+scale independently from one another. cpufreq-cpu0 is fairly
+close to this, but assumes that there is only one clock for all
+CPUs. Add a driver to support the Krait configuration.
+
+TODO: Merge into cpufreq-cpu0? Or make generic?
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/cpufreq/Kconfig | 13 +++
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/cpufreq-krait.c | 190 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 204 insertions(+)
+ create mode 100644 drivers/cpufreq/cpufreq-krait.c
+
+diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
+index 4b029c0..4051528 100644
+--- a/drivers/cpufreq/Kconfig
++++ b/drivers/cpufreq/Kconfig
+@@ -194,6 +194,19 @@ config GENERIC_CPUFREQ_CPU0
+
+ If in doubt, say N.
+
++config GENERIC_CPUFREQ_KRAIT
++ tristate "Krait cpufreq driver"
++ depends on HAVE_CLK && OF
++ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
++ depends on !CPU_THERMAL || THERMAL
++ select PM_OPP
++ help
++ This adds a generic cpufreq driver for CPU0 frequency management.
++ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
++ systems which share clock and voltage across all CPUs.
++
++ If in doubt, say N.
++
+ menu "x86 CPU frequency scaling drivers"
+ depends on X86
+ source "drivers/cpufreq/Kconfig.x86"
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index 7494565..f6f4485 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
+ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
+
+ obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o
++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o
+
+ ##################################################################################
+ # x86 drivers.
+diff --git a/drivers/cpufreq/cpufreq-krait.c b/drivers/cpufreq/cpufreq-krait.c
+new file mode 100644
+index 0000000..7b38b9c
+--- /dev/null
++++ b/drivers/cpufreq/cpufreq-krait.c
+@@ -0,0 +1,190 @@
++/*
++ * 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/slab.h>
++#include <linux/thermal.h>
++
++static unsigned int transition_latency;
++
++static struct device *cpu_dev;
++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks);
++static struct cpufreq_frequency_table *freq_table;
++static struct thermal_cooling_device *cdev;
++
++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index)
++{
++ unsigned long volt = 0, volt_old = 0;
++ unsigned int old_freq, new_freq;
++ long freq_Hz, freq_exact;
++ int ret;
++ struct clk *cpu_clk;
++
++ 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;
++
++ 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);
++
++ ret = clk_set_rate(cpu_clk, freq_exact);
++ if (ret)
++ pr_err("failed to set clock rate: %d\n", ret);
++
++ 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;
++ int ret;
++ unsigned int cpu;
++ struct device *dev;
++ struct clk *clk;
++
++ 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;
++ }
++
++ for_each_possible_cpu(cpu) {
++ dev = get_cpu_device(cpu);
++ if (!dev) {
++ pr_err("failed to get krait device\n");
++ ret = -ENOENT;
++ goto out_put_node;
++ }
++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(clk)) {
++ ret = PTR_ERR(clk);
++ goto out_put_node;
++ }
++ }
++
++ ret = of_init_opp_table(cpu_dev);
++ if (ret) {
++ pr_err("failed to init OPP table: %d\n", ret);
++ goto out_put_node;
++ }
++
++ 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;
++ }
++
++ if (of_property_read_u32(np, "clock-latency", &transition_latency))
++ transition_latency = CPUFREQ_ETERNAL;
++
++ 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:
++ 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);
++
++ 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");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch
new file mode 100644
index 0000000..c432210
--- /dev/null
+++ b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch
@@ -0,0 +1,104 @@
+From f1db56284b01b1212a211023dcaa7846fd07d0ec Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Fri, 30 May 2014 17:16:53 -0700
+Subject: [PATCH 173/182] cpufreq: Add module to register cpufreq-krait device
+
+Register a cpufreq-krait device whenever we detect that a
+"qcom,krait" compatible CPU is present in DT.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/cpufreq/Kconfig.arm | 8 +++++++
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/qcom-cpufreq.c | 48 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 57 insertions(+)
+ create mode 100644 drivers/cpufreq/qcom-cpufreq.c
+
+diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
+index 3129749..6ae884d 100644
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -123,6 +123,14 @@ config ARM_OMAP2PLUS_CPUFREQ
+ depends on ARCH_OMAP2PLUS
+ default ARCH_OMAP2PLUS
+
++config ARM_QCOM_CPUFREQ
++ tristate "Qualcomm based"
++ depends on ARCH_QCOM
++ help
++ This adds the CPUFreq driver for Qualcomm SoC based boards.
++
++ If in doubt, say N.
++
+ config ARM_S3C_CPUFREQ
+ bool
+ help
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index f6f4485..f5d18a3 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -60,6 +60,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
+ obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
+ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
+ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
+ obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o
+ obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o
+ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
+diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
+new file mode 100644
+index 0000000..71f4387
+--- /dev/null
++++ b/drivers/cpufreq/qcom-cpufreq.c
+@@ -0,0 +1,48 @@
++/* 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/kernel.h>
++#include <linux/module.h>
++#include <linux/cpu.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++
++static int qcom_cpufreq_driver_init(void)
++{
++ struct platform_device_info devinfo = { .name = "cpufreq-krait", };
++ struct device *cpu_dev;
++ struct device_node *np;
++ struct platform_device *pdev;
++
++ 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);
++
++ pdev = platform_device_register_full(&devinfo);
++
++ return PTR_ERR_OR_ZERO(pdev);
++}
++module_init(qcom_cpufreq_driver_init);
++
++MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch
new file mode 100644
index 0000000..38dbd65
--- /dev/null
+++ b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch
@@ -0,0 +1,121 @@
+From b9b3815f2a71af88ca68d3524ee4d9b6b4739257 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 15:57:06 -0700
+Subject: [PATCH 174/182] clk: qcom: Add HFPLLs to IPQ806X driver
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+
+diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
+index d80dc69..83a73cb 100644
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -30,6 +30,7 @@
+ #include "clk-pll.h"
+ #include "clk-rcg.h"
+ #include "clk-branch.h"
++#include "clk-hfpll.h"
+ #include "reset.h"
+
+ static struct clk_pll pll0 = {
+@@ -102,6 +103,85 @@ static struct clk_regmap pll8_vote = {
+ },
+ };
+
++static struct hfpll_data hfpll0_data = {
++ .mode_reg = 0x3200,
++ .l_reg = 0x3208,
++ .m_reg = 0x320c,
++ .n_reg = 0x3210,
++ .config_reg = 0x3204,
++ .status_reg = 0x321c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3214,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll0 = {
++ .d = &hfpll0_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll0",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
++};
++
++static struct hfpll_data hfpll1_data = {
++ .mode_reg = 0x3240,
++ .l_reg = 0x3248,
++ .m_reg = 0x324c,
++ .n_reg = 0x3250,
++ .config_reg = 0x3244,
++ .status_reg = 0x325c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3314,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll1 = {
++ .d = &hfpll1_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll1",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
++};
++
++static struct hfpll_data hfpll_l2_data = {
++ .mode_reg = 0x3300,
++ .l_reg = 0x3308,
++ .m_reg = 0x330c,
++ .n_reg = 0x3310,
++ .config_reg = 0x3304,
++ .status_reg = 0x331c,
++ .config_val = 0x7845c665,
++ .droop_reg = 0x3314,
++ .droop_val = 0x0108c000,
++ .min_rate = 600000000UL,
++ .max_rate = 1800000000UL,
++};
++
++static struct clk_hfpll hfpll_l2 = {
++ .d = &hfpll_l2_data,
++ .clkr.hw.init = &(struct clk_init_data){
++ .parent_names = (const char *[]){ "pxo" },
++ .num_parents = 1,
++ .name = "hfpll_l2",
++ .ops = &clk_ops_hfpll,
++ .flags = CLK_IGNORE_UNUSED,
++ },
++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
++};
++
++
+ static struct clk_pll pll14 = {
+ .l_reg = 0x31c4,
+ .m_reg = 0x31c8,
+@@ -2878,6 +2958,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
+ [NSSTCM_CLK] = &nss_tcm_clk.clkr,
+ [NSS_CORE_CLK] = &nss_core_clk,
++ [PLL9] = &hfpll0.clkr,
++ [PLL10] = &hfpll1.clkr,
++ [PLL12] = &hfpll_l2.clkr,
+ };
+
+ static const struct qcom_reset_map gcc_ipq806x_resets[] = {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch
new file mode 100644
index 0000000..6fea560
--- /dev/null
+++ b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch
@@ -0,0 +1,96 @@
+From 258a3ea23ec048603debe1621c681b0cc733f236 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 16:04:37 -0700
+Subject: [PATCH 175/182] ARM: dts: ipq8064: Add necessary DT data for Krait
+ cpufreq
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 45 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 6be6ac9..97e4c3d 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -23,6 +23,22 @@
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
++ clocks = <&kraitcc 0>;
++ clock-names = "cpu";
++ operating-points = <
++ /* kHz ignored */
++ 1400000 1000000
++ 1200000 1000000
++ 1000000 1000000
++ 800000 1000000
++ 600000 1000000
++ 384000 1000000
++ >;
++ clock-latency = <100000>;
++
++ cooling-min-state = <0>;
++ cooling-max-state = <10>;
++ #cooling-cells = <2>;
+ };
+
+ cpu@1 {
+@@ -33,6 +49,22 @@
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc1>;
+ qcom,saw = <&saw1>;
++ clocks = <&kraitcc 1>;
++ clock-names = "cpu";
++ operating-points = <
++ /* kHz ignored */
++ 1400000 1000000
++ 1200000 1000000
++ 1000000 1000000
++ 800000 1000000
++ 600000 1000000
++ 384000 1000000
++ >;
++ clock-latency = <100000>;
++
++ cooling-min-state = <0>;
++ cooling-max-state = <10>;
++ #cooling-cells = <2>;
+ };
+
+ L2: l2-cache {
+@@ -46,6 +78,11 @@
+ interrupts = <1 10 0x304>;
+ };
+
++ kraitcc: clock-controller {
++ compatible = "qcom,krait-cc-v1";
++ #clock-cells = <1>;
++ };
++
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+@@ -102,11 +139,19 @@
+ acc0: clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu0_aux";
+ };
+
+ acc1: clock-controller@2098000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
++ clock-output-names = "acpu1_aux";
++ };
++
++ l2cc: clock-controller@2011000 {
++ compatible = "qcom,kpss-gcc";
++ reg = <0x2011000 0x1000>;
++ clock-output-names = "acpu_l2_aux";
+ };
+
+ saw0: regulator@2089000 {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch
new file mode 100644
index 0000000..e565602
--- /dev/null
+++ b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch
@@ -0,0 +1,44 @@
+From e8f4d4bf1e4974bf8d17c6702fa4c410637216f3 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Mon, 30 Jun 2014 10:50:14 -0700
+Subject: [PATCH 176/182] ARM: qcom_defconfig: Enable CPUfreq options
+
+Enable required options to allow the CPUfreq driver to probe on
+Krait based platforms.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm/configs/qcom_defconfig | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
+index 3d55d79..3bc72eb 100644
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -31,6 +31,13 @@ CONFIG_HIGHPTE=y
+ CONFIG_CLEANCACHE=y
+ CONFIG_ARM_APPENDED_DTB=y
+ CONFIG_ARM_ATAG_DTB_COMPAT=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
++CONFIG_GENERIC_CPUFREQ_KRAIT=y
++CONFIG_ARM_QCOM_CPUFREQ=y
+ CONFIG_CPU_IDLE=y
+ CONFIG_VFP=y
+ CONFIG_NEON=y
+@@ -149,6 +156,9 @@ CONFIG_IPQ_GCC_806X=y
+ CONFIG_MSM_GCC_8660=y
+ CONFIG_MSM_MMCC_8960=y
+ CONFIG_MSM_MMCC_8974=y
++CONFIG_QCOM_HFPLL=y
++CONFIG_KPSS_XCC=y
++CONFIG_KRAITCC=y
+ CONFIG_MSM_IOMMU=y
+ CONFIG_PHY_QCOM_IPQ806X_SATA=y
+ CONFIG_EXT2_FS=y
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch
new file mode 100644
index 0000000..d7d301d
--- /dev/null
+++ b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch
@@ -0,0 +1,931 @@
+From 8984e3fc6db029479d6aa78882b39235379aebff Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Wed, 14 May 2014 13:45:07 -0500
+Subject: [PATCH 177/182] dmaengine: Add QCOM ADM DMA driver
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8960 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions. The controller also provides flow
+control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from peripherals
+and also incorporates CRCI (client rate control interface) flow control.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/dma/Kconfig | 10 +
+ drivers/dma/Makefile | 1 +
+ drivers/dma/qcom_adm.c | 871 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 882 insertions(+)
+ create mode 100644 drivers/dma/qcom_adm.c
+
+diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
+index f87cef9..79155fa 100644
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -410,4 +410,14 @@ config QCOM_BAM_DMA
+ Enable support for the QCOM BAM DMA controller. This controller
+ provides DMA capabilities for a variety of on-chip devices.
+
++config QCOM_ADM
++ tristate "Qualcomm ADM support"
++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ ---help---
++ Enable support for the Qualcomm ADM DMA controller. This controller
++ provides DMA capabilities for both general purpose and on-chip
++ peripheral devices.
++
+ endif
+diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
+index 5150c82..4a4f521 100644
+--- a/drivers/dma/Makefile
++++ b/drivers/dma/Makefile
+@@ -46,3 +46,4 @@ obj-$(CONFIG_K3_DMA) += k3dma.o
+ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
+ obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
+ obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+diff --git a/drivers/dma/qcom_adm.c b/drivers/dma/qcom_adm.c
+new file mode 100644
+index 0000000..035f606
+--- /dev/null
++++ b/drivers/dma/qcom_adm.c
+@@ -0,0 +1,871 @@
++/*
++ * 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/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 HI_CH_CMD_PTR(chan, ee) (4*chan + 0x20800*ee)
++#define HI_CH_RSLT(chan, ee) (0x40 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE0(chan, ee) (0x80 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE1(chan, ee) (0xc0 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE2(chan, ee) (0x100 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE3(chan, ee) (0x140 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE4(chan, ee) (0x180 + 4*chan + 0x20800*ee)
++#define HI_CH_FLUSH_STATE5(chan, ee) (0x1c0 + 4*chan + 0x20800*ee)
++#define HI_CH_STATUS_SD(chan, ee) (0x200 + 4*chan + 0x20800*ee)
++#define HI_CH_CONF(chan) (0x240 + 4*chan)
++#define HI_CH_RSLT_CONF(chan, ee) (0x300 + 4*chan + 0x20800*ee)
++#define HI_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + 0x20800*ee)
++#define HI_CI_CONF(ci) (0x390 + 4*ci)
++#define HI_CRCI_CONF0 0x3d0
++#define HI_CRCI_CONF1 0x3d4
++#define HI_GP_CTL 0x3d8
++#define HI_CRCI_CTL(chan, ee) (0x400 + 0x4*chan + 0x20800*ee)
++
++/* channel status */
++#define CH_STATUS_VALID BIT(1)
++
++/* channel result */
++#define CH_RSLT_VALID BIT(31)
++#define CH_RSLT_ERR BIT(3)
++#define CH_RSLT_FLUSH BIT(2)
++#define CH_RSLT_TPD BIT(1)
++
++/* channel conf */
++#define CH_CONF_MPU_DISABLE BIT(11)
++#define CH_CONF_PERM_MPU_CONF BIT(9)
++#define CH_CONF_FLUSH_RSLT_EN BIT(8)
++#define CH_CONF_FORCE_RSLT_EN BIT(7)
++#define CH_CONF_IRQ_EN BIT(6)
++
++/* channel result conf */
++#define CH_RSLT_CONF_FLUSH_EN BIT(1)
++#define CH_RSLT_CONF_IRQ_EN BIT(0)
++
++/* CRCI CTL */
++#define CRCI_CTL_RST BIT(17)
++
++/* CI configuration */
++#define CI_RANGE_END(x) (x << 24)
++#define CI_RANGE_START(x) (x << 16)
++#define CI_BURST_4_WORDS 0x4
++#define CI_BURST_8_WORDS 0x8
++
++/* GP CTL */
++#define GP_CTL_LP_EN BIT(12)
++#define GP_CTL_LP_CNT(x) (x << 8)
++
++/* Command pointer list entry */
++#define CPLE_LP BIT(31)
++
++/* Command list entry */
++#define CMD_LC BIT(31)
++#define CMD_DST_CRCI(n) (((n) & 0xf) << 7)
++#define CMD_SRC_CRCI(n) (((n) & 0xf) << 3)
++
++#define CMD_TYPE_SINGLE 0x0
++#define CMD_TYPE_BOX 0x3
++
++#define ADM_DESC_ALIGN 8
++#define ADM_MAX_XFER (SZ_64K-1)
++#define ADM_MAX_ROWS (SZ_64K-1)
++
++/* Command Pointer List Entry */
++#define CMD_LP BIT(31)
++#define CMD_PT_MASK (0x3 << 29)
++#define CMD_ADDR_MASK 0x3fffffff
++
++struct adm_desc_hw {
++ u32 cmd;
++ u32 src_addr;
++ u32 dst_addr;
++ u32 row_len;
++ u32 num_rows;
++ u32 row_offset;
++};
++
++struct adm_cmd_ptr_list {
++ u32 cple; /* command ptr list entry */
++ struct adm_desc_hw desc[0];
++};
++
++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;
++
++ struct adm_cmd_ptr_list *cpl;
++ u32 num_desc;
++};
++
++struct adm_chan {
++ struct virt_dma_chan vc;
++ struct adm_device *adev;
++
++ /* parsed from DT */
++ u32 id; /* channel id */
++ u32 crci; /* CRCI to be used for transfers */
++ u32 blk_size; /* block size for CRCI, default 16 byte */
++
++ 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 num_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_alloc_chan - Allocates channel resources for DMA channel
++ *
++ * This function is effectively a stub, as we don't need to setup any resources
++ */
++static int adm_alloc_chan(struct dma_chan *chan)
++{
++ return 0;
++}
++
++/**
++ * 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_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, rows, num_desc = 0, idx = 0, desc_offset;
++ struct adm_desc_hw *desc;
++ struct adm_cmd_ptr_list *cpl;
++ u32 burst = ADM_MAX_XFER;
++
++
++ if (!is_slave_direction(direction)) {
++ dev_err(adev->dev, "invalid dma direction\n");
++ return NULL;
++ }
++
++ /* if using CRCI flow control, validate burst settings */
++ if (achan->slave.device_fc) {
++ burst = (direction == DMA_MEM_TO_DEV) ?
++ achan->slave.dst_maxburst :
++ achan->slave.src_maxburst;
++
++ if (!burst) {
++ dev_err(adev->dev, "invalid burst value w/ crci: %d\n",
++ burst);
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
++ /* iterate through sgs and compute allocation size of structures */
++ for_each_sg(sgl, sg, sg_len, i) {
++
++ /* calculate boxes using burst */
++ rows = DIV_ROUND_UP(sg_dma_len(sg), burst);
++ num_desc += DIV_ROUND_UP(rows, ADM_MAX_ROWS);
++
++ /* flow control requires length as a multiple of burst */
++ if (achan->slave.device_fc && (sg_dma_len(sg) % burst)) {
++ dev_err(adev->dev, "length is not multiple of burst\n");
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++ if (!async_desc)
++ return ERR_PTR(-ENOMEM);
++
++ async_desc->dma_len = num_desc * sizeof(*desc) + sizeof(*cpl) +
++ 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->num_desc = num_desc;
++ async_desc->adev = adev;
++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN);
++ desc_offset = (u32)desc - (u32)async_desc->cpl;
++
++ /* init cmd list */
++ cpl->cple |= CPLE_LP;
++ cpl->cple |= (async_desc->dma_addr + desc_offset) >> 3;
++
++ for_each_sg(sgl, sg, sg_len, i) {
++ unsigned int remainder = sg_dma_len(sg);
++ unsigned int curr_offset = 0;
++ unsigned int row_len;
++
++ do {
++ desc[idx].cmd = CMD_TYPE_BOX;
++ desc[idx].row_offset = 0;
++
++ if (direction == DMA_DEV_TO_MEM) {
++ desc[idx].dst_addr = sg_dma_address(sg) +
++ curr_offset;
++ desc[idx].src_addr = achan->slave.src_addr;
++ desc[idx].cmd |= CMD_SRC_CRCI(achan->crci);
++ desc[idx].row_offset = burst;
++ } else {
++ desc[idx].src_addr = sg_dma_address(sg) +
++ curr_offset;
++ desc[idx].dst_addr = achan->slave.dst_addr;
++ desc[idx].cmd |= CMD_DST_CRCI(achan->crci);
++ desc[idx].row_offset = burst << 16;
++ }
++
++ if (remainder < burst) {
++ rows = 1;
++ row_len = remainder;
++ } else {
++ rows = remainder / burst;
++ rows = min_t(u32, rows, ADM_MAX_ROWS);
++ row_len = burst;
++ }
++
++ desc[idx].num_rows = rows << 16 | rows;
++ desc[idx].row_len = row_len << 16 | row_len;
++
++ remainder -= row_len * rows;
++ async_desc->length += row_len * rows;
++ curr_offset += row_len * rows;
++
++ idx++;
++ } while (remainder > 0);
++ }
++
++ /* set last command flag */
++ desc[idx - 1].cmd |= CMD_LC;
++
++ /* reset channel error */
++ achan->error = 0;
++
++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++}
++
++/**
++ * adm_slave_config - set slave configuration for channel
++ * @chan: dma channel
++ * @cfg: slave configuration
++ *
++ * Sets slave configuration for channel
++ *
++ */
++static int adm_slave_config(struct adm_chan *achan,
++ struct dma_slave_config *cfg)
++{
++ int ret = 0;
++ u32 burst;
++ struct adm_device *adev = achan->adev;
++
++ memcpy(&achan->slave, cfg, sizeof(*cfg));
++
++ /* set channel CRCI burst, if applicable */
++ if (achan->crci) {
++ burst = max_t(u32, cfg->src_maxburst, cfg->dst_maxburst);
++
++ switch (burst) {
++ case 16:
++ achan->blk_size = 0;
++ break;
++ case 32:
++ achan->blk_size = 1;
++ break;
++ case 64:
++ achan->blk_size = 2;
++ break;
++ case 128:
++ achan->blk_size = 3;
++ break;
++ case 192:
++ achan->blk_size = 4;
++ break;
++ case 256:
++ achan->blk_size = 5;
++ break;
++ default:
++ achan->slave.src_maxburst = 0;
++ achan->slave.dst_maxburst = 0;
++ ret = -EINVAL;
++ break;
++ }
++
++ if (!ret)
++ writel(achan->blk_size,
++ adev->regs + HI_CRCI_CTL(achan->id, adev->ee));
++ }
++
++ return ret;
++}
++
++/**
++ * 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 void adm_terminate_all(struct adm_chan *achan)
++{
++ struct adm_device *adev = achan->adev;
++ unsigned long flags;
++ LIST_HEAD(head);
++
++ /* send flush command to terminate current transaction */
++ writel_relaxed(0x0,
++ adev->regs + HI_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++ spin_lock_irqsave(&achan->vc.lock, flags);
++ vchan_get_all_descriptors(&achan->vc, &head);
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ vchan_dma_desc_free_list(&achan->vc, &head);
++}
++
++/**
++ * adm_control - DMA device control
++ * @chan: dma channel
++ * @cmd: control cmd
++ * @arg: cmd argument
++ *
++ * Perform DMA control command
++ *
++ */
++static int adm_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
++ unsigned long arg)
++{
++ struct adm_chan *achan = to_adm_chan(chan);
++ unsigned long flag;
++ int ret = 0;
++
++ switch (cmd) {
++ case DMA_SLAVE_CONFIG:
++ spin_lock_irqsave(&achan->vc.lock, flag);
++ ret = adm_slave_config(achan, (struct dma_slave_config *)arg);
++ spin_unlock_irqrestore(&achan->vc.lock, flag);
++ break;
++
++ case DMA_TERMINATE_ALL:
++ adm_terminate_all(achan);
++ break;
++
++ default:
++ ret = -ENXIO;
++ break;
++ };
++
++ return ret;
++}
++
++/**
++ * 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;
++ struct adm_desc_hw *desc;
++ struct adm_cmd_ptr_list *cpl;
++
++ 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;
++
++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN);
++
++ if (!achan->initialized) {
++ /* enable interrupts */
++ writel(CH_CONF_IRQ_EN | CH_CONF_FLUSH_RSLT_EN |
++ CH_CONF_FORCE_RSLT_EN | CH_CONF_PERM_MPU_CONF |
++ CH_CONF_MPU_DISABLE,
++ adev->regs + HI_CH_CONF(achan->id));
++
++ writel(CH_RSLT_CONF_IRQ_EN | CH_RSLT_CONF_FLUSH_EN,
++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee));
++
++ if (achan->crci)
++ writel(achan->blk_size, adev->regs +
++ HI_CRCI_CTL(achan->crci, adev->ee));
++
++ achan->initialized = 1;
++ }
++
++ /* make sure IRQ enable doesn't get reordered */
++ wmb();
++
++ /* write next command list out to the CMD FIFO */
++ writel(round_up(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++ adev->regs + HI_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 +
++ HI_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++ for (i = 0; i < 16; i++) {
++ struct adm_chan *achan = &adev->channels[i];
++ u32 status, result;
++ if (srcs & BIT(i)) {
++ status = readl_relaxed(adev->regs +
++ HI_CH_STATUS_SD(i, adev->ee));
++
++ /* if no result present, skip */
++ if (!(status & CH_STATUS_VALID))
++ continue;
++
++ result = readl_relaxed(adev->regs +
++ HI_CH_RSLT(i, adev->ee));
++
++ /* no valid results, skip */
++ if (!(result & CH_RSLT_VALID))
++ continue;
++
++ /* flag error if transaction was flushed or failed */
++ if (result & (CH_RSLT_ERR | 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);
++
++ 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;
++ else if (achan->curr_txd && achan->curr_txd->vd.tx.cookie == cookie)
++ residue = achan->curr_txd->length;
++
++ spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++ dma_set_residue(txstate, residue);
++
++ if (achan->error)
++ return DMA_ERROR;
++
++ return ret;
++}
++
++static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec,
++ struct of_dma *of)
++{
++ struct adm_device *adev = container_of(of->of_dma_data,
++ struct adm_device, common);
++ struct adm_chan *achan;
++ struct dma_chan *chan;
++ unsigned int request;
++ unsigned int crci;
++
++ if (dma_spec->args_count != 2) {
++ dev_err(adev->dev, "incorrect number of dma arguments\n");
++ return NULL;
++ }
++
++ request = dma_spec->args[0];
++ if (request >= adev->num_channels)
++ return NULL;
++
++ crci = dma_spec->args[1];
++
++ chan = dma_get_slave_channel(&(adev->channels[request].vc.chan));
++
++ if (!chan)
++ return NULL;
++
++ achan = to_adm_chan(chan);
++ achan->crci = crci;
++
++ return chan;
++}
++
++/**
++ * 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))
++ return PTR_ERR(adev->iface_clk);
++
++ ret = clk_prepare_enable(adev->iface_clk);
++ if (ret) {
++ dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++ return ret;
++ }
++
++ 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");
++ return PTR_ERR(adev->clk_reset);
++ }
++
++ 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");
++ return PTR_ERR(adev->c0_reset);
++ }
++
++ 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");
++ return PTR_ERR(adev->c1_reset);
++ }
++
++ 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");
++ return PTR_ERR(adev->c2_reset);
++ }
++
++ 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->num_channels = 16;
++
++ adev->channels = devm_kcalloc(adev->dev, adev->num_channels,
++ sizeof(*adev->channels), GFP_KERNEL);
++
++ if (!adev->channels) {
++ ret = -ENOMEM;
++ goto err_disable_clk;
++ }
++
++ /* allocate and initialize channels */
++ INIT_LIST_HEAD(&adev->common.channels);
++
++ for (i = 0; i < adev->num_channels; i++)
++ adm_channel_init(adev, &adev->channels[i], i);
++
++ /* reset CRCIs */
++ for (i = 0; i < 16; i++)
++ writel(CRCI_CTL_RST, adev->regs + HI_CRCI_CTL(i, adev->ee));
++
++ /* configure client interfaces */
++ writel(CI_RANGE_START(0x40) | CI_RANGE_END(0xb0) | CI_BURST_8_WORDS,
++ adev->regs + HI_CI_CONF(0));
++ writel(CI_RANGE_START(0x2a) | CI_RANGE_END(0x2c) | CI_BURST_8_WORDS,
++ adev->regs + HI_CI_CONF(1));
++ writel(CI_RANGE_START(0x12) | CI_RANGE_END(0x28) | CI_BURST_8_WORDS,
++ adev->regs + HI_CI_CONF(2));
++ writel(GP_CTL_LP_EN | GP_CTL_LP_CNT(0xf), adev->regs + HI_GP_CTL);
++
++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++ 0, "adm_dma", adev);
++ if (ret)
++ goto err_disable_clk;
++
++ platform_set_drvdata(pdev, adev);
++
++ adev->common.dev = adev->dev;
++ adev->common.dev->dma_parms = &adev->dma_parms;
++
++ /* set capabilities */
++ dma_cap_zero(adev->common.cap_mask);
++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++ /* initialize dmaengine apis */
++ adev->common.device_alloc_chan_resources = adm_alloc_chan;
++ adev->common.device_free_chan_resources = adm_free_chan;
++ adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++ adev->common.device_control = adm_control;
++ adev->common.device_issue_pending = adm_issue_pending;
++ adev->common.device_tx_status = adm_tx_status;
++
++ ret = dma_async_device_register(&adev->common);
++ if (ret) {
++ dev_err(adev->dev, "failed to register dma async device\n");
++ goto err_disable_clk;
++ }
++
++ ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate,
++ &adev->common);
++ if (ret)
++ goto err_unregister_dma;
++
++ return 0;
++
++err_unregister_dma:
++ dma_async_device_unregister(&adev->common);
++err_disable_clk:
++ clk_disable_unprepare(adev->core_clk);
++ clk_disable_unprepare(adev->iface_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);
++
++ devm_free_irq(adev->dev, adev->irq, adev);
++
++ for (i = 0; i < adev->num_channels; i++) {
++ achan = &adev->channels[i];
++ writel(CH_CONF_FLUSH_RSLT_EN,
++ adev->regs + HI_CH_CONF(achan->id));
++ writel(CH_RSLT_CONF_FLUSH_EN,
++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee));
++
++ adm_terminate_all(&adev->channels[i]);
++ }
++
++ 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",
++ .owner = THIS_MODULE,
++ .of_match_table = adm_of_match,
++ },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch
new file mode 100644
index 0000000..a80fcfa
--- /dev/null
+++ b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch
@@ -0,0 +1,82 @@
+From 331294fa5c703536e27b79e9c112d162393f725a Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 13:55:10 -0500
+Subject: [PATCH 178/182] dmaengine: qcom_adm: Add device tree binding
+
+Add device tree binding support for the QCOM ADM DMA driver.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ Documentation/devicetree/bindings/dma/qcom_adm.txt | 60 ++++++++++++++++++++
+ 1 file changed, 60 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/dma/qcom_adm.txt
+
+diff --git a/Documentation/devicetree/bindings/dma/qcom_adm.txt b/Documentation/devicetree/bindings/dma/qcom_adm.txt
+new file mode 100644
+index 0000000..7f05cb5
+--- /dev/null
++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt
+@@ -0,0 +1,60 @@
++QCOM ADM DMA Controller
++
++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.
++- clocks: Should contain the core clock and interface clock.
++- clock-names: Must contain "core" for the core clock and "iface" for the
++ interface clock.
++- resets: Must contain an entry for each entry in reset names.
++- reset-names: Must include the following entries:
++ - clk
++ - c0
++ - c1
++ - c2
++- qcom,ee: indicates the security domain identifier used in the secure world.
++
++Example:
++ adm_dma: dma@18300000 {
++ compatible = "qcom,adm";
++ reg = <0x18300000 0x100000>;
++ interrupts = <0 170 0>;
++ #dma-cells = <2>;
++
++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
++ clock-names = "core", "iface";
++
++ resets = <&gcc ADM0_RESET>,
++ <&gcc ADM0_C0_RESET>,
++ <&gcc ADM0_C1_RESET>,
++ <&gcc ADM0_C2_RESET>;
++ reset-names = "clk", "c0", "c1", "c2";
++ qcom,ee = <0>;
++ };
++
++DMA clients must use the format descripted in the dma.txt file, using a three
++cell specifier for each channel.
++
++Each dmas request consists of 3 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.
++
++Example:
++
++ spi4: spi@1a280000 {
++ status = "ok";
++ spi-max-frequency = <50000000>;
++
++ pinctrl-0 = <&spi_pins>;
++ pinctrl-names = "default";
++
++ cs-gpios = <&qcom_pinmux 20 0>;
++
++ dmas = <&adm_dma 6 9>,
++ <&adm_dma 5 10>;
++ dma-names = "rx", "tx";
++ };
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch
new file mode 100644
index 0000000..c35aeb1
--- /dev/null
+++ b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch
@@ -0,0 +1,495 @@
+From 8322bafdcee1d7eaf15540ff013415bff1eacb28 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 10:50:24 -0500
+Subject: [PATCH 179/182] spi: qup: Add DMA capabilities
+
+This patch adds DMA capabilities to the spi-qup driver. If DMA channels are
+present, the QUP will use DMA instead of block mode for transfers to/from SPI
+peripherals for transactions larger greater than the length of a block.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 340 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index c137226..28754ae 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -22,6 +22,8 @@
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
+
+ #define QUP_CONFIG 0x0000
+ #define QUP_STATE 0x0004
+@@ -116,6 +118,8 @@
+
+ #define SPI_NUM_CHIPSELECTS 4
+
++#define SPI_MAX_XFER (SZ_64K - 64)
++
+ /* high speed mode is when bus rate is greater then 26MHz */
+ #define SPI_HS_MIN_RATE 26000000
+ #define SPI_MAX_RATE 50000000
+@@ -143,6 +147,14 @@ struct spi_qup {
+ int tx_bytes;
+ int rx_bytes;
+ int qup_v1;
++ int use_dma;
++
++ struct dma_chan *rx_chan;
++ struct dma_slave_config rx_conf;
++ struct dma_chan *tx_chan;
++ struct dma_slave_config tx_conf;
++ void *dummy;
++ atomic_t dma_outstanding;
+ };
+
+
+@@ -266,6 +278,221 @@ static void spi_qup_fifo_write(struct spi_qup *controller,
+ }
+ }
+
++static void qup_dma_callback(void *data)
++{
++ struct spi_qup *controller = data;
++
++ if (atomic_dec_and_test(&controller->dma_outstanding))
++ complete(&controller->done);
++}
++
++static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer)
++{
++ struct dma_async_tx_descriptor *rxd, *txd;
++ dma_cookie_t rx_cookie, tx_cookie;
++ u32 xfer_len, rx_align = 0, tx_align = 0, n_words;
++ struct scatterlist tx_sg[2], rx_sg[2];
++ int ret = 0;
++ u32 bytes_to_xfer = xfer->len;
++ u32 offset = 0;
++ u32 rx_nents = 0, tx_nents = 0;
++ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0;
++
++
++ if (xfer->rx_buf) {
++ rx_dma = dma_map_single(controller->dev, xfer->rx_buf,
++ xfer->len, DMA_FROM_DEVICE);
++
++ if (dma_mapping_error(controller->dev, rx_dma)) {
++ ret = -ENOMEM;
++ return ret;
++ }
++
++ /* check to see if we need dummy buffer for leftover bytes */
++ rx_align = xfer->len % controller->in_blk_sz;
++ if (rx_align) {
++ rx_dummy_dma = dma_map_single(controller->dev,
++ controller->dummy, controller->in_fifo_sz,
++ DMA_FROM_DEVICE);
++
++ if (dma_mapping_error(controller->dev, rx_dummy_dma)) {
++ ret = -ENOMEM;
++ goto err_map_rx_dummy;
++ }
++ }
++ }
++
++ if (xfer->tx_buf) {
++ tx_dma = dma_map_single(controller->dev,
++ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
++
++ if (dma_mapping_error(controller->dev, tx_dma)) {
++ ret = -ENOMEM;
++ goto err_map_tx;
++ }
++
++ /* check to see if we need dummy buffer for leftover bytes */
++ tx_align = xfer->len % controller->out_blk_sz;
++ if (tx_align) {
++ memcpy(controller->dummy + SZ_1K,
++ xfer->tx_buf + xfer->len - tx_align,
++ tx_align);
++ memset(controller->dummy + SZ_1K + tx_align, 0,
++ controller->out_blk_sz - tx_align);
++
++ tx_dummy_dma = dma_map_single(controller->dev,
++ controller->dummy + SZ_1K,
++ controller->out_blk_sz, DMA_TO_DEVICE);
++
++ if (dma_mapping_error(controller->dev, tx_dummy_dma)) {
++ ret = -ENOMEM;
++ goto err_map_tx_dummy;
++ }
++ }
++ }
++
++ atomic_set(&controller->dma_outstanding, 0);
++
++ while (bytes_to_xfer > 0) {
++ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER);
++ n_words = DIV_ROUND_UP(xfer_len, controller->w_size);
++
++ /* write out current word count to controller */
++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++
++ reinit_completion(&controller->done);
++
++ if (xfer->tx_buf) {
++ /* recalc align for each transaction */
++ tx_align = xfer_len % controller->out_blk_sz;
++
++ if (tx_align)
++ tx_nents = 2;
++ else
++ tx_nents = 1;
++
++ /* initialize scatterlists */
++ sg_init_table(tx_sg, tx_nents);
++ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align;
++ sg_dma_address(&tx_sg[0]) = tx_dma + offset;
++
++ /* account for non block size transfer */
++ if (tx_align) {
++ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz;
++ sg_dma_address(&tx_sg[1]) = tx_dummy_dma;
++ }
++
++ txd = dmaengine_prep_slave_sg(controller->tx_chan,
++ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0);
++ if (!txd) {
++ ret = -ENOMEM;
++ goto err_unmap;
++ }
++
++ atomic_inc(&controller->dma_outstanding);
++
++ txd->callback = qup_dma_callback;
++ txd->callback_param = controller;
++
++ tx_cookie = dmaengine_submit(txd);
++
++ dma_async_issue_pending(controller->tx_chan);
++ }
++
++ if (xfer->rx_buf) {
++ /* recalc align for each transaction */
++ rx_align = xfer_len % controller->in_blk_sz;
++
++ if (rx_align)
++ rx_nents = 2;
++ else
++ rx_nents = 1;
++
++ /* initialize scatterlists */
++ sg_init_table(rx_sg, rx_nents);
++ sg_dma_address(&rx_sg[0]) = rx_dma + offset;
++ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align;
++
++ /* account for non block size transfer */
++ if (rx_align) {
++ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz;
++ sg_dma_address(&rx_sg[1]) = rx_dummy_dma;
++ }
++
++ rxd = dmaengine_prep_slave_sg(controller->rx_chan,
++ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0);
++ if (!rxd) {
++ ret = -ENOMEM;
++ goto err_unmap;
++ }
++
++ atomic_inc(&controller->dma_outstanding);
++
++ rxd->callback = qup_dma_callback;
++ rxd->callback_param = controller;
++
++ rx_cookie = dmaengine_submit(rxd);
++
++ dma_async_issue_pending(controller->rx_chan);
++ }
++
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set EXECUTE state\n");
++ goto err_unmap;
++ }
++
++ if (!wait_for_completion_timeout(&controller->done,
++ msecs_to_jiffies(1000))) {
++ ret = -ETIMEDOUT;
++
++ /* clear out all the DMA transactions */
++ if (xfer->tx_buf)
++ dmaengine_terminate_all(controller->tx_chan);
++ if (xfer->rx_buf)
++ dmaengine_terminate_all(controller->rx_chan);
++
++ goto err_unmap;
++ }
++
++ if (rx_align)
++ memcpy(xfer->rx_buf + offset + xfer->len - rx_align,
++ controller->dummy, rx_align);
++
++ /* adjust remaining bytes to transfer */
++ bytes_to_xfer -= xfer_len;
++ offset += xfer_len;
++
++
++ /* reset mini-core state so we can program next transaction */
++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++ dev_err(controller->dev, "cannot set RESET state\n");
++ goto err_unmap;
++ }
++ }
++
++ ret = 0;
++
++err_unmap:
++ if (tx_align)
++ dma_unmap_single(controller->dev, tx_dummy_dma,
++ controller->out_fifo_sz, DMA_TO_DEVICE);
++err_map_tx_dummy:
++ if (xfer->tx_buf)
++ dma_unmap_single(controller->dev, tx_dma, xfer->len,
++ DMA_TO_DEVICE);
++err_map_tx:
++ if (rx_align)
++ dma_unmap_single(controller->dev, rx_dummy_dma,
++ controller->in_fifo_sz, DMA_FROM_DEVICE);
++err_map_rx_dummy:
++ if (xfer->rx_buf)
++ dma_unmap_single(controller->dev, rx_dma, xfer->len,
++ DMA_FROM_DEVICE);
++
++ return ret;
++}
++
+ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ {
+ struct spi_qup *controller = dev_id;
+@@ -315,11 +542,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ error = -EIO;
+ }
+
+- if (opflags & QUP_OP_IN_SERVICE_FLAG)
+- spi_qup_fifo_read(controller, xfer);
++ if (!controller->use_dma) {
++ if (opflags & QUP_OP_IN_SERVICE_FLAG)
++ spi_qup_fifo_read(controller, xfer);
+
+- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+- spi_qup_fifo_write(controller, xfer);
++ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++ spi_qup_fifo_write(controller, xfer);
++ }
+
+ spin_lock_irqsave(&controller->lock, flags);
+ controller->error = error;
+@@ -339,6 +568,8 @@ 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;
+ int ret, n_words, w_size;
++ size_t dma_align = dma_get_cache_alignment();
++ u32 dma_available = 0;
+
+ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+ dev_err(controller->dev, "too big size for loopback %d > %d\n",
+@@ -367,6 +598,13 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ n_words = xfer->len / w_size;
+ controller->w_size = w_size;
+
++ if (controller->rx_chan &&
++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align) &&
++ !is_vmalloc_addr(xfer->tx_buf) &&
++ !is_vmalloc_addr(xfer->rx_buf))
++ dma_available = 1;
++
+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+ mode = QUP_IO_M_MODE_FIFO;
+ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+@@ -374,19 +612,30 @@ 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);
+- } else {
++ controller->use_dma = 0;
++ } else if (!dma_available) {
+ mode = QUP_IO_M_MODE_BLOCK;
+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+ /* must be zero for BLOCK and BAM */
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ controller->use_dma = 0;
++ } else {
++ mode = QUP_IO_M_MODE_DMOV;
++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++ controller->use_dma = 1;
+ }
+
+ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+ /* Set input and output transfer mode */
+ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++ if (!controller->use_dma)
++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++ else
++ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
++
+ iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+ iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+
+@@ -419,11 +668,20 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
+ config |= xfer->bits_per_word - 1;
+ config |= QUP_CONFIG_SPI_MODE;
++
++ if (controller->use_dma) {
++ if (!xfer->tx_buf)
++ config |= QUP_CONFIG_NO_OUTPUT;
++ if (!xfer->rx_buf)
++ config |= QUP_CONFIG_NO_INPUT;
++ }
++
+ writel_relaxed(config, controller->base + QUP_CONFIG);
+
+ /* only write to OPERATIONAL_MASK when register is present */
+ if (!controller->qup_v1)
+ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
++
+ return 0;
+ }
+
+@@ -452,26 +710,32 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ controller->tx_bytes = 0;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+- dev_warn(controller->dev, "cannot set RUN state\n");
+- goto exit;
+- }
++ if (controller->use_dma) {
++ ret = spi_qup_do_dma(controller, xfer);
++ } else {
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set RUN state\n");
++ goto exit;
++ }
+
+- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
+- dev_warn(controller->dev, "cannot set PAUSE state\n");
+- goto exit;
+- }
++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
++ dev_warn(controller->dev, "cannot set PAUSE state\n");
++ goto exit;
++ }
+
+- spi_qup_fifo_write(controller, xfer);
++ spi_qup_fifo_write(controller, xfer);
+
+- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+- dev_warn(controller->dev, "cannot set EXECUTE state\n");
+- goto exit;
+- }
++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++ dev_warn(controller->dev, "cannot set EXECUTE state\n");
++ goto exit;
++ }
+
+- if (!wait_for_completion_timeout(&controller->done, timeout))
+- ret = -ETIMEDOUT;
++ if (!ret && !wait_for_completion_timeout(&controller->done,
++ timeout))
++ ret = -ETIMEDOUT;
++ }
+ exit:
++
+ spi_qup_set_state(controller, QUP_STATE_RESET);
+ spin_lock_irqsave(&controller->lock, flags);
+ controller->xfer = NULL;
+@@ -553,6 +817,7 @@ static int spi_qup_probe(struct platform_device *pdev)
+ master->transfer_one = spi_qup_transfer_one;
+ master->dev.of_node = pdev->dev.of_node;
+ master->auto_runtime_pm = true;
++ master->dma_alignment = dma_get_cache_alignment();
+
+ platform_set_drvdata(pdev, master);
+
+@@ -612,6 +877,55 @@ static int spi_qup_probe(struct platform_device *pdev)
+ writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
+ base + SPI_ERROR_FLAGS_EN);
+
++ /* allocate dma resources, if available */
++ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
++ if (controller->rx_chan) {
++ controller->tx_chan =
++ dma_request_slave_channel(&pdev->dev, "tx");
++
++ if (!controller->tx_chan) {
++ dev_err(&pdev->dev, "Failed to allocate dma tx chan");
++ dma_release_channel(controller->rx_chan);
++ }
++
++ /* set DMA parameters */
++ controller->rx_conf.device_fc = 1;
++ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO;
++ controller->rx_conf.src_maxburst = controller->in_blk_sz;
++
++ controller->tx_conf.device_fc = 1;
++ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO;
++ controller->tx_conf.dst_maxburst = controller->out_blk_sz;
++
++ if (dmaengine_slave_config(controller->rx_chan,
++ &controller->rx_conf)) {
++ dev_err(&pdev->dev, "failed to configure RX channel\n");
++
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ } else if (dmaengine_slave_config(controller->tx_chan,
++ &controller->tx_conf)) {
++ dev_err(&pdev->dev, "failed to configure TX channel\n");
++
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ }
++
++ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE,
++ GFP_KERNEL);
++
++ if (!controller->dummy) {
++ dma_release_channel(controller->rx_chan);
++ dma_release_channel(controller->tx_chan);
++ controller->tx_chan = NULL;
++ controller->rx_chan = NULL;
++ }
++ }
++
+ /* if earlier version of the QUP, disable INPUT_OVERRUN */
+ if (controller->qup_v1)
+ writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
+@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platform_device *pdev)
+ if (ret)
+ return ret;
+
++ if (controller->rx_chan)
++ dma_release_channel(controller->rx_chan);
++ if (controller->tx_chan)
++ dma_release_channel(controller->tx_chan);
++
+ clk_disable_unprepare(controller->cclk);
+ clk_disable_unprepare(controller->iclk);
+
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch
new file mode 100644
index 0000000..5fed041
--- /dev/null
+++ b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch
@@ -0,0 +1,90 @@
+From c78ae23b6c174c9f2e0973a247942b6b4adb7e82 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 13:02:59 -0500
+Subject: [PATCH 180/182] ARM: dts: Add ADM DMA nodes and SPI linkage
+
+This patch adds the ADM DMA controller DT nodes and also enables the use of dma
+in SPI.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 11 +++++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++---
+ 2 files changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+index 2b2d63c..c54a3ee 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -44,6 +44,10 @@
+ drive-strength = <10>;
+ bias-none;
+ };
++ cs {
++ pins = "gpio20";
++ drive-strength = <12>;
++ };
+ };
+ nand_pins: nand_pins {
+ mux {
+@@ -100,12 +104,17 @@
+
+ cs-gpios = <&qcom_pinmux 20 0>;
+
++ dmas = <&adm_dma 6 9>,
++ <&adm_dma 5 10>;
++ dma-names = "rx", "tx";
++
+ flash: m25p80@0 {
+ compatible = "s25fl256s1";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <50000000>;
+ reg = <0>;
++ m25p,fast-read;
+
+ partition@0 {
+ label = "rootfs";
+@@ -140,8 +149,10 @@
+ ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */
+ 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */
+ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++
+ };
+
++
+ sata-phy@1b400000 {
+ status = "ok";
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+index 97e4c3d..f74e923 100644
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -421,19 +421,21 @@
+ compatible = "qcom,adm";
+ reg = <0x18300000 0x100000>;
+ interrupts = <0 170 0>;
++ #dma-cells = <2>;
+
+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
+- clock-names = "core_clk", "iface_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 = "adm", "pbus", "c0", "c1", "c2";
++ reset-names = "clk", "pbus", "c0", "c1", "c2";
++ qcom,ee = <0>;
+
+ status = "disabled";
++
+ };
+
+ nand@0x1ac00000 {
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch
new file mode 100644
index 0000000..763a245
--- /dev/null
+++ b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch
@@ -0,0 +1,46 @@
+From ae0e9ee3bcfafc5bd5932b544e0a5c107948fc97 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 30 Jun 2014 21:17:26 -0500
+Subject: [PATCH 181/182] mtd: nand: qcom: Align clk and reset names
+
+This patch aligns the clk and reset names to the same ones that the dmaengine
+driver uses.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/mtd/nand/qcom_adm_dma.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c
+index 46d8473..542f901 100644
+--- a/drivers/mtd/nand/qcom_adm_dma.c
++++ b/drivers/mtd/nand/qcom_adm_dma.c
+@@ -568,14 +568,14 @@ static int msm_dmov_init_clocks(struct platform_device *pdev)
+ int adm = (pdev->id >= 0) ? pdev->id : 0;
+ int ret;
+
+- dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk");
++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(dmov_conf[adm].clk)) {
+ printk(KERN_ERR "%s: Error getting adm_clk\n", __func__);
+ dmov_conf[adm].clk = NULL;
+ return -ENOENT;
+ }
+
+- dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk");
++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(dmov_conf[adm].pclk)) {
+ dmov_conf[adm].pclk = NULL;
+ /* pclk not present on all SoCs, don't bail on failure */
+@@ -690,7 +690,7 @@ static int msm_dmov_probe(struct platform_device *pdev)
+ }
+
+ /* get resets */
+- dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm");
++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "clk");
+ if (IS_ERR(dmov_conf[adm].adm_reset)) {
+ dev_err(&pdev->dev, "failed to get adm reset\n");
+ ret = PTR_ERR(dmov_conf[adm].adm_reset);
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch
new file mode 100644
index 0000000..87e2fad
--- /dev/null
+++ b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch
@@ -0,0 +1,54 @@
+From 0771849495b4128cac2faf7d49c85c729fc48b20 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Mon, 30 Jun 2014 21:18:39 -0500
+Subject: [PATCH 182/182] qcom: Kconfig: Make drivers mutually exclusive
+
+This patch makes sure QCOM ADM dmaengine and QCOM Nand cannot be enabled at the
+same time. This is an issue because the dma drivers will conflict with one
+another.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/dma/Kconfig | 2 +-
+ drivers/mtd/nand/Kconfig | 6 +++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
+index 79155fa..ed7a5f6 100644
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -412,7 +412,7 @@ config QCOM_BAM_DMA
+
+ config QCOM_ADM
+ tristate "Qualcomm ADM support"
+- depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
++ depends on !MTD_QCOM_ADM && (ARCH_QCOM || (COMPILE_TEST && OF && ARM))
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ ---help---
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index 6e3842f..4a84264 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -511,15 +511,15 @@ config MTD_NAND_XWAY
+ to the External Bus Unit (EBU).
+
+ config MTD_QCOM_DMA
+- tristate "QCMO NAND DMA Support"
+- depends on ARCH_QCOM && MTD_QCOM_NAND
++ tristate "QCOM NAND DMA Support"
++ depends on !QCOM_ADM && ARCH_QCOM && MTD_QCOM_NAND
+ default n
+ help
+ DMA support for QCOM NAND
+
+ config MTD_QCOM_NAND
+ tristate "QCOM NAND Device Support"
+- depends on MTD && ARCH_QCOM
++ depends on MTD && ARCH_QCOM && !QCOM_ADM
+ select CRC16
+ select BITREVERSE
+ select MTD_NAND_IDS
+--
+1.7.10.4
+
diff --git a/target/linux/ipq806x/profiles/default.mk b/target/linux/ipq806x/profiles/default.mk
new file mode 100644
index 0000000..ded0164
--- /dev/null
+++ b/target/linux/ipq806x/profiles/default.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2014 The Linux Foundation. All rights reserved.
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/Default
+ NAME:=Default Profile (minimum package set)
+ PACKAGES:= \
+ kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev
+endef
+
+define Profile/Default/Description
+ Default package set compatible with most boards.
+endef
+$(eval $(call Profile,Default))