ipq806x: fix 3.18 support
authorJohn Crispin <john@phrozen.org>
Fri, 27 May 2016 20:27:28 +0000 (22:27 +0200)
committerJohn Crispin <john@phrozen.org>
Fri, 27 May 2016 20:28:15 +0000 (22:28 +0200)
accidentially removed the files in the v4.4 commit

Signed-off-by: John Crispin <john@phrozen.org>
81 files changed:
target/linux/ipq806x/a [new file with mode: 0644]
target/linux/ipq806x/config-3.18 [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/710-stmmac-fix-ipq806x-DMA-configuration.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch [new file with mode: 0644]
target/linux/ipq806x/patches-3.18/802-ARM-qcom-add-TPLink-C2600-device-tree.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch [new file with mode: 0644]

diff --git a/target/linux/ipq806x/a b/target/linux/ipq806x/a
new file mode 100644 (file)
index 0000000..eddecef
--- /dev/null
@@ -0,0 +1,464 @@
+commit be3de98aef65403129a4b714ddce8ab71f563209
+Author: Ash Benz <ash.benz@bk.ru>
+Date:   Fri May 27 22:26:45 2016 +0800
+
+    ipq806x/dts: Add Archer C2600 DTS
+    
+    Signed-off-by: Ash Benz <ash.benz@bk.ru>
+
+diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
+index 52cc1fa..77391d0 100644
+--- a/target/linux/ipq806x/image/Makefile
++++ b/target/linux/ipq806x/image/Makefile
+@@ -36,11 +36,6 @@ define Build/append-dtb
+       cat $(DTS_DIR)/$(DEVICE_DTS).dtb >> $@
+ endef
+-define Build/append-new-dtb
+-       $(call Image/BuildDTB,../dts/$(DEVICE_DTS).dts,$@.dtb)
+-       cat $@.dtb >> $@
+-endef
+-
+ define Build/append-file
+       cat $(1) >> $@
+ endef
+@@ -95,7 +90,7 @@ define Device/TpSafeImage
+       PROFILES += $$(DEVICE_NAME)
+       FILESYSTEMS := squashfs
+       KERNEL_SUFFIX := -uImage
+-      KERNEL = kernel-bin | append-new-dtb | uImage none
++      KERNEL = kernel-bin | append-dtb | uImage none
+       KERNEL_NAME := zImage
+       TPLINK_BOARD_NAME :=
+       IMAGES := factory.bin sysupgrade.bin
+diff --git a/target/linux/ipq806x/patches-4.4/802-ARM-qcom-add-TPLink-C2600-device-tree.patch b/target/linux/ipq806x/patches-4.4/802-ARM-qcom-add-TPLink-C2600-device-tree.patch
+new file mode 100644
+index 0000000..8952f33
+--- /dev/null
++++ b/target/linux/ipq806x/patches-4.4/802-ARM-qcom-add-TPLink-C2600-device-tree.patch
+@@ -0,0 +1,425 @@
++--- a/arch/arm/boot/dts/Makefile
+++++ b/arch/arm/boot/dts/Makefile
++@@ -506,6 +506,7 @@
++      qcom-apq8084-ifc6540.dtb \
++      qcom-apq8084-mtp.dtb \
++      qcom-ipq8064-ap148.dtb \
+++     qcom-ipq8064-c2600.dtb \
++      qcom-ipq8064-db149.dtb \
++      qcom-ipq8064-r7500.dtb \
++      qcom-ipq8064-d7800.dtb \
++--- /dev/null
+++++ b/arch/arm/boot/dts/qcom-ipq8064-c2600.dts
++@@ -0,0 +1,412 @@
+++#include "qcom-ipq8064-v1.0.dtsi"
+++#include <dt-bindings/input/input.h>
+++
+++/ {
+++     model = "TP-Link Archer C2600";
+++     compatible = "tplink,c2600", "qcom,ipq8064";
+++
+++     memory@0 {
+++             reg = <0x42000000 0x1e000000>;
+++             device_type = "memory";
+++     };
+++
+++     reserved-memory {
+++             #address-cells = <1>;
+++             #size-cells = <1>;
+++             ranges;
+++             rsvd@41200000 {
+++                     reg = <0x41200000 0x300000>;
+++                     no-map;
+++             };
+++     };
+++
+++     aliases {
+++             serial0 = &uart4;
+++             mdio-gpio0 = &mdio0;
+++     };
+++
+++     chosen {
+++             linux,stdout-path = "serial0:115200n8";
+++     };
+++
+++     soc {
+++             pinmux@800000 {
+++                     i2c4_pins: i2c4_pinmux {
+++                             pins = "gpio12", "gpio13";
+++                             function = "gsbi4";
+++                             bias-disable;
+++                     };
+++
+++                     spi_pins: spi_pins {
+++                             mux {
+++                                     pins = "gpio18", "gpio19", "gpio21";
+++                                     function = "gsbi5";
+++                                     drive-strength = <10>;
+++                                     bias-none;
+++                             };
+++                     };
+++
+++                     nand_pins: nand_pins {
+++                             mux {
+++                                     pins = "gpio34", "gpio35", "gpio36",
+++                                            "gpio37", "gpio38", "gpio39",
+++                                            "gpio40", "gpio41", "gpio42",
+++                                            "gpio43", "gpio44", "gpio45",
+++                                            "gpio46", "gpio47";
+++                                     function = "nand";
+++                                     drive-strength = <10>;
+++                                     bias-disable;
+++                             };
+++
+++                             pullups {
+++                                     pins = "gpio39";
+++                                     bias-pull-up;
+++                             };
+++
+++                             hold {
+++                                     pins = "gpio40", "gpio41", "gpio42",
+++                                            "gpio43", "gpio44", "gpio45",
+++                                            "gpio46", "gpio47";
+++                                     bias-bus-hold;
+++                             };
+++                     };
+++
+++                     mdio0_pins: mdio0_pins {
+++                             mux {
+++                                     pins = "gpio0", "gpio1";
+++                                     function = "gpio";
+++                                     drive-strength = <8>;
+++                                     bias-disable;
+++                             };
+++                     };
+++
+++                     rgmii2_pins: rgmii2_pins {
+++                             mux {
+++                                     pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
+++                                            "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
+++                                     function = "rgmii2";
+++                                     drive-strength = <8>;
+++                                     bias-disable;
+++                             };
+++                     };
+++             };
+++
+++             gsbi@16300000 {
+++                     qcom,mode = <GSBI_PROT_I2C_UART>;
+++                     status = "ok";
+++                     serial@16340000 {
+++                             status = "ok";
+++                     };
+++                     /*
+++                      * The i2c device on gsbi4 should not be enabled.
+++                      * On ipq806x designs gsbi4 i2c is meant for exclusive
+++                      * RPM usage. Turning this on in kernel manifests as
+++                      * i2c failure for the RPM.
+++                      */
+++             };
+++
+++             gsbi5: gsbi@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>;
+++
+++                                     SBL1@0 {
+++                                             label = "SBL1";
+++                                             reg = <0x0 0x20000>;
+++                                             read-only;
+++                                     };
+++                                     MIBIB@20000 {
+++                                             label = "MIBIB";
+++                                             reg = <0x20000 0x20000>;
+++                                             read-only;
+++                                     };
+++                                     SBL2@40000 {
+++                                             label = "SBL2";
+++                                             reg = <0x40000 0x20000>;
+++                                             read-only;
+++                                     };
+++                                     SBL3@60000 {
+++                                             label = "SBL3";
+++                                             reg = <0x60000 0x30000>;
+++                                             read-only;
+++                                     };
+++                                     DDRCONFIG@90000 {
+++                                             label = "DDRCONFIG";
+++                                             reg = <0x90000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     SSD@a0000 {
+++                                             label = "SSD";
+++                                             reg = <0xa0000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     TZ@b0000 {
+++                                             label = "TZ";
+++                                             reg = <0xb0000 0x30000>;
+++                                             read-only;
+++                                     };
+++                                     RPM@e0000 {
+++                                             label = "RPM";
+++                                             reg = <0xe0000 0x20000>;
+++                                             read-only;
+++                                     };
+++                                     fs-uboot@100000 {
+++                                             label = "fs-uboot";
+++                                             reg = <0x100000 0x70000>;
+++                                             read-only;
+++                                     };
+++                                     uboot-env@170000 {
+++                                             label = "uboot-env";
+++                                             reg = <0x170000 0x40000>;
+++                                             read-only;
+++                                     };
+++                                     radio@1b0000 {
+++                                             label = "radio";
+++                                             reg = <0x1b0000 0x40000>;
+++                                             read-only;
+++                                     };
+++                                     os-image@1f0000 {
+++                                             label = "os-image";
+++                                             reg = <0x1f0000 0x200000>;
+++                                     };
+++                                     rootfs@3f0000 {
+++                                             label = "rootfs";
+++                                             reg = <0x3f0000 0x1b00000>;
+++                                     };
+++                                     defaultmac: default-mac@1ef0000 {
+++                                             label = "default-mac";
+++                                             reg = <0x1ef0000 0x00200>;
+++                                             read-only;
+++                                     };
+++                                     pin@1ef0200 {
+++                                             label = "pin";
+++                                             reg = <0x1ef0200 0x00200>;
+++                                             read-only;
+++                                     };
+++                                     product-info@1ef0400 {
+++                                             label = "product-info";
+++                                             reg = <0x1ef0400 0x0fc00>;
+++                                             read-only;
+++                                     };
+++                                     partition-table@1f00000 {
+++                                             label = "partition-table";
+++                                             reg = <0x1f00000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     soft-version@1f10000 {
+++                                             label = "soft-version";
+++                                             reg = <0x1f10000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     support-list@1f20000 {
+++                                             label = "support-list";
+++                                             reg = <0x1f20000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     profile@1f30000 {
+++                                             label = "profile";
+++                                             reg = <0x1f30000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     default-config@1f40000 {
+++                                             label = "default-config";
+++                                             reg = <0x1f40000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     user-config@1f50000 {
+++                                             label = "user-config";
+++                                             reg = <0x1f50000 0x40000>;
+++                                             read-only;
+++                                     };
+++                                     qos-db@1f90000 {
+++                                             label = "qos-db";
+++                                             reg = <0x1f90000 0x40000>;
+++                                             read-only;
+++                                     };
+++                                     usb-config@1fd0000 {
+++                                             label = "usb-config";
+++                                             reg = <0x1fd0000 0x10000>;
+++                                             read-only;
+++                                     };
+++                                     log@1fe0000 {
+++                                             label = "log";
+++                                             reg = <0x1fe0000 0x20000>;
+++                                             read-only;
+++                                     };
+++                             };
+++                     };
+++             };
+++
+++             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";
+++             };
+++
+++             pcie0: pci@1b500000 {
+++                     status = "ok";
+++                     phy-tx0-term-offset = <7>;
+++             };
+++
+++             pcie1: pci@1b700000 {
+++                     status = "ok";
+++                     phy-tx0-term-offset = <7>;
+++             };
+++
+++             mdio0: mdio {
+++                     compatible = "virtual,mdio-gpio";
+++                     #address-cells = <1>;
+++                     #size-cells = <0>;
+++                     gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
+++                     pinctrl-0 = <&mdio0_pins>;
+++                     pinctrl-names = "default";
+++
+++                     phy0: ethernet-phy@0 {
+++                             device_type = "ethernet-phy";
+++                             reg = <0>;
+++                             qca,ar8327-initvals = <
+++                                     0x00004 0x7600000   /* PAD0_MODE */
+++                                     0x00008 0x1000000   /* PAD5_MODE */
+++                                     0x0000c 0x80        /* PAD6_MODE */
+++                                     0x000e4 0xaa545     /* MAC_POWER_SEL */
+++                                     0x000e0 0xc74164de  /* SGMII_CTRL */
+++                                     0x0007c 0x4e        /* PORT0_STATUS */
+++                                     0x00094 0x4e        /* PORT6_STATUS */
+++                                     >;
+++                     };
+++
+++                     phy4: ethernet-phy@4 {
+++                             device_type = "ethernet-phy";
+++                             reg = <4>;
+++                     };
+++             };
+++
+++             gmac1: ethernet@37200000 {
+++                     status = "ok";
+++                     phy-mode = "rgmii";
+++                     qcom,id = <1>;
+++
+++                     pinctrl-0 = <&rgmii2_pins>;
+++                     pinctrl-names = "default";
+++
+++                     mtd-mac-address = <&defaultmac 0x8>;
+++                     mtd-mac-address-increment = <1>;
+++
+++                     fixed-link {
+++                             speed = <1000>;
+++                             full-duplex;
+++                     };
+++             };
+++
+++             gmac2: ethernet@37400000 {
+++                     status = "ok";
+++                     phy-mode = "sgmii";
+++                     qcom,id = <2>;
+++
+++                     mtd-mac-address = <&defaultmac 0x8>;
+++
+++                     fixed-link {
+++                             speed = <1000>;
+++                             full-duplex;
+++                     };
+++             };
+++     };
+++
+++     gpio-keys {
+++             compatible = "gpio-keys";
+++
+++             wifi {
+++                     label = "wifi";
+++                     gpios = <&qcom_pinmux 49 1>;
+++                     linux,code = <KEY_WLAN>;
+++             };
+++
+++             reset {
+++                     label = "reset";
+++                     gpios = <&qcom_pinmux 64 1>;
+++                     linux,code = <KEY_RESTART>;
+++             };
+++
+++             wps {
+++                     label = "wps";
+++                     gpios = <&qcom_pinmux 65 1>;
+++                     linux,code = <KEY_WPS_BUTTON>;
+++             };
+++                ledgeneral {
+++                     label = "ledgeneral";
+++                     gpios = <&qcom_pinmux 16 1>;
+++                     linux,code = <KEY_DOLLAR>;
+++             };
+++     };
+++
+++     gpio-leds {
+++             compatible = "gpio-leds";
+++
+++             lan {
+++                     label = "lan:blue";
+++                     gpios = <&qcom_pinmux 6 0>;
+++             };
+++                usb4 {
+++                     label = "usb_4:blue";
+++                     gpios = <&qcom_pinmux 7 0>;
+++             };
+++                usb2 {
+++                     label = "usb_2:blue";
+++                     gpios = <&qcom_pinmux 8 0>;
+++             };
+++                wps {
+++                     label = "wps:blue";
+++                     gpios = <&qcom_pinmux 9 0>;
+++             };
+++                wan_blue {
+++                     label = "wan:blue";
+++                     gpios = <&qcom_pinmux 33 1>;
+++             };
+++                status {
+++                     label = "status:blue";
+++                     gpios = <&qcom_pinmux 53 0>;
+++                     default-state = "on";
+++             };
+++                ledgnr {
+++                     label = "ledgnr:blue";
+++                     gpios = <&qcom_pinmux 66 0>;
+++             };
+++        };
+++};
+++
+++&adm_dma {
+++     status = "ok";
+++};
diff --git a/target/linux/ipq806x/config-3.18 b/target/linux/ipq806x/config-3.18
new file mode 100644 (file)
index 0000000..40e3a75
--- /dev/null
@@ -0,0 +1,450 @@
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_APM_EMULATION is not set
+CONFIG_APQ_GCC_8084=y
+CONFIG_APQ_MMCC_8084=y
+CONFIG_AR8216_PHY=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MSM8960=y
+CONFIG_ARCH_MSM8974=y
+CONFIG_ARCH_MSM8X60=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_ARM=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
+# CONFIG_ARM_ATAG_DTB_COMPAT is not set
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_ARM_QCOM_CPUFREQ=y
+# CONFIG_ARM_SP805_WATCHDOG is not set
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_AT803X_PHY=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_BOUNCE=y
+CONFIG_BUILD_BIN2C=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_CLEANCACHE=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_QCOM=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_COMPACTION=y
+CONFIG_COREDUMP=y
+# CONFIG_CPUFREQ_DT is not set
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+# CONFIG_CPU_THERMAL is not set
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+# CONFIG_CRC32_SARWATE is not set
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_RNG2=y
+# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
+# CONFIG_CRYPTO_SHA512_ARM_NEON is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_CRYPTO_XZ=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_UART_8250 is not set
+# CONFIG_DEBUG_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_DWMAC_GENERIC is not set
+CONFIG_DWMAC_IPQ806X=y
+# CONFIG_DWMAC_LPC18XX is not set
+# CONFIG_DWMAC_MESON is not set
+# CONFIG_DWMAC_ROCKCHIP is not set
+# CONFIG_DWMAC_SOCFPGA is not set
+# CONFIG_DWMAC_STI is not set
+# CONFIG_DWMAC_SUNXI is not set
+# CONFIG_DW_DMAC_CORE is not set
+# CONFIG_DW_DMAC_PCI is not set
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_ETHERNET_PACKET_MANGLE=y
+CONFIG_FIXED_PHY=y
+CONFIG_FREEZER=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_CPUFREQ_KRAIT=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=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_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIO_MSM_V2 is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_HAVE_ARCH_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_HWSPINLOCK=y
+CONFIG_HWSPINLOCK_QCOM=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_HZ_FIXED=0
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_QUP=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_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_LEDS_REGULATOR is not set
+CONFIG_LIBFDT=y
+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_MFD_QCOM_RPM=y
+# CONFIG_MFD_SPMI_PMIC is not set
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MIGRATION=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MSM_GCC_8660=y
+CONFIG_MSM_GCC_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_IMPA7 is not set
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_QCOM=y
+CONFIG_MTD_QCOM_SMEM_PARTS=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPLIT_FIRMWARE=y
+CONFIG_MTD_SPLIT_FIT_FW=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEON=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_ADDRESS_PCI=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_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_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_MSI=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PHYLIB=y
+# CONFIG_PHY_QCOM_APQ8064_SATA is not set
+CONFIG_PHY_QCOM_IPQ806X_SATA=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_APQ8064=y
+# CONFIG_PINCTRL_APQ8084 is not set
+CONFIG_PINCTRL_IPQ8064=y
+CONFIG_PINCTRL_MSM=y
+# CONFIG_PINCTRL_MSM8960 is not set
+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_OPP=y
+CONFIG_PM_SLEEP=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_BRCMSTB is not set
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_GPIO_RESTART is not set
+# CONFIG_POWER_RESET_LTC2952 is not set
+CONFIG_POWER_RESET_MSM=y
+# CONFIG_POWER_RESET_SYSCON is not set
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_RCU=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_QCOM_ADM=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_WDT=y
+CONFIG_RAS=y
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_RCU_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_QCOM_RPM=y
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_CMOS is not set
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_QUP=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_STOP_MACHINE=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_STATS=y
+CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_XZ=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_UID16=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_OF=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_VFIO is not set
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL_TI is not set
+# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
+CONFIG_XPS=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch
new file mode 100644 (file)
index 0000000..e110bf7
--- /dev/null
@@ -0,0 +1,522 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: spi: qup: Add DMA capabilities
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 4432401
+Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
+To: Mark Brown <broonie@kernel.org>
+Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
+       Daniel Sneddon <dsneddon@codeaurora.org>,
+       Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+       "Ivan T. Ivanov" <iivanov@mm-sol.com>,
+       linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+       linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 16:06:21 -0500
+
+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 than the length of a block.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+.../devicetree/bindings/spi/qcom,spi-qup.txt       |   10 +
+ drivers/spi/spi-qup.c                              |  361 ++++++++++++++++++--
+ 2 files changed, 350 insertions(+), 21 deletions(-)
+
+--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -27,6 +27,11 @@ Optional properties:
+ - spi-max-frequency: Specifies maximum SPI clock frequency,
+                      Units - Hz. Definition as per
+                      Documentation/devicetree/bindings/spi/spi-bus.txt
++- dmas :             Two DMA channel specifiers following the convention outlined
++                     in bindings/dma/dma.txt
++- dma-names:         Names for the dma channels, if present. There must be at
++                     least one channel named "tx" for transmit and named "rx" for
++                     receive.
+ - 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
+@@ -51,6 +56,10 @@ Example:
+               clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
+               clock-names = "core", "iface";
++              dmas = <&blsp2_bam 2>,
++                      <&blsp2_bam 3>;
++              dma-names = "rx", "tx";
++
+               pinctrl-names = "default";
+               pinctrl-0 = <&spi8_default>;
+--- 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,17 @@ 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;
++      dma_addr_t              rx_dma;
++      dma_addr_t              tx_dma;
++      void                    *dummy;
++      atomic_t                dma_outstanding;
+ };
+@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp
+       }
+ }
++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 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i
+               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 +571,8 @@ static int spi_qup_io_config(struct spi_
+       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 +601,11 @@ static int spi_qup_io_config(struct spi_
+       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))
++              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 +613,31 @@ static int spi_qup_io_config(struct spi_
+               /* must be zero for FIFO */
+               writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+               writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-      } else {
++              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,6 +670,14 @@ static int spi_qup_io_config(struct spi_
+       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 */
+@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s
+       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 (!wait_for_completion_timeout(&controller->done, timeout))
++                      ret = -ETIMEDOUT;
++      }
+ exit:
+       spi_qup_set_state(controller, QUP_STATE_RESET);
+       spin_lock_irqsave(&controller->lock, flags);
+@@ -554,6 +817,7 @@ static int spi_qup_probe(struct platform
+       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);
+@@ -619,6 +883,56 @@ static int spi_qup_probe(struct platform
+                       QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
+                       base + QUP_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;
++              }
++      }
++
++
+       writel_relaxed(0, base + SPI_CONFIG);
+       writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
+@@ -731,6 +1045,11 @@ static int spi_qup_remove(struct platfor
+       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);
diff --git a/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch
new file mode 100644 (file)
index 0000000..62ee5b4
--- /dev/null
@@ -0,0 +1,376 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3] spi: qup: Fix incorrect block transfers
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 5007321
+Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org>
+To: Mark Brown <broonie@kernel.org>
+Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org,
+       "Ivan T. Ivanov" <iivanov@mm-sol.com>,
+       Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+       Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>
+Date: Tue, 30 Sep 2014 16:21:28 -0500
+
+This patch fixes a number of errors with the QUP block transfer mode.  Errors
+manifested themselves as input underruns, output overruns, and timed out
+transactions.
+
+The block mode does not require the priming that occurs in FIFO mode.  At the
+moment that the QUP is placed into the RUN state, the QUP will immediately raise
+an interrupt if the request is a write.  Therefore, there is no need to prime
+the pump.
+
+In addition, the block transfers require that whole blocks of data are
+read/written at a time.  The last block of data that completes a transaction may
+contain less than a full blocks worth of data.
+
+Each block of data results in an input/output service interrupt accompanied with
+a input/output block flag set.  Additional block reads/writes require clearing
+of the service flag.  It is ok to check for additional blocks of data in the
+ISR, but you have to ack every block you transfer.  Imbalanced acks result in
+early return from complete transactions with pending interrupts that still have
+to be ack'd.  The next transaction can be affected by these interrupts.
+Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set.
+
+Changes from v2:
+- Added in additional completion check so that transaction done is not
+  prematurely signaled.
+- Fixed various review comments.
+
+Changes from v1:
+- Split out read/write block function.
+- Removed extraneous checks for transfer length
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+drivers/spi/spi-qup.c |  201 ++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 148 insertions(+), 53 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -82,6 +82,8 @@
+ #define QUP_IO_M_MODE_BAM             3
+ /* QUP_OPERATIONAL fields */
++#define QUP_OP_IN_BLOCK_READ_REQ      BIT(13)
++#define QUP_OP_OUT_BLOCK_WRITE_REQ    BIT(12)
+ #define QUP_OP_MAX_INPUT_DONE_FLAG    BIT(11)
+ #define QUP_OP_MAX_OUTPUT_DONE_FLAG   BIT(10)
+ #define QUP_OP_IN_SERVICE_FLAG                BIT(9)
+@@ -147,6 +149,7 @@ struct spi_qup {
+       int                     tx_bytes;
+       int                     rx_bytes;
+       int                     qup_v1;
++      int                     mode;
+       int                     use_dma;
+@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_
+       return 0;
+ }
+-
+-static void spi_qup_fifo_read(struct spi_qup *controller,
+-                          struct spi_transfer *xfer)
++static void spi_qup_fill_read_buffer(struct spi_qup *controller,
++      struct spi_transfer *xfer, u32 data)
+ {
+       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;
+-              }
++      int idx, shift;
+-              for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++      if (rx_buf)
++              for (idx = 0; idx < controller->w_size; idx++) {
+                       /*
+                        * The data format depends on bytes per SPI word:
+                        *  4 bytes: 0x12345678
+@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi
+                        *  1 byte : 0x00000012
+                        */
+                       shift = BITS_PER_BYTE;
+-                      shift *= (w_size - idx - 1);
+-                      rx_buf[controller->rx_bytes] = word >> shift;
++                      shift *= (controller->w_size - idx - 1);
++                      rx_buf[controller->rx_bytes + idx] = data >> shift;
++              }
++
++      controller->rx_bytes += controller->w_size;
++}
++
++static void spi_qup_prepare_write_data(struct spi_qup *controller,
++      struct spi_transfer *xfer, u32 *data)
++{
++      const u8 *tx_buf = xfer->tx_buf;
++      u32 val;
++      int idx;
++
++      *data = 0;
++
++      if (tx_buf)
++              for (idx = 0; idx < controller->w_size; idx++) {
++                      val = tx_buf[controller->tx_bytes + idx];
++                      *data |= val << (BITS_PER_BYTE * (3 - idx));
+               }
++
++      controller->tx_bytes += controller->w_size;
++}
++
++static void spi_qup_fifo_read(struct spi_qup *controller,
++                          struct spi_transfer *xfer)
++{
++      u32 data;
++
++      /* clear service request */
++      writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++                      controller->base + QUP_OPERATIONAL);
++
++      while (controller->rx_bytes < xfer->len) {
++              if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++                  QUP_OP_IN_FIFO_NOT_EMPTY))
++                      break;
++
++              data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
++
++              spi_qup_fill_read_buffer(controller, xfer, data);
+       }
+ }
+ static void spi_qup_fifo_write(struct spi_qup *controller,
+-                          struct spi_transfer *xfer)
++      struct spi_transfer *xfer)
+ {
+-      const u8 *tx_buf = xfer->tx_buf;
+-      u32 word, state, data;
+-      int idx, w_size;
++      u32 data;
+-      w_size = controller->w_size;
++      /* clear service request */
++      writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++              controller->base + QUP_OPERATIONAL);
+       while (controller->tx_bytes < xfer->len) {
+-              state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+-              if (state & QUP_OP_OUT_FIFO_FULL)
++              if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
++                              QUP_OP_OUT_FIFO_FULL)
+                       break;
+-              word = 0;
+-              for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
++              spi_qup_prepare_write_data(controller, xfer, &data);
++              writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO);
+-                      if (!tx_buf) {
+-                              controller->tx_bytes += w_size;
+-                              break;
+-                      }
++      }
++}
+-                      data = tx_buf[controller->tx_bytes];
+-                      word |= data << (BITS_PER_BYTE * (3 - idx));
+-              }
++static void spi_qup_block_read(struct spi_qup *controller,
++      struct spi_transfer *xfer)
++{
++      u32 data;
++      u32 reads_per_blk = controller->in_blk_sz >> 2;
++      u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size;
++      int i;
++
++      do {
++              /* ACK by clearing service flag */
++              writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++                      controller->base + QUP_OPERATIONAL);
++
++              /* transfer up to a block size of data in a single pass */
++              for (i = 0; num_words && i < reads_per_blk; i++, num_words--) {
++
++                      /* read data and fill up rx buffer */
++                      data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
++                      spi_qup_fill_read_buffer(controller, xfer, data);
++              }
++
++              /* check to see if next block is ready */
++              if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++                      QUP_OP_IN_BLOCK_READ_REQ))
++                      break;
+-              writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
+-      }
++      } while (num_words);
++
++      /*
++       * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
++       * reads, it has to be cleared again at the very end
++       */
++      if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
++              QUP_OP_MAX_INPUT_DONE_FLAG)
++              writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++                      controller->base + QUP_OPERATIONAL);
++
++}
++
++static void spi_qup_block_write(struct spi_qup *controller,
++      struct spi_transfer *xfer)
++{
++      u32 data;
++      u32 writes_per_blk = controller->out_blk_sz >> 2;
++      u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size;
++      int i;
++
++      do {
++              /* ACK by clearing service flag */
++              writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++                      controller->base + QUP_OPERATIONAL);
++
++              /* transfer up to a block size of data in a single pass */
++              for (i = 0; num_words && i < writes_per_blk; i++, num_words--) {
++
++                      /* swizzle the bytes for output and write out */
++                      spi_qup_prepare_write_data(controller, xfer, &data);
++                      writel_relaxed(data,
++                              controller->base + QUP_OUTPUT_FIFO);
++              }
++
++              /* check to see if next block is ready */
++              if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
++                      QUP_OP_OUT_BLOCK_WRITE_REQ))
++                      break;
++
++      } while (num_words);
+ }
+ static void qup_dma_callback(void *data)
+@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i
+       writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
+       writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
+-      writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+       if (!xfer) {
++              writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+               dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
+                                   qup_err, spi_err, opflags);
+               return IRQ_HANDLED;
+@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i
+       }
+       if (!controller->use_dma) {
+-              if (opflags & QUP_OP_IN_SERVICE_FLAG)
+-                      spi_qup_fifo_read(controller, xfer);
++              if (opflags & QUP_OP_IN_SERVICE_FLAG) {
++                      if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
++                              spi_qup_block_read(controller, xfer);
++                      else
++                              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) {
++                      if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ)
++                              spi_qup_block_write(controller, xfer);
++                      else
++                              spi_qup_fifo_write(controller, xfer);
++              }
+       }
+       spin_lock_irqsave(&controller->lock, flags);
+@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i
+       controller->xfer = xfer;
+       spin_unlock_irqrestore(&controller->lock, flags);
+-      if (controller->rx_bytes == xfer->len || error)
++      if ((controller->rx_bytes == xfer->len &&
++              (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
+               complete(&controller->done);
+       return IRQ_HANDLED;
+@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+ 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;
++      u32 config, iomode;
+       int ret, n_words, w_size;
+       size_t dma_align = dma_get_cache_alignment();
+       u32 dma_available = 0;
+@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_
+               dma_available = 1;
+       if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+-              mode = QUP_IO_M_MODE_FIFO;
++              controller->mode = QUP_IO_M_MODE_FIFO;
+               writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+               writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+               /* must be zero for FIFO */
+@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_
+               writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+               controller->use_dma = 0;
+       } else if (!dma_available) {
+-              mode = QUP_IO_M_MODE_BLOCK;
++              controller->mode = QUP_IO_M_MODE_BLOCK;
+               writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+               writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+               /* must be zero for BLOCK and BAM */
+@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_
+               writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+               controller->use_dma = 0;
+       } else {
+-              mode = QUP_IO_M_MODE_DMOV;
++              controller->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;
+@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_
+       else
+               iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+-      iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+-      iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++      iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++      iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+       writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s
+                       goto exit;
+               }
+-              spi_qup_fifo_write(controller, xfer);
++              if (controller->mode == QUP_IO_M_MODE_FIFO)
++                      spi_qup_fifo_write(controller, xfer);
+               if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+                       dev_warn(controller->dev, "cannot set EXECUTE state\n");
+@@ -741,6 +836,7 @@ exit:
+       if (!ret)
+               ret = controller->error;
+       spin_unlock_irqrestore(&controller->lock, flags);
++
+       return ret;
+ }
diff --git a/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch
new file mode 100644 (file)
index 0000000..7052227
--- /dev/null
@@ -0,0 +1,56 @@
+From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001
+From: Andy Gross <agross@codeaurora.org>
+Date: Thu, 15 Jan 2015 17:56:02 -0800
+Subject: [PATCH] spi: qup: Ensure done detection
+
+This patch fixes an issue where a SPI transaction has completed, but the done
+condition is missed.  This occurs because at the time of interrupt the
+MAX_INPUT_DONE_FLAG is not asserted.  However, in the process of reading blocks
+of data from the FIFO, the last portion of data comes in.
+
+The opflags read at the beginning of the irq handler no longer matches the
+current opflag state.  To get around this condition, the block read function
+should update the opflags so that done detection is correct after the return.
+
+Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp
+ }
+ static void spi_qup_block_read(struct spi_qup *controller,
+-      struct spi_transfer *xfer)
++      struct spi_transfer *xfer, u32 *opflags)
+ {
+       u32 data;
+       u32 reads_per_blk = controller->in_blk_sz >> 2;
+@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp
+       /*
+        * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
+-       * reads, it has to be cleared again at the very end
++       * reads, it has to be cleared again at the very end.  However, be sure
++       * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
++       * present and this is used to determine if transaction is complete
+        */
+-      if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
+-              QUP_OP_MAX_INPUT_DONE_FLAG)
++      *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++      if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
+               writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
+                       controller->base + QUP_OPERATIONAL);
+@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i
+       if (!controller->use_dma) {
+               if (opflags & QUP_OP_IN_SERVICE_FLAG) {
+                       if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
+-                              spi_qup_block_read(controller, xfer);
++                              spi_qup_block_read(controller, xfer, &opflags);
+                       else
+                               spi_qup_fifo_read(controller, xfer);
+               }
diff --git a/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch
new file mode 100644 (file)
index 0000000..68489a8
--- /dev/null
@@ -0,0 +1,67 @@
+From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:19:30 -0800
+Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding
+
+MSM watchdog configuration happens in the same register block as the
+timer, so we'll use the same binding as the existing timer.
+
+The qcom-wdt will now be probed when devicetree has an entry compatible
+with "qcom,kpss-timer" or "qcom-scss-timer".
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/watchdog/qcom-wdt.c
++++ b/drivers/watchdog/qcom-wdt.c
+@@ -20,9 +20,9 @@
+ #include <linux/reboot.h>
+ #include <linux/watchdog.h>
+-#define WDT_RST               0x0
+-#define WDT_EN                0x8
+-#define WDT_BITE_TIME 0x24
++#define WDT_RST               0x38
++#define WDT_EN                0x40
++#define WDT_BITE_TIME 0x5C
+ struct qcom_wdt {
+       struct watchdog_device  wdd;
+@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platfor
+ {
+       struct qcom_wdt *wdt;
+       struct resource *res;
++      struct device_node *np = pdev->dev.of_node;
++      u32 percpu_offset;
+       int ret;
+       wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platfor
+               return -ENOMEM;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++      /* We use CPU0's DGT for the watchdog */
++      if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
++              percpu_offset = 0;
++
++      res->start += percpu_offset;
++      res->end += percpu_offset;
++
+       wdt->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(wdt->base))
+               return PTR_ERR(wdt->base);
+@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platfo
+ }
+ static const struct of_device_id qcom_wdt_of_table[] = {
+-      { .compatible = "qcom,kpss-wdt-msm8960", },
+-      { .compatible = "qcom,kpss-wdt-apq8064", },
+-      { .compatible = "qcom,kpss-wdt-ipq8064", },
++      { .compatible = "qcom,kpss-timer" },
++      { .compatible = "qcom,scss-timer" },
+       { },
+ };
+ MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
diff --git a/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch
new file mode 100644 (file)
index 0000000..ae96776
--- /dev/null
@@ -0,0 +1,48 @@
+From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:27:39 -0800
+Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064
+
+Add the watchdog related entries to the Krait Processor Sub-system
+(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock
+description of SLEEP_CLK, which will do for now.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -60,6 +60,14 @@
+               };
+       };
++      clocks {
++              sleep_clk: sleep_clk {
++                      compatible = "fixed-clock";
++                      clock-frequency = <32768>;
++                      #clock-cells = <0>;
++              };
++      };
++
+       soc: soc {
+               #address-cells = <1>;
+               #size-cells = <1>;
+@@ -89,10 +97,14 @@
+                       compatible = "qcom,kpss-timer", "qcom,msm-timer";
+                       interrupts = <1 1 0x301>,
+                                    <1 2 0x301>,
+-                                   <1 3 0x301>;
++                                   <1 3 0x301>,
++                                   <1 4 0x301>,
++                                   <1 5 0x301>;
+                       reg = <0x0200a000 0x100>;
+                       clock-frequency = <25000000>,
+                                         <32768>;
++                      clocks = <&sleep_clk>;
++                      clock-names = "sleep";
+                       cpu-offset = <0x80000>;
+               };
diff --git a/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch
new file mode 100644 (file)
index 0000000..6876768
--- /dev/null
@@ -0,0 +1,50 @@
+From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 19 Feb 2015 20:36:27 -0800
+Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc
+
+The watchdog has been reworked to use the same DT node as the timer.
+This change is updating the device tree doc accordingly.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+--- a/Documentation/devicetree/bindings/arm/msm/timer.txt
++++ b/Documentation/devicetree/bindings/arm/msm/timer.txt
+@@ -9,11 +9,17 @@ Properties:
+                "qcom,scss-timer" - scorpion subsystem
+ - interrupts : Interrupts for the the debug timer, the first general purpose
+-               timer, and optionally a second general purpose timer in that
+-               order.
++               timer, and optionally a second general purpose timer, and
++               optionally as well, 2 watchdog interrupts, in that order.
+ - reg : Specifies the base address of the timer registers.
++- clocks: Reference to the parent clocks, one per output clock. The parents
++          must appear in the same order as the clock names.
++
++- clock-names: The name of the clocks as free-form strings. They should be in
++               the same order as the clocks.
++
+ - clock-frequency : The frequency of the debug timer and the general purpose
+                     timer(s) in Hz in that order.
+@@ -29,9 +35,13 @@ Example:
+                compatible = "qcom,scss-timer", "qcom,msm-timer";
+                interrupts = <1 1 0x301>,
+                             <1 2 0x301>,
+-                            <1 3 0x301>;
++                            <1 3 0x301>,
++                            <1 4 0x301>,
++                            <1 5 0x301>;
+                reg = <0x0200a000 0x100>;
+                clock-frequency = <19200000>,
+                                  <32768>;
++               clocks = <&sleep_clk>;
++               clock-names = "sleep";
+                cpu-offset = <0x40000>;
+        };
diff --git a/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch
new file mode 100644 (file)
index 0000000..a61481e
--- /dev/null
@@ -0,0 +1,46 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -14,6 +14,14 @@
+               };
+       };
++      alias {
++              serial0 = &uart4;
++      };
++
++      chosen {
++              linux,stdout-path = "serial0:115200n8";
++      };
++
+       soc {
+               pinmux@800000 {
+                       i2c4_pins: i2c4_pinmux {
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -140,7 +140,7 @@
+                       ranges;
+                       status = "disabled";
+-                      serial@12490000 {
++                      uart2: serial@12490000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x12490000 0x1000>,
+                                     <0x12480000 0x1000>;
+@@ -175,7 +175,7 @@
+                       ranges;
+                       status = "disabled";
+-                      serial@16340000 {
++                      uart4: serial@16340000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x16340000 0x1000>,
+                                     <0x16300000 0x1000>;
+@@ -209,7 +209,7 @@
+                       ranges;
+                       status = "disabled";
+-                      serial@1a240000 {
++                      uart5: serial@1a240000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x1a240000 0x1000>,
+                                     <0x1a200000 0x1000>;
diff --git a/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch
new file mode 100644 (file)
index 0000000..bfdb30f
--- /dev/null
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -77,15 +77,7 @@
+                                       spi-max-frequency = <50000000>;
+                                       reg = <0>;
+-                                      partition@0 {
+-                                              label = "rootfs";
+-                                              reg = <0x0 0x1000000>;
+-                                      };
+-
+-                                      partition@1 {
+-                                              label = "scratch";
+-                                              reg = <0x1000000 0x1000000>;
+-                                      };
++                                      linux,part-probe = "qcom-smem";
+                               };
+                       };
+               };
diff --git a/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch b/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch
new file mode 100644 (file)
index 0000000..bd6ec1e
--- /dev/null
@@ -0,0 +1,160 @@
+From a32d6e7c8fca6371a2614924b89981bc912b6378 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 7 Apr 2015 19:58:58 -0700
+Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree
+
+Add basic DB149 (IPQ806x based platform) device-tree. It supports UART,
+SATA, USB2, USB3 and NOR flash.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/Makefile               |   1 +
+ arch/arm/boot/dts/qcom-ipq8064-db149.dts | 257 +++++++++++++++++++++++++++++++
+ 2 files changed, 258 insertions(+)
+ create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -360,6 +360,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
+       qcom-apq8084-ifc6540.dtb \
+       qcom-apq8084-mtp.dtb \
+       qcom-ipq8064-ap148.dtb \
++      qcom-ipq8064-db149.dtb \
+       qcom-msm8660-surf.dtb \
+       qcom-msm8960-cdp.dtb \
+       qcom-msm8974-sony-xperia-honami.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -0,0 +1,132 @@
++#include "qcom-ipq8064-v1.0.dtsi"
++
++/ {
++      model = "Qualcomm IPQ8064/DB149";
++      compatible = "qcom,ipq8064-db149", "qcom,ipq8064";
++
++      reserved-memory {
++              #address-cells = <1>;
++              #size-cells = <1>;
++              ranges;
++              rsvd@41200000 {
++                      reg = <0x41200000 0x300000>;
++                      no-map;
++              };
++      };
++
++      alias {
++              serial0 = &uart2;
++      };
++
++      chosen {
++              linux,stdout-path = "serial0:115200n8";
++      };
++
++      soc {
++              pinmux@800000 {
++                      i2c4_pins: i2c4_pinmux {
++                              pins = "gpio12", "gpio13";
++                              function = "gsbi4";
++                              bias-disable;
++                      };
++
++                      spi_pins: spi_pins {
++                              mux {
++                                      pins = "gpio18", "gpio19", "gpio21";
++                                      function = "gsbi5";
++                                      drive-strength = <10>;
++                                      bias-none;
++                              };
++                      };
++              };
++
++              gsbi2: gsbi@12480000 {
++                      qcom,mode = <GSBI_PROT_I2C_UART>;
++                      status = "ok";
++                      uart2: serial@12490000 {
++                              status = "ok";
++                      };
++              };
++
++              gsbi5: gsbi@1a200000 {
++                      qcom,mode = <GSBI_PROT_SPI>;
++                      status = "ok";
++
++                      spi4: spi@1a280000 {
++                              status = "ok";
++                              spi-max-frequency = <50000000>;
++
++                              pinctrl-0 = <&spi_pins>;
++                              pinctrl-names = "default";
++
++                              cs-gpios = <&qcom_pinmux 20 0>;
++
++                              flash: m25p80@0 {
++                                      compatible = "s25fl256s1";
++                                      #address-cells = <1>;
++                                      #size-cells = <1>;
++                                      spi-max-frequency = <50000000>;
++                                      reg = <0>;
++                                      m25p,fast-read;
++
++                                      partition@0 {
++                                              label = "lowlevel_init";
++                                              reg = <0x0 0x1b0000>;
++                                      };
++
++                                      partition@1 {
++                                              label = "u-boot";
++                                              reg = <0x1b0000 0x80000>;
++                                      };
++
++                                      partition@2 {
++                                              label = "u-boot-env";
++                                              reg = <0x230000 0x40000>;
++                                      };
++
++                                      partition@3 {
++                                              label = "caldata";
++                                              reg = <0x270000 0x40000>;
++                                      };
++
++                                      partition@4 {
++                                              label = "firmware";
++                                              reg = <0x2b0000 0x1d50000>;
++                                      };
++                              };
++                      };
++              };
++
++              sata-phy@1b400000 {
++                      status = "ok";
++              };
++
++              sata@29000000 {
++                      status = "ok";
++              };
++
++              phy@100f8800 {          /* USB3 port 1 HS phy */
++                      status = "ok";
++              };
++
++              phy@100f8830 {          /* USB3 port 1 SS phy */
++                      status = "ok";
++              };
++
++              phy@110f8800 {          /* USB3 port 0 HS phy */
++                      status = "ok";
++              };
++
++              phy@110f8830 {          /* USB3 port 0 SS phy */
++                      status = "ok";
++              };
++
++              usb30@0 {
++                      status = "ok";
++              };
++
++              usb30@1 {
++                      status = "ok";
++              };
++      };
++};
diff --git a/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch
new file mode 100644 (file)
index 0000000..319859b
--- /dev/null
@@ -0,0 +1,53 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -46,15 +46,12 @@
+                       serial@16340000 {
+                               status = "ok";
+                       };
+-
+-                      i2c4: i2c@16380000 {
+-                              status = "ok";
+-
+-                              clock-frequency = <200000>;
+-
+-                              pinctrl-0 = <&i2c4_pins>;
+-                              pinctrl-names = "default";
+-                      };
++                      /*
++                       * The i2c device on gsbi4 should not be enabled.
++                       * On ipq806x designs gsbi4 i2c is meant for exclusive
++                       * RPM usage. Turning this on in kernel manifests as
++                       * i2c failure for the RPM.
++                       */
+               };
+               gsbi5: gsbi@1a200000 {
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -794,7 +794,7 @@ static struct clk_rcg gsbi7_qup_src = {
+                       .parent_names = gcc_pxo_pll8,
+                       .num_parents = 2,
+                       .ops = &clk_rcg_ops,
+-                      .flags = CLK_SET_PARENT_GATE,
++                      .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
+               },
+       },
+ };
+@@ -810,7 +810,7 @@ static struct clk_branch gsbi7_qup_clk =
+                       .parent_names = (const char *[]){ "gsbi7_qup_src" },
+                       .num_parents = 1,
+                       .ops = &clk_branch_ops,
+-                      .flags = CLK_SET_RATE_PARENT,
++                      .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+               },
+       },
+ };
+@@ -858,7 +858,7 @@ static struct clk_branch gsbi4_h_clk = {
+               .hw.init = &(struct clk_init_data){
+                       .name = "gsbi4_h_clk",
+                       .ops = &clk_branch_ops,
+-                      .flags = CLK_IS_ROOT,
++                      .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
+               },
+       },
+ };
diff --git a/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch
new file mode 100644 (file)
index 0000000..f026ed9
--- /dev/null
@@ -0,0 +1,14 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -4,6 +4,11 @@
+       model = "Qualcomm IPQ8064/AP148";
+       compatible = "qcom,ipq8064-ap148", "qcom,ipq8064";
++      memory@0 {
++              reg = <0x42000000 0x1e000000>;
++              device_type = "memory";
++      };
++
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
diff --git a/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch b/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch
new file mode 100644 (file)
index 0000000..04f35b7
--- /dev/null
@@ -0,0 +1,167 @@
+From fb7737e949e31d8a71acee6bbb670f32dbd2a2c0 Mon Sep 17 00:00:00 2001
+From: Suman Anna <s-anna@ti.com>
+Date: Wed, 4 Mar 2015 20:01:14 -0600
+Subject: [PATCH] hwspinlock/core: add device tree support
+
+This patch adds a new OF-friendly API of_hwspin_lock_get_id()
+for hwspinlock clients to use/request locks from a hwspinlock
+device instantiated through a device-tree blob. This new API
+can be used by hwspinlock clients to get the id for a specific
+lock using the phandle + args specifier, so that it can be
+requested using the available hwspin_lock_request_specific()
+API.
+
+Signed-off-by: Suman Anna <s-anna@ti.com>
+Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+[small comment clarification]
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ Documentation/hwspinlock.txt         | 10 +++++
+ drivers/hwspinlock/hwspinlock_core.c | 79 ++++++++++++++++++++++++++++++++++++
+ include/linux/hwspinlock.h           |  7 ++++
+ 3 files changed, 96 insertions(+)
+
+--- a/Documentation/hwspinlock.txt
++++ b/Documentation/hwspinlock.txt
+@@ -48,6 +48,16 @@ independent, drivers.
+      ids for predefined purposes.
+      Should be called from a process context (might sleep).
++  int of_hwspin_lock_get_id(struct device_node *np, int index);
++   - retrieve the global lock id for an OF phandle-based specific lock.
++     This function provides a means for DT users of a hwspinlock module
++     to get the global lock id of a specific hwspinlock, so that it can
++     be requested using the normal hwspin_lock_request_specific() API.
++     The function returns a lock id number on success, -EPROBE_DEFER if
++     the hwspinlock device is not yet registered with the core, or other
++     error values.
++     Should be called from a process context (might sleep).
++
+   int hwspin_lock_free(struct hwspinlock *hwlock);
+    - free a previously-assigned hwspinlock; returns 0 on success, or an
+      appropriate error code on failure (e.g. -EINVAL if the hwspinlock
+--- a/drivers/hwspinlock/hwspinlock_core.c
++++ b/drivers/hwspinlock/hwspinlock_core.c
+@@ -27,6 +27,7 @@
+ #include <linux/hwspinlock.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/mutex.h>
++#include <linux/of.h>
+ #include "hwspinlock_internal.h"
+@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *
+ }
+ EXPORT_SYMBOL_GPL(__hwspin_unlock);
++/**
++ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
++ * @bank: the hwspinlock device bank
++ * @hwlock_spec: hwlock specifier as found in the device tree
++ *
++ * This is a simple translation function, suitable for hwspinlock platform
++ * drivers that only has a lock specifier length of 1.
++ *
++ * Returns a relative index of the lock within a specified bank on success,
++ * or -EINVAL on invalid specifier cell count.
++ */
++static inline int
++of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)
++{
++      if (WARN_ON(hwlock_spec->args_count != 1))
++              return -EINVAL;
++
++      return hwlock_spec->args[0];
++}
++
++/**
++ * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock
++ * @np: device node from which to request the specific hwlock
++ * @index: index of the hwlock in the list of values
++ *
++ * This function provides a means for DT users of the hwspinlock module to
++ * get the global lock id of a specific hwspinlock using the phandle of the
++ * hwspinlock device, so that it can be requested using the normal
++ * hwspin_lock_request_specific() API.
++ *
++ * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
++ * device is not yet registered, -EINVAL on invalid args specifier value or an
++ * appropriate error as returned from the OF parsing of the DT client node.
++ */
++int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++      struct of_phandle_args args;
++      struct hwspinlock *hwlock;
++      struct radix_tree_iter iter;
++      void **slot;
++      int id;
++      int ret;
++
++      ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,
++                                       &args);
++      if (ret)
++              return ret;
++
++      /* Find the hwspinlock device: we need its base_id */
++      ret = -EPROBE_DEFER;
++      rcu_read_lock();
++      radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {
++              hwlock = radix_tree_deref_slot(slot);
++              if (unlikely(!hwlock))
++                      continue;
++
++              if (hwlock->bank->dev->of_node == args.np) {
++                      ret = 0;
++                      break;
++              }
++      }
++      rcu_read_unlock();
++      if (ret < 0)
++              goto out;
++
++      id = of_hwspin_lock_simple_xlate(&args);
++      if (id < 0 || id >= hwlock->bank->num_locks) {
++              ret = -EINVAL;
++              goto out;
++      }
++      id += hwlock->bank->base_id;
++
++out:
++      of_node_put(args.np);
++      return ret ? ret : id;
++}
++EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);
++
+ static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
+ {
+       struct hwspinlock *tmp;
+--- a/include/linux/hwspinlock.h
++++ b/include/linux/hwspinlock.h
+@@ -26,6 +26,7 @@
+ #define HWLOCK_IRQ    0x02    /* Disable interrupts, don't save state */
+ struct device;
++struct device_node;
+ struct hwspinlock;
+ struct hwspinlock_device;
+ struct hwspinlock_ops;
+@@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspin
+ struct hwspinlock *hwspin_lock_request(void);
+ struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
+ int hwspin_lock_free(struct hwspinlock *hwlock);
++int of_hwspin_lock_get_id(struct device_node *np, int index);
+ int hwspin_lock_get_id(struct hwspinlock *hwlock);
+ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
+                                                       unsigned long *);
+@@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock *
+ {
+ }
++static inline int of_hwspin_lock_get_id(struct device_node *np, int index)
++{
++      return 0;
++}
++
+ static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)
+ {
+       return 0;
diff --git a/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch b/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch
new file mode 100644 (file)
index 0000000..581b199
--- /dev/null
@@ -0,0 +1,234 @@
+From 19a0f61224d2d91860fa8291ab63cb104ee86bdd Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Tue, 24 Mar 2015 10:11:05 -0700
+Subject: [PATCH] hwspinlock: qcom: Add support for Qualcomm HW Mutex block
+
+Add driver for Qualcomm Hardware Mutex block found in many Qualcomm
+SoCs.
+
+Based on initial effort by Kumar Gala <galak@codeaurora.org>
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Reviewed-by: Jeffrey Hugo <jhugo@codeaurora.org>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/Kconfig           |  12 +++
+ drivers/hwspinlock/Makefile          |   1 +
+ drivers/hwspinlock/qcom_hwspinlock.c | 181 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 194 insertions(+)
+ create mode 100644 drivers/hwspinlock/qcom_hwspinlock.c
+
+--- a/drivers/hwspinlock/Kconfig
++++ b/drivers/hwspinlock/Kconfig
+@@ -18,6 +18,18 @@ config HWSPINLOCK_OMAP
+         If unsure, say N.
++config HWSPINLOCK_QCOM
++      tristate "Qualcomm Hardware Spinlock device"
++      depends on ARCH_QCOM
++      select HWSPINLOCK
++      select MFD_SYSCON
++      help
++        Say y here to support the Qualcomm Hardware Mutex functionality, which
++        provides a synchronisation mechanism for the various processors on
++        the SoC.
++
++        If unsure, say N.
++
+ config HSEM_U8500
+       tristate "STE Hardware Semaphore functionality"
+       depends on ARCH_U8500
+--- a/drivers/hwspinlock/Makefile
++++ b/drivers/hwspinlock/Makefile
+@@ -4,4 +4,5 @@
+ obj-$(CONFIG_HWSPINLOCK)              += hwspinlock_core.o
+ obj-$(CONFIG_HWSPINLOCK_OMAP)         += omap_hwspinlock.o
++obj-$(CONFIG_HWSPINLOCK_QCOM)         += qcom_hwspinlock.o
+ obj-$(CONFIG_HSEM_U8500)              += u8500_hsem.o
+--- /dev/null
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -0,0 +1,181 @@
++/*
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2015, Sony Mobile Communications AB
++ *
++ * 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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++
++#include "hwspinlock_internal.h"
++
++#define QCOM_MUTEX_APPS_PROC_ID       1
++#define QCOM_MUTEX_NUM_LOCKS  32
++
++static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
++{
++      struct regmap_field *field = lock->priv;
++      u32 lock_owner;
++      int ret;
++
++      ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
++      if (ret)
++              return ret;
++
++      ret = regmap_field_read(field, &lock_owner);
++      if (ret)
++              return ret;
++
++      return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
++}
++
++static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
++{
++      struct regmap_field *field = lock->priv;
++      u32 lock_owner;
++      int ret;
++
++      ret = regmap_field_read(field, &lock_owner);
++      if (ret) {
++              pr_err("%s: unable to query spinlock owner\n", __func__);
++              return;
++      }
++
++      if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
++              pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
++                              __func__, lock_owner);
++      }
++
++      ret = regmap_field_write(field, 0);
++      if (ret)
++              pr_err("%s: failed to unlock spinlock\n", __func__);
++}
++
++static const struct hwspinlock_ops qcom_hwspinlock_ops = {
++      .trylock        = qcom_hwspinlock_trylock,
++      .unlock         = qcom_hwspinlock_unlock,
++};
++
++static const struct of_device_id qcom_hwspinlock_of_match[] = {
++      { .compatible = "qcom,sfpb-mutex" },
++      { .compatible = "qcom,tcsr-mutex" },
++      { }
++};
++MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
++
++static int qcom_hwspinlock_probe(struct platform_device *pdev)
++{
++      struct hwspinlock_device *bank;
++      struct device_node *syscon;
++      struct reg_field field;
++      struct regmap *regmap;
++      size_t array_size;
++      u32 stride;
++      u32 base;
++      int ret;
++      int i;
++
++      syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
++      if (!syscon) {
++              dev_err(&pdev->dev, "no syscon property\n");
++              return -ENODEV;
++      }
++
++      regmap = syscon_node_to_regmap(syscon);
++      if (IS_ERR(regmap))
++              return PTR_ERR(regmap);
++
++      ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "no offset in syscon\n");
++              return -EINVAL;
++      }
++
++      ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "no stride syscon\n");
++              return -EINVAL;
++      }
++
++      array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
++      bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
++      if (!bank)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, bank);
++
++      for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
++              field.reg = base + i * stride;
++              field.lsb = 0;
++              field.msb = 32;
++
++              bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
++                                                           regmap, field);
++      }
++
++      pm_runtime_enable(&pdev->dev);
++
++      ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops,
++                                 0, QCOM_MUTEX_NUM_LOCKS);
++      if (ret)
++              pm_runtime_disable(&pdev->dev);
++
++      return ret;
++}
++
++static int qcom_hwspinlock_remove(struct platform_device *pdev)
++{
++      struct hwspinlock_device *bank = platform_get_drvdata(pdev);
++      int ret;
++
++      ret = hwspin_lock_unregister(bank);
++      if (ret) {
++              dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
++              return ret;
++      }
++
++      pm_runtime_disable(&pdev->dev);
++
++      return 0;
++}
++
++static struct platform_driver qcom_hwspinlock_driver = {
++      .probe          = qcom_hwspinlock_probe,
++      .remove         = qcom_hwspinlock_remove,
++      .driver         = {
++              .name   = "qcom_hwspinlock",
++              .of_match_table = qcom_hwspinlock_of_match,
++      },
++};
++
++static int __init qcom_hwspinlock_init(void)
++{
++      return platform_driver_register(&qcom_hwspinlock_driver);
++}
++/* board init code might need to reserve hwspinlocks for predefined purposes */
++postcore_initcall(qcom_hwspinlock_init);
++
++static void __exit qcom_hwspinlock_exit(void)
++{
++      platform_driver_unregister(&qcom_hwspinlock_driver);
++}
++module_exit(qcom_hwspinlock_exit);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");
diff --git a/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch b/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch
new file mode 100644 (file)
index 0000000..70d5a58
--- /dev/null
@@ -0,0 +1,26 @@
+From bd5717a4632cdecafe82d03de7dcb3b1876e2828 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Fri, 26 Jun 2015 14:47:21 -0700
+Subject: [PATCH] hwspinlock: qcom: Correct msb in regmap_field
+
+msb of the regmap_field was mistakenly given the value 32, to set all bits
+in the regmap update mask; although incorrect this worked until 921cc294,
+where the mask calculation was corrected.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
+---
+ drivers/hwspinlock/qcom_hwspinlock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwspinlock/qcom_hwspinlock.c
++++ b/drivers/hwspinlock/qcom_hwspinlock.c
+@@ -123,7 +123,7 @@ static int qcom_hwspinlock_probe(struct
+       for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
+               field.reg = base + i * stride;
+               field.lsb = 0;
+-              field.msb = 32;
++              field.msb = 31;
+               bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
+                                                            regmap, field);
diff --git a/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch
new file mode 100644 (file)
index 0000000..4a35325
--- /dev/null
@@ -0,0 +1,34 @@
+From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:45:26 -0700
+Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts
+
+Add one new node to the ipq806x.dtsi file to declare & register the
+hardware spinlock devices. This mechanism is required to be used by
+other drivers such as SMEM.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -291,5 +291,17 @@
+                       #clock-cells = <1>;
+                       #reset-cells = <1>;
+               };
++
++              sfpb_mutex_block: syscon@1200600 {
++                      compatible = "syscon";
++                      reg = <0x01200600 0x100>;
++              };
+       };
++
++      sfpb_mutex: sfpb-mutex {
++              compatible = "qcom,sfpb-mutex";
++              syscon = <&sfpb_mutex_block 4 4>;
++
++              #hwlock-cells = <1>;
++      };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch
new file mode 100644 (file)
index 0000000..d22db22
--- /dev/null
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202201
+Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com>
+To: Rob Herring <robh+dt@kernel.org>, Pawel Moll <pawel.moll@arm.com>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Ian Campbell <ijc+devicetree@hellion.org.uk>,
+       Kumar Gala <galak@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>, 
+       Andry Gross <agross@codeaurora.org>
+Cc: <devicetree@vger.kernel.org>,
+       linux-arm-msm <linux-arm-msm@vger.kernel.org>,
+       <linux-kernel@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:57 -0700
+
+Add device tree binding documentation for the Qualcom Shared Memory
+manager.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+
+---
+Changes since v1:
+- None
+
+ .../devicetree/bindings/soc/qcom/qcom,smem.txt     | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt
+@@ -0,0 +1,49 @@
++Qualcomm Shared Memory binding
++
++This binding describes the Qualcomm Shared Memory, used to share data between
++various subsystems and OSes in Qualcomm platforms.
++
++- compatible:
++      Usage: required
++      Value type: <stringlist>
++      Definition: must be:
++                  "qcom,smem"
++
++- memory-region:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: handle to memory reservation for main smem memory region.
++
++- reg:
++      Usage: optional
++      Value type: <prop-encoded-array>
++      Definition: base address and size pair for any additional memory areas
++                  of the shared memory.
++
++- hwspinlocks:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: reference to a hwspinlock used to protect allocations from
++                  the shared memory
++
++= EXAMPLE
++
++      reserved-memory {
++              #address-cells = <1>;
++              #size-cells = <1>;
++              ranges;
++
++              smem_region: smem@fa00000 {
++                      reg = <0xfa00000 0x200000>;
++                      no-map;
++              };
++      };
++
++      smem@fa00000 {
++              compatible = "qcom,smem";
++
++              memory-region = <&smem_region>;
++              reg = <0xfc428000 0x4000>;
++
++              hwlocks = <&tcsr_mutex 3>;
++      };
diff --git a/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch
new file mode 100644 (file)
index 0000000..c8cff1a
--- /dev/null
@@ -0,0 +1,841 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+X-Patchwork-Id: 6202211
+Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com>
+To: Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+       David Brown <davidb@codeaurora.org>, Jeffrey Hugo <jhugo@codeaurora.org>
+Cc: <linux-kernel@vger.kernel.org>, <linux-arm-msm@vger.kernel.org>,
+       <linux-soc@vger.kernel.org>
+Date: Sat, 11 Apr 2015 16:32:58 -0700
+
+The Shared Memory Manager driver implements an interface for allocating
+and accessing items in the memory area shared among all of the
+processors in a Qualcomm platform.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Reviewed-by: Andy Gross <agross@codeaurora.org>
+Tested-by: Andy Gross <agross@codeaurora.org>
+
+---
+Changes since v1:
+- ioremapping the regions nocache
+- improved documentation of the two regions of partitions
+- corrected free space check in private allocator
+
+ drivers/soc/qcom/Kconfig      |   7 +
+ drivers/soc/qcom/Makefile     |   1 +
+ drivers/soc/qcom/smem.c       | 768 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/soc/qcom/smem.h |  14 +
+ 4 files changed, 790 insertions(+)
+ create mode 100644 drivers/soc/qcom/smem.c
+ create mode 100644 include/linux/soc/qcom/smem.h
+
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -9,3 +9,10 @@ config QCOM_GSBI
+           functions for connecting the underlying serial UART, SPI, and I2C
+           devices to the output pins.
++config QCOM_SMEM
++      tristate "Qualcomm Shared Memory Manager (SMEM)"
++      depends on ARCH_QCOM
++      help
++        Say y here to enable support for the Qualcomm Shared Memory Manager.
++        The driver provides an interface to items in a heap shared among all
++        processors in a Qualcomm platform.
+--- a/drivers/soc/qcom/Makefile
++++ b/drivers/soc/qcom/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_QCOM_GSBI)       +=      qcom_gsbi.o
++obj-$(CONFIG_QCOM_SMEM) +=    smem.o
+--- /dev/null
++++ b/drivers/soc/qcom/smem.c
+@@ -0,0 +1,768 @@
++/*
++ * Copyright (c) 2015, Sony Mobile Communications AB.
++ * 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/hwspinlock.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/soc/qcom/smem.h>
++
++/*
++ * The Qualcomm shared memory system is a allocate only heap structure that
++ * consists of one of more memory areas that can be accessed by the processors
++ * in the SoC.
++ *
++ * All systems contains a global heap, accessible by all processors in the SoC,
++ * with a table of contents data structure (@smem_header) at the beginning of
++ * the main shared memory block.
++ *
++ * The global header contains metadata for allocations as well as a fixed list
++ * of 512 entries (@smem_global_entry) that can be initialized to reference
++ * parts of the shared memory space.
++ *
++ *
++ * In addition to this global heap a set of "private" heaps can be set up at
++ * boot time with access restrictions so that only certain processor pairs can
++ * access the data.
++ *
++ * These partitions are referenced from an optional partition table
++ * (@smem_ptable), that is found 4kB from the end of the main smem region. The
++ * partition table entries (@smem_ptable_entry) lists the involved processors
++ * (or hosts) and their location in the main shared memory region.
++ *
++ * Each partition starts with a header (@smem_partition_header) that identifies
++ * the partition and holds properties for the two internal memory regions. The
++ * two regions are cached and non-cached memory respectively. Each region
++ * contain a link list of allocation headers (@smem_private_entry) followed by
++ * their data.
++ *
++ * Items in the non-cached region are allocated from the start of the partition
++ * while items in the cached region are allocated from the end. The free area
++ * is hence the region between the cached and non-cached offsets.
++ *
++ *
++ * To synchronize allocations in the shared memory heaps a remote spinlock must
++ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
++ * platforms.
++ *
++ */
++
++/**
++  * struct smem_proc_comm - proc_comm communication struct (legacy)
++  * @command: current command to be executed
++  * @status:  status of the currently requested command
++  * @params:  parameters to the command
++  */
++struct smem_proc_comm {
++      u32 command;
++      u32 status;
++      u32 params[2];
++};
++
++/**
++ * struct smem_global_entry - entry to reference smem items on the heap
++ * @allocated:        boolean to indicate if this entry is used
++ * @offset:   offset to the allocated space
++ * @size:     size of the allocated space, 8 byte aligned
++ * @aux_base: base address for the memory region used by this unit, or 0 for
++ *            the default region. bits 0,1 are reserved
++ */
++struct smem_global_entry {
++      u32 allocated;
++      u32 offset;
++      u32 size;
++      u32 aux_base; /* bits 1:0 reserved */
++};
++#define AUX_BASE_MASK         0xfffffffc
++
++/**
++ * struct smem_header - header found in beginning of primary smem region
++ * @proc_comm:                proc_comm communication interface (legacy)
++ * @version:          array of versions for the various subsystems
++ * @initialized:      boolean to indicate that smem is initialized
++ * @free_offset:      index of the first unallocated byte in smem
++ * @available:                number of bytes available for allocation
++ * @reserved:         reserved field, must be 0
++ * toc:                       array of references to items
++ */
++struct smem_header {
++      struct smem_proc_comm proc_comm[4];
++      u32 version[32];
++      u32 initialized;
++      u32 free_offset;
++      u32 available;
++      u32 reserved;
++      struct smem_global_entry toc[];
++};
++
++/**
++ * struct smem_ptable_entry - one entry in the @smem_ptable list
++ * @offset:   offset, within the main shared memory region, of the partition
++ * @size:     size of the partition
++ * @flags:    flags for the partition (currently unused)
++ * @host0:    first processor/host with access to this partition
++ * @host1:    second processor/host with access to this partition
++ * @reserved: reserved entries for later use
++ */
++struct smem_ptable_entry {
++      u32 offset;
++      u32 size;
++      u32 flags;
++      u16 host0;
++      u16 host1;
++      u32 reserved[8];
++};
++
++/**
++ * struct smem_ptable - partition table for the private partitions
++ * @magic:    magic number, must be SMEM_PTABLE_MAGIC
++ * @version:  version of the partition table
++ * @num_entries: number of partitions in the table
++ * @reserved: for now reserved entries
++ * @entry:    list of @smem_ptable_entry for the @num_entries partitions
++ */
++struct smem_ptable {
++      u32 magic;
++      u32 version;
++      u32 num_entries;
++      u32 reserved[5];
++      struct smem_ptable_entry entry[];
++};
++#define SMEM_PTABLE_MAGIC     0x434f5424 /* "$TOC" */
++
++/**
++ * struct smem_partition_header - header of the partitions
++ * @magic:    magic number, must be SMEM_PART_MAGIC
++ * @host0:    first processor/host with access to this partition
++ * @host1:    second processor/host with access to this partition
++ * @size:     size of the partition
++ * @offset_free_uncached: offset to the first free byte of uncached memory in
++ *            this partition
++ * @offset_free_cached: offset to the first free byte of cached memory in this
++ *            partition
++ * @reserved: for now reserved entries
++ */
++struct smem_partition_header {
++      u32 magic;
++      u16 host0;
++      u16 host1;
++      u32 size;
++      u32 offset_free_uncached;
++      u32 offset_free_cached;
++      u32 reserved[3];
++};
++#define SMEM_PART_MAGIC               0x54525024 /* "$PRT" */
++
++/**
++ * struct smem_private_entry - header of each item in the private partition
++ * @canary:   magic number, must be SMEM_PRIVATE_CANARY
++ * @item:     identifying number of the smem item
++ * @size:     size of the data, including padding bytes
++ * @padding_data: number of bytes of padding of data
++ * @padding_hdr: number of bytes of padding between the header and the data
++ * @reserved: for now reserved entry
++ */
++struct smem_private_entry {
++      u16 canary;
++      u16 item;
++      u32 size; /* includes padding bytes */
++      u16 padding_data;
++      u16 padding_hdr;
++      u32 reserved;
++};
++#define SMEM_PRIVATE_CANARY   0xa5a5
++
++/*
++ * Item 3 of the global heap contains an array of versions for the various
++ * software components in the SoC. We verify that the boot loader version is
++ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
++ */
++#define SMEM_ITEM_VERSION     3
++#define  SMEM_MASTER_SBL_VERSION_INDEX        7
++#define  SMEM_EXPECTED_VERSION                11
++
++/*
++ * The first 8 items are only to be allocated by the boot loader while
++ * initializing the heap.
++ */
++#define SMEM_ITEM_LAST_FIXED  8
++
++/* Highest accepted item number, for both global and private heaps */
++#define SMEM_ITEM_LAST                512
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS                0
++
++/* Max number of processors/hosts in a system */
++#define SMEM_HOST_COUNT               7
++
++/**
++ * struct smem_region - representation of a chunk of memory used for smem
++ * @aux_base: identifier of aux_mem base
++ * @virt_base:        virtual base address of memory with this aux_mem identifier
++ * @size:     size of the memory region
++ */
++struct smem_region {
++      u32 aux_base;
++      void __iomem *virt_base;
++      size_t size;
++};
++
++/**
++ * struct qcom_smem - device data for the smem device
++ * @dev:      device pointer
++ * @hwlock:   reference to a hwspinlock
++ * @partitions:       list of pointers to partitions affecting the current
++ *            processor/host
++ * @num_regions: number of @regions
++ * @regions:  list of the memory regions defining the shared memory
++ */
++struct qcom_smem {
++      struct device *dev;
++
++      struct hwspinlock *hwlock;
++
++      struct smem_partition_header *partitions[SMEM_HOST_COUNT];
++
++      unsigned num_regions;
++      struct smem_region regions[0];
++};
++
++/* Pointer to the one and only smem handle */
++static struct qcom_smem *__smem;
++
++/* Timeout (ms) for the trylock of remote spinlocks */
++#define HWSPINLOCK_TIMEOUT    1000
++
++static int qcom_smem_alloc_private(struct qcom_smem *smem,
++                                 unsigned host,
++                                 unsigned item,
++                                 size_t size)
++{
++      struct smem_partition_header *phdr;
++      struct smem_private_entry *hdr;
++      size_t alloc_size;
++      void *p;
++
++      /* We're not going to find it if there's no matching partition */
++      if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++              return -ENOENT;
++
++      phdr = smem->partitions[host];
++
++      p = (void *)phdr + sizeof(*phdr);
++      while (p < (void *)phdr + phdr->offset_free_uncached) {
++              hdr = p;
++
++              if (hdr->canary != SMEM_PRIVATE_CANARY) {
++                      dev_err(smem->dev,
++                              "Found invalid canary in host %d partition\n",
++                              host);
++                      return -EINVAL;
++              }
++
++              if (hdr->item == item)
++                      return -EEXIST;
++
++              p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++      }
++
++      /* Check that we don't grow into the cached region */
++      alloc_size = sizeof(*hdr) + ALIGN(size, 8);
++      if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) {
++              dev_err(smem->dev, "Out of memory\n");
++              return -ENOSPC;
++      }
++
++      hdr = p;
++      hdr->canary = SMEM_PRIVATE_CANARY;
++      hdr->item = item;
++      hdr->size = ALIGN(size, 8);
++      hdr->padding_data = hdr->size - size;
++      hdr->padding_hdr = 0;
++
++      /*
++       * Ensure the header is written before we advance the free offset, so
++       * that remote processors that does not take the remote spinlock still
++       * gets a consistent view of the linked list.
++       */
++      wmb();
++      phdr->offset_free_uncached += alloc_size;
++
++      return 0;
++}
++
++static int qcom_smem_alloc_global(struct qcom_smem *smem,
++                                unsigned item,
++                                size_t size)
++{
++      struct smem_header *header;
++      struct smem_global_entry *entry;
++
++      if (WARN_ON(item >= SMEM_ITEM_LAST))
++              return -EINVAL;
++
++      header = smem->regions[0].virt_base;
++      entry = &header->toc[item];
++      if (entry->allocated)
++              return -EEXIST;
++
++      size = ALIGN(size, 8);
++      if (WARN_ON(size > header->available))
++              return -ENOMEM;
++
++      entry->offset = header->free_offset;
++      entry->size = size;
++
++      /*
++       * Ensure the header is consistent before we mark the item allocated,
++       * so that remote processors will get a consistent view of the item
++       * even though they do not take the spinlock on read.
++       */
++      wmb();
++      entry->allocated = 1;
++
++      header->free_offset += size;
++      header->available -= size;
++
++      return 0;
++}
++
++/**
++ * qcom_smem_alloc - allocate space for a smem item
++ * @host:     remote processor id, or -1
++ * @item:     smem item handle
++ * @size:     number of bytes to be allocated
++ *
++ * Allocate space for a given smem item of size @size, given that the item is
++ * not yet allocated.
++ */
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
++{
++      unsigned long flags;
++      int ret;
++
++      if (!__smem)
++              return -EPROBE_DEFER;
++
++      if (item < SMEM_ITEM_LAST_FIXED) {
++              dev_err(__smem->dev,
++                      "Rejecting allocation of static entry %d\n", item);
++              return -EINVAL;
++      }
++
++      ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++                                        HWSPINLOCK_TIMEOUT,
++                                        &flags);
++      if (ret)
++              return ret;
++
++      ret = qcom_smem_alloc_private(__smem, host, item, size);
++      if (ret == -ENOENT)
++              ret = qcom_smem_alloc_global(__smem, item, size);
++
++      hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++
++      return ret;
++}
++EXPORT_SYMBOL(qcom_smem_alloc);
++
++static int qcom_smem_get_global(struct qcom_smem *smem,
++                              unsigned item,
++                              void **ptr,
++                              size_t *size)
++{
++      struct smem_header *header;
++      struct smem_region *area;
++      struct smem_global_entry *entry;
++      u32 aux_base;
++      unsigned i;
++
++      if (WARN_ON(item >= SMEM_ITEM_LAST))
++              return -EINVAL;
++
++      header = smem->regions[0].virt_base;
++      entry = &header->toc[item];
++      if (!entry->allocated)
++              return -ENXIO;
++
++      if (ptr != NULL) {
++              aux_base = entry->aux_base & AUX_BASE_MASK;
++
++              for (i = 0; i < smem->num_regions; i++) {
++                      area = &smem->regions[i];
++
++                      if (area->aux_base == aux_base || !aux_base) {
++                              *ptr = area->virt_base + entry->offset;
++                              break;
++                      }
++              }
++      }
++      if (size != NULL)
++              *size = entry->size;
++
++      return 0;
++}
++
++static int qcom_smem_get_private(struct qcom_smem *smem,
++                               unsigned host,
++                               unsigned item,
++                               void **ptr,
++                               size_t *size)
++{
++      struct smem_partition_header *phdr;
++      struct smem_private_entry *hdr;
++      void *p;
++
++      /* We're not going to find it if there's no matching partition */
++      if (host >= SMEM_HOST_COUNT || !smem->partitions[host])
++              return -ENOENT;
++
++      phdr = smem->partitions[host];
++
++      p = (void *)phdr + sizeof(*phdr);
++      while (p < (void *)phdr + phdr->offset_free_uncached) {
++              hdr = p;
++
++              if (hdr->canary != SMEM_PRIVATE_CANARY) {
++                      dev_err(smem->dev,
++                              "Found invalid canary in host %d partition\n",
++                              host);
++                      return -EINVAL;
++              }
++
++              if (hdr->item == item) {
++                      if (ptr != NULL)
++                              *ptr = p + sizeof(*hdr) + hdr->padding_hdr;
++
++                      if (size != NULL)
++                              *size = hdr->size - hdr->padding_data;
++
++                      return 0;
++              }
++
++              p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
++      }
++
++      return -ENOENT;
++}
++
++/**
++ * qcom_smem_get - resolve ptr of size of a smem item
++ * @host:     the remote processor, or -1
++ * @item:     smem item handle
++ * @ptr:      pointer to be filled out with address of the item
++ * @size:     pointer to be filled out with size of the item
++ *
++ * Looks up pointer and size of a smem item.
++ */
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size)
++{
++      unsigned long flags;
++      int ret;
++
++      if (!__smem)
++              return -EPROBE_DEFER;
++
++      ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
++                                        HWSPINLOCK_TIMEOUT,
++                                        &flags);
++      if (ret)
++              return ret;
++
++      ret = qcom_smem_get_private(__smem, host, item, ptr, size);
++      if (ret == -ENOENT)
++              ret = qcom_smem_get_global(__smem, item, ptr, size);
++
++      hwspin_unlock_irqrestore(__smem->hwlock, &flags);
++      return ret;
++
++}
++EXPORT_SYMBOL(qcom_smem_get);
++
++/**
++ * qcom_smem_get_free_space - retrieve amont of free space in a partition
++ * @host:     the remote processor identifing a partition, or -1
++ *
++ * To be used by smem clients as a quick way to determine if any new
++ * allocations has been made.
++ */
++int qcom_smem_get_free_space(unsigned host)
++{
++      struct smem_partition_header *phdr;
++      struct smem_header *header;
++      unsigned ret;
++
++      if (!__smem)
++              return -EPROBE_DEFER;
++
++      if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
++              phdr = __smem->partitions[host];
++              ret = phdr->offset_free_uncached;
++      } else {
++              header = __smem->regions[0].virt_base;
++              ret = header->available;
++      }
++
++      return ret;
++}
++EXPORT_SYMBOL(qcom_smem_get_free_space);
++
++static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
++{
++      unsigned *versions;
++      size_t size;
++      int ret;
++
++      ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION,
++                                 (void **)&versions, &size);
++      if (ret < 0) {
++              dev_err(smem->dev, "Unable to read the version item\n");
++              return -ENOENT;
++      }
++
++      if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
++              dev_err(smem->dev, "Version item is too small\n");
++              return -EINVAL;
++      }
++
++      return versions[SMEM_MASTER_SBL_VERSION_INDEX];
++}
++
++static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
++                                        unsigned local_host)
++{
++      struct smem_partition_header *header;
++      struct smem_ptable_entry *entry;
++      struct smem_ptable *ptable;
++      unsigned remote_host;
++      int i;
++
++      ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024;
++      if (ptable->magic != SMEM_PTABLE_MAGIC)
++              return 0;
++
++      if (ptable->version != 1) {
++              dev_err(smem->dev,
++                      "Unsupported partition header version %d\n",
++                      ptable->version);
++              return -EINVAL;
++      }
++
++      for (i = 0; i < ptable->num_entries; i++) {
++              entry = &ptable->entry[i];
++
++              if (entry->host0 != local_host && entry->host1 != local_host)
++                      continue;
++
++              if (!entry->offset)
++                      continue;
++
++              if (!entry->size)
++                      continue;
++
++              if (entry->host0 == local_host)
++                      remote_host = entry->host1;
++              else
++                      remote_host = entry->host0;
++
++              if (smem->partitions[remote_host]) {
++                      dev_err(smem->dev,
++                              "Already found a partition for host %d\n",
++                              remote_host);
++                      return -EINVAL;
++              }
++
++              header = smem->regions[0].virt_base + entry->offset;
++
++              if (header->magic != SMEM_PART_MAGIC) {
++                      dev_err(smem->dev,
++                              "Partition %d has invalid magic\n", i);
++                      return -EINVAL;
++              }
++
++              if (header->host0 != local_host && header->host1 != local_host) {
++                      dev_err(smem->dev,
++                              "Partition %d hosts are invalid\n", i);
++                      return -EINVAL;
++              }
++
++              if (header->host0 != remote_host && header->host1 != remote_host) {
++                      dev_err(smem->dev,
++                              "Partition %d hosts are invalid\n", i);
++                      return -EINVAL;
++              }
++
++              if (header->size != entry->size) {
++                      dev_err(smem->dev,
++                              "Partition %d has invalid size\n", i);
++                      return -EINVAL;
++              }
++
++              if (header->offset_free_uncached > header->size) {
++                      dev_err(smem->dev,
++                              "Partition %d has invalid free pointer\n", i);
++                      return -EINVAL;
++              }
++
++              smem->partitions[remote_host] = header;
++      }
++
++      return 0;
++}
++
++static int qcom_smem_count_mem_regions(struct platform_device *pdev)
++{
++      struct resource *res;
++      int num_regions = 0;
++      int i;
++
++      for (i = 0; i < pdev->num_resources; i++) {
++              res = &pdev->resource[i];
++
++              if (resource_type(res) == IORESOURCE_MEM)
++                      num_regions++;
++      }
++
++      return num_regions;
++}
++
++static int qcom_smem_probe(struct platform_device *pdev)
++{
++      struct smem_header *header;
++      struct device_node *np;
++      struct qcom_smem *smem;
++      struct resource *res;
++      struct resource r;
++      size_t array_size;
++      int num_regions = 0;
++      int hwlock_id;
++      u32 version;
++      int ret;
++      int i;
++
++      num_regions = qcom_smem_count_mem_regions(pdev) + 1;
++
++      array_size = num_regions * sizeof(struct smem_region);
++      smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL);
++      if (!smem)
++              return -ENOMEM;
++
++      smem->dev = &pdev->dev;
++      smem->num_regions = num_regions;
++
++      np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
++      if (!np) {
++              dev_err(&pdev->dev, "No memory-region specified\n");
++              return -EINVAL;
++      }
++
++      ret = of_address_to_resource(np, 0, &r);
++      of_node_put(np);
++      if (ret)
++              return ret;
++
++      smem->regions[0].aux_base = (u32)r.start;
++      smem->regions[0].size = resource_size(&r);
++      smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev,
++                                                        r.start,
++                                                        resource_size(&r));
++      if (!smem->regions[0].virt_base)
++              return -ENOMEM;
++
++      for (i = 1; i < num_regions; i++) {
++              res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1);
++
++              smem->regions[i].aux_base = (u32)res->start;
++              smem->regions[i].size = resource_size(res);
++              smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev,
++                                                                res->start,
++                                                                resource_size(res));
++              if (!smem->regions[i].virt_base)
++                      return -ENOMEM;
++      }
++
++      header = smem->regions[0].virt_base;
++      if (header->initialized != 1 || header->reserved) {
++              dev_err(&pdev->dev, "SMEM is not initilized by SBL\n");
++              return -EINVAL;
++      }
++
++      version = qcom_smem_get_sbl_version(smem);
++      if (version >> 16 != SMEM_EXPECTED_VERSION) {
++              dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version);
++              return -EINVAL;
++      }
++
++      ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
++      if (ret < 0)
++              return ret;
++
++      hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
++      if (hwlock_id < 0) {
++              dev_err(&pdev->dev, "failed to retrieve hwlock\n");
++              return hwlock_id;
++      }
++
++      smem->hwlock = hwspin_lock_request_specific(hwlock_id);
++      if (!smem->hwlock)
++              return -ENXIO;
++
++      __smem = smem;
++
++      return 0;
++}
++
++static int qcom_smem_remove(struct platform_device *pdev)
++{
++      hwspin_lock_free(__smem->hwlock);
++      __smem = NULL;
++
++      return 0;
++}
++
++static const struct of_device_id qcom_smem_of_match[] = {
++      { .compatible = "qcom,smem" },
++      {}
++};
++MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
++
++static struct platform_driver qcom_smem_driver = {
++      .probe = qcom_smem_probe,
++      .remove = qcom_smem_remove,
++      .driver  = {
++              .name = "qcom_smem",
++              .of_match_table = qcom_smem_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++
++static int __init qcom_smem_init(void)
++{
++      return platform_driver_register(&qcom_smem_driver);
++}
++arch_initcall(qcom_smem_init);
++
++static void __exit qcom_smem_exit(void)
++{
++      platform_driver_unregister(&qcom_smem_driver);
++}
++module_exit(qcom_smem_exit)
++
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
++MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
++MODULE_LICENSE("GPLv2");
+--- /dev/null
++++ b/include/linux/soc/qcom/smem.h
+@@ -0,0 +1,14 @@
++#ifndef __QCOM_SMEM_H__
++#define __QCOM_SMEM_H__
++
++struct device_node;
++struct qcom_smem;
++
++#define QCOM_SMEM_HOST_ANY -1
++
++int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size);
++
++int qcom_smem_get_free_space(unsigned host);
++
++#endif
diff --git a/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch
new file mode 100644 (file)
index 0000000..b2b9a74
--- /dev/null
@@ -0,0 +1,36 @@
+From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Fri, 14 Aug 2015 11:17:20 -0700
+Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts
+
+SMEM is used on IPQ806x to store various board related information such
+as boot device and flash partition layout. We'll declare it as a device
+so we can make use of it thanks to the new SMEM soc driver.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -54,7 +54,7 @@
+                       no-map;
+               };
+-              smem@41000000 {
++              smem: smem@41000000 {
+                       reg = <0x41000000 0x200000>;
+                       no-map;
+               };
+@@ -304,4 +304,10 @@
+               #hwlock-cells = <1>;
+       };
++
++      smem {
++              compatible = "qcom,smem";
++              memory-region = <&smem>;
++              hwlocks = <&sfpb_mutex 3>;
++      };
+ };
diff --git a/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch
new file mode 100644 (file)
index 0000000..b2c8cd5
--- /dev/null
@@ -0,0 +1,277 @@
+From 0501f76b138cf1dc11a313bb7a094da524b79337 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Thu, 13 Aug 2015 09:53:14 -0700
+Subject: [PATCH 3/3] mtd: add SMEM parser for QCOM platforms
+
+On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
+used to store partition layout. This new parser can now be used to read
+SMEM and use it to register an MTD layout according to its content.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ drivers/mtd/Kconfig          |   7 ++
+ drivers/mtd/Makefile         |   1 +
+ drivers/mtd/qcom_smem_part.c | 231 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 239 insertions(+)
+ create mode 100644 drivers/mtd/qcom_smem_part.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -200,6 +200,13 @@ config MTD_MYLOADER_PARTS
+         You will still need the parsing functions to be called by the driver
+         for your particular device. It won't happen automatically.
++config MTD_QCOM_SMEM_PARTS
++      tristate "QCOM SMEM partitioning support"
++      depends on QCOM_SMEM
++      help
++        This provides partitions parser for QCOM devices using SMEM
++        such as IPQ806x.
++
+ comment "User Modules And Translation Layers"
+ #
+--- /dev/null
++++ b/drivers/mtd/qcom_smem_part.c
+@@ -0,0 +1,231 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/spi/spi.h>
++#include <linux/module.h>
++
++#include <linux/soc/qcom/smem.h>
++
++/* Processor/host identifier for the application processor */
++#define SMEM_HOST_APPS                        0
++
++/* SMEM items index */
++#define SMEM_AARM_PARTITION_TABLE     9
++#define SMEM_BOOT_FLASH_TYPE          421
++#define SMEM_BOOT_FLASH_BLOCK_SIZE    424
++
++/* SMEM Flash types */
++#define SMEM_FLASH_NAND                       2
++#define SMEM_FLASH_SPI                        6
++
++#define SMEM_PART_NAME_SZ             16
++#define SMEM_PARTS_MAX                        32
++
++struct smem_partition {
++      char name[SMEM_PART_NAME_SZ];
++      __le32 start;
++      __le32 size;
++      __le32 attr;
++};
++
++struct smem_partition_table {
++      u8 magic[8];
++      __le32 version;
++      __le32 len;
++      struct smem_partition parts[SMEM_PARTS_MAX];
++};
++
++/* SMEM Magic values in partition table */
++static const u8 SMEM_PTABLE_MAGIC[] = {
++      0xaa, 0x73, 0xee, 0x55,
++      0xdb, 0xbd, 0x5e, 0xe3,
++};
++
++static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
++{
++      int ret;
++      size_t size;
++
++      ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
++                          (void **) smem_blksz, &size);
++
++      if (ret < 0) {
++              pr_err("Unable to read flash blksz from SMEM\n");
++              return -ENOENT;
++      }
++
++      if (size != sizeof(**smem_blksz)) {
++              pr_err("Invalid flash blksz size in SMEM\n");
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qcom_smem_get_flash_type(u64 **smem_flash_type)
++{
++      int ret;
++      size_t size;
++
++      ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
++                          (void **) smem_flash_type, &size);
++
++      if (ret < 0) {
++              pr_err("Unable to read flash type from SMEM\n");
++              return -ENOENT;
++      }
++
++      if (size != sizeof(**smem_flash_type)) {
++              pr_err("Invalid flash type size in SMEM\n");
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
++{
++      int ret;
++      size_t size;
++
++      ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
++                          (void **) pparts, &size);
++
++      if (ret < 0) {
++              pr_err("Unable to read partition table from SMEM\n");
++              return -ENOENT;
++      }
++
++      return 0;
++}
++
++static int of_dev_node_match(struct device *dev, void *data)
++{
++      return dev->of_node == data;
++}
++
++static bool is_spi_device(struct device_node *np)
++{
++      struct device *dev;
++
++      dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
++      if (!dev)
++              return false;
++
++      put_device(dev);
++      return true;
++}
++
++static int parse_qcom_smem_partitions(struct mtd_info *master,
++                                    struct mtd_partition **pparts,
++                                    struct mtd_part_parser_data *data)
++{
++      struct smem_partition_table *smem_parts;
++      u64 *smem_flash_type, *smem_blksz;
++      struct mtd_partition *mtd_parts;
++      struct device_node *of_node = data->of_node;
++      int i, ret;
++
++      /*
++       * SMEM will only store the partition table of the boot device.
++       * If this is not the boot device, do not return any partition.
++       */
++      ret = qcom_smem_get_flash_type(&smem_flash_type);
++      if (ret < 0)
++              return ret;
++
++      if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
++          || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
++              return 0;
++
++      /*
++       * Just for sanity purpose, make sure the block size in SMEM matches the
++       * block size of the MTD device
++       */
++      ret = qcom_smem_get_flash_blksz(&smem_blksz);
++      if (ret < 0)
++              return ret;
++
++      if (*smem_blksz != master->erasesize) {
++              pr_err("SMEM block size differs from MTD block size\n");
++              return -EINVAL;
++      }
++
++      /* Get partition pointer from SMEM */
++      ret = qcom_smem_get_flash_partitions(&smem_parts);
++      if (ret < 0)
++              return ret;
++
++      if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
++                 sizeof(SMEM_PTABLE_MAGIC))) {
++              pr_err("SMEM partition magic invalid\n");
++              return -EINVAL;
++      }
++
++      /* Allocate and populate the mtd structures */
++      mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
++                          GFP_KERNEL);
++      if (!mtd_parts)
++              return -ENOMEM;
++
++      for (i = 0; i < smem_parts->len; i++) {
++              struct smem_partition *s_part = &smem_parts->parts[i];
++              struct mtd_partition *m_part = &mtd_parts[i];
++
++              m_part->name = s_part->name;
++              m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
++              m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
++
++              /*
++               * The last SMEM partition may have its size marked as
++               * something like 0xffffffff, which means "until the end of the
++               * flash device". In this case, truncate it.
++               */
++              if (m_part->offset + m_part->size > master->size)
++                      m_part->size = master->size - m_part->offset;
++      }
++
++      *pparts = mtd_parts;
++
++      return smem_parts->len;
++}
++
++static struct mtd_part_parser qcom_smem_parser = {
++      .owner = THIS_MODULE,
++      .parse_fn = parse_qcom_smem_partitions,
++      .name = "qcom-smem",
++};
++
++static int __init qcom_smem_parser_init(void)
++{
++      register_mtd_parser(&qcom_smem_parser);
++      return 0;
++}
++
++static void __exit qcom_smem_parser_exit(void)
++{
++      deregister_mtd_parser(&qcom_smem_parser);
++}
++
++module_init(qcom_smem_parser_init);
++module_exit(qcom_smem_parser_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS)  += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS)       += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS)       += bcm47xxpart.o
+ obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS)     += mtd_blkdevs.o
diff --git a/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
new file mode 100644 (file)
index 0000000..25803b8
--- /dev/null
@@ -0,0 +1,511 @@
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -256,4 +256,15 @@ config PHY_STIH41X_USB
+         Enable this to support the USB transceiver that is part of
+         STMicroelectronics STiH41x SoC series.
++config PHY_QCOM_DWC3
++      tristate "QCOM DWC3 USB PHY support"
++      depends on ARCH_QCOM
++      depends on HAS_IOMEM
++      depends on OF
++      select GENERIC_PHY
++      help
++        This option enables support for the Synopsis PHYs present inside the
++        Qualcomm USB3.0 DWC3 controller.  This driver supports both HS and SS
++        PHY controllers.
++
+ endmenu
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) +=
+ obj-$(CONFIG_PHY_XGENE)                       += phy-xgene.o
+ obj-$(CONFIG_PHY_STIH407_USB)         += phy-stih407-usb.o
+ obj-$(CONFIG_PHY_STIH41X_USB)         += phy-stih41x-usb.o
++obj-$(CONFIG_PHY_QCOM_DWC3)           += phy-qcom-dwc3.o
+--- /dev/null
++++ b/drivers/phy/phy-qcom-dwc3.c
+@@ -0,0 +1,483 @@
++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++/**
++ *  USB QSCRATCH Hardware registers
++ */
++#define QSCRATCH_GENERAL_CFG          (0x08)
++#define HSUSB_PHY_CTRL_REG            (0x10)
++
++/* PHY_CTRL_REG */
++#define HSUSB_CTRL_DMSEHV_CLAMP                       BIT(24)
++#define HSUSB_CTRL_USB2_SUSPEND                       BIT(23)
++#define HSUSB_CTRL_UTMI_CLK_EN                        BIT(21)
++#define       HSUSB_CTRL_UTMI_OTG_VBUS_VALID          BIT(20)
++#define HSUSB_CTRL_USE_CLKCORE                        BIT(18)
++#define HSUSB_CTRL_DPSEHV_CLAMP                       BIT(17)
++#define HSUSB_CTRL_COMMONONN                  BIT(11)
++#define HSUSB_CTRL_ID_HV_CLAMP                        BIT(9)
++#define HSUSB_CTRL_OTGSESSVLD_CLAMP           BIT(8)
++#define HSUSB_CTRL_CLAMP_EN                   BIT(7)
++#define HSUSB_CTRL_RETENABLEN                 BIT(1)
++#define HSUSB_CTRL_POR                                BIT(0)
++
++/* QSCRATCH_GENERAL_CFG */
++#define HSUSB_GCFG_XHCI_REV           BIT(2)
++
++/**
++ *  USB QSCRATCH Hardware registers
++ */
++#define SSUSB_PHY_CTRL_REG            (0x00)
++#define SSUSB_PHY_PARAM_CTRL_1                (0x04)
++#define SSUSB_PHY_PARAM_CTRL_2                (0x08)
++#define CR_PROTOCOL_DATA_IN_REG               (0x0c)
++#define CR_PROTOCOL_DATA_OUT_REG      (0x10)
++#define CR_PROTOCOL_CAP_ADDR_REG      (0x14)
++#define CR_PROTOCOL_CAP_DATA_REG      (0x18)
++#define CR_PROTOCOL_READ_REG          (0x1c)
++#define CR_PROTOCOL_WRITE_REG         (0x20)
++
++/* PHY_CTRL_REG */
++#define SSUSB_CTRL_REF_USE_PAD                BIT(28)
++#define SSUSB_CTRL_TEST_POWERDOWN     BIT(27)
++#define SSUSB_CTRL_LANE0_PWR_PRESENT  BIT(24)
++#define SSUSB_CTRL_SS_PHY_EN          BIT(8)
++#define SSUSB_CTRL_SS_PHY_RESET               BIT(7)
++
++/* SSPHY control registers */
++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)        (0x1006 + 0x100 * lane)
++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)       (0x1002 + 0x100 * lane)
++
++/* RX OVRD IN HI bits */
++#define RX_OVRD_IN_HI_RX_RESET_OVRD           BIT(13)
++#define RX_OVRD_IN_HI_RX_RX_RESET             BIT(12)
++#define RX_OVRD_IN_HI_RX_EQ_OVRD              BIT(11)
++#define RX_OVRD_IN_HI_RX_EQ_MASK              0x0700
++#define RX_OVRD_IN_HI_RX_EQ_SHIFT             8
++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD           BIT(7)
++#define RX_OVRD_IN_HI_RX_EQ_EN                        BIT(6)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD      BIT(5)
++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK      0x0018
++#define RX_OVRD_IN_HI_RX_RATE_OVRD            BIT(2)
++#define RX_OVRD_IN_HI_RX_RATE_MASK            0x0003
++
++/* TX OVRD DRV LO register bits */
++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
++#define TX_OVRD_DRV_LO_PREEMPH_MASK   0x3F80
++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT  7
++#define TX_OVRD_DRV_LO_EN             BIT(14)
++
++struct qcom_dwc3_usb_phy {
++      void __iomem            *base;
++      struct device           *dev;
++      struct phy *phy;
++
++      int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
++      int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
++
++      struct clk              *xo_clk;
++      struct clk              *ref_clk;
++};
++
++/**
++ * Write register and read back masked value to confirm it is written
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @offset - register offset.
++ * @mask - register bitmask specifying what should be updated
++ * @val - value to write.
++ */
++static inline void qcom_dwc3_phy_write_readback(
++      struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
++      const u32 mask, u32 val)
++{
++      u32 write_val, tmp = readl(phy_dwc3->base + offset);
++
++      tmp &= ~mask;           /* retain other bits */
++      write_val = tmp | val;
++
++      writel(write_val, phy_dwc3->base + offset);
++
++      /* Read back to see if val was written */
++      tmp = readl(phy_dwc3->base + offset);
++      tmp &= mask;            /* clear other bits */
++
++      if (tmp != val)
++              dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
++                      val, offset);
++}
++
++static int wait_for_latch(void __iomem *addr)
++{
++      u32 retry = 10;
++
++      while (true) {
++              if (!readl(addr))
++                      break;
++
++              if (--retry == 0)
++                      return -ETIMEDOUT;
++
++              usleep_range(10, 20);
++      }
++
++      return 0;
++}
++
++/**
++ * Write SSPHY register
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to write.
++ * @val - value to write.
++ */
++static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
++{
++      int ret;
++
++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(val, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
++      if (ret)
++              goto err_wait;
++
++      writel(0x1, base + CR_PROTOCOL_WRITE_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
++
++err_wait:
++      return ret;
++}
++
++/**
++ * Read SSPHY register.
++ *
++ * @base - QCOM DWC3 PHY base virtual address.
++ * @addr - SSPHY address to read.
++ */
++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
++{
++      int ret;
++      bool first_read = true;
++
++      writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
++      writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++      if (ret)
++              goto err_wait;
++
++      /*
++       * Due to hardware bug, first read of SSPHY register might be
++       * incorrect. Hence as workaround, SW should perform SSPHY register
++       * read twice, but use only second read and ignore first read.
++       */
++retry:
++      writel(0x1, base + CR_PROTOCOL_READ_REG);
++
++      ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++      if (ret)
++              goto err_wait;
++
++      if (first_read) {
++              readl(base + CR_PROTOCOL_DATA_OUT_REG);
++              first_read = false;
++              goto retry;
++      }
++
++      *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
++
++err_wait:
++      return ret;
++}
++
++static int qcom_dwc3_phy_power_on(struct phy *phy)
++{
++      int ret;
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      ret = clk_prepare_enable(phy_dwc3->xo_clk);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(phy_dwc3->ref_clk);
++      if (ret)
++              clk_disable_unprepare(phy_dwc3->xo_clk);
++
++      return ret;
++}
++
++static int qcom_dwc3_phy_power_off(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      clk_disable_unprepare(phy_dwc3->ref_clk);
++      clk_disable_unprepare(phy_dwc3->xo_clk);
++
++      return 0;
++}
++
++static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++      u32 val;
++
++      /*
++       * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
++       * enable clamping, and disable RETENTION (power-on default is ENABLED)
++       */
++      val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
++              HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN |
++              HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
++              HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
++              HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
++
++      /* use core clock if external reference is not present */
++      if (!phy_dwc3->xo_clk)
++              val |= HSUSB_CTRL_USE_CLKCORE;
++
++      writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
++      usleep_range(2000, 2200);
++
++      /* Disable (bypass) VBUS and ID filters */
++      writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
++
++      return 0;
++}
++
++static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++      int ret;
++      u32 data = 0;
++
++      /* reset phy */
++      data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
++              phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      usleep_range(2000, 2200);
++      writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++      /* clear REF_PAD if we don't have XO clk */
++      if (!phy_dwc3->xo_clk)
++              data &= ~SSUSB_CTRL_REF_USE_PAD;
++      else
++              data |= SSUSB_CTRL_REF_USE_PAD;
++
++      writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++      msleep(30);
++
++      data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
++      writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++      /*
++       * Fix RX Equalization setting as follows
++       * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
++       * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
++       * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
++       * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
++       */
++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++                      SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
++      if (ret)
++              goto err_phy_trans;
++
++      data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
++      data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
++      data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
++      data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
++      data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++              SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
++      if (ret)
++              goto err_phy_trans;
++
++      /*
++       * Set EQ and TX launch amplitudes as follows
++       * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
++       * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
++       * LANE0.TX_OVRD_DRV_LO.EN set to 1.
++       */
++      ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
++      if (ret)
++              goto err_phy_trans;
++
++      data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
++      data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
++      data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
++      data |= 0x7f;
++      data |= TX_OVRD_DRV_LO_EN;
++      ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++              SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
++      if (ret)
++              goto err_phy_trans;
++
++      /*
++       * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
++       * TX_FULL_SWING [26:20] amplitude to 127
++       * TX_DEEMPH_3_5DB [13:8] to 22
++       * LOS_BIAS [2:0] to 0x5
++       */
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
++                                 0x07f03f07, 0x07f01605);
++
++err_phy_trans:
++      return ret;
++}
++
++static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3)
++{
++      /* Sequence to put SSPHY in low power state:
++       * 1. Clear REF_PHY_EN in PHY_CTRL_REG
++       * 2. Clear REF_USE_PAD in PHY_CTRL_REG
++       * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
++       * 4. Disable SSPHY ref clk
++       */
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              SSUSB_CTRL_SS_PHY_EN, 0x0);
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              SSUSB_CTRL_REF_USE_PAD, 0x0);
++      qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
++              0x0, SSUSB_CTRL_TEST_POWERDOWN);
++
++      return 0;
++}
++
++static int qcom_dwc3_phy_init(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      if (phy_dwc3->phy_init)
++              return phy_dwc3->phy_init(phy_dwc3);
++
++      return 0;
++}
++
++static int qcom_dwc3_phy_exit(struct phy *phy)
++{
++      struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
++      if (phy_dwc3->phy_exit)
++              return qcom_dwc3_ss_phy_exit(phy_dwc3);
++
++      return 0;
++}
++
++static struct phy_ops qcom_dwc3_phy_ops = {
++      .init           = qcom_dwc3_phy_init,
++      .exit           = qcom_dwc3_phy_exit,
++      .power_on       = qcom_dwc3_phy_power_on,
++      .power_off      = qcom_dwc3_phy_power_off,
++      .owner          = THIS_MODULE,
++};
++
++static const struct of_device_id qcom_dwc3_phy_table[] = {
++      { .compatible = "qcom,dwc3-hs-usb-phy", },
++      { .compatible = "qcom,dwc3-ss-usb-phy", },
++      { /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
++
++static int qcom_dwc3_phy_probe(struct platform_device *pdev)
++{
++      struct qcom_dwc3_usb_phy        *phy_dwc3;
++      struct phy_provider             *phy_provider;
++      struct resource                 *res;
++
++      phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
++      if (!phy_dwc3)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, phy_dwc3);
++
++      phy_dwc3->dev = &pdev->dev;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
++      if (IS_ERR(phy_dwc3->base))
++              return PTR_ERR(phy_dwc3->base);
++
++      phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
++      if (IS_ERR(phy_dwc3->ref_clk)) {
++              dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
++              return PTR_ERR(phy_dwc3->ref_clk);
++      }
++
++      if (of_device_is_compatible(pdev->dev.of_node,
++                      "qcom,dwc3-hs-usb-phy")) {
++              clk_set_rate(phy_dwc3->ref_clk, 60000000);
++              phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
++      } else if (of_device_is_compatible(pdev->dev.of_node,
++                      "qcom,dwc3-ss-usb-phy")) {
++              phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
++              phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
++              clk_set_rate(phy_dwc3->ref_clk, 125000000);
++      } else {
++              dev_err(phy_dwc3->dev, "Unknown phy\n");
++              return -EINVAL;
++      }
++
++      phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
++      if (IS_ERR(phy_dwc3->xo_clk)) {
++              dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
++              phy_dwc3->xo_clk = NULL;
++      }
++
++      phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops,
++                                      NULL);
++
++      if (IS_ERR(phy_dwc3->phy))
++              return PTR_ERR(phy_dwc3->phy);
++
++      phy_set_drvdata(phy_dwc3->phy, phy_dwc3);
++
++      phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
++                      of_phy_simple_xlate);
++
++      if (IS_ERR(phy_provider))
++              return PTR_ERR(phy_provider);
++
++      return 0;
++}
++
++static struct platform_driver qcom_dwc3_phy_driver = {
++      .probe          = qcom_dwc3_phy_probe,
++      .driver         = {
++              .name   = "qcom-dwc3-usb-phy",
++              .owner  = THIS_MODULE,
++              .of_match_table = qcom_dwc3_phy_table,
++      },
++};
++
++module_platform_driver(qcom_dwc3_phy_driver);
++
++MODULE_ALIAS("platform:phy-qcom-dwc3");
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
diff --git a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch
new file mode 100644 (file)
index 0000000..e2d03d4
--- /dev/null
@@ -0,0 +1,126 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -91,5 +91,29 @@
+               sata@29000000 {
+                       status = "ok";
+               };
++
++              phy@100f8800 {          /* USB3 port 1 HS phy */
++                      status = "ok";
++              };
++
++              phy@100f8830 {          /* USB3 port 1 SS phy */
++                      status = "ok";
++              };
++
++              phy@110f8800 {          /* USB3 port 0 HS phy */
++                      status = "ok";
++              };
++
++              phy@110f8830 {          /* USB3 port 0 SS phy */
++                      status = "ok";
++              };
++
++              usb30@0 {
++                      status = "ok";
++              };
++
++              usb30@1 {
++                      status = "ok";
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -296,6 +296,91 @@
+                       compatible = "syscon";
+                       reg = <0x01200600 0x100>;
+               };
++
++              hs_phy_1: phy@100f8800 {
++                      compatible = "qcom,dwc3-hs-usb-phy";
++                      reg = <0x100f8800 0x30>;
++                      clocks = <&gcc USB30_1_UTMI_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              ss_phy_1: phy@100f8830 {
++                      compatible = "qcom,dwc3-ss-usb-phy";
++                      reg = <0x100f8830 0x30>;
++                      clocks = <&gcc USB30_1_MASTER_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              hs_phy_0: phy@110f8800 {
++                      compatible = "qcom,dwc3-hs-usb-phy";
++                      reg = <0x110f8800 0x30>;
++                      clocks = <&gcc USB30_0_UTMI_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              ss_phy_0: phy@110f8830 {
++                      compatible = "qcom,dwc3-ss-usb-phy";
++                      reg = <0x110f8830 0x30>;
++                      clocks = <&gcc USB30_0_MASTER_CLK>;
++                      clock-names = "ref";
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
++              usb3_0: usb30@0 {
++                      compatible = "qcom,dwc3";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&gcc USB30_0_MASTER_CLK>;
++                      clock-names = "core";
++
++                      ranges;
++
++                      status = "disabled";
++
++                      dwc3@11000000 {
++                              compatible = "snps,dwc3";
++                              reg = <0x11000000 0xcd00>;
++                              interrupts = <0 110 0x4>;
++                              phys = <&hs_phy_0>, <&ss_phy_0>;
++                              phy-names = "usb2-phy", "usb3-phy";
++                              tx-fifo-resize;
++                              dr_mode = "host";
++                      };
++              };
++
++              usb3_1: usb30@1 {
++                      compatible = "qcom,dwc3";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&gcc USB30_1_MASTER_CLK>;
++                      clock-names = "core";
++
++                      ranges;
++
++                      status = "disabled";
++
++                      dwc3@10000000 {
++                              compatible = "snps,dwc3";
++                              reg = <0x10000000 0xcd00>;
++                              interrupts = <0 205 0x4>;
++                              phys = <&hs_phy_1>, <&ss_phy_1>;
++                              phy-names = "usb2-phy", "usb3-phy";
++                              tx-fifo-resize;
++                              dr_mode = "host";
++                      };
++              };
++
+       };
+       sfpb_mutex: sfpb-mutex {
diff --git a/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch b/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch
new file mode 100644 (file)
index 0000000..752f3f7
--- /dev/null
@@ -0,0 +1,249 @@
+--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
+@@ -6,7 +6,8 @@ configuration settings.  The mode settin
+ the 4 GSBI IOs.
+ Required properties:
+-- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
++- compatible: Should contain "qcom,gsbi-v1.0.0"
++- cell-index: Should contain the GSBI index
+ - reg: Address range for GSBI registers
+ - clocks: required clock
+ - clock-names: must contain "iface" entry
+@@ -16,6 +17,8 @@ Required properties:
+ 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.
++- syscon-tcsr: indicates phandle of TCSR syscon node.  Required if child uses
++  dma.
+ Required properties if child node exists:
+ - #address-cells: Must be 1
+@@ -39,6 +42,7 @@ Example for APQ8064:
+       gsbi4@16300000 {
+               compatible = "qcom,gsbi-v1.0.0";
++              cell-index = <4>;
+               reg = <0x16300000 0x100>;
+               clocks = <&gcc GSBI4_H_CLK>;
+               clock-names = "iface";
+@@ -48,6 +52,8 @@ Example for APQ8064:
+               qcom,mode = <GSBI_PROT_I2C_UART>;
+               qcom,crci = <GSBI_CRCI_QUP>;
++              syscon-tcsr = <&tcsr>;
++
+               /* child nodes go under here */
+               i2c_qup4: i2c@16380000 {
+@@ -76,3 +82,9 @@ Example for APQ8064:
+               };
+       };
++      tcsr: syscon@1a400000 {
++              compatible = "qcom,apq8064-tcsr", "syscon";
++              reg = <0x1a400000 0x100>;
++      };
++
++
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -4,6 +4,7 @@
+ config QCOM_GSBI
+         tristate "QCOM General Serial Bus Interface"
+         depends on ARCH_QCOM
++        select MFD_SYSCON
+         help
+           Say y here to enable GSBI support.  The GSBI provides control
+           functions for connecting the underlying serial UART, SPI, and I2C
+--- a/drivers/soc/qcom/qcom_gsbi.c
++++ b/drivers/soc/qcom/qcom_gsbi.c
+@@ -18,22 +18,129 @@
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++#include <dt-bindings/soc/qcom,gsbi.h>
+ #define GSBI_CTRL_REG         0x0000
+ #define GSBI_PROTOCOL_SHIFT   4
++#define MAX_GSBI              12
++
++#define TCSR_ADM_CRCI_BASE    0x70
++
++struct crci_config {
++      u32 num_rows;
++      const u32 (*array)[MAX_GSBI];
++};
++
++static const u32 crci_ipq8064[][MAX_GSBI] = {
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++};
++
++static const struct crci_config config_ipq8064 = {
++      .num_rows = ARRAY_SIZE(crci_ipq8064),
++      .array = crci_ipq8064,
++};
++
++static const unsigned int crci_apq8064[][MAX_GSBI] = {
++      {
++              0x001800, 0x006000, 0x000030, 0x0000c0,
++              0x000300, 0x000400, 0x000000, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++      {
++              0x000000, 0x000000, 0x000000, 0x000000,
++              0x000000, 0x000020, 0x0000c0, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++};
++
++static const struct crci_config config_apq8064 = {
++      .num_rows = ARRAY_SIZE(crci_apq8064),
++      .array = crci_apq8064,
++};
++
++static const unsigned int crci_msm8960[][MAX_GSBI] = {
++      {
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000400, 0x000000, 0x000000,
++              0x000000, 0x000000, 0x000000, 0x000000
++      },
++      {
++              0x000000, 0x000000, 0x000000, 0x000000,
++              0x000000, 0x000020, 0x0000c0, 0x000300,
++              0x001800, 0x006000, 0x000000, 0x000000
++      },
++};
++
++static const struct crci_config config_msm8960 = {
++      .num_rows = ARRAY_SIZE(crci_msm8960),
++      .array = crci_msm8960,
++};
++
++static const unsigned int crci_msm8660[][MAX_GSBI] = {
++      {       /* ADM 0 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 0 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 1 - A */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++      {       /* ADM 1 - B */
++              0x000003, 0x00000c, 0x000030, 0x0000c0,
++              0x000300, 0x000c00, 0x003000, 0x00c000,
++              0x030000, 0x0c0000, 0x300000, 0xc00000
++      },
++};
++
++static const struct crci_config config_msm8660 = {
++      .num_rows = ARRAY_SIZE(crci_msm8660),
++      .array = crci_msm8660,
++};
+ struct gsbi_info {
+       struct clk *hclk;
+       u32 mode;
+       u32 crci;
++      struct regmap *tcsr;
++};
++
++static const struct of_device_id tcsr_dt_match[] = {
++      { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
++      { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
++      { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
++      { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
++      { },
+ };
+ static int gsbi_probe(struct platform_device *pdev)
+ {
+       struct device_node *node = pdev->dev.of_node;
++      struct device_node *tcsr_node;
++      const struct of_device_id *match;
+       struct resource *res;
+       void __iomem *base;
+       struct gsbi_info *gsbi;
++      int i;
++      u32 mask, gsbi_num;
++      const struct crci_config *config = NULL;
+       gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
+@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_de
+       if (IS_ERR(base))
+               return PTR_ERR(base);
++      /* get the tcsr node and setup the config and regmap */
++      gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
++
++      if (!IS_ERR(gsbi->tcsr)) {
++              tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
++              if (tcsr_node) {
++                      match = of_match_node(tcsr_dt_match, tcsr_node);
++                      if (match)
++                              config = match->data;
++                      else
++                              dev_warn(&pdev->dev, "no matching TCSR\n");
++
++                      of_node_put(tcsr_node);
++              }
++      }
++
++      if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
++              dev_err(&pdev->dev, "missing cell-index\n");
++              return -EINVAL;
++      }
++
++      if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
++              dev_err(&pdev->dev, "invalid cell-index\n");
++              return -EINVAL;
++      }
++
+       if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
+               dev_err(&pdev->dev, "missing mode configuration\n");
+               return -EINVAL;
+@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_de
+       writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
+                               base + GSBI_CTRL_REG);
++      /*
++       * modify tcsr to reflect mode and ADM CRCI mux
++       * Each gsbi contains a pair of bits, one for RX and one for TX
++       * SPI mode requires both bits cleared, otherwise they are set
++       */
++      if (config) {
++              for (i = 0; i < config->num_rows; i++) {
++                      mask = config->array[i][gsbi_num - 1];
++
++                      if (gsbi->mode == GSBI_PROT_SPI)
++                              regmap_update_bits(gsbi->tcsr,
++                                      TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
++                      else
++                              regmap_update_bits(gsbi->tcsr,
++                                      TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
++
++              }
++      }
++
+       /* make sure the gsbi control write is not reordered */
+       wmb();
diff --git a/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch b/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch
new file mode 100644 (file)
index 0000000..322e1d9
--- /dev/null
@@ -0,0 +1,65 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -132,6 +132,7 @@
+               gsbi2: gsbi@12480000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <2>;
+                       reg = <0x12480000 0x100>;
+                       clocks = <&gcc GSBI2_H_CLK>;
+                       clock-names = "iface";
+@@ -140,6 +141,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart2: serial@12490000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x12490000 0x1000>,
+@@ -167,6 +170,7 @@
+               gsbi4: gsbi@16300000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <4>;
+                       reg = <0x16300000 0x100>;
+                       clocks = <&gcc GSBI4_H_CLK>;
+                       clock-names = "iface";
+@@ -175,6 +179,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart4: serial@16340000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x16340000 0x1000>,
+@@ -201,6 +207,7 @@
+               gsbi5: gsbi@1a200000 {
+                       compatible = "qcom,gsbi-v1.0.0";
++                      cell-index = <5>;
+                       reg = <0x1a200000 0x100>;
+                       clocks = <&gcc GSBI5_H_CLK>;
+                       clock-names = "iface";
+@@ -209,6 +216,8 @@
+                       ranges;
+                       status = "disabled";
++                      syscon-tcsr = <&tcsr>;
++
+                       uart5: serial@1a240000 {
+                               compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+                               reg = <0x1a240000 0x1000>,
+@@ -279,6 +288,11 @@
+                       status = "disabled";
+               };
++              tcsr: syscon@1a400000 {
++                      compatible = "qcom,tcsr-ipq8064", "syscon";
++                      reg = <0x1a400000 0x100>;
++              };
++
+               qcom,ssbi@500000 {
+                       compatible = "qcom,ssbi";
+                       reg = <0x00500000 0x1000>;
diff --git a/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch
new file mode 100644 (file)
index 0000000..41f91fa
--- /dev/null
@@ -0,0 +1,263 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326181
+Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:16 +0300
+
+Document Qualcomm PCIe driver devicetree bindings.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+.../devicetree/bindings/pci/qcom,pcie.txt          |  231 ++++++++++++++++++++
+ 1 files changed, 231 insertions(+), 0 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+@@ -0,0 +1,231 @@
++* Qualcomm PCI express root complex
++
++- compatible:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Value shall include
++                  - "qcom,pcie-v0" for apq/ipq8064
++                  - "qcom,pcie-v1" for apq8084
++
++- reg:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: Register ranges as listed in the reg-names property
++
++- reg-names:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Must include the following entries
++                  - "parf"   Qualcomm specific registers
++                  - "dbi"    Designware PCIe registers
++                  - "elbi"   External local bus interface registers
++                  - "config" PCIe configuration space
++
++- device_type:
++      Usage: required
++      Value type: <string>
++      Definition: Should be "pci". As specified in designware-pcie.txt
++
++- #address-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: Should be set to 3. As specified in designware-pcie.txt
++
++- #size-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: Should be set 2. As specified in designware-pcie.txt
++
++- ranges:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: As specified in designware-pcie.txt
++
++- interrupts:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: MSI interrupt
++
++- interrupt-names:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Should contain "msi"
++
++- #interrupt-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: Should be 1. As specified in designware-pcie.txt
++
++- interrupt-map-mask:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: As specified in designware-pcie.txt
++
++- interrupt-map:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: As specified in designware-pcie.txt
++
++- clocks:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: List of phandle and clock specifier pairs as listed
++                  in clock-names property
++
++- clock-names:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Should contain the following entries
++                  * should be populated for v0 and v1
++                      - "iface"      Configuration AHB clock
++
++                  * should be populated for v0
++                      - "core"       Clocks the pcie hw block
++                      - "phy"        Clocks the pcie PHY block
++
++                  * should be populated for v1
++                      - "aux"        Auxiliary (AUX) clock
++                      - "bus_master" Master AXI clock
++                      - "bus_slave"  Slave AXI clock
++
++- resets:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: List of phandle and reset specifier pairs as listed
++                  in reset-names property
++
++- reset-names:
++      Usage: required
++      Value type: <stringlist>
++      Definition: Should contain the following entries
++                  * should be populated for v0
++                      - "axi"  AXI reset
++                      - "ahb"  AHB reset
++                      - "por"  POR reset
++                      - "pci"  PCI reset
++                      - "phy"  PHY reset
++
++                  * should be populated for v1
++                      - "core" Core reset
++
++- power-domains:
++      Usage: required (for v1 only)
++      Value type: <prop-encoded-array>
++      Definition: A phandle and power domain specifier pair to the
++                  power domain which is responsible for collapsing
++                  and restoring power to the peripheral
++
++- <name>-supply:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles to the power supply regulator(s)
++                  * should be populated for v0 and v1
++                      - "vdda"        core analog power supply
++
++                  * should be populated for v0
++                      - "vdda_phy"    analog power supply for PHY
++                      - "vdda_refclk" analog power supply for IC which generate
++                                      reference clock
++
++- phys:
++      Usage: required (for v1 only)
++      Value type: <phandle>
++      Definition: List of phandle(s) as listed in phy-names property
++
++- phy-names:
++      Usage: required (for v1 only)
++      Value type: <stringlist>
++      Definition: Should contain "pciephy"
++
++- <name>-gpio:
++      Usage: optional
++      Value type: <prop-encoded-array>
++      Definition: List of phandle and gpio specifier pairs. Should contain
++                  - "perst"  PCIe endpoint reset signal line
++                  - "pewake" PCIe endpoint wake signal line
++
++- pinctrl-0:
++      Usage: required
++      Value type: <phandle>
++      Definition: List of phandles pointing at a pin(s) configuration
++
++- pinctrl-names
++      Usage: required
++      Value type: <stringlist>
++      Definition: List of names of pinctrl-0 state
++
++* Example for v0
++      pcie0: pci@1b500000 {
++              compatible = "qcom,pcie-v0";
++              reg = <0x1b500000 0x1000
++                     0x1b502000 0x80
++                     0x1b600000 0x100
++                     0x0ff00000 0x100000>;
++              reg-names = "dbi", "elbi", "parf", "config";
++              device_type = "pci";
++              linux,pci-domain = <0>;
++              bus-range = <0x00 0xff>;
++              num-lanes = <1>;
++              #address-cells = <3>;
++              #size-cells = <2>;
++              ranges = <0x81000000 0 0          0x0fe00000 0 0x00100000   /* I/O */
++                        0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */
++              interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
++              interrupt-names = "msi";
++              #interrupt-cells = <1>;
++              interrupt-map-mask = <0 0 0 0x7>;
++              interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                              <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                              <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                              <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++              clocks = <&gcc PCIE_A_CLK>,
++                       <&gcc PCIE_H_CLK>,
++                       <&gcc PCIE_PHY_CLK>;
++              clock-names = "core", "iface", "phy";
++              resets = <&gcc PCIE_ACLK_RESET>,
++                       <&gcc PCIE_HCLK_RESET>,
++                       <&gcc PCIE_POR_RESET>,
++                       <&gcc PCIE_PCI_RESET>,
++                       <&gcc PCIE_PHY_RESET>;
++              reset-names = "axi", "ahb", "por", "pci", "phy";
++      };
++
++* Example for v1
++      pcie0@fc520000 {
++              compatible = "qcom,pcie-v1";
++              reg = <0xfc520000 0x2000>,
++                    <0xff000000 0x1000>,
++                    <0xff001000 0x1000>,
++                    <0xff002000 0x2000>;
++              reg-names = "parf", "dbi", "elbi", "config";
++              device_type = "pci";
++              linux,pci-domain = <0>;
++              bus-range = <0x00 0xff>;
++              num-lanes = <1>;
++              #address-cells = <3>;
++              #size-cells = <2>;
++              ranges = <0x81000000 0 0          0xff200000 0 0x00100000   /* I/O */
++                        0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
++              interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
++              interrupt-names = "msi";
++              #interrupt-cells = <1>;
++              interrupt-map-mask = <0 0 0 0x7>;
++              interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                              <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                              <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                              <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++              clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
++                       <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
++                       <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
++                       <&gcc GCC_PCIE_0_AUX_CLK>;
++              clock-names = "iface", "master_bus", "slave_bus", "aux";
++              resets = <&gcc GCC_PCIE_0_BCR>;
++              reset-names = "core";
++              power-domains = <&gcc PCIE0_GDSC>;
++              vdda-supply = <&pma8084_l3>;
++              phys = <&pciephy0>;
++              phy-names = "pciephy";
++              perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
++              pinctrl-0 = <&pcie0_pins_default>;
++              pinctrl-names = "default";
++      };
diff --git a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch
new file mode 100644 (file)
index 0000000..a0c9c7c
--- /dev/null
@@ -0,0 +1,753 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver
+From: Stanimir Varbanov <svarbanov@mm-sol.com>
+X-Patchwork-Id: 6326161
+Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com>
+To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
+       Mark Rutland <mark.rutland@arm.com>,
+       Grant Likely <grant.likely@linaro.org>,
+       Bjorn Helgaas <bhelgaas@google.com>,
+       Kishon Vijay Abraham I <kishon@ti.com>,
+       Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
+Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
+       linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
+       linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
+       Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
+       Stanimir Varbanov <svarbanov@mm-sol.com>
+Date: Mon,  4 May 2015 15:42:17 +0300
+
+The PCIe driver reuse the Designware common code for host
+and MSI initialization, and also program the Qualcomm
+application specific registers.
+
+Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
+
+---
+MAINTAINERS                  |    7 +
+ drivers/pci/host/Kconfig     |    9 +
+ drivers/pci/host/Makefile    |    1 +
+ drivers/pci/host/pcie-qcom.c |  677 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 694 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/pci/host/pcie-qcom.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7127,6 +7127,13 @@ L:      linux-pci@vger.kernel.org
+ S:    Maintained
+ F:    drivers/pci/host/*spear*
++PCIE DRIVER FOR QUALCOMM MSM
++M:    Stanimir Varbanov <svarbanov@mm-sol.com>
++L:    linux-pci@vger.kernel.org
++L:    linux-arm-msm@vger.kernel.org
++S:    Maintained
++F:    drivers/pci/host/*qcom*
++
+ PCMCIA SUBSYSTEM
+ P:    Linux PCMCIA Team
+ L:    linux-pcmcia@lists.infradead.org
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -91,4 +91,13 @@ config PCI_XGENE
+         There are 5 internal PCIe ports available. Each port is GEN3 capable
+         and have varied lanes from x1 to x8.
++config PCIE_QCOM
++      bool "Qualcomm PCIe controller"
++      depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
++      select PCIE_DW
++      select PCIEPORTBUS
++      help
++        Say Y here to enable PCIe controller support on Qualcomm SoCs. The
++        PCIe controller use Designware core plus Qualcomm specific hardware
++        wrappers.
+ endmenu
+--- /dev/null
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -0,0 +1,677 @@
++/*
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_gpio.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/phy/phy.h>
++#include <linux/regulator/consumer.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "pcie-designware.h"
++
++#define PCIE20_PARF_PHY_CTRL                  0x40
++#define PCIE20_PARF_PHY_REFCLK                        0x4C
++#define PCIE20_PARF_DBI_BASE_ADDR             0x168
++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE               0x16c
++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT     0x178
++
++#define PCIE20_ELBI_SYS_CTRL                  0x04
++#define PCIE20_ELBI_SYS_STTS                  0x08
++#define XMLH_LINK_UP                          BIT(10)
++
++#define PCIE20_CAP                            0x70
++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
++
++#define PERST_DELAY_MIN_US                    1000
++#define PERST_DELAY_MAX_US                    1005
++
++#define LINKUP_DELAY_MIN_US                   5000
++#define LINKUP_DELAY_MAX_US                   5100
++#define LINKUP_RETRIES_COUNT                  20
++
++#define PCIE_V0                                       0       /* apq8064 */
++#define PCIE_V1                                       1       /* apq8084 */
++
++struct qcom_pcie_resources_v0 {
++      struct clk *iface_clk;
++      struct clk *core_clk;
++      struct clk *phy_clk;
++      struct reset_control *pci_reset;
++      struct reset_control *axi_reset;
++      struct reset_control *ahb_reset;
++      struct reset_control *por_reset;
++      struct reset_control *phy_reset;
++      struct regulator *vdda;
++      struct regulator *vdda_phy;
++      struct regulator *vdda_refclk;
++};
++
++struct qcom_pcie_resources_v1 {
++      struct clk *iface;
++      struct clk *aux;
++      struct clk *master_bus;
++      struct clk *slave_bus;
++      struct reset_control *core;
++      struct regulator *vdda;
++};
++
++union pcie_resources {
++      struct qcom_pcie_resources_v0 v0;
++      struct qcom_pcie_resources_v1 v1;
++};
++
++struct qcom_pcie {
++      struct pcie_port pp;
++      struct device *dev;
++      union pcie_resources res;
++      void __iomem *parf;
++      void __iomem *dbi;
++      void __iomem *elbi;
++      struct phy *phy;
++      struct gpio_desc *reset;
++      unsigned int version;
++};
++
++#define to_qcom_pcie(x)               container_of(x, struct qcom_pcie, pp)
++
++static inline void
++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
++{
++      u32 val = readl(addr);
++
++      val &= ~clear_mask;
++      val |= set_mask;
++      writel(val, addr);
++}
++
++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
++{
++      int val, active_low;
++
++      if (IS_ERR_OR_NULL(pcie->reset))
++              return;
++
++      active_low = gpiod_is_active_low(pcie->reset);
++
++      if (assert)
++              val = !!active_low;
++      else
++              val = !active_low;
++
++      gpiod_set_value(pcie->reset, val);
++
++      usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
++}
++
++static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 1);
++}
++
++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
++{
++      qcom_ep_reset_assert_deassert(pcie, 0);
++}
++
++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
++{
++      struct pcie_port *pp = arg;
++
++      return dw_handle_msi_irq(pp);
++}
++
++static int qcom_pcie_link_up(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
++
++      return val & BIT(29) ? 1 : 0;
++}
++
++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++
++      reset_control_assert(res->pci_reset);
++      reset_control_assert(res->axi_reset);
++      reset_control_assert(res->ahb_reset);
++      reset_control_assert(res->por_reset);
++      reset_control_assert(res->pci_reset);
++      clk_disable_unprepare(res->iface_clk);
++      clk_disable_unprepare(res->core_clk);
++      clk_disable_unprepare(res->phy_clk);
++      regulator_disable(res->vdda);
++      regulator_disable(res->vdda_phy);
++      regulator_disable(res->vdda_refclk);
++}
++
++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++
++      reset_control_assert(res->core);
++      clk_disable_unprepare(res->slave_bus);
++      clk_disable_unprepare(res->master_bus);
++      clk_disable_unprepare(res->iface);
++      clk_disable_unprepare(res->aux);
++      regulator_disable(res->vdda);
++}
++
++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      int ret;
++
++      ret = regulator_enable(res->vdda);
++      if (ret) {
++              dev_err(dev, "cannot enable vdda regulator\n");
++              return ret;
++      }
++
++      ret = regulator_enable(res->vdda_refclk);
++      if (ret) {
++              dev_err(dev, "cannot enable vdda_refclk regulator\n");
++              goto err_refclk;
++      }
++
++      ret = regulator_enable(res->vdda_phy);
++      if (ret) {
++              dev_err(dev, "cannot enable vdda_phy regulator\n");
++              goto err_vdda_phy;
++      }
++
++      ret = clk_prepare_enable(res->iface_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable iface clock\n");
++              goto err_iface;
++      }
++
++      ret = clk_prepare_enable(res->core_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable core clock\n");
++              goto err_clk_core;
++      }
++
++      ret = clk_prepare_enable(res->phy_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable phy clock\n");
++              goto err_clk_phy;
++      }
++
++      ret = reset_control_deassert(res->ahb_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert ahb reset\n");
++              goto err_reset_ahb;
++      }
++
++      return 0;
++
++err_reset_ahb:
++      clk_disable_unprepare(res->phy_clk);
++err_clk_phy:
++      clk_disable_unprepare(res->core_clk);
++err_clk_core:
++      clk_disable_unprepare(res->iface_clk);
++err_iface:
++      regulator_disable(res->vdda_phy);
++err_vdda_phy:
++      regulator_disable(res->vdda_refclk);
++err_refclk:
++      regulator_disable(res->vdda);
++      return ret;
++}
++
++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++      struct device *dev = pcie->dev;
++      int ret;
++
++      ret = reset_control_deassert(res->core);
++      if (ret) {
++              dev_err(dev, "cannot deassert core reset\n");
++              return ret;
++      }
++
++      ret = clk_prepare_enable(res->aux);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable aux clock\n");
++              goto err_res;
++      }
++
++      ret = clk_prepare_enable(res->iface);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable iface clock\n");
++              goto err_aux;
++      }
++
++      ret = clk_prepare_enable(res->master_bus);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable master_bus clock\n");
++              goto err_iface;
++      }
++
++      ret = clk_prepare_enable(res->slave_bus);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable slave_bus clock\n");
++              goto err_master;
++      }
++
++      ret = regulator_enable(res->vdda);
++      if (ret) {
++              dev_err(dev, "cannot enable vdda regulator\n");
++              goto err_slave;
++      }
++
++      return 0;
++
++err_slave:
++      clk_disable_unprepare(res->slave_bus);
++err_master:
++      clk_disable_unprepare(res->master_bus);
++err_iface:
++      clk_disable_unprepare(res->iface);
++err_aux:
++      clk_disable_unprepare(res->aux);
++err_res:
++      reset_control_assert(res->core);
++
++      return ret;
++}
++
++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++
++      res->vdda = devm_regulator_get(dev, "vdda");
++      if (IS_ERR(res->vdda))
++              return PTR_ERR(res->vdda);
++
++      res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
++      if (IS_ERR(res->vdda_phy))
++              return PTR_ERR(res->vdda_phy);
++
++      res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
++      if (IS_ERR(res->vdda_refclk))
++              return PTR_ERR(res->vdda_refclk);
++
++      res->iface_clk = devm_clk_get(dev, "iface");
++      if (IS_ERR(res->iface_clk))
++              return PTR_ERR(res->iface_clk);
++
++      res->core_clk = devm_clk_get(dev, "core");
++      if (IS_ERR(res->core_clk))
++              return PTR_ERR(res->core_clk);
++
++      res->phy_clk = devm_clk_get(dev, "phy");
++      if (IS_ERR(res->phy_clk))
++              return PTR_ERR(res->phy_clk);
++
++      res->pci_reset = devm_reset_control_get(dev, "pci");
++      if (IS_ERR(res->pci_reset))
++              return PTR_ERR(res->pci_reset);
++
++      res->axi_reset = devm_reset_control_get(dev, "axi");
++      if (IS_ERR(res->axi_reset))
++              return PTR_ERR(res->axi_reset);
++
++      res->ahb_reset = devm_reset_control_get(dev, "ahb");
++      if (IS_ERR(res->ahb_reset))
++              return PTR_ERR(res->ahb_reset);
++
++      res->por_reset = devm_reset_control_get(dev, "por");
++      if (IS_ERR(res->por_reset))
++              return PTR_ERR(res->por_reset);
++
++      res->phy_reset = devm_reset_control_get(dev, "phy");
++      if (IS_ERR(res->phy_reset))
++              return PTR_ERR(res->phy_reset);
++
++      return 0;
++}
++
++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
++{
++      struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
++      struct device *dev = pcie->dev;
++
++      res->vdda = devm_regulator_get(dev, "vdda");
++      if (IS_ERR(res->vdda))
++              return PTR_ERR(res->vdda);
++
++      res->iface = devm_clk_get(dev, "iface");
++      if (IS_ERR(res->iface))
++              return PTR_ERR(res->iface);
++
++      res->aux = devm_clk_get(dev, "aux");
++      if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER)
++              return -EPROBE_DEFER;
++      else if (IS_ERR(res->aux))
++              res->aux = NULL;
++
++      res->master_bus = devm_clk_get(dev, "master_bus");
++      if (IS_ERR(res->master_bus))
++              return PTR_ERR(res->master_bus);
++
++      res->slave_bus = devm_clk_get(dev, "slave_bus");
++      if (IS_ERR(res->slave_bus))
++              return PTR_ERR(res->slave_bus);
++
++      res->core = devm_reset_control_get(dev, "core");
++      if (IS_ERR(res->core))
++              return PTR_ERR(res->core);
++
++      return 0;
++}
++
++static int qcom_pcie_enable_link_training(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct device *dev = pp->dev;
++      int retries;
++      u32 val;
++
++      /* enable link training */
++      writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
++
++      /* wait for up to 100ms for the link to come up */
++      retries = LINKUP_RETRIES_COUNT;
++      do {
++              val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
++              if (val & XMLH_LINK_UP)
++                      break;
++              usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
++      } while (retries--);
++
++      if (retries < 0 || !dw_pcie_link_up(pp)) {
++              dev_err(dev, "link initialization failed\n");
++              return -ENXIO;
++      }
++
++      return 0;
++}
++
++static void qcom_pcie_host_init_v1(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v1(pcie);
++      if (ret)
++              return;
++
++      /* change DBI base address */
++      writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
++                            0, BIT(31));
++
++      ret = phy_init(pcie->phy);
++      if (ret)
++              goto err_res;
++
++      ret = phy_power_on(pcie->phy);
++      if (ret)
++              goto err_phy;
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++
++err:
++      qcom_ep_reset_assert(pcie);
++      phy_power_off(pcie->phy);
++err_phy:
++      phy_exit(pcie->phy);
++err_res:
++      qcom_pcie_disable_resources_v1(pcie);
++}
++
++static void qcom_pcie_host_init_v0(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++      struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
++      struct device *dev = pcie->dev;
++      int ret;
++
++      qcom_ep_reset_assert(pcie);
++
++      ret = qcom_pcie_enable_resources_v0(pcie);
++      if (ret)
++              return;
++
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
++
++      /* enable external reference clock */
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
++
++      ret = reset_control_deassert(res->phy_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert phy reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->pci_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert pci reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->por_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert por reset\n");
++              return;
++      }
++
++      ret = reset_control_deassert(res->axi_reset);
++      if (ret) {
++              dev_err(dev, "cannot deassert axi reset\n");
++              return;
++      }
++
++      /* wait 150ms for clock acquisition */
++      usleep_range(10000, 15000);
++
++      dw_pcie_setup_rc(pp);
++
++      if (IS_ENABLED(CONFIG_PCI_MSI))
++              dw_pcie_msi_init(pp);
++
++      qcom_ep_reset_deassert(pcie);
++
++      ret = qcom_pcie_enable_link_training(pp);
++      if (ret)
++              goto err;
++
++      return;
++err:
++      qcom_ep_reset_assert(pcie);
++      qcom_pcie_disable_resources_v0(pcie);
++}
++
++static void qcom_pcie_host_init(struct pcie_port *pp)
++{
++      struct qcom_pcie *pcie = to_qcom_pcie(pp);
++
++      if (pcie->version == PCIE_V0)
++              return qcom_pcie_host_init_v0(pp);
++      else
++              return qcom_pcie_host_init_v1(pp);
++}
++
++static int
++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val)
++{
++      /* the device class is not reported correctly from the register */
++      if (where == PCI_CLASS_REVISION && size == 4) {
++              *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
++              *val &= ~(0xffff << 16);
++              *val |= PCI_CLASS_BRIDGE_PCI << 16;
++              return PCIBIOS_SUCCESSFUL;
++      }
++
++      return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
++                              size, val);
++}
++
++static struct pcie_host_ops qcom_pcie_ops = {
++      .link_up = qcom_pcie_link_up,
++      .host_init = qcom_pcie_host_init,
++      .rd_own_conf = qcom_pcie_rd_own_conf,
++};
++
++static const struct of_device_id qcom_pcie_match[] = {
++      { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
++      { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
++      { }
++};
++
++static int qcom_pcie_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      const struct of_device_id *match;
++      struct resource *res;
++      struct qcom_pcie *pcie;
++      struct pcie_port *pp;
++      int ret;
++
++      match = of_match_node(qcom_pcie_match, dev->of_node);
++      if (!match)
++              return -ENXIO;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pcie->version = (unsigned int)match->data;
++
++      pcie->reset = devm_gpiod_get_optional(dev, "perst");
++      if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
++              return PTR_ERR(pcie->reset);
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
++      pcie->parf = devm_ioremap_resource(dev, res);
++      if (IS_ERR(pcie->parf))
++              return PTR_ERR(pcie->parf);
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
++      pcie->dbi = devm_ioremap_resource(dev, res);
++      if (IS_ERR(pcie->dbi))
++              return PTR_ERR(pcie->dbi);
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
++      pcie->elbi = devm_ioremap_resource(dev, res);
++      if (IS_ERR(pcie->elbi))
++              return PTR_ERR(pcie->elbi);
++
++      pcie->phy = devm_phy_optional_get(dev, "pciephy");
++      if (IS_ERR(pcie->phy))
++              return PTR_ERR(pcie->phy);
++
++      pcie->dev = dev;
++
++      if (pcie->version == PCIE_V0)
++              ret = qcom_pcie_get_resources_v0(pcie);
++      else
++              ret = qcom_pcie_get_resources_v1(pcie);
++
++      if (ret)
++              return ret;
++
++      pp = &pcie->pp;
++      pp->dev = dev;
++      pp->dbi_base = pcie->dbi;
++      pp->root_bus_nr = -1;
++      pp->ops = &qcom_pcie_ops;
++
++      if (IS_ENABLED(CONFIG_PCI_MSI)) {
++              pp->msi_irq = platform_get_irq_byname(pdev, "msi");
++              if (pp->msi_irq < 0) {
++                      dev_err(dev, "cannot get msi irq\n");
++                      return pp->msi_irq;
++              }
++
++              ret = devm_request_irq(dev, pp->msi_irq,
++                                     qcom_pcie_msi_irq_handler,
++                                     IRQF_SHARED, "qcom-pcie-msi", pp);
++              if (ret) {
++                      dev_err(dev, "cannot request msi irq\n");
++                      return ret;
++              }
++      }
++
++      ret = dw_pcie_host_init(pp);
++      if (ret) {
++              dev_err(dev, "cannot initialize host\n");
++              return ret;
++      }
++
++      platform_set_drvdata(pdev, pcie);
++
++      return 0;
++}
++
++static int qcom_pcie_remove(struct platform_device *pdev)
++{
++      struct qcom_pcie *pcie = platform_get_drvdata(pdev);
++
++      qcom_ep_reset_assert(pcie);
++      phy_power_off(pcie->phy);
++      phy_exit(pcie->phy);
++      if (pcie->version == PCIE_V0)
++              qcom_pcie_disable_resources_v0(pcie);
++      else
++              qcom_pcie_disable_resources_v1(pcie);
++
++      return 0;
++}
++
++static struct platform_driver qcom_pcie_driver = {
++      .probe = qcom_pcie_probe,
++      .remove = qcom_pcie_remove,
++      .driver = {
++              .name = "qcom-pcie",
++              .of_match_table = qcom_pcie_match,
++      },
++};
++
++module_platform_driver(qcom_pcie_driver);
++
++MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qcom-pcie");
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+ obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
diff --git a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch
new file mode 100644 (file)
index 0000000..a57de6c
--- /dev/null
@@ -0,0 +1,244 @@
+From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:01:42 -0700
+Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms
+
+qcom-pcie driver now supports version 0 of the controller. This change
+adds the corresponding entries to the IPQ806x dtsi file and
+corresponding platform (AP148).
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq8064-ap148.dts |  30 ++++++++
+ arch/arm/boot/dts/qcom-ipq8064.dtsi      | 124 +++++++++++++++++++++++++++++++
+ 2 files changed, 154 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -115,5 +115,15 @@
+               usb30@1 {
+                       status = "ok";
+               };
++
++              pcie0: pci@1b500000 {
++                      status = "ok";
++                      phy-tx0-term-offset = <7>;
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++                      phy-tx0-term-offset = <7>;
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
+@@ -128,5 +128,17 @@
+               usb30@1 {
+                       status = "ok";
+               };
++
++              pcie0: pci@1b500000 {
++                      status = "ok";
++              };
++
++              pcie1: pci@1b700000 {
++                      status = "ok";
++              };
++
++              pcie2: pci@1b900000 {
++                      status = "ok";
++              };
+       };
+ };
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -3,6 +3,9 @@
+ #include "skeleton.dtsi"
+ #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+ #include <dt-bindings/soc/qcom,gsbi.h>
++#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/gpio/gpio.h>
+ / {
+       model = "Qualcomm IPQ8064";
+@@ -83,6 +86,33 @@
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
+                       interrupts = <0 32 0x4>;
++
++                      pcie0_pins: pcie0_pinmux {
++                              mux {
++                                      pins = "gpio3";
++                                      function = "pcie1_rst";
++                                      drive-strength = <12>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie1_pins: pcie1_pinmux {
++                              mux {
++                                      pins = "gpio48";
++                                      function = "pcie2_rst";
++                                      drive-strength = <12>;
++                                      bias-disable;
++                              };
++                      };
++
++                      pcie2_pins: pcie2_pinmux {
++                              mux {
++                                      pins = "gpio63";
++                                      function = "pcie3_rst";
++                                      drive-strength = <12>;
++                                      bias-disable;
++                              };
++                      };
+               };
+               intc: interrupt-controller@2000000 {
+@@ -311,6 +341,144 @@
+                       reg = <0x01200600 0x100>;
+               };
++              pcie0: pci@1b500000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b500000 0x1000
++                             0x1b502000 0x80
++                             0x1b600000 0x100
++                             0x0ff00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <0>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_A_CLK>,
++                               <&gcc PCIE_H_CLK>,
++                               <&gcc PCIE_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_ACLK_RESET>,
++                               <&gcc PCIE_HCLK_RESET>,
++                               <&gcc PCIE_POR_RESET>,
++                               <&gcc PCIE_PCI_RESET>,
++                               <&gcc PCIE_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      pinctrl-0 = <&pcie0_pins>;
++                      pinctrl-names = "default";
++
++                      perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>;
++
++                      status = "disabled";
++              };
++
++              pcie1: pci@1b700000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b700000 0x1000
++                             0x1b702000 0x80
++                             0x1b800000 0x100
++                             0x31f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <1>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_1_A_CLK>,
++                               <&gcc PCIE_1_H_CLK>,
++                               <&gcc PCIE_1_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_1_ACLK_RESET>,
++                               <&gcc PCIE_1_HCLK_RESET>,
++                               <&gcc PCIE_1_POR_RESET>,
++                               <&gcc PCIE_1_PCI_RESET>,
++                               <&gcc PCIE_1_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      pinctrl-0 = <&pcie1_pins>;
++                      pinctrl-names = "default";
++
++                      perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>;
++
++                      status = "disabled";
++              };
++
++              pcie2: pci@1b900000 {
++                      compatible = "qcom,pcie-v0";
++                      reg = <0x1b900000 0x1000
++                             0x1b902000 0x80
++                             0x1ba00000 0x100
++                             0x35f00000 0x100000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <2>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */
++
++                      interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      clocks = <&gcc PCIE_2_A_CLK>,
++                               <&gcc PCIE_2_H_CLK>,
++                               <&gcc PCIE_2_PHY_CLK>;
++                      clock-names = "core", "iface", "phy";
++
++                      resets = <&gcc PCIE_2_ACLK_RESET>,
++                               <&gcc PCIE_2_HCLK_RESET>,
++                               <&gcc PCIE_2_POR_RESET>,
++                               <&gcc PCIE_2_PCI_RESET>,
++                               <&gcc PCIE_2_PHY_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy";
++
++                      pinctrl-0 = <&pcie2_pins>;
++                      pinctrl-names = "default";
++
++                      perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>;
++
++                      status = "disabled";
++              };
++
+               hs_phy_1: phy@100f8800 {
+                       compatible = "qcom,dwc3-hs-usb-phy";
+                       reg = <0x100f8800 0x30>;
diff --git a/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch
new file mode 100644 (file)
index 0000000..4f0e45f
--- /dev/null
@@ -0,0 +1,29 @@
+From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001
+From: Mathieu Olivari <mathieu@codeaurora.org>
+Date: Tue, 21 Apr 2015 19:09:07 -0700
+Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is
+ enabled
+
+If multiple PCIe devices are present in the system, the kernel will
+panic at boot time when trying to scan the PCI buses. This happens on
+IPQ806x based platforms, which has 3 PCIe ports.
+
+Enabling this option allows the kernel to assign the pci-domains
+according to the device-tree content. This allows multiple PCIe
+controllers to coexist in the system.
+
+Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
+---
+ arch/arm/mach-qcom/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
+       select CLKSRC_OF
+       select PINCTRL
+       select QCOM_SCM if SMP
++      select PCI_DOMAINS if PCI
+       help
+         Support for Qualcomm's devicetree based systems.
diff --git a/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch
new file mode 100644 (file)
index 0000000..c00abca
--- /dev/null
@@ -0,0 +1,311 @@
+--- a/drivers/pci/host/pcie-qcom.c
++++ b/drivers/pci/host/pcie-qcom.c
+@@ -29,8 +29,53 @@
+ #include "pcie-designware.h"
++/* DBI registers */
++#define PCIE20_CAP                            0x70
++#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
++
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0               0x818
++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1               0x81c
++
++#define PCIE20_PLR_IATU_VIEWPORT              0x900
++#define PCIE20_PLR_IATU_REGION_OUTBOUND               (0x0 << 31)
++#define PCIE20_PLR_IATU_REGION_INDEX(x)               (x << 0)
++
++#define PCIE20_PLR_IATU_CTRL1                 0x904
++#define PCIE20_PLR_IATU_TYPE_CFG0             (0x4 << 0)
++#define PCIE20_PLR_IATU_TYPE_MEM              (0x0 << 0)
++
++#define PCIE20_PLR_IATU_CTRL2                 0x908
++#define PCIE20_PLR_IATU_ENABLE                        BIT(31)
++
++#define PCIE20_PLR_IATU_LBAR                  0x90C
++#define PCIE20_PLR_IATU_UBAR                  0x910
++#define PCIE20_PLR_IATU_LAR                   0x914
++#define PCIE20_PLR_IATU_LTAR                  0x918
++#define PCIE20_PLR_IATU_UTAR                  0x91c
++
++#define MSM_PCIE_DEV_CFG_ADDR                 0x01000000
++
++/* PARF registers */
++#define PCIE20_PARF_PCS_DEEMPH                        0x34
++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x)          (x << 16)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x)    (x << 8)
++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x)      (x << 0)
++
++#define PCIE20_PARF_PCS_SWING                 0x38
++#define PCS_SWING_TX_SWING_FULL(x)            (x << 8)
++#define PCS_SWING_TX_SWING_LOW(x)             (x << 0)
++
+ #define PCIE20_PARF_PHY_CTRL                  0x40
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK     (0x1f << 16)
++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x)               (x << 16)
++
+ #define PCIE20_PARF_PHY_REFCLK                        0x4C
++#define REF_SSP_EN                            BIT(16)
++#define REF_USE_PAD                           BIT(12)
++
++#define PCIE20_PARF_CONFIG_BITS                       0x50
++#define PHY_RX0_EQ(x)                         (x << 24)
++
+ #define PCIE20_PARF_DBI_BASE_ADDR             0x168
+ #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE               0x16c
+ #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT     0x178
+@@ -39,9 +84,6 @@
+ #define PCIE20_ELBI_SYS_STTS                  0x08
+ #define XMLH_LINK_UP                          BIT(10)
+-#define PCIE20_CAP                            0x70
+-#define PCIE20_CAP_LINKCTRLSTATUS             (PCIE20_CAP + 0x10)
+-
+ #define PERST_DELAY_MIN_US                    1000
+ #define PERST_DELAY_MAX_US                    1005
+@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 {
+       struct clk *iface_clk;
+       struct clk *core_clk;
+       struct clk *phy_clk;
++      struct clk *aux_clk;
++      struct clk *ref_clk;
+       struct reset_control *pci_reset;
+       struct reset_control *axi_reset;
+       struct reset_control *ahb_reset;
+       struct reset_control *por_reset;
+       struct reset_control *phy_reset;
++      struct reset_control *ext_reset;
+       struct regulator *vdda;
+       struct regulator *vdda_phy;
+       struct regulator *vdda_refclk;
++      uint8_t phy_tx0_term_offset;
+ };
+ struct qcom_pcie_resources_v1 {
+@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl
+ static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
+ {
+-      int val, active_low;
+-
+       if (IS_ERR_OR_NULL(pcie->reset))
+               return;
+-      active_low = gpiod_is_active_low(pcie->reset);
+-
+-      if (assert)
+-              val = !!active_low;
+-      else
+-              val = !active_low;
+-
+-      gpiod_set_value(pcie->reset, val);
+-
++      gpiod_set_value(pcie->reset, assert);
+       usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
+ }
+@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_
+       reset_control_assert(res->axi_reset);
+       reset_control_assert(res->ahb_reset);
+       reset_control_assert(res->por_reset);
+-      reset_control_assert(res->pci_reset);
++      reset_control_assert(res->phy_reset);
++      reset_control_assert(res->ext_reset);
+       clk_disable_unprepare(res->iface_clk);
+       clk_disable_unprepare(res->core_clk);
+       clk_disable_unprepare(res->phy_clk);
++      clk_disable_unprepare(res->aux_clk);
++      clk_disable_unprepare(res->ref_clk);
+       regulator_disable(res->vdda);
+       regulator_disable(res->vdda_phy);
+       regulator_disable(res->vdda_refclk);
+@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0
+               goto err_vdda_phy;
+       }
++      ret = reset_control_deassert(res->ext_reset);
++      if (ret) {
++              dev_err(dev, "cannot assert ext reset\n");
++              goto err_reset_ext;
++      }
++
+       ret = clk_prepare_enable(res->iface_clk);
+       if (ret) {
+               dev_err(dev, "cannot prepare/enable iface clock\n");
+@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0
+               goto err_clk_phy;
+       }
++      ret = clk_prepare_enable(res->aux_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable aux clock\n");
++              goto err_clk_aux;
++      }
++
++      ret = clk_prepare_enable(res->ref_clk);
++      if (ret) {
++              dev_err(dev, "cannot prepare/enable ref clock\n");
++              goto err_clk_ref;
++      }
++
+       ret = reset_control_deassert(res->ahb_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert ahb reset\n");
+               goto err_reset_ahb;
+       }
++      udelay(1);
+       return 0;
+ err_reset_ahb:
++      clk_disable_unprepare(res->ref_clk);
++err_clk_ref:
++      clk_disable_unprepare(res->aux_clk);
++err_clk_aux:
+       clk_disable_unprepare(res->phy_clk);
+ err_clk_phy:
+       clk_disable_unprepare(res->core_clk);
+ err_clk_core:
+       clk_disable_unprepare(res->iface_clk);
+ err_iface:
++      reset_control_assert(res->ext_reset);
++err_reset_ext:
+       regulator_disable(res->vdda_phy);
+ err_vdda_phy:
+       regulator_disable(res->vdda_refclk);
+@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st
+       if (IS_ERR(res->phy_clk))
+               return PTR_ERR(res->phy_clk);
++      res->aux_clk = devm_clk_get(dev, "aux");
++      if (IS_ERR(res->aux_clk))
++              return PTR_ERR(res->aux_clk);
++
++      res->ref_clk = devm_clk_get(dev, "ref");
++      if (IS_ERR(res->ref_clk))
++              return PTR_ERR(res->ref_clk);
++
+       res->pci_reset = devm_reset_control_get(dev, "pci");
+       if (IS_ERR(res->pci_reset))
+               return PTR_ERR(res->pci_reset);
+@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st
+       if (IS_ERR(res->phy_reset))
+               return PTR_ERR(res->phy_reset);
++      res->ext_reset = devm_reset_control_get(dev, "ext");
++      if (IS_ERR(res->ext_reset))
++              return PTR_ERR(res->ext_reset);
++
++      if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
++                              &res->phy_tx0_term_offset))
++              res->phy_tx0_term_offset = 0;
++
+       return 0;
+ }
+@@ -461,6 +541,57 @@ err_res:
+       qcom_pcie_disable_resources_v1(pcie);
+ }
++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev)
++{
++      struct pcie_port *pp = &pcie->pp;
++
++      /*
++       * program and enable address translation region 0 (device config
++       * address space); region type config;
++       * axi config address range to device config address range
++       */
++      writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++             PCIE20_PLR_IATU_REGION_INDEX(0),
++             pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++      writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++      writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++      writel(pp->cfg0_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++      writel((pp->cfg0_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++      writel((pp->cfg0_mod_base + pp->cfg0_size - 1),
++             pcie->dbi + PCIE20_PLR_IATU_LAR);
++      writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++      writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR);
++}
++
++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie)
++{
++      struct pcie_port *pp = &pcie->pp;
++
++      /*
++       * program and enable address translation region 2 (device resource
++       * address space); region type memory;
++       * axi device bar address range to device bar address range
++       */
++      writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
++             PCIE20_PLR_IATU_REGION_INDEX(2),
++             pcie->dbi + PCIE20_PLR_IATU_VIEWPORT);
++
++      writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1);
++      writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2);
++      writel(pp->mem_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR);
++      writel((pp->mem_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR);
++      writel(pp->mem_mod_base + pp->mem_size - 1,
++             pcie->dbi + PCIE20_PLR_IATU_LAR);
++      writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR);
++      writel(upper_32_bits(pp->mem_bus_addr),
++             pcie->dbi + PCIE20_PLR_IATU_UTAR);
++
++      /* 256B PCIE buffer setting */
++      writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
++      writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
++}
++
+ static void qcom_pcie_host_init_v0(struct pcie_port *pp)
+ {
+       struct qcom_pcie *pcie = to_qcom_pcie(pp);
+@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc
+       qcom_ep_reset_assert(pcie);
++      reset_control_assert(res->ahb_reset);
++
+       ret = qcom_pcie_enable_resources_v0(pcie);
+       if (ret)
+               return;
+       writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
+-      /* enable external reference clock */
+-      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
++      /* Set Tx termination offset */
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL,
++                    PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
++                    PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
++
++      /* PARF programming */
++      writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
++             PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
++             PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
++             pcie->parf + PCIE20_PARF_PCS_DEEMPH);
++      writel(PCS_SWING_TX_SWING_FULL(0x78) |
++             PCS_SWING_TX_SWING_LOW(0x78),
++             pcie->parf + PCIE20_PARF_PCS_SWING);
++      writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
++
++      /* Enable reference clock */
++      writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK,
++                    REF_USE_PAD, REF_SSP_EN);
++      /* De-assert PHY, PCIe, POR and AXI resets */
+       ret = reset_control_deassert(res->phy_reset);
+       if (ret) {
+               dev_err(dev, "cannot deassert phy reset\n");
+@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc
+       if (ret)
+               goto err;
++      qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR);
++      qcom_pcie_prog_viewport_mem2_outbound(pcie);
++
+       return;
+ err:
+       qcom_ep_reset_assert(pcie);
diff --git a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch
new file mode 100644 (file)
index 0000000..9f32e8f
--- /dev/null
@@ -0,0 +1,80 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -369,15 +369,21 @@
+                       clocks = <&gcc PCIE_A_CLK>,
+                                <&gcc PCIE_H_CLK>,
+-                               <&gcc PCIE_PHY_CLK>;
+-                      clock-names = "core", "iface", "phy";
++                               <&gcc PCIE_PHY_CLK>,
++                               <&gcc PCIE_AUX_CLK>,
++                               <&gcc PCIE_ALT_REF_CLK>;
++                      clock-names = "core", "iface", "phy", "aux", "ref";
++
++                      assigned-clocks = <&gcc PCIE_ALT_REF_CLK>;
++                      assigned-clock-rates = <100000000>;
+                       resets = <&gcc PCIE_ACLK_RESET>,
+                                <&gcc PCIE_HCLK_RESET>,
+                                <&gcc PCIE_POR_RESET>,
+                                <&gcc PCIE_PCI_RESET>,
+-                               <&gcc PCIE_PHY_RESET>;
+-                      reset-names = "axi", "ahb", "por", "pci", "phy";
++                               <&gcc PCIE_PHY_RESET>,
++                               <&gcc PCIE_EXT_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+                       pinctrl-0 = <&pcie0_pins>;
+                       pinctrl-names = "default";
+@@ -415,15 +421,21 @@
+                       clocks = <&gcc PCIE_1_A_CLK>,
+                                <&gcc PCIE_1_H_CLK>,
+-                               <&gcc PCIE_1_PHY_CLK>;
+-                      clock-names = "core", "iface", "phy";
++                               <&gcc PCIE_1_PHY_CLK>,
++                               <&gcc PCIE_1_AUX_CLK>,
++                               <&gcc PCIE_1_ALT_REF_CLK>;
++                      clock-names = "core", "iface", "phy", "aux", "ref";
++
++                      assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>;
++                      assigned-clock-rates = <100000000>;
+                       resets = <&gcc PCIE_1_ACLK_RESET>,
+                                <&gcc PCIE_1_HCLK_RESET>,
+                                <&gcc PCIE_1_POR_RESET>,
+                                <&gcc PCIE_1_PCI_RESET>,
+-                               <&gcc PCIE_1_PHY_RESET>;
+-                      reset-names = "axi", "ahb", "por", "pci", "phy";
++                               <&gcc PCIE_1_PHY_RESET>,
++                               <&gcc PCIE_1_EXT_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+                       pinctrl-0 = <&pcie1_pins>;
+                       pinctrl-names = "default";
+@@ -461,15 +473,21 @@
+                       clocks = <&gcc PCIE_2_A_CLK>,
+                                <&gcc PCIE_2_H_CLK>,
+-                               <&gcc PCIE_2_PHY_CLK>;
+-                      clock-names = "core", "iface", "phy";
++                               <&gcc PCIE_2_PHY_CLK>,
++                               <&gcc PCIE_2_AUX_CLK>,
++                               <&gcc PCIE_2_ALT_REF_CLK>;
++                      clock-names = "core", "iface", "phy", "aux", "ref";
++
++                      assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>;
++                      assigned-clock-rates = <100000000>;
+                       resets = <&gcc PCIE_2_ACLK_RESET>,
+                                <&gcc PCIE_2_HCLK_RESET>,
+                                <&gcc PCIE_2_POR_RESET>,
+                                <&gcc PCIE_2_PCI_RESET>,
+-                               <&gcc PCIE_2_PHY_RESET>;
+-                      reset-names = "axi", "ahb", "por", "pci", "phy";
++                               <&gcc PCIE_2_PHY_RESET>,
++                               <&gcc PCIE_2_EXT_RESET>;
++                      reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
+                       pinctrl-0 = <&pcie2_pins>;
+                       pinctrl-names = "default";
diff --git a/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch b/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch
new file mode 100644 (file)
index 0000000..d819142
--- /dev/null
@@ -0,0 +1,654 @@
+From 58e214382bdd1eb48c5a3519182bddcb26edabad Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Wed, 26 Nov 2014 13:51:00 -0800
+Subject: [PATCH] mfd: qcom-rpm: Driver for the Qualcomm RPM
+
+Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960
+and 8064 based devices. The driver exposes resources that child drivers
+can operate on; to implementing regulator, clock and bus frequency
+drivers.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ drivers/mfd/Kconfig          |  14 ++
+ drivers/mfd/Makefile         |   1 +
+ drivers/mfd/qcom_rpm.c       | 581 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/qcom_rpm.h |  13 +
+ 4 files changed, 609 insertions(+)
+ create mode 100644 drivers/mfd/qcom_rpm.c
+ create mode 100644 include/linux/mfd/qcom_rpm.h
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -567,6 +567,20 @@ config MFD_PM8921_CORE
+         Say M here if you want to include support for PM8921 chip as a module.
+         This will build a module called "pm8921-core".
++config MFD_QCOM_RPM
++      tristate "Qualcomm Resource Power Manager (RPM)"
++      depends on ARCH_QCOM && OF
++      help
++        If you say yes to this option, support will be included for the
++        Resource Power Manager system found in the Qualcomm 8660, 8960 and
++        8064 based devices.
++
++        This is required to access many regulators, clocks and bus
++        frequencies controlled by the RPM on these devices.
++
++        Say M here if you want to include support for the Qualcomm RPM as a
++        module. This will build a module called "qcom_rpm".
++
+ config MFD_SPMI_PMIC
+       tristate "Qualcomm SPMI PMICs"
+       depends on ARCH_QCOM || COMPILE_TEST
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE)      += si476x-
+ obj-$(CONFIG_MFD_CS5535)      += cs5535-mfd.o
+ obj-$(CONFIG_MFD_OMAP_USB_HOST)       += omap-usb-host.o omap-usb-tll.o
+ obj-$(CONFIG_MFD_PM8921_CORE)         += pm8921-core.o ssbi.o
++obj-$(CONFIG_MFD_QCOM_RPM)    += qcom_rpm.o
+ obj-$(CONFIG_MFD_SPMI_PMIC)   += qcom-spmi-pmic.o
+ obj-$(CONFIG_TPS65911_COMPARATOR)     += tps65911-comparator.o
+ obj-$(CONFIG_MFD_TPS65090)    += tps65090.o
+--- /dev/null
++++ b/drivers/mfd/qcom_rpm.c
+@@ -0,0 +1,581 @@
++/*
++ * Copyright (c) 2014, Sony Mobile Communications AB.
++ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
++ * Author: Bjorn Andersson <bjorn.andersson@sonymobile.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 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/platform_device.h>
++#include <linux/of_platform.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/mfd/qcom_rpm.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regmap.h>
++
++#include <dt-bindings/mfd/qcom-rpm.h>
++
++struct qcom_rpm_resource {
++      unsigned target_id;
++      unsigned status_id;
++      unsigned select_id;
++      unsigned size;
++};
++
++struct qcom_rpm_data {
++      u32 version;
++      const struct qcom_rpm_resource *resource_table;
++      unsigned n_resources;
++};
++
++struct qcom_rpm {
++      struct device *dev;
++      struct regmap *ipc_regmap;
++      unsigned ipc_offset;
++      unsigned ipc_bit;
++
++      struct completion ack;
++      struct mutex lock;
++
++      void __iomem *status_regs;
++      void __iomem *ctrl_regs;
++      void __iomem *req_regs;
++
++      u32 ack_status;
++
++      const struct qcom_rpm_data *data;
++};
++
++#define RPM_STATUS_REG(rpm, i)        ((rpm)->status_regs + (i) * 4)
++#define RPM_CTRL_REG(rpm, i)  ((rpm)->ctrl_regs + (i) * 4)
++#define RPM_REQ_REG(rpm, i)   ((rpm)->req_regs + (i) * 4)
++
++#define RPM_REQUEST_TIMEOUT   (5 * HZ)
++
++#define RPM_REQUEST_CONTEXT   3
++#define RPM_REQ_SELECT                11
++#define RPM_ACK_CONTEXT               15
++#define RPM_ACK_SELECTOR      23
++#define RPM_SELECT_SIZE               7
++
++#define RPM_NOTIFICATION      BIT(30)
++#define RPM_REJECTED          BIT(31)
++
++#define RPM_SIGNAL            BIT(2)
++
++static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = {
++      [QCOM_RPM_CXO_CLK] =                    { 25, 9, 5, 1 },
++      [QCOM_RPM_PXO_CLK] =                    { 26, 10, 6, 1 },
++      [QCOM_RPM_APPS_FABRIC_CLK] =            { 27, 11, 8, 1 },
++      [QCOM_RPM_SYS_FABRIC_CLK] =             { 28, 12, 9, 1 },
++      [QCOM_RPM_MM_FABRIC_CLK] =              { 29, 13, 10, 1 },
++      [QCOM_RPM_DAYTONA_FABRIC_CLK] =         { 30, 14, 11, 1 },
++      [QCOM_RPM_SFPB_CLK] =                   { 31, 15, 12, 1 },
++      [QCOM_RPM_CFPB_CLK] =                   { 32, 16, 13, 1 },
++      [QCOM_RPM_MMFPB_CLK] =                  { 33, 17, 14, 1 },
++      [QCOM_RPM_EBI1_CLK] =                   { 34, 18, 16, 1 },
++      [QCOM_RPM_APPS_FABRIC_HALT] =           { 35, 19, 18, 1 },
++      [QCOM_RPM_APPS_FABRIC_MODE] =           { 37, 20, 19, 1 },
++      [QCOM_RPM_APPS_FABRIC_IOCTL] =          { 40, 21, 20, 1 },
++      [QCOM_RPM_APPS_FABRIC_ARB] =            { 41, 22, 21, 12 },
++      [QCOM_RPM_SYS_FABRIC_HALT] =            { 53, 23, 22, 1 },
++      [QCOM_RPM_SYS_FABRIC_MODE] =            { 55, 24, 23, 1 },
++      [QCOM_RPM_SYS_FABRIC_IOCTL] =           { 58, 25, 24, 1 },
++      [QCOM_RPM_SYS_FABRIC_ARB] =             { 59, 26, 25, 30 },
++      [QCOM_RPM_MM_FABRIC_HALT] =             { 89, 27, 26, 1 },
++      [QCOM_RPM_MM_FABRIC_MODE] =             { 91, 28, 27, 1 },
++      [QCOM_RPM_MM_FABRIC_IOCTL] =            { 94, 29, 28, 1 },
++      [QCOM_RPM_MM_FABRIC_ARB] =              { 95, 30, 29, 21 },
++      [QCOM_RPM_PM8921_SMPS1] =               { 116, 31, 30, 2 },
++      [QCOM_RPM_PM8921_SMPS2] =               { 118, 33, 31, 2 },
++      [QCOM_RPM_PM8921_SMPS3] =               { 120, 35, 32, 2 },
++      [QCOM_RPM_PM8921_SMPS4] =               { 122, 37, 33, 2 },
++      [QCOM_RPM_PM8921_SMPS5] =               { 124, 39, 34, 2 },
++      [QCOM_RPM_PM8921_SMPS6] =               { 126, 41, 35, 2 },
++      [QCOM_RPM_PM8921_SMPS7] =               { 128, 43, 36, 2 },
++      [QCOM_RPM_PM8921_SMPS8] =               { 130, 45, 37, 2 },
++      [QCOM_RPM_PM8921_LDO1] =                { 132, 47, 38, 2 },
++      [QCOM_RPM_PM8921_LDO2] =                { 134, 49, 39, 2 },
++      [QCOM_RPM_PM8921_LDO3] =                { 136, 51, 40, 2 },
++      [QCOM_RPM_PM8921_LDO4] =                { 138, 53, 41, 2 },
++      [QCOM_RPM_PM8921_LDO5] =                { 140, 55, 42, 2 },
++      [QCOM_RPM_PM8921_LDO6] =                { 142, 57, 43, 2 },
++      [QCOM_RPM_PM8921_LDO7] =                { 144, 59, 44, 2 },
++      [QCOM_RPM_PM8921_LDO8] =                { 146, 61, 45, 2 },
++      [QCOM_RPM_PM8921_LDO9] =                { 148, 63, 46, 2 },
++      [QCOM_RPM_PM8921_LDO10] =               { 150, 65, 47, 2 },
++      [QCOM_RPM_PM8921_LDO11] =               { 152, 67, 48, 2 },
++      [QCOM_RPM_PM8921_LDO12] =               { 154, 69, 49, 2 },
++      [QCOM_RPM_PM8921_LDO13] =               { 156, 71, 50, 2 },
++      [QCOM_RPM_PM8921_LDO14] =               { 158, 73, 51, 2 },
++      [QCOM_RPM_PM8921_LDO15] =               { 160, 75, 52, 2 },
++      [QCOM_RPM_PM8921_LDO16] =               { 162, 77, 53, 2 },
++      [QCOM_RPM_PM8921_LDO17] =               { 164, 79, 54, 2 },
++      [QCOM_RPM_PM8921_LDO18] =               { 166, 81, 55, 2 },
++      [QCOM_RPM_PM8921_LDO19] =               { 168, 83, 56, 2 },
++      [QCOM_RPM_PM8921_LDO20] =               { 170, 85, 57, 2 },
++      [QCOM_RPM_PM8921_LDO21] =               { 172, 87, 58, 2 },
++      [QCOM_RPM_PM8921_LDO22] =               { 174, 89, 59, 2 },
++      [QCOM_RPM_PM8921_LDO23] =               { 176, 91, 60, 2 },
++      [QCOM_RPM_PM8921_LDO24] =               { 178, 93, 61, 2 },
++      [QCOM_RPM_PM8921_LDO25] =               { 180, 95, 62, 2 },
++      [QCOM_RPM_PM8921_LDO26] =               { 182, 97, 63, 2 },
++      [QCOM_RPM_PM8921_LDO27] =               { 184, 99, 64, 2 },
++      [QCOM_RPM_PM8921_LDO28] =               { 186, 101, 65, 2 },
++      [QCOM_RPM_PM8921_LDO29] =               { 188, 103, 66, 2 },
++      [QCOM_RPM_PM8921_CLK1] =                { 190, 105, 67, 2 },
++      [QCOM_RPM_PM8921_CLK2] =                { 192, 107, 68, 2 },
++      [QCOM_RPM_PM8921_LVS1] =                { 194, 109, 69, 1 },
++      [QCOM_RPM_PM8921_LVS2] =                { 195, 110, 70, 1 },
++      [QCOM_RPM_PM8921_LVS3] =                { 196, 111, 71, 1 },
++      [QCOM_RPM_PM8921_LVS4] =                { 197, 112, 72, 1 },
++      [QCOM_RPM_PM8921_LVS5] =                { 198, 113, 73, 1 },
++      [QCOM_RPM_PM8921_LVS6] =                { 199, 114, 74, 1 },
++      [QCOM_RPM_PM8921_LVS7] =                { 200, 115, 75, 1 },
++      [QCOM_RPM_PM8821_SMPS1] =               { 201, 116, 76, 2 },
++      [QCOM_RPM_PM8821_SMPS2] =               { 203, 118, 77, 2 },
++      [QCOM_RPM_PM8821_LDO1] =                { 205, 120, 78, 2 },
++      [QCOM_RPM_PM8921_NCP] =                 { 207, 122, 80, 2 },
++      [QCOM_RPM_CXO_BUFFERS] =                { 209, 124, 81, 1 },
++      [QCOM_RPM_USB_OTG_SWITCH] =             { 210, 125, 82, 1 },
++      [QCOM_RPM_HDMI_SWITCH] =                { 211, 126, 83, 1 },
++      [QCOM_RPM_DDR_DMM] =                    { 212, 127, 84, 2 },
++      [QCOM_RPM_VDDMIN_GPIO] =                { 215, 131, 89, 1 },
++};
++
++static const struct qcom_rpm_data apq8064_template = {
++      .version = 3,
++      .resource_table = apq8064_rpm_resource_table,
++      .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table),
++};
++
++static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = {
++      [QCOM_RPM_CXO_CLK] =                    { 32, 12, 5, 1 },
++      [QCOM_RPM_PXO_CLK] =                    { 33, 13, 6, 1 },
++      [QCOM_RPM_PLL_4] =                      { 34, 14, 7, 1 },
++      [QCOM_RPM_APPS_FABRIC_CLK] =            { 35, 15, 8, 1 },
++      [QCOM_RPM_SYS_FABRIC_CLK] =             { 36, 16, 9, 1 },
++      [QCOM_RPM_MM_FABRIC_CLK] =              { 37, 17, 10, 1 },
++      [QCOM_RPM_DAYTONA_FABRIC_CLK] =         { 38, 18, 11, 1 },
++      [QCOM_RPM_SFPB_CLK] =                   { 39, 19, 12, 1 },
++      [QCOM_RPM_CFPB_CLK] =                   { 40, 20, 13, 1 },
++      [QCOM_RPM_MMFPB_CLK] =                  { 41, 21, 14, 1 },
++      [QCOM_RPM_SMI_CLK] =                    { 42, 22, 15, 1 },
++      [QCOM_RPM_EBI1_CLK] =                   { 43, 23, 16, 1 },
++      [QCOM_RPM_APPS_L2_CACHE_CTL] =          { 44, 24, 17, 1 },
++      [QCOM_RPM_APPS_FABRIC_HALT] =           { 45, 25, 18, 2 },
++      [QCOM_RPM_APPS_FABRIC_MODE] =           { 47, 26, 19, 3 },
++      [QCOM_RPM_APPS_FABRIC_ARB] =            { 51, 28, 21, 6 },
++      [QCOM_RPM_SYS_FABRIC_HALT] =            { 63, 29, 22, 2 },
++      [QCOM_RPM_SYS_FABRIC_MODE] =            { 65, 30, 23, 3 },
++      [QCOM_RPM_SYS_FABRIC_ARB] =             { 69, 32, 25, 22 },
++      [QCOM_RPM_MM_FABRIC_HALT] =             { 105, 33, 26, 2 },
++      [QCOM_RPM_MM_FABRIC_MODE] =             { 107, 34, 27, 3 },
++      [QCOM_RPM_MM_FABRIC_ARB] =              { 111, 36, 29, 23 },
++      [QCOM_RPM_PM8901_SMPS0] =               { 134, 37, 30, 2 },
++      [QCOM_RPM_PM8901_SMPS1] =               { 136, 39, 31, 2 },
++      [QCOM_RPM_PM8901_SMPS2] =               { 138, 41, 32, 2 },
++      [QCOM_RPM_PM8901_SMPS3] =               { 140, 43, 33, 2 },
++      [QCOM_RPM_PM8901_SMPS4] =               { 142, 45, 34, 2 },
++      [QCOM_RPM_PM8901_LDO0] =                { 144, 47, 35, 2 },
++      [QCOM_RPM_PM8901_LDO1] =                { 146, 49, 36, 2 },
++      [QCOM_RPM_PM8901_LDO2] =                { 148, 51, 37, 2 },
++      [QCOM_RPM_PM8901_LDO3] =                { 150, 53, 38, 2 },
++      [QCOM_RPM_PM8901_LDO4] =                { 152, 55, 39, 2 },
++      [QCOM_RPM_PM8901_LDO5] =                { 154, 57, 40, 2 },
++      [QCOM_RPM_PM8901_LDO6] =                { 156, 59, 41, 2 },
++      [QCOM_RPM_PM8901_LVS0] =                { 158, 61, 42, 1 },
++      [QCOM_RPM_PM8901_LVS1] =                { 159, 62, 43, 1 },
++      [QCOM_RPM_PM8901_LVS2] =                { 160, 63, 44, 1 },
++      [QCOM_RPM_PM8901_LVS3] =                { 161, 64, 45, 1 },
++      [QCOM_RPM_PM8901_MVS] =                 { 162, 65, 46, 1 },
++      [QCOM_RPM_PM8058_SMPS0] =               { 163, 66, 47, 2 },
++      [QCOM_RPM_PM8058_SMPS1] =               { 165, 68, 48, 2 },
++      [QCOM_RPM_PM8058_SMPS2] =               { 167, 70, 49, 2 },
++      [QCOM_RPM_PM8058_SMPS3] =               { 169, 72, 50, 2 },
++      [QCOM_RPM_PM8058_SMPS4] =               { 171, 74, 51, 2 },
++      [QCOM_RPM_PM8058_LDO0] =                { 173, 76, 52, 2 },
++      [QCOM_RPM_PM8058_LDO1] =                { 175, 78, 53, 2 },
++      [QCOM_RPM_PM8058_LDO2] =                { 177, 80, 54, 2 },
++      [QCOM_RPM_PM8058_LDO3] =                { 179, 82, 55, 2 },
++      [QCOM_RPM_PM8058_LDO4] =                { 181, 84, 56, 2 },
++      [QCOM_RPM_PM8058_LDO5] =                { 183, 86, 57, 2 },
++      [QCOM_RPM_PM8058_LDO6] =                { 185, 88, 58, 2 },
++      [QCOM_RPM_PM8058_LDO7] =                { 187, 90, 59, 2 },
++      [QCOM_RPM_PM8058_LDO8] =                { 189, 92, 60, 2 },
++      [QCOM_RPM_PM8058_LDO9] =                { 191, 94, 61, 2 },
++      [QCOM_RPM_PM8058_LDO10] =               { 193, 96, 62, 2 },
++      [QCOM_RPM_PM8058_LDO11] =               { 195, 98, 63, 2 },
++      [QCOM_RPM_PM8058_LDO12] =               { 197, 100, 64, 2 },
++      [QCOM_RPM_PM8058_LDO13] =               { 199, 102, 65, 2 },
++      [QCOM_RPM_PM8058_LDO14] =               { 201, 104, 66, 2 },
++      [QCOM_RPM_PM8058_LDO15] =               { 203, 106, 67, 2 },
++      [QCOM_RPM_PM8058_LDO16] =               { 205, 108, 68, 2 },
++      [QCOM_RPM_PM8058_LDO17] =               { 207, 110, 69, 2 },
++      [QCOM_RPM_PM8058_LDO18] =               { 209, 112, 70, 2 },
++      [QCOM_RPM_PM8058_LDO19] =               { 211, 114, 71, 2 },
++      [QCOM_RPM_PM8058_LDO20] =               { 213, 116, 72, 2 },
++      [QCOM_RPM_PM8058_LDO21] =               { 215, 118, 73, 2 },
++      [QCOM_RPM_PM8058_LDO22] =               { 217, 120, 74, 2 },
++      [QCOM_RPM_PM8058_LDO23] =               { 219, 122, 75, 2 },
++      [QCOM_RPM_PM8058_LDO24] =               { 221, 124, 76, 2 },
++      [QCOM_RPM_PM8058_LDO25] =               { 223, 126, 77, 2 },
++      [QCOM_RPM_PM8058_LVS0] =                { 225, 128, 78, 1 },
++      [QCOM_RPM_PM8058_LVS1] =                { 226, 129, 79, 1 },
++      [QCOM_RPM_PM8058_NCP] =                 { 227, 130, 80, 2 },
++      [QCOM_RPM_CXO_BUFFERS] =                { 229, 132, 81, 1 },
++};
++
++static const struct qcom_rpm_data msm8660_template = {
++      .version = 2,
++      .resource_table = msm8660_rpm_resource_table,
++      .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table),
++};
++
++static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = {
++      [QCOM_RPM_CXO_CLK] =                    { 25, 9, 5, 1 },
++      [QCOM_RPM_PXO_CLK] =                    { 26, 10, 6, 1 },
++      [QCOM_RPM_APPS_FABRIC_CLK] =            { 27, 11, 8, 1 },
++      [QCOM_RPM_SYS_FABRIC_CLK] =             { 28, 12, 9, 1 },
++      [QCOM_RPM_MM_FABRIC_CLK] =              { 29, 13, 10, 1 },
++      [QCOM_RPM_DAYTONA_FABRIC_CLK] =         { 30, 14, 11, 1 },
++      [QCOM_RPM_SFPB_CLK] =                   { 31, 15, 12, 1 },
++      [QCOM_RPM_CFPB_CLK] =                   { 32, 16, 13, 1 },
++      [QCOM_RPM_MMFPB_CLK] =                  { 33, 17, 14, 1 },
++      [QCOM_RPM_EBI1_CLK] =                   { 34, 18, 16, 1 },
++      [QCOM_RPM_APPS_FABRIC_HALT] =           { 35, 19, 18, 1 },
++      [QCOM_RPM_APPS_FABRIC_MODE] =           { 37, 20, 19, 1 },
++      [QCOM_RPM_APPS_FABRIC_IOCTL] =          { 40, 21, 20, 1 },
++      [QCOM_RPM_APPS_FABRIC_ARB] =            { 41, 22, 21, 12 },
++      [QCOM_RPM_SYS_FABRIC_HALT] =            { 53, 23, 22, 1 },
++      [QCOM_RPM_SYS_FABRIC_MODE] =            { 55, 24, 23, 1 },
++      [QCOM_RPM_SYS_FABRIC_IOCTL] =           { 58, 25, 24, 1 },
++      [QCOM_RPM_SYS_FABRIC_ARB] =             { 59, 26, 25, 29 },
++      [QCOM_RPM_MM_FABRIC_HALT] =             { 88, 27, 26, 1 },
++      [QCOM_RPM_MM_FABRIC_MODE] =             { 90, 28, 27, 1 },
++      [QCOM_RPM_MM_FABRIC_IOCTL] =            { 93, 29, 28, 1 },
++      [QCOM_RPM_MM_FABRIC_ARB] =              { 94, 30, 29, 23 },
++      [QCOM_RPM_PM8921_SMPS1] =               { 117, 31, 30, 2 },
++      [QCOM_RPM_PM8921_SMPS2] =               { 119, 33, 31, 2 },
++      [QCOM_RPM_PM8921_SMPS3] =               { 121, 35, 32, 2 },
++      [QCOM_RPM_PM8921_SMPS4] =               { 123, 37, 33, 2 },
++      [QCOM_RPM_PM8921_SMPS5] =               { 125, 39, 34, 2 },
++      [QCOM_RPM_PM8921_SMPS6] =               { 127, 41, 35, 2 },
++      [QCOM_RPM_PM8921_SMPS7] =               { 129, 43, 36, 2 },
++      [QCOM_RPM_PM8921_SMPS8] =               { 131, 45, 37, 2 },
++      [QCOM_RPM_PM8921_LDO1] =                { 133, 47, 38, 2 },
++      [QCOM_RPM_PM8921_LDO2] =                { 135, 49, 39, 2 },
++      [QCOM_RPM_PM8921_LDO3] =                { 137, 51, 40, 2 },
++      [QCOM_RPM_PM8921_LDO4] =                { 139, 53, 41, 2 },
++      [QCOM_RPM_PM8921_LDO5] =                { 141, 55, 42, 2 },
++      [QCOM_RPM_PM8921_LDO6] =                { 143, 57, 43, 2 },
++      [QCOM_RPM_PM8921_LDO7] =                { 145, 59, 44, 2 },
++      [QCOM_RPM_PM8921_LDO8] =                { 147, 61, 45, 2 },
++      [QCOM_RPM_PM8921_LDO9] =                { 149, 63, 46, 2 },
++      [QCOM_RPM_PM8921_LDO10] =               { 151, 65, 47, 2 },
++      [QCOM_RPM_PM8921_LDO11] =               { 153, 67, 48, 2 },
++      [QCOM_RPM_PM8921_LDO12] =               { 155, 69, 49, 2 },
++      [QCOM_RPM_PM8921_LDO13] =               { 157, 71, 50, 2 },
++      [QCOM_RPM_PM8921_LDO14] =               { 159, 73, 51, 2 },
++      [QCOM_RPM_PM8921_LDO15] =               { 161, 75, 52, 2 },
++      [QCOM_RPM_PM8921_LDO16] =               { 163, 77, 53, 2 },
++      [QCOM_RPM_PM8921_LDO17] =               { 165, 79, 54, 2 },
++      [QCOM_RPM_PM8921_LDO18] =               { 167, 81, 55, 2 },
++      [QCOM_RPM_PM8921_LDO19] =               { 169, 83, 56, 2 },
++      [QCOM_RPM_PM8921_LDO20] =               { 171, 85, 57, 2 },
++      [QCOM_RPM_PM8921_LDO21] =               { 173, 87, 58, 2 },
++      [QCOM_RPM_PM8921_LDO22] =               { 175, 89, 59, 2 },
++      [QCOM_RPM_PM8921_LDO23] =               { 177, 91, 60, 2 },
++      [QCOM_RPM_PM8921_LDO24] =               { 179, 93, 61, 2 },
++      [QCOM_RPM_PM8921_LDO25] =               { 181, 95, 62, 2 },
++      [QCOM_RPM_PM8921_LDO26] =               { 183, 97, 63, 2 },
++      [QCOM_RPM_PM8921_LDO27] =               { 185, 99, 64, 2 },
++      [QCOM_RPM_PM8921_LDO28] =               { 187, 101, 65, 2 },
++      [QCOM_RPM_PM8921_LDO29] =               { 189, 103, 66, 2 },
++      [QCOM_RPM_PM8921_CLK1] =                { 191, 105, 67, 2 },
++      [QCOM_RPM_PM8921_CLK2] =                { 193, 107, 68, 2 },
++      [QCOM_RPM_PM8921_LVS1] =                { 195, 109, 69, 1 },
++      [QCOM_RPM_PM8921_LVS2] =                { 196, 110, 70, 1 },
++      [QCOM_RPM_PM8921_LVS3] =                { 197, 111, 71, 1 },
++      [QCOM_RPM_PM8921_LVS4] =                { 198, 112, 72, 1 },
++      [QCOM_RPM_PM8921_LVS5] =                { 199, 113, 73, 1 },
++      [QCOM_RPM_PM8921_LVS6] =                { 200, 114, 74, 1 },
++      [QCOM_RPM_PM8921_LVS7] =                { 201, 115, 75, 1 },
++      [QCOM_RPM_PM8921_NCP] =                 { 202, 116, 80, 2 },
++      [QCOM_RPM_CXO_BUFFERS] =                { 204, 118, 81, 1 },
++      [QCOM_RPM_USB_OTG_SWITCH] =             { 205, 119, 82, 1 },
++      [QCOM_RPM_HDMI_SWITCH] =                { 206, 120, 83, 1 },
++      [QCOM_RPM_DDR_DMM] =                    { 207, 121, 84, 2 },
++};
++
++static const struct qcom_rpm_data msm8960_template = {
++      .version = 3,
++      .resource_table = msm8960_rpm_resource_table,
++      .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
++};
++
++static const struct of_device_id qcom_rpm_of_match[] = {
++      { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
++      { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
++      { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
++      { }
++};
++MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
++
++int qcom_rpm_write(struct qcom_rpm *rpm,
++                 int state,
++                 int resource,
++                 u32 *buf, size_t count)
++{
++      const struct qcom_rpm_resource *res;
++      const struct qcom_rpm_data *data = rpm->data;
++      u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
++      int left;
++      int ret = 0;
++      int i;
++
++      if (WARN_ON(resource < 0 || resource >= data->n_resources))
++              return -EINVAL;
++
++      res = &data->resource_table[resource];
++      if (WARN_ON(res->size != count))
++              return -EINVAL;
++
++      mutex_lock(&rpm->lock);
++
++      for (i = 0; i < res->size; i++)
++              writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i));
++
++      bitmap_set((unsigned long *)sel_mask, res->select_id, 1);
++      for (i = 0; i < ARRAY_SIZE(sel_mask); i++) {
++              writel_relaxed(sel_mask[i],
++                             RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i));
++      }
++
++      writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));
++
++      reinit_completion(&rpm->ack);
++      regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
++
++      left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT);
++      if (!left)
++              ret = -ETIMEDOUT;
++      else if (rpm->ack_status & RPM_REJECTED)
++              ret = -EIO;
++
++      mutex_unlock(&rpm->lock);
++
++      return ret;
++}
++EXPORT_SYMBOL(qcom_rpm_write);
++
++static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev)
++{
++      struct qcom_rpm *rpm = dev;
++      u32 ack;
++      int i;
++
++      ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
++      for (i = 0; i < RPM_SELECT_SIZE; i++)
++              writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i));
++      writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
++
++      if (ack & RPM_NOTIFICATION) {
++              dev_warn(rpm->dev, "ignoring notification!\n");
++      } else {
++              rpm->ack_status = ack;
++              complete(&rpm->ack);
++      }
++
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev)
++{
++      struct qcom_rpm *rpm = dev;
++
++      regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
++      dev_err(rpm->dev, "RPM triggered fatal error\n");
++
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev)
++{
++      return IRQ_HANDLED;
++}
++
++static int qcom_rpm_probe(struct platform_device *pdev)
++{
++      const struct of_device_id *match;
++      struct device_node *syscon_np;
++      struct resource *res;
++      struct qcom_rpm *rpm;
++      u32 fw_version[3];
++      int irq_wakeup;
++      int irq_ack;
++      int irq_err;
++      int ret;
++
++      rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL);
++      if (!rpm)
++              return -ENOMEM;
++
++      rpm->dev = &pdev->dev;
++      mutex_init(&rpm->lock);
++      init_completion(&rpm->ack);
++
++      irq_ack = platform_get_irq_byname(pdev, "ack");
++      if (irq_ack < 0) {
++              dev_err(&pdev->dev, "required ack interrupt missing\n");
++              return irq_ack;
++      }
++
++      irq_err = platform_get_irq_byname(pdev, "err");
++      if (irq_err < 0) {
++              dev_err(&pdev->dev, "required err interrupt missing\n");
++              return irq_err;
++      }
++
++      irq_wakeup = platform_get_irq_byname(pdev, "wakeup");
++      if (irq_wakeup < 0) {
++              dev_err(&pdev->dev, "required wakeup interrupt missing\n");
++              return irq_wakeup;
++      }
++
++      match = of_match_device(qcom_rpm_of_match, &pdev->dev);
++      rpm->data = match->data;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(rpm->status_regs))
++              return PTR_ERR(rpm->status_regs);
++      rpm->ctrl_regs = rpm->status_regs + 0x400;
++      rpm->req_regs = rpm->status_regs + 0x600;
++
++      syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
++      if (!syscon_np) {
++              dev_err(&pdev->dev, "no qcom,ipc node\n");
++              return -ENODEV;
++      }
++
++      rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
++      if (IS_ERR(rpm->ipc_regmap))
++              return PTR_ERR(rpm->ipc_regmap);
++
++      ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
++                                       &rpm->ipc_offset);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "no offset in qcom,ipc\n");
++              return -EINVAL;
++      }
++
++      ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
++                                       &rpm->ipc_bit);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "no bit in qcom,ipc\n");
++              return -EINVAL;
++      }
++
++      dev_set_drvdata(&pdev->dev, rpm);
++
++      fw_version[0] = readl(RPM_STATUS_REG(rpm, 0));
++      fw_version[1] = readl(RPM_STATUS_REG(rpm, 1));
++      fw_version[2] = readl(RPM_STATUS_REG(rpm, 2));
++      if (fw_version[0] != rpm->data->version) {
++              dev_err(&pdev->dev,
++                      "RPM version %u.%u.%u incompatible with driver version %u",
++                      fw_version[0],
++                      fw_version[1],
++                      fw_version[2],
++                      rpm->data->version);
++              return -EFAULT;
++      }
++
++      dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
++                                                      fw_version[1],
++                                                      fw_version[2]);
++
++      ret = devm_request_irq(&pdev->dev,
++                             irq_ack,
++                             qcom_rpm_ack_interrupt,
++                             IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
++                             "qcom_rpm_ack",
++                             rpm);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request ack interrupt\n");
++              return ret;
++      }
++
++      ret = irq_set_irq_wake(irq_ack, 1);
++      if (ret)
++              dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n");
++
++      ret = devm_request_irq(&pdev->dev,
++                             irq_err,
++                             qcom_rpm_err_interrupt,
++                             IRQF_TRIGGER_RISING,
++                             "qcom_rpm_err",
++                             rpm);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request err interrupt\n");
++              return ret;
++      }
++
++      ret = devm_request_irq(&pdev->dev,
++                             irq_wakeup,
++                             qcom_rpm_wakeup_interrupt,
++                             IRQF_TRIGGER_RISING,
++                             "qcom_rpm_wakeup",
++                             rpm);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request wakeup interrupt\n");
++              return ret;
++      }
++
++      ret = irq_set_irq_wake(irq_wakeup, 1);
++      if (ret)
++              dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n");
++
++      return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
++}
++
++static int qcom_rpm_remove(struct platform_device *pdev)
++{
++      of_platform_depopulate(&pdev->dev);
++      return 0;
++}
++
++static struct platform_driver qcom_rpm_driver = {
++      .probe = qcom_rpm_probe,
++      .remove = qcom_rpm_remove,
++      .driver  = {
++              .name  = "qcom_rpm",
++              .of_match_table = qcom_rpm_of_match,
++      },
++};
++
++static int __init qcom_rpm_init(void)
++{
++      return platform_driver_register(&qcom_rpm_driver);
++}
++arch_initcall(qcom_rpm_init);
++
++static void __exit qcom_rpm_exit(void)
++{
++      platform_driver_unregister(&qcom_rpm_driver);
++}
++module_exit(qcom_rpm_exit)
++
++MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver");
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+--- /dev/null
++++ b/include/linux/mfd/qcom_rpm.h
+@@ -0,0 +1,13 @@
++#ifndef __QCOM_RPM_H__
++#define __QCOM_RPM_H__
++
++#include <linux/types.h>
++
++struct qcom_rpm;
++
++#define QCOM_RPM_ACTIVE_STATE 0
++#define QCOM_RPM_SLEEP_STATE  1
++
++int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count);
++
++#endif
diff --git a/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch
new file mode 100644 (file)
index 0000000..e5e9e4e
--- /dev/null
@@ -0,0 +1,71 @@
+From 4d54b0adfa67476e6509bc8646b9dbac642e8a29 Mon Sep 17 00:00:00 2001
+From: Josh Cartwright <joshc@codeaurora.org>
+Date: Thu, 26 Mar 2015 11:29:26 -0700
+Subject: [PATCH] mfd: qcom_rpm: Add support for IPQ8064
+
+The IPQ8064 also includes an RPM following the same message structure as
+other chips.  In addition, it supports a few new resource types to
+support the NSS fabric clocks and the SMB208/SMB209 regulators found on
+the reference boards.
+
+Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ drivers/mfd/qcom_rpm.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 41 insertions(+)
+
+--- a/drivers/mfd/qcom_rpm.c
++++ b/drivers/mfd/qcom_rpm.c
+@@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm896
+       .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
+ };
++static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = {
++      [QCOM_RPM_CXO_CLK] =                    { 25, 9, 5, 1 },
++      [QCOM_RPM_PXO_CLK] =                    { 26, 10, 6, 1 },
++      [QCOM_RPM_APPS_FABRIC_CLK] =            { 27, 11, 8, 1 },
++      [QCOM_RPM_SYS_FABRIC_CLK] =             { 28, 12, 9, 1 },
++      [QCOM_RPM_NSS_FABRIC_0_CLK] =           { 29, 13, 10, 1 },
++      [QCOM_RPM_DAYTONA_FABRIC_CLK] =         { 30, 14, 11, 1 },
++      [QCOM_RPM_SFPB_CLK] =                   { 31, 15, 12, 1 },
++      [QCOM_RPM_CFPB_CLK] =                   { 32, 16, 13, 1 },
++      [QCOM_RPM_NSS_FABRIC_1_CLK] =           { 33, 17, 14, 1 },
++      [QCOM_RPM_EBI1_CLK] =                   { 34, 18, 16, 1 },
++      [QCOM_RPM_APPS_FABRIC_HALT] =           { 35, 19, 18, 2 },
++      [QCOM_RPM_APPS_FABRIC_MODE] =           { 37, 20, 19, 3 },
++      [QCOM_RPM_APPS_FABRIC_IOCTL] =          { 40, 21, 20, 1 },
++      [QCOM_RPM_APPS_FABRIC_ARB] =            { 41, 22, 21, 12 },
++      [QCOM_RPM_SYS_FABRIC_HALT] =            { 53, 23, 22, 2 },
++      [QCOM_RPM_SYS_FABRIC_MODE] =            { 55, 24, 23, 3 },
++      [QCOM_RPM_SYS_FABRIC_IOCTL] =           { 58, 25, 24, 1 },
++      [QCOM_RPM_SYS_FABRIC_ARB] =             { 59, 26, 25, 30 },
++      [QCOM_RPM_MM_FABRIC_HALT] =             { 89, 27, 26, 2 },
++      [QCOM_RPM_MM_FABRIC_MODE] =             { 91, 28, 27, 3 },
++      [QCOM_RPM_MM_FABRIC_IOCTL] =            { 94, 29, 28, 1 },
++      [QCOM_RPM_MM_FABRIC_ARB] =              { 95, 30, 29, 2 },
++      [QCOM_RPM_CXO_BUFFERS] =                { 209, 33, 31, 1 },
++      [QCOM_RPM_USB_OTG_SWITCH] =             { 210, 34, 32, 1 },
++      [QCOM_RPM_HDMI_SWITCH] =                { 211, 35, 33, 1 },
++      [QCOM_RPM_DDR_DMM] =                    { 212, 36, 34, 2 },
++      [QCOM_RPM_VDDMIN_GPIO] =                { 215, 40, 39, 1 },
++      [QCOM_RPM_SMB208_S1a] =                 { 216, 41, 90, 2 },
++      [QCOM_RPM_SMB208_S1b] =                 { 218, 43, 91, 2 },
++      [QCOM_RPM_SMB208_S2a] =                 { 220, 45, 92, 2 },
++      [QCOM_RPM_SMB208_S2b] =                 { 222, 47, 93, 2 },
++};
++
++static const struct qcom_rpm_data ipq806x_template = {
++      .version = 3,
++      .resource_table = ipq806x_rpm_resource_table,
++      .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table),
++};
++
+ static const struct of_device_id qcom_rpm_of_match[] = {
+       { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
+       { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
+       { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
++      { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
+       { }
+ };
+ MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
diff --git a/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch b/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch
new file mode 100644 (file)
index 0000000..a65d2c5
--- /dev/null
@@ -0,0 +1,247 @@
+From aa0c4b815045420ea54d5ae5362f5a0190609d46 Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Date: Wed, 26 Nov 2014 13:50:59 -0800
+Subject: [PATCH] mfd: devicetree: bindings: Add Qualcomm RPM DT binding
+
+Add binding for the Qualcomm Resource Power Manager (RPM) found in 8660,
+8960 and 8064 based devices.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
+Signed-off-by: Lee Jones <lee.jones@linaro.org>
+---
+ Documentation/devicetree/bindings/mfd/qcom-rpm.txt |  70 ++++++++++
+ include/dt-bindings/mfd/qcom-rpm.h                 | 154 +++++++++++++++++++++
+ 2 files changed, 224 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+ create mode 100644 include/dt-bindings/mfd/qcom-rpm.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+@@ -0,0 +1,70 @@
++Qualcomm Resource Power Manager (RPM)
++
++This driver is used to interface with the Resource Power Manager (RPM) found in
++various Qualcomm platforms. The RPM allows each component in the system to vote
++for state of the system resources, such as clocks, regulators and bus
++frequencies.
++
++- compatible:
++      Usage: required
++      Value type: <string>
++      Definition: must be one of:
++                  "qcom,rpm-apq8064"
++                  "qcom,rpm-msm8660"
++                  "qcom,rpm-msm8960"
++
++- reg:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: base address and size of the RPM's message ram
++
++- interrupts:
++      Usage: required
++      Value type: <prop-encoded-array>
++      Definition: three entries specifying the RPM's:
++                  1. acknowledgement interrupt
++                  2. error interrupt
++                  3. wakeup interrupt
++
++- interrupt-names:
++      Usage: required
++      Value type: <string-array>
++      Definition: must be the three strings "ack", "err" and "wakeup", in order
++
++- #address-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: must be 1
++
++- #size-cells:
++      Usage: required
++      Value type: <u32>
++      Definition: must be 0
++
++- qcom,ipc:
++      Usage: required
++      Value type: <prop-encoded-array>
++
++      Definition: three entries specifying the outgoing ipc bit used for
++                  signaling the RPM:
++                  - phandle to a syscon node representing the apcs registers
++                  - u32 representing offset to the register within the syscon
++                  - u32 representing the ipc bit within the register
++
++
++= EXAMPLE
++
++      #include <dt-bindings/mfd/qcom-rpm.h>
++
++      rpm@108000 {
++              compatible = "qcom,rpm-msm8960";
++              reg = <0x108000 0x1000>;
++              qcom,ipc = <&apcs 0x8 2>;
++
++              interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
++              interrupt-names = "ack", "err", "wakeup";
++
++              #address-cells = <1>;
++              #size-cells = <0>;
++      };
++
+--- /dev/null
++++ b/include/dt-bindings/mfd/qcom-rpm.h
+@@ -0,0 +1,154 @@
++/*
++ * This header provides constants for the Qualcomm RPM bindings.
++ */
++
++#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H
++#define _DT_BINDINGS_MFD_QCOM_RPM_H
++
++/*
++ * Constants use to identify individual resources in the RPM.
++ */
++#define QCOM_RPM_APPS_FABRIC_ARB              1
++#define QCOM_RPM_APPS_FABRIC_CLK              2
++#define QCOM_RPM_APPS_FABRIC_HALT             3
++#define QCOM_RPM_APPS_FABRIC_IOCTL            4
++#define QCOM_RPM_APPS_FABRIC_MODE             5
++#define QCOM_RPM_APPS_L2_CACHE_CTL            6
++#define QCOM_RPM_CFPB_CLK                     7
++#define QCOM_RPM_CXO_BUFFERS                  8
++#define QCOM_RPM_CXO_CLK                      9
++#define QCOM_RPM_DAYTONA_FABRIC_CLK           10
++#define QCOM_RPM_DDR_DMM                      11
++#define QCOM_RPM_EBI1_CLK                     12
++#define QCOM_RPM_HDMI_SWITCH                  13
++#define QCOM_RPM_MMFPB_CLK                    14
++#define QCOM_RPM_MM_FABRIC_ARB                        15
++#define QCOM_RPM_MM_FABRIC_CLK                        16
++#define QCOM_RPM_MM_FABRIC_HALT                       17
++#define QCOM_RPM_MM_FABRIC_IOCTL              18
++#define QCOM_RPM_MM_FABRIC_MODE                       19
++#define QCOM_RPM_PLL_4                                20
++#define QCOM_RPM_PM8058_LDO0                  21
++#define QCOM_RPM_PM8058_LDO1                  22
++#define QCOM_RPM_PM8058_LDO2                  23
++#define QCOM_RPM_PM8058_LDO3                  24
++#define QCOM_RPM_PM8058_LDO4                  25
++#define QCOM_RPM_PM8058_LDO5                  26
++#define QCOM_RPM_PM8058_LDO6                  27
++#define QCOM_RPM_PM8058_LDO7                  28
++#define QCOM_RPM_PM8058_LDO8                  29
++#define QCOM_RPM_PM8058_LDO9                  30
++#define QCOM_RPM_PM8058_LDO10                 31
++#define QCOM_RPM_PM8058_LDO11                 32
++#define QCOM_RPM_PM8058_LDO12                 33
++#define QCOM_RPM_PM8058_LDO13                 34
++#define QCOM_RPM_PM8058_LDO14                 35
++#define QCOM_RPM_PM8058_LDO15                 36
++#define QCOM_RPM_PM8058_LDO16                 37
++#define QCOM_RPM_PM8058_LDO17                 38
++#define QCOM_RPM_PM8058_LDO18                 39
++#define QCOM_RPM_PM8058_LDO19                 40
++#define QCOM_RPM_PM8058_LDO20                 41
++#define QCOM_RPM_PM8058_LDO21                 42
++#define QCOM_RPM_PM8058_LDO22                 43
++#define QCOM_RPM_PM8058_LDO23                 44
++#define QCOM_RPM_PM8058_LDO24                 45
++#define QCOM_RPM_PM8058_LDO25                 46
++#define QCOM_RPM_PM8058_LVS0                  47
++#define QCOM_RPM_PM8058_LVS1                  48
++#define QCOM_RPM_PM8058_NCP                   49
++#define QCOM_RPM_PM8058_SMPS0                 50
++#define QCOM_RPM_PM8058_SMPS1                 51
++#define QCOM_RPM_PM8058_SMPS2                 52
++#define QCOM_RPM_PM8058_SMPS3                 53
++#define QCOM_RPM_PM8058_SMPS4                 54
++#define QCOM_RPM_PM8821_LDO1                  55
++#define QCOM_RPM_PM8821_SMPS1                 56
++#define QCOM_RPM_PM8821_SMPS2                 57
++#define QCOM_RPM_PM8901_LDO0                  58
++#define QCOM_RPM_PM8901_LDO1                  59
++#define QCOM_RPM_PM8901_LDO2                  60
++#define QCOM_RPM_PM8901_LDO3                  61
++#define QCOM_RPM_PM8901_LDO4                  62
++#define QCOM_RPM_PM8901_LDO5                  63
++#define QCOM_RPM_PM8901_LDO6                  64
++#define QCOM_RPM_PM8901_LVS0                  65
++#define QCOM_RPM_PM8901_LVS1                  66
++#define QCOM_RPM_PM8901_LVS2  &n