diff options
author | John Crispin <blogic@openwrt.org> | 2014-08-30 09:32:58 +0000 |
---|---|---|
committer | John Crispin <blogic@openwrt.org> | 2014-08-30 09:32:58 +0000 |
commit | a297ec8e13d10274155e0850228bb06c7648e541 (patch) | |
tree | a75e0d80b1664b748d679ea2e7f4797f58837703 /target/linux/ipq806x/patches | |
parent | 9f7cb06591148e3698ac731f7b8734e4d3d21b33 (diff) | |
download | upstream-a297ec8e13d10274155e0850228bb06c7648e541.tar.gz upstream-a297ec8e13d10274155e0850228bb06c7648e541.tar.bz2 upstream-a297ec8e13d10274155e0850228bb06c7648e541.zip |
ipq806x: Add support for IPQ806x chip family
Patches are generated using the "format-patch" command from the
following location:
*https://www.codeaurora.org/cgit/quic/kernel/galak-msm/log/?h=apq_ipq_base
*rev=0771849495b4128cac2faf7d49c85c729fc48b20
Patches numbered 76/77/102/103 have already been integrated in 3.14.12,
so they're not in this list.
All these patches are either integrated are pending integration into
kernel.org, therefore these patches should go away once the kernel
gets upgraded to 3.16.
Support is currently limited to AP148 board but can be extended to other
platforms in the future.
These changes do not cover ethernet connectivity.
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@42334 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/ipq806x/patches')
178 files changed, 45766 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch new file mode 100644 index 0000000000..52d4cf313e --- /dev/null +++ b/target/linux/ipq806x/patches/0001-ARM-dts-msm-split-out-msm8660-and-msm8960-soc-into-d.patch @@ -0,0 +1,313 @@ +From 3cdba35369b404875849008ea97cf1705e6060ed Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 23 Jan 2014 14:09:54 -0600 +Subject: [PATCH 001/182] ARM: dts: msm: split out msm8660 and msm8960 soc + into dts include + +Pull the SoC device tree bits into their own files so other boards based +on these SoCs can include them and reduce duplication across a number of +boards. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660-surf.dts | 59 +------------------------- + arch/arm/boot/dts/qcom-msm8660.dtsi | 63 ++++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-msm8960-cdp.dts | 66 +---------------------------- + arch/arm/boot/dts/qcom-msm8960.dtsi | 70 +++++++++++++++++++++++++++++++ + 4 files changed, 135 insertions(+), 123 deletions(-) + create mode 100644 arch/arm/boot/dts/qcom-msm8660.dtsi + create mode 100644 arch/arm/boot/dts/qcom-msm8960.dtsi + +diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts +index 68a72f5..169bad9 100644 +--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts ++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts +@@ -1,63 +1,6 @@ +-/dts-v1/; +- +-/include/ "skeleton.dtsi" +- +-#include <dt-bindings/clock/qcom,gcc-msm8660.h> ++#include "qcom-msm8660.dtsi" + + / { + model = "Qualcomm MSM8660 SURF"; + compatible = "qcom,msm8660-surf", "qcom,msm8660"; +- interrupt-parent = <&intc>; +- +- intc: interrupt-controller@2080000 { +- compatible = "qcom,msm-8660-qgic"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02080000 0x1000 >, +- < 0x02081000 0x1000 >; +- }; +- +- timer@2000000 { +- compatible = "qcom,scss-timer", "qcom,msm-timer"; +- interrupts = <1 0 0x301>, +- <1 1 0x301>, +- <1 2 0x301>; +- reg = <0x02000000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x40000>; +- }; +- +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- reg = <0x00800000 0x4000>; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <173>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8660"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; +- +- serial@19c40000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x19c40000 0x1000>, +- <0x19c00000 0x1000>; +- interrupts = <0 195 0x0>; +- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; +- clock-names = "core", "iface"; +- }; +- +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +new file mode 100644 +index 0000000..69d6c4e +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -0,0 +1,63 @@ ++/dts-v1/; ++ ++/include/ "skeleton.dtsi" ++ ++#include <dt-bindings/clock/qcom,gcc-msm8660.h> ++ ++/ { ++ model = "Qualcomm MSM8660"; ++ compatible = "qcom,msm8660"; ++ interrupt-parent = <&intc>; ++ ++ intc: interrupt-controller@2080000 { ++ compatible = "qcom,msm-8660-qgic"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02080000 0x1000 >, ++ < 0x02081000 0x1000 >; ++ }; ++ ++ timer@2000000 { ++ compatible = "qcom,scss-timer", "qcom,msm-timer"; ++ interrupts = <1 0 0x301>, ++ <1 1 0x301>, ++ <1 2 0x301>; ++ reg = <0x02000000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x40000>; ++ }; ++ ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ reg = <0x00800000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <173>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8660"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ serial@19c40000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x19c40000 0x1000>, ++ <0x19c00000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +index 7c30de4..a58fb88 100644 +--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts ++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +@@ -1,70 +1,6 @@ +-/dts-v1/; +- +-/include/ "skeleton.dtsi" +- +-#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include "qcom-msm8960.dtsi" + + / { + model = "Qualcomm MSM8960 CDP"; + compatible = "qcom,msm8960-cdp", "qcom,msm8960"; +- interrupt-parent = <&intc>; +- +- intc: interrupt-controller@2000000 { +- compatible = "qcom,msm-qgic2"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02000000 0x1000 >, +- < 0x02002000 0x1000 >; +- }; +- +- timer@200a000 { +- compatible = "qcom,kpss-timer", "qcom,msm-timer"; +- interrupts = <1 1 0x301>, +- <1 2 0x301>, +- <1 3 0x301>; +- reg = <0x0200a000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x80000>; +- }; +- +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <150>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- reg = <0x800000 0x4000>; +- }; +- +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8960"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; +- +- clock-controller@4000000 { +- compatible = "qcom,mmcc-msm8960"; +- reg = <0x4000000 0x1000>; +- #clock-cells = <1>; +- #reset-cells = <1>; +- }; +- +- serial@16440000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x16440000 0x1000>, +- <0x16400000 0x1000>; +- interrupts = <0 154 0x0>; +- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; +- clock-names = "core", "iface"; +- }; +- +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +new file mode 100644 +index 0000000..ff00282 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -0,0 +1,70 @@ ++/dts-v1/; ++ ++/include/ "skeleton.dtsi" ++ ++#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++ ++/ { ++ model = "Qualcomm MSM8960"; ++ compatible = "qcom,msm8960"; ++ interrupt-parent = <&intc>; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02000000 0x1000 >, ++ < 0x02002000 0x1000 >; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <150>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x800000 0x4000>; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8960"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ clock-controller@4000000 { ++ compatible = "qcom,mmcc-msm8960"; ++ reg = <0x4000000 0x1000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ ++ serial@16440000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16440000 0x1000>, ++ <0x16400000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch new file mode 100644 index 0000000000..72b6618e23 --- /dev/null +++ b/target/linux/ipq806x/patches/0002-ARM-msm-Remove-pen_release-usage.patch @@ -0,0 +1,223 @@ +From 18d53dfa103e63154fb8e548d55016d6ad210d28 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 21 Jun 2013 12:17:37 -0700 +Subject: [PATCH 002/182] ARM: msm: Remove pen_release usage + +pen_release is no longer required as the synchronization +is now managed by generic arm code. +This is done as suggested in https://lkml.org/lkml/2013/6/4/184 + +Cc: Russell King <linux@arm.linux.org.uk> +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Makefile | 2 +- + arch/arm/mach-msm/headsmp.S | 39 --------------------------------------- + arch/arm/mach-msm/hotplug.c | 31 ++++--------------------------- + arch/arm/mach-msm/platsmp.c | 37 +++---------------------------------- + 4 files changed, 8 insertions(+), 101 deletions(-) + delete mode 100644 arch/arm/mach-msm/headsmp.S + +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 8e307a1..721f27f 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -19,7 +19,7 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o + CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) + + obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +-obj-$(CONFIG_SMP) += headsmp.o platsmp.o ++obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o +diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S +deleted file mode 100644 +index 6c62c3f..0000000 +--- a/arch/arm/mach-msm/headsmp.S ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* +- * linux/arch/arm/mach-realview/headsmp.S +- * +- * Copyright (c) 2003 ARM Limited +- * All Rights Reserved +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include <linux/linkage.h> +-#include <linux/init.h> +- +-/* +- * MSM specific entry point for secondary CPUs. This provides +- * a "holding pen" into which all secondary cores are held until we're +- * ready for them to initialise. +- */ +-ENTRY(msm_secondary_startup) +- mrc p15, 0, r0, c0, c0, 5 +- and r0, r0, #15 +- adr r4, 1f +- ldmia r4, {r5, r6} +- sub r4, r4, r5 +- add r6, r6, r4 +-pen: ldr r7, [r6] +- cmp r7, r0 +- bne pen +- +- /* +- * we've been released from the holding pen: secondary_stack +- * should now contain the SVC stack for this core +- */ +- b secondary_startup +-ENDPROC(msm_secondary_startup) +- +- .align +-1: .long . +- .long pen_release +diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c +index 326a872..cea80fc 100644 +--- a/arch/arm/mach-msm/hotplug.c ++++ b/arch/arm/mach-msm/hotplug.c +@@ -24,33 +24,10 @@ static inline void cpu_leave_lowpower(void) + + static inline void platform_do_lowpower(unsigned int cpu) + { +- /* Just enter wfi for now. TODO: Properly shut off the cpu. */ +- for (;;) { +- /* +- * here's the WFI +- */ +- asm("wfi" +- : +- : +- : "memory", "cc"); +- +- if (pen_release == cpu_logical_map(cpu)) { +- /* +- * OK, proper wakeup, we're done +- */ +- break; +- } +- +- /* +- * getting here, means that we have come out of WFI without +- * having been woken up - this shouldn't happen +- * +- * The trouble is, letting people know about this is not really +- * possible, since we are currently running incoherently, and +- * therefore cannot safely call printk() or anything else +- */ +- pr_debug("CPU%u: spurious wakeup call\n", cpu); +- } ++ asm("wfi" ++ : ++ : ++ : "memory", "cc"); + } + + /* +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +index f10a1f5..3721b31 100644 +--- a/arch/arm/mach-msm/platsmp.c ++++ b/arch/arm/mach-msm/platsmp.c +@@ -12,13 +12,10 @@ + #include <linux/errno.h> + #include <linux/delay.h> + #include <linux/device.h> +-#include <linux/jiffies.h> + #include <linux/smp.h> + #include <linux/io.h> + +-#include <asm/cacheflush.h> + #include <asm/cputype.h> +-#include <asm/mach-types.h> + #include <asm/smp_plat.h> + + #include "scm-boot.h" +@@ -28,7 +25,7 @@ + #define SCSS_CPU1CORE_RESET 0xD80 + #define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 + +-extern void msm_secondary_startup(void); ++extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); + +@@ -41,13 +38,6 @@ static inline int get_core_count(void) + static void msm_secondary_init(unsigned int cpu) + { + /* +- * let the primary processor know we're out of the +- * pen, then head off into the C entry point +- */ +- pen_release = -1; +- smp_wmb(); +- +- /* + * Synchronise with the boot thread. + */ + spin_lock(&boot_lock); +@@ -57,7 +47,7 @@ static void msm_secondary_init(unsigned int cpu) + static void prepare_cold_cpu(unsigned int cpu) + { + int ret; +- ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup), ++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup), + SCM_FLAG_COLDBOOT_CPU1); + if (ret == 0) { + void __iomem *sc1_base_ptr; +@@ -75,7 +65,6 @@ static void prepare_cold_cpu(unsigned int cpu) + + static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + { +- unsigned long timeout; + static int cold_boot_done; + + /* Only need to bring cpu out of reset this way once */ +@@ -91,39 +80,19 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + spin_lock(&boot_lock); + + /* +- * The secondary processor is waiting to be released from +- * the holding pen - release it, then wait for it to flag +- * that it has been released by resetting pen_release. +- * +- * Note that "pen_release" is the hardware CPU ID, whereas +- * "cpu" is Linux's internal ID. +- */ +- pen_release = cpu_logical_map(cpu); +- sync_cache_w(&pen_release); +- +- /* + * Send the secondary CPU a soft interrupt, thereby causing + * the boot monitor to read the system wide flags register, + * and branch to the address found there. + */ + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + +- timeout = jiffies + (1 * HZ); +- while (time_before(jiffies, timeout)) { +- smp_rmb(); +- if (pen_release == -1) +- break; +- +- udelay(10); +- } +- + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + spin_unlock(&boot_lock); + +- return pen_release != -1 ? -ENOSYS : 0; ++ return 0; + } + + /* +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch new file mode 100644 index 0000000000..b6e9bbac57 --- /dev/null +++ b/target/linux/ipq806x/patches/0003-ARM-msm-kill-off-hotplug.c.patch @@ -0,0 +1,120 @@ +From b5a3a19e3efa6238c6a00a8f36a8ab2c25eeebc3 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 31 Jan 2014 13:48:29 -0600 +Subject: [PATCH 003/182] ARM: msm: kill off hotplug.c + +Right now hotplug.c only really implements msm_cpu_die as a wfi. Just +move that implementation into platsmp.c. At the same time we use the +existing wfi() instead of inline asm. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Makefile | 1 - + arch/arm/mach-msm/common.h | 1 - + arch/arm/mach-msm/hotplug.c | 51 ------------------------------------------- + arch/arm/mach-msm/platsmp.c | 7 ++++++ + 4 files changed, 7 insertions(+), 53 deletions(-) + delete mode 100644 arch/arm/mach-msm/hotplug.c + +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 721f27f..8327f60 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -18,7 +18,6 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o + + CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) + +-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o + obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o +diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h +index 33c7725..0a4899b 100644 +--- a/arch/arm/mach-msm/common.h ++++ b/arch/arm/mach-msm/common.h +@@ -24,7 +24,6 @@ extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, + unsigned int mtype, void *caller); + + extern struct smp_operations msm_smp_ops; +-extern void msm_cpu_die(unsigned int cpu); + + struct msm_mmc_platform_data; + +diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c +deleted file mode 100644 +index cea80fc..0000000 +--- a/arch/arm/mach-msm/hotplug.c ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- * Copyright (C) 2002 ARM Ltd. +- * All Rights Reserved +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include <linux/kernel.h> +-#include <linux/errno.h> +-#include <linux/smp.h> +- +-#include <asm/smp_plat.h> +- +-#include "common.h" +- +-static inline void cpu_enter_lowpower(void) +-{ +-} +- +-static inline void cpu_leave_lowpower(void) +-{ +-} +- +-static inline void platform_do_lowpower(unsigned int cpu) +-{ +- asm("wfi" +- : +- : +- : "memory", "cc"); +-} +- +-/* +- * platform-specific code to shutdown a CPU +- * +- * Called with IRQs disabled +- */ +-void __ref msm_cpu_die(unsigned int cpu) +-{ +- /* +- * we're ready for shutdown now, so do it +- */ +- cpu_enter_lowpower(); +- platform_do_lowpower(cpu); +- +- /* +- * bring this CPU back into the world of cache +- * coherency, and then restore interrupts +- */ +- cpu_leave_lowpower(); +-} +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +index 3721b31..251a91e 100644 +--- a/arch/arm/mach-msm/platsmp.c ++++ b/arch/arm/mach-msm/platsmp.c +@@ -29,6 +29,13 @@ extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); + ++#ifdef CONFIG_HOTPLUG_CPU ++static void __ref msm_cpu_die(unsigned int cpu) ++{ ++ wfi(); ++} ++#endif ++ + static inline int get_core_count(void) + { + /* 1 + the PART[1:0] field of MIDR */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch new file mode 100644 index 0000000000..fd730c1e47 --- /dev/null +++ b/target/linux/ipq806x/patches/0004-clocksource-qcom-Move-clocksource-code-out-of-mach-m.patch @@ -0,0 +1,788 @@ +From 00009eabeb2074bef5c89e576a7a6d827c12c3d9 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 16:17:30 -0600 +Subject: [PATCH 004/182] clocksource: qcom: Move clocksource code out of + mach-msm + +We intend to share the clocksource code for MSM platforms between legacy +and multiplatform supported qcom SoCs. + +Acked-by: Olof Johansson <olof@lixom.net> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/Kconfig | 13 +- + arch/arm/mach-msm/Makefile | 1 - + arch/arm/mach-msm/timer.c | 333 -------------------------------------- + drivers/clocksource/Kconfig | 3 + + drivers/clocksource/Makefile | 1 + + drivers/clocksource/qcom-timer.c | 329 +++++++++++++++++++++++++++++++++++++ + 6 files changed, 338 insertions(+), 342 deletions(-) + delete mode 100644 arch/arm/mach-msm/timer.c + create mode 100644 drivers/clocksource/qcom-timer.c + +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index 9625cf3..3c4eca7 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -21,7 +21,7 @@ config ARCH_MSM8X60 + select CPU_V7 + select HAVE_SMP + select MSM_SCM if SMP +- select MSM_TIMER ++ select CLKSRC_QCOM + + config ARCH_MSM8960 + bool "Enable support for MSM8960" +@@ -29,7 +29,7 @@ config ARCH_MSM8960 + select CPU_V7 + select HAVE_SMP + select MSM_SCM if SMP +- select MSM_TIMER ++ select CLKSRC_QCOM + + config ARCH_MSM8974 + bool "Enable support for MSM8974" +@@ -54,7 +54,7 @@ config ARCH_MSM7X00A + select MACH_TROUT if !MACH_HALIBUT + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_SMD_PKG3 + + config ARCH_MSM7X30 +@@ -66,7 +66,7 @@ config ARCH_MSM7X30 + select MSM_GPIOMUX + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_VIC + + config ARCH_QSD8X50 +@@ -78,7 +78,7 @@ config ARCH_QSD8X50 + select MSM_GPIOMUX + select MSM_PROC_COMM + select MSM_SMD +- select MSM_TIMER ++ select CLKSRC_QCOM + select MSM_VIC + + endchoice +@@ -153,7 +153,4 @@ config MSM_GPIOMUX + config MSM_SCM + bool + +-config MSM_TIMER +- bool +- + endif +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 8327f60..04b1bee 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -1,4 +1,3 @@ +-obj-$(CONFIG_MSM_TIMER) += timer.o + obj-$(CONFIG_MSM_PROC_COMM) += clock.o + + obj-$(CONFIG_MSM_VIC) += irq-vic.o +diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c +deleted file mode 100644 +index fd16449..0000000 +--- a/arch/arm/mach-msm/timer.c ++++ /dev/null +@@ -1,333 +0,0 @@ +-/* +- * +- * Copyright (C) 2007 Google, Inc. +- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include <linux/clocksource.h> +-#include <linux/clockchips.h> +-#include <linux/cpu.h> +-#include <linux/init.h> +-#include <linux/interrupt.h> +-#include <linux/irq.h> +-#include <linux/io.h> +-#include <linux/of.h> +-#include <linux/of_address.h> +-#include <linux/of_irq.h> +-#include <linux/sched_clock.h> +- +-#include <asm/mach/time.h> +- +-#include "common.h" +- +-#define TIMER_MATCH_VAL 0x0000 +-#define TIMER_COUNT_VAL 0x0004 +-#define TIMER_ENABLE 0x0008 +-#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) +-#define TIMER_ENABLE_EN BIT(0) +-#define TIMER_CLEAR 0x000C +-#define DGT_CLK_CTL 0x10 +-#define DGT_CLK_CTL_DIV_4 0x3 +-#define TIMER_STS_GPT0_CLR_PEND BIT(10) +- +-#define GPT_HZ 32768 +- +-#define MSM_DGT_SHIFT 5 +- +-static void __iomem *event_base; +-static void __iomem *sts_base; +- +-static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) +-{ +- struct clock_event_device *evt = dev_id; +- /* Stop the timer tick */ +- if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { +- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- ctrl &= ~TIMER_ENABLE_EN; +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +- } +- evt->event_handler(evt); +- return IRQ_HANDLED; +-} +- +-static int msm_timer_set_next_event(unsigned long cycles, +- struct clock_event_device *evt) +-{ +- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- +- ctrl &= ~TIMER_ENABLE_EN; +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +- +- writel_relaxed(ctrl, event_base + TIMER_CLEAR); +- writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); +- +- if (sts_base) +- while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) +- cpu_relax(); +- +- writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); +- return 0; +-} +- +-static void msm_timer_set_mode(enum clock_event_mode mode, +- struct clock_event_device *evt) +-{ +- u32 ctrl; +- +- ctrl = readl_relaxed(event_base + TIMER_ENABLE); +- ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); +- +- switch (mode) { +- case CLOCK_EVT_MODE_RESUME: +- case CLOCK_EVT_MODE_PERIODIC: +- break; +- case CLOCK_EVT_MODE_ONESHOT: +- /* Timer is enabled in set_next_event */ +- break; +- case CLOCK_EVT_MODE_UNUSED: +- case CLOCK_EVT_MODE_SHUTDOWN: +- break; +- } +- writel_relaxed(ctrl, event_base + TIMER_ENABLE); +-} +- +-static struct clock_event_device __percpu *msm_evt; +- +-static void __iomem *source_base; +- +-static notrace cycle_t msm_read_timer_count(struct clocksource *cs) +-{ +- return readl_relaxed(source_base + TIMER_COUNT_VAL); +-} +- +-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) +-{ +- /* +- * Shift timer count down by a constant due to unreliable lower bits +- * on some targets. +- */ +- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; +-} +- +-static struct clocksource msm_clocksource = { +- .name = "dg_timer", +- .rating = 300, +- .read = msm_read_timer_count, +- .mask = CLOCKSOURCE_MASK(32), +- .flags = CLOCK_SOURCE_IS_CONTINUOUS, +-}; +- +-static int msm_timer_irq; +-static int msm_timer_has_ppi; +- +-static int msm_local_timer_setup(struct clock_event_device *evt) +-{ +- int cpu = smp_processor_id(); +- int err; +- +- evt->irq = msm_timer_irq; +- evt->name = "msm_timer"; +- evt->features = CLOCK_EVT_FEAT_ONESHOT; +- evt->rating = 200; +- evt->set_mode = msm_timer_set_mode; +- evt->set_next_event = msm_timer_set_next_event; +- evt->cpumask = cpumask_of(cpu); +- +- clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); +- +- if (msm_timer_has_ppi) { +- enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); +- } else { +- err = request_irq(evt->irq, msm_timer_interrupt, +- IRQF_TIMER | IRQF_NOBALANCING | +- IRQF_TRIGGER_RISING, "gp_timer", evt); +- if (err) +- pr_err("request_irq failed\n"); +- } +- +- return 0; +-} +- +-static void msm_local_timer_stop(struct clock_event_device *evt) +-{ +- evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); +- disable_percpu_irq(evt->irq); +-} +- +-static int msm_timer_cpu_notify(struct notifier_block *self, +- unsigned long action, void *hcpu) +-{ +- /* +- * Grab cpu pointer in each case to avoid spurious +- * preemptible warnings +- */ +- switch (action & ~CPU_TASKS_FROZEN) { +- case CPU_STARTING: +- msm_local_timer_setup(this_cpu_ptr(msm_evt)); +- break; +- case CPU_DYING: +- msm_local_timer_stop(this_cpu_ptr(msm_evt)); +- break; +- } +- +- return NOTIFY_OK; +-} +- +-static struct notifier_block msm_timer_cpu_nb = { +- .notifier_call = msm_timer_cpu_notify, +-}; +- +-static u64 notrace msm_sched_clock_read(void) +-{ +- return msm_clocksource.read(&msm_clocksource); +-} +- +-static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, +- bool percpu) +-{ +- struct clocksource *cs = &msm_clocksource; +- int res = 0; +- +- msm_timer_irq = irq; +- msm_timer_has_ppi = percpu; +- +- msm_evt = alloc_percpu(struct clock_event_device); +- if (!msm_evt) { +- pr_err("memory allocation failed for clockevents\n"); +- goto err; +- } +- +- if (percpu) +- res = request_percpu_irq(irq, msm_timer_interrupt, +- "gp_timer", msm_evt); +- +- if (res) { +- pr_err("request_percpu_irq failed\n"); +- } else { +- res = register_cpu_notifier(&msm_timer_cpu_nb); +- if (res) { +- free_percpu_irq(irq, msm_evt); +- goto err; +- } +- +- /* Immediately configure the timer on the boot CPU */ +- msm_local_timer_setup(__this_cpu_ptr(msm_evt)); +- } +- +-err: +- writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); +- res = clocksource_register_hz(cs, dgt_hz); +- if (res) +- pr_err("clocksource_register failed\n"); +- sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); +-} +- +-#ifdef CONFIG_OF +-static void __init msm_dt_timer_init(struct device_node *np) +-{ +- u32 freq; +- int irq; +- struct resource res; +- u32 percpu_offset; +- void __iomem *base; +- void __iomem *cpu0_base; +- +- base = of_iomap(np, 0); +- if (!base) { +- pr_err("Failed to map event base\n"); +- return; +- } +- +- /* We use GPT0 for the clockevent */ +- irq = irq_of_parse_and_map(np, 1); +- if (irq <= 0) { +- pr_err("Can't get irq\n"); +- return; +- } +- +- /* We use CPU0's DGT for the clocksource */ +- if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) +- percpu_offset = 0; +- +- if (of_address_to_resource(np, 0, &res)) { +- pr_err("Failed to parse DGT resource\n"); +- return; +- } +- +- cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); +- if (!cpu0_base) { +- pr_err("Failed to map source base\n"); +- return; +- } +- +- if (of_property_read_u32(np, "clock-frequency", &freq)) { +- pr_err("Unknown frequency\n"); +- return; +- } +- +- event_base = base + 0x4; +- sts_base = base + 0x88; +- source_base = cpu0_base + 0x24; +- freq /= 4; +- writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); +- +- msm_timer_init(freq, 32, irq, !!percpu_offset); +-} +-CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); +-CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); +-#endif +- +-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, +- u32 sts) +-{ +- void __iomem *base; +- +- base = ioremap(addr, SZ_256); +- if (!base) { +- pr_err("Failed to map timer base\n"); +- return -ENOMEM; +- } +- event_base = base + event; +- source_base = base + source; +- if (sts) +- sts_base = base + sts; +- +- return 0; +-} +- +-void __init msm7x01_timer_init(void) +-{ +- struct clocksource *cs = &msm_clocksource; +- +- if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0)) +- return; +- cs->read = msm_read_timer_count_shift; +- cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); +- /* 600 KHz */ +- msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7, +- false); +-} +- +-void __init msm7x30_timer_init(void) +-{ +- if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80)) +- return; +- msm_timer_init(24576000 / 4, 32, 1, false); +-} +- +-void __init qsd8x50_timer_init(void) +-{ +- if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34)) +- return; +- msm_timer_init(19200000 / 4, 32, 7, false); +-} +diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig +index cd6950f..6510ec4 100644 +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -140,3 +140,6 @@ config VF_PIT_TIMER + bool + help + Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. ++ ++config CLKSRC_QCOM ++ bool +diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile +index c7ca50a..2e0c0cc 100644 +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o + obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o + obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o + obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o ++obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o + + obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o + obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o +diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c +new file mode 100644 +index 0000000..dca829e +--- /dev/null ++++ b/drivers/clocksource/qcom-timer.c +@@ -0,0 +1,329 @@ ++/* ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/clocksource.h> ++#include <linux/clockchips.h> ++#include <linux/cpu.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/sched_clock.h> ++ ++#define TIMER_MATCH_VAL 0x0000 ++#define TIMER_COUNT_VAL 0x0004 ++#define TIMER_ENABLE 0x0008 ++#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) ++#define TIMER_ENABLE_EN BIT(0) ++#define TIMER_CLEAR 0x000C ++#define DGT_CLK_CTL 0x10 ++#define DGT_CLK_CTL_DIV_4 0x3 ++#define TIMER_STS_GPT0_CLR_PEND BIT(10) ++ ++#define GPT_HZ 32768 ++ ++#define MSM_DGT_SHIFT 5 ++ ++static void __iomem *event_base; ++static void __iomem *sts_base; ++ ++static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = dev_id; ++ /* Stop the timer tick */ ++ if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { ++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ctrl &= ~TIMER_ENABLE_EN; ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++ } ++ evt->event_handler(evt); ++ return IRQ_HANDLED; ++} ++ ++static int msm_timer_set_next_event(unsigned long cycles, ++ struct clock_event_device *evt) ++{ ++ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ++ ctrl &= ~TIMER_ENABLE_EN; ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++ ++ writel_relaxed(ctrl, event_base + TIMER_CLEAR); ++ writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); ++ ++ if (sts_base) ++ while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) ++ cpu_relax(); ++ ++ writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); ++ return 0; ++} ++ ++static void msm_timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ u32 ctrl; ++ ++ ctrl = readl_relaxed(event_base + TIMER_ENABLE); ++ ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_RESUME: ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ /* Timer is enabled in set_next_event */ ++ break; ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ } ++ writel_relaxed(ctrl, event_base + TIMER_ENABLE); ++} ++ ++static struct clock_event_device __percpu *msm_evt; ++ ++static void __iomem *source_base; ++ ++static notrace cycle_t msm_read_timer_count(struct clocksource *cs) ++{ ++ return readl_relaxed(source_base + TIMER_COUNT_VAL); ++} ++ ++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) ++{ ++ /* ++ * Shift timer count down by a constant due to unreliable lower bits ++ * on some targets. ++ */ ++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; ++} ++ ++static struct clocksource msm_clocksource = { ++ .name = "dg_timer", ++ .rating = 300, ++ .read = msm_read_timer_count, ++ .mask = CLOCKSOURCE_MASK(32), ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++}; ++ ++static int msm_timer_irq; ++static int msm_timer_has_ppi; ++ ++static int msm_local_timer_setup(struct clock_event_device *evt) ++{ ++ int cpu = smp_processor_id(); ++ int err; ++ ++ evt->irq = msm_timer_irq; ++ evt->name = "msm_timer"; ++ evt->features = CLOCK_EVT_FEAT_ONESHOT; ++ evt->rating = 200; ++ evt->set_mode = msm_timer_set_mode; ++ evt->set_next_event = msm_timer_set_next_event; ++ evt->cpumask = cpumask_of(cpu); ++ ++ clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); ++ ++ if (msm_timer_has_ppi) { ++ enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); ++ } else { ++ err = request_irq(evt->irq, msm_timer_interrupt, ++ IRQF_TIMER | IRQF_NOBALANCING | ++ IRQF_TRIGGER_RISING, "gp_timer", evt); ++ if (err) ++ pr_err("request_irq failed\n"); ++ } ++ ++ return 0; ++} ++ ++static void msm_local_timer_stop(struct clock_event_device *evt) ++{ ++ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); ++ disable_percpu_irq(evt->irq); ++} ++ ++static int msm_timer_cpu_notify(struct notifier_block *self, ++ unsigned long action, void *hcpu) ++{ ++ /* ++ * Grab cpu pointer in each case to avoid spurious ++ * preemptible warnings ++ */ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_STARTING: ++ msm_local_timer_setup(this_cpu_ptr(msm_evt)); ++ break; ++ case CPU_DYING: ++ msm_local_timer_stop(this_cpu_ptr(msm_evt)); ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block msm_timer_cpu_nb = { ++ .notifier_call = msm_timer_cpu_notify, ++}; ++ ++static u64 notrace msm_sched_clock_read(void) ++{ ++ return msm_clocksource.read(&msm_clocksource); ++} ++ ++static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, ++ bool percpu) ++{ ++ struct clocksource *cs = &msm_clocksource; ++ int res = 0; ++ ++ msm_timer_irq = irq; ++ msm_timer_has_ppi = percpu; ++ ++ msm_evt = alloc_percpu(struct clock_event_device); ++ if (!msm_evt) { ++ pr_err("memory allocation failed for clockevents\n"); ++ goto err; ++ } ++ ++ if (percpu) ++ res = request_percpu_irq(irq, msm_timer_interrupt, ++ "gp_timer", msm_evt); ++ ++ if (res) { ++ pr_err("request_percpu_irq failed\n"); ++ } else { ++ res = register_cpu_notifier(&msm_timer_cpu_nb); ++ if (res) { ++ free_percpu_irq(irq, msm_evt); ++ goto err; ++ } ++ ++ /* Immediately configure the timer on the boot CPU */ ++ msm_local_timer_setup(__this_cpu_ptr(msm_evt)); ++ } ++ ++err: ++ writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); ++ res = clocksource_register_hz(cs, dgt_hz); ++ if (res) ++ pr_err("clocksource_register failed\n"); ++ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); ++} ++ ++#ifdef CONFIG_OF ++static void __init msm_dt_timer_init(struct device_node *np) ++{ ++ u32 freq; ++ int irq; ++ struct resource res; ++ u32 percpu_offset; ++ void __iomem *base; ++ void __iomem *cpu0_base; ++ ++ base = of_iomap(np, 0); ++ if (!base) { ++ pr_err("Failed to map event base\n"); ++ return; ++ } ++ ++ /* We use GPT0 for the clockevent */ ++ irq = irq_of_parse_and_map(np, 1); ++ if (irq <= 0) { ++ pr_err("Can't get irq\n"); ++ return; ++ } ++ ++ /* We use CPU0's DGT for the clocksource */ ++ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) ++ percpu_offset = 0; ++ ++ if (of_address_to_resource(np, 0, &res)) { ++ pr_err("Failed to parse DGT resource\n"); ++ return; ++ } ++ ++ cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); ++ if (!cpu0_base) { ++ pr_err("Failed to map source base\n"); ++ return; ++ } ++ ++ if (of_property_read_u32(np, "clock-frequency", &freq)) { ++ pr_err("Unknown frequency\n"); ++ return; ++ } ++ ++ event_base = base + 0x4; ++ sts_base = base + 0x88; ++ source_base = cpu0_base + 0x24; ++ freq /= 4; ++ writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); ++ ++ msm_timer_init(freq, 32, irq, !!percpu_offset); ++} ++CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); ++CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); ++#endif ++ ++static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, ++ u32 sts) ++{ ++ void __iomem *base; ++ ++ base = ioremap(addr, SZ_256); ++ if (!base) { ++ pr_err("Failed to map timer base\n"); ++ return -ENOMEM; ++ } ++ event_base = base + event; ++ source_base = base + source; ++ if (sts) ++ sts_base = base + sts; ++ ++ return 0; ++} ++ ++void __init msm7x01_timer_init(void) ++{ ++ struct clocksource *cs = &msm_clocksource; ++ ++ if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0)) ++ return; ++ cs->read = msm_read_timer_count_shift; ++ cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); ++ /* 600 KHz */ ++ msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7, ++ false); ++} ++ ++void __init msm7x30_timer_init(void) ++{ ++ if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80)) ++ return; ++ msm_timer_init(24576000 / 4, 32, 1, false); ++} ++ ++void __init qsd8x50_timer_init(void) ++{ ++ if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34)) ++ return; ++ msm_timer_init(19200000 / 4, 32, 7, false); ++} +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch new file mode 100644 index 0000000000..c23faf0640 --- /dev/null +++ b/target/linux/ipq806x/patches/0005-ARM-qcom-Split-Qualcomm-support-into-legacy-and-mult.patch @@ -0,0 +1,1482 @@ +From 8c2a00c0129d6f718245f7a613c2bb28976b7973 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 21 Jan 2014 17:14:10 -0600 +Subject: [PATCH 005/182] ARM: qcom: Split Qualcomm support into legacy and + multiplatform + +Introduce a new mach-qcom that will support SoCs that intend to be +multiplatform compatible while keeping mach-msm to legacy SoC/board +support that will not transition over to multiplatform. + +As part of this, we move support for MSM8X60, MSM8960 and MSM8974 over +to mach-qcom. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + MAINTAINERS | 8 ++ + arch/arm/Kconfig | 7 +- + arch/arm/Kconfig.debug | 2 +- + arch/arm/Makefile | 1 + + arch/arm/boot/dts/Makefile | 6 +- + arch/arm/mach-msm/Kconfig | 45 +------ + arch/arm/mach-msm/Makefile | 6 - + arch/arm/mach-msm/board-dt.c | 41 ------ + arch/arm/mach-msm/platsmp.c | 137 ------------------- + arch/arm/mach-msm/scm-boot.c | 39 ------ + arch/arm/mach-msm/scm-boot.h | 22 --- + arch/arm/mach-msm/scm.c | 299 ----------------------------------------- + arch/arm/mach-msm/scm.h | 25 ---- + arch/arm/mach-qcom/Kconfig | 33 +++++ + arch/arm/mach-qcom/Makefile | 5 + + arch/arm/mach-qcom/board.c | 40 ++++++ + arch/arm/mach-qcom/platsmp.c | 137 +++++++++++++++++++ + arch/arm/mach-qcom/scm-boot.c | 39 ++++++ + arch/arm/mach-qcom/scm-boot.h | 22 +++ + arch/arm/mach-qcom/scm.c | 299 +++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/scm.h | 25 ++++ + 21 files changed, 619 insertions(+), 619 deletions(-) + delete mode 100644 arch/arm/mach-msm/board-dt.c + delete mode 100644 arch/arm/mach-msm/platsmp.c + delete mode 100644 arch/arm/mach-msm/scm-boot.c + delete mode 100644 arch/arm/mach-msm/scm-boot.h + delete mode 100644 arch/arm/mach-msm/scm.c + delete mode 100644 arch/arm/mach-msm/scm.h + create mode 100644 arch/arm/mach-qcom/Kconfig + create mode 100644 arch/arm/mach-qcom/Makefile + create mode 100644 arch/arm/mach-qcom/board.c + create mode 100644 arch/arm/mach-qcom/platsmp.c + create mode 100644 arch/arm/mach-qcom/scm-boot.c + create mode 100644 arch/arm/mach-qcom/scm-boot.h + create mode 100644 arch/arm/mach-qcom/scm.c + create mode 100644 arch/arm/mach-qcom/scm.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 900d98e..7d23402 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1168,6 +1168,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) + W: http://www.arm.linux.org.uk/ + S: Maintained + ++ARM/QUALCOMM SUPPORT ++M: Kumar Gala <galak@codeaurora.org> ++M: David Brown <davidb@codeaurora.org> ++L: linux-arm-msm@vger.kernel.org ++S: Maintained ++F: arch/arm/mach-qcom/ ++T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git ++ + ARM/RADISYS ENP2611 MACHINE SUPPORT + M: Lennert Buytenhek <kernel@wantstofly.org> + L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 1594945..d02ce70 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -657,9 +657,8 @@ config ARCH_PXA + help + Support for Intel/Marvell's PXA2xx/PXA3xx processor line. + +-config ARCH_MSM_NODT +- bool "Qualcomm MSM" +- select ARCH_MSM ++config ARCH_MSM ++ bool "Qualcomm MSM (non-multiplatform)" + select ARCH_REQUIRE_GPIOLIB + select COMMON_CLK + select GENERIC_CLOCKEVENTS +@@ -1005,6 +1004,8 @@ source "arch/arm/plat-pxa/Kconfig" + + source "arch/arm/mach-mmp/Kconfig" + ++source "arch/arm/mach-qcom/Kconfig" ++ + source "arch/arm/mach-realview/Kconfig" + + source "arch/arm/mach-rockchip/Kconfig" +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 0531da8..4491c7b 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -956,7 +956,7 @@ config DEBUG_STI_UART + + config DEBUG_MSM_UART + bool +- depends on ARCH_MSM ++ depends on ARCH_MSM || ARCH_QCOM + + config DEBUG_LL_INCLUDE + string +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 08a9ef5..51e5bed 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2 + machine-$(CONFIG_ARCH_ORION5X) += orion5x + machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell + machine-$(CONFIG_ARCH_PXA) += pxa ++machine-$(CONFIG_ARCH_QCOM) += qcom + machine-$(CONFIG_ARCH_REALVIEW) += realview + machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip + machine-$(CONFIG_ARCH_RPC) += rpc +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 0320303..4a89023 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -119,9 +119,6 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-cloudbox.dtb \ + kirkwood-ts219-6282.dtb + dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb + dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb +-dtb-$(CONFIG_ARCH_MSM) += qcom-msm8660-surf.dtb \ +- qcom-msm8960-cdp.dtb \ +- qcom-apq8074-dragonboard.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ + armada-370-mirabox.dtb \ + armada-370-netgear-rn102.dtb \ +@@ -234,6 +231,9 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \ + dra7-evm.dtb + dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb + dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb ++dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \ ++ qcom-msm8960-cdp.dtb \ ++ qcom-apq8074-dragonboard.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ + ste-hrefprev60-stuib.dtb \ + ste-hrefprev60-tvk.dtb \ +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index 3c4eca7..a7f959e 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -1,50 +1,9 @@ +-config ARCH_MSM +- bool +- +-config ARCH_MSM_DT +- bool "Qualcomm MSM DT Support" if ARCH_MULTI_V7 +- select ARCH_MSM +- select ARCH_REQUIRE_GPIOLIB +- select CLKSRC_OF +- select GENERIC_CLOCKEVENTS +- help +- Support for Qualcomm's devicetree based MSM systems. +- + if ARCH_MSM + +-menu "Qualcomm MSM SoC Selection" +- depends on ARCH_MSM_DT +- +-config ARCH_MSM8X60 +- bool "Enable support for MSM8X60" +- select ARM_GIC +- select CPU_V7 +- select HAVE_SMP +- select MSM_SCM if SMP +- select CLKSRC_QCOM +- +-config ARCH_MSM8960 +- bool "Enable support for MSM8960" +- select ARM_GIC +- select CPU_V7 +- select HAVE_SMP +- select MSM_SCM if SMP +- select CLKSRC_QCOM +- +-config ARCH_MSM8974 +- bool "Enable support for MSM8974" +- select ARM_GIC +- select CPU_V7 +- select HAVE_ARM_ARCH_TIMER +- select HAVE_SMP +- select MSM_SCM if SMP +- +-endmenu +- + choice + prompt "Qualcomm MSM SoC Type" + default ARCH_MSM7X00A +- depends on ARCH_MSM_NODT ++ depends on ARCH_MSM + + config ARCH_MSM7X00A + bool "MSM7x00A / MSM7x01A" +@@ -99,7 +58,7 @@ config MSM_VIC + bool + + menu "Qualcomm MSM Board Type" +- depends on ARCH_MSM_NODT ++ depends on ARCH_MSM + + config MACH_HALIBUT + depends on ARCH_MSM +diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile +index 04b1bee..27c078a 100644 +--- a/arch/arm/mach-msm/Makefile ++++ b/arch/arm/mach-msm/Makefile +@@ -13,17 +13,11 @@ obj-$(CONFIG_ARCH_QSD8X50) += dma.o io.o + + obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o + obj-$(CONFIG_MSM_SMD) += last_radio_log.o +-obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o +- +-CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +- +-obj-$(CONFIG_SMP) += platsmp.o + + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o + obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o + obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o + obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o +-obj-$(CONFIG_ARCH_MSM_DT) += board-dt.o + obj-$(CONFIG_MSM_GPIOMUX) += gpiomux.o + obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o +diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c +deleted file mode 100644 +index 1f11d93..0000000 +--- a/arch/arm/mach-msm/board-dt.c ++++ /dev/null +@@ -1,41 +0,0 @@ +-/* Copyright (c) 2010-2012,2013 The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include <linux/init.h> +-#include <linux/of.h> +-#include <linux/of_platform.h> +- +-#include <asm/mach/arch.h> +-#include <asm/mach/map.h> +- +-#include "common.h" +- +-static const char * const msm_dt_match[] __initconst = { +- "qcom,msm8660-fluid", +- "qcom,msm8660-surf", +- "qcom,msm8960-cdp", +- NULL +-}; +- +-static const char * const apq8074_dt_match[] __initconst = { +- "qcom,apq8074-dragonboard", +- NULL +-}; +- +-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)") +- .smp = smp_ops(msm_smp_ops), +- .dt_compat = msm_dt_match, +-MACHINE_END +- +-DT_MACHINE_START(APQ_DT, "Qualcomm MSM (Flattened Device Tree)") +- .dt_compat = apq8074_dt_match, +-MACHINE_END +diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c +deleted file mode 100644 +index 251a91e..0000000 +--- a/arch/arm/mach-msm/platsmp.c ++++ /dev/null +@@ -1,137 +0,0 @@ +-/* +- * Copyright (C) 2002 ARM Ltd. +- * All Rights Reserved +- * Copyright (c) 2010, Code Aurora Forum. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include <linux/init.h> +-#include <linux/errno.h> +-#include <linux/delay.h> +-#include <linux/device.h> +-#include <linux/smp.h> +-#include <linux/io.h> +- +-#include <asm/cputype.h> +-#include <asm/smp_plat.h> +- +-#include "scm-boot.h" +-#include "common.h" +- +-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 +-#define SCSS_CPU1CORE_RESET 0xD80 +-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 +- +-extern void secondary_startup(void); +- +-static DEFINE_SPINLOCK(boot_lock); +- +-#ifdef CONFIG_HOTPLUG_CPU +-static void __ref msm_cpu_die(unsigned int cpu) +-{ +- wfi(); +-} +-#endif +- +-static inline int get_core_count(void) +-{ +- /* 1 + the PART[1:0] field of MIDR */ +- return ((read_cpuid_id() >> 4) & 3) + 1; +-} +- +-static void msm_secondary_init(unsigned int cpu) +-{ +- /* +- * Synchronise with the boot thread. +- */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); +-} +- +-static void prepare_cold_cpu(unsigned int cpu) +-{ +- int ret; +- ret = scm_set_boot_addr(virt_to_phys(secondary_startup), +- SCM_FLAG_COLDBOOT_CPU1); +- if (ret == 0) { +- void __iomem *sc1_base_ptr; +- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); +- if (sc1_base_ptr) { +- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); +- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); +- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); +- iounmap(sc1_base_ptr); +- } +- } else +- printk(KERN_DEBUG "Failed to set secondary core boot " +- "address\n"); +-} +- +-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) +-{ +- static int cold_boot_done; +- +- /* Only need to bring cpu out of reset this way once */ +- if (cold_boot_done == false) { +- prepare_cold_cpu(cpu); +- cold_boot_done = true; +- } +- +- /* +- * set synchronisation state between this boot processor +- * and the secondary one +- */ +- spin_lock(&boot_lock); +- +- /* +- * Send the secondary CPU a soft interrupt, thereby causing +- * the boot monitor to read the system wide flags register, +- * and branch to the address found there. +- */ +- arch_send_wakeup_ipi_mask(cpumask_of(cpu)); +- +- /* +- * now the secondary core is starting up let it run its +- * calibrations, then wait for it to finish +- */ +- spin_unlock(&boot_lock); +- +- return 0; +-} +- +-/* +- * Initialise the CPU possible map early - this describes the CPUs +- * which may be present or become present in the system. The msm8x60 +- * does not support the ARM SCU, so just set the possible cpu mask to +- * NR_CPUS. +- */ +-static void __init msm_smp_init_cpus(void) +-{ +- unsigned int i, ncores = get_core_count(); +- +- if (ncores > nr_cpu_ids) { +- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", +- ncores, nr_cpu_ids); +- ncores = nr_cpu_ids; +- } +- +- for (i = 0; i < ncores; i++) +- set_cpu_possible(i, true); +-} +- +-static void __init msm_smp_prepare_cpus(unsigned int max_cpus) +-{ +-} +- +-struct smp_operations msm_smp_ops __initdata = { +- .smp_init_cpus = msm_smp_init_cpus, +- .smp_prepare_cpus = msm_smp_prepare_cpus, +- .smp_secondary_init = msm_secondary_init, +- .smp_boot_secondary = msm_boot_secondary, +-#ifdef CONFIG_HOTPLUG_CPU +- .cpu_die = msm_cpu_die, +-#endif +-}; +diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c +deleted file mode 100644 +index 45cee3e..0000000 +--- a/arch/arm/mach-msm/scm-boot.c ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA. +- */ +- +-#include <linux/module.h> +-#include <linux/slab.h> +- +-#include "scm.h" +-#include "scm-boot.h" +- +-/* +- * Set the cold/warm boot address for one of the CPU cores. +- */ +-int scm_set_boot_addr(phys_addr_t addr, int flags) +-{ +- struct { +- unsigned int flags; +- phys_addr_t addr; +- } cmd; +- +- cmd.addr = addr; +- cmd.flags = flags; +- return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, +- &cmd, sizeof(cmd), NULL, 0); +-} +-EXPORT_SYMBOL(scm_set_boot_addr); +diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h +deleted file mode 100644 +index 7be32ff..0000000 +--- a/arch/arm/mach-msm/scm-boot.h ++++ /dev/null +@@ -1,22 +0,0 @@ +-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +-#ifndef __MACH_SCM_BOOT_H +-#define __MACH_SCM_BOOT_H +- +-#define SCM_BOOT_ADDR 0x1 +-#define SCM_FLAG_COLDBOOT_CPU1 0x1 +-#define SCM_FLAG_WARMBOOT_CPU1 0x2 +-#define SCM_FLAG_WARMBOOT_CPU0 0x4 +- +-int scm_set_boot_addr(phys_addr_t addr, int flags); +- +-#endif +diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c +deleted file mode 100644 +index c536fd6..0000000 +--- a/arch/arm/mach-msm/scm.c ++++ /dev/null +@@ -1,299 +0,0 @@ +-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA. +- */ +- +-#include <linux/slab.h> +-#include <linux/io.h> +-#include <linux/module.h> +-#include <linux/mutex.h> +-#include <linux/errno.h> +-#include <linux/err.h> +- +-#include <asm/cacheflush.h> +- +-#include "scm.h" +- +-/* Cache line size for msm8x60 */ +-#define CACHELINESIZE 32 +- +-#define SCM_ENOMEM -5 +-#define SCM_EOPNOTSUPP -4 +-#define SCM_EINVAL_ADDR -3 +-#define SCM_EINVAL_ARG -2 +-#define SCM_ERROR -1 +-#define SCM_INTERRUPTED 1 +- +-static DEFINE_MUTEX(scm_lock); +- +-/** +- * struct scm_command - one SCM command buffer +- * @len: total available memory for command and response +- * @buf_offset: start of command buffer +- * @resp_hdr_offset: start of response buffer +- * @id: command to be executed +- * @buf: buffer returned from scm_get_command_buffer() +- * +- * An SCM command is laid out in memory as follows: +- * +- * ------------------- <--- struct scm_command +- * | command header | +- * ------------------- <--- scm_get_command_buffer() +- * | command buffer | +- * ------------------- <--- struct scm_response and +- * | response header | scm_command_to_response() +- * ------------------- <--- scm_get_response_buffer() +- * | response buffer | +- * ------------------- +- * +- * There can be arbitrary padding between the headers and buffers so +- * you should always use the appropriate scm_get_*_buffer() routines +- * to access the buffers in a safe manner. +- */ +-struct scm_command { +- u32 len; +- u32 buf_offset; +- u32 resp_hdr_offset; +- u32 id; +- u32 buf[0]; +-}; +- +-/** +- * struct scm_response - one SCM response buffer +- * @len: total available memory for response +- * @buf_offset: start of response data relative to start of scm_response +- * @is_complete: indicates if the command has finished processing +- */ +-struct scm_response { +- u32 len; +- u32 buf_offset; +- u32 is_complete; +-}; +- +-/** +- * alloc_scm_command() - Allocate an SCM command +- * @cmd_size: size of the command buffer +- * @resp_size: size of the response buffer +- * +- * Allocate an SCM command, including enough room for the command +- * and response headers as well as the command and response buffers. +- * +- * Returns a valid &scm_command on success or %NULL if the allocation fails. +- */ +-static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size) +-{ +- struct scm_command *cmd; +- size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size + +- resp_size; +- +- cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); +- if (cmd) { +- cmd->len = len; +- cmd->buf_offset = offsetof(struct scm_command, buf); +- cmd->resp_hdr_offset = cmd->buf_offset + cmd_size; +- } +- return cmd; +-} +- +-/** +- * free_scm_command() - Free an SCM command +- * @cmd: command to free +- * +- * Free an SCM command. +- */ +-static inline void free_scm_command(struct scm_command *cmd) +-{ +- kfree(cmd); +-} +- +-/** +- * scm_command_to_response() - Get a pointer to a scm_response +- * @cmd: command +- * +- * Returns a pointer to a response for a command. +- */ +-static inline struct scm_response *scm_command_to_response( +- const struct scm_command *cmd) +-{ +- return (void *)cmd + cmd->resp_hdr_offset; +-} +- +-/** +- * scm_get_command_buffer() - Get a pointer to a command buffer +- * @cmd: command +- * +- * Returns a pointer to the command buffer of a command. +- */ +-static inline void *scm_get_command_buffer(const struct scm_command *cmd) +-{ +- return (void *)cmd->buf; +-} +- +-/** +- * scm_get_response_buffer() - Get a pointer to a response buffer +- * @rsp: response +- * +- * Returns a pointer to a response buffer of a response. +- */ +-static inline void *scm_get_response_buffer(const struct scm_response *rsp) +-{ +- return (void *)rsp + rsp->buf_offset; +-} +- +-static int scm_remap_error(int err) +-{ +- switch (err) { +- case SCM_ERROR: +- return -EIO; +- case SCM_EINVAL_ADDR: +- case SCM_EINVAL_ARG: +- return -EINVAL; +- case SCM_EOPNOTSUPP: +- return -EOPNOTSUPP; +- case SCM_ENOMEM: +- return -ENOMEM; +- } +- return -EINVAL; +-} +- +-static u32 smc(u32 cmd_addr) +-{ +- int context_id; +- register u32 r0 asm("r0") = 1; +- register u32 r1 asm("r1") = (u32)&context_id; +- register u32 r2 asm("r2") = cmd_addr; +- do { +- asm volatile( +- __asmeq("%0", "r0") +- __asmeq("%1", "r0") +- __asmeq("%2", "r1") +- __asmeq("%3", "r2") +-#ifdef REQUIRES_SEC +- ".arch_extension sec\n" +-#endif +- "smc #0 @ switch to secure world\n" +- : "=r" (r0) +- : "r" (r0), "r" (r1), "r" (r2) +- : "r3"); +- } while (r0 == SCM_INTERRUPTED); +- +- return r0; +-} +- +-static int __scm_call(const struct scm_command *cmd) +-{ +- int ret; +- u32 cmd_addr = virt_to_phys(cmd); +- +- /* +- * Flush the entire cache here so callers don't have to remember +- * to flush the cache when passing physical addresses to the secure +- * side in the buffer. +- */ +- flush_cache_all(); +- ret = smc(cmd_addr); +- if (ret < 0) +- ret = scm_remap_error(ret); +- +- return ret; +-} +- +-/** +- * scm_call() - Send an SCM command +- * @svc_id: service identifier +- * @cmd_id: command identifier +- * @cmd_buf: command buffer +- * @cmd_len: length of the command buffer +- * @resp_buf: response buffer +- * @resp_len: length of the response buffer +- * +- * Sends a command to the SCM and waits for the command to finish processing. +- */ +-int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, +- void *resp_buf, size_t resp_len) +-{ +- int ret; +- struct scm_command *cmd; +- struct scm_response *rsp; +- +- cmd = alloc_scm_command(cmd_len, resp_len); +- if (!cmd) +- return -ENOMEM; +- +- cmd->id = (svc_id << 10) | cmd_id; +- if (cmd_buf) +- memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len); +- +- mutex_lock(&scm_lock); +- ret = __scm_call(cmd); +- mutex_unlock(&scm_lock); +- if (ret) +- goto out; +- +- rsp = scm_command_to_response(cmd); +- do { +- u32 start = (u32)rsp; +- u32 end = (u32)scm_get_response_buffer(rsp) + resp_len; +- start &= ~(CACHELINESIZE - 1); +- while (start < end) { +- asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) +- : "memory"); +- start += CACHELINESIZE; +- } +- } while (!rsp->is_complete); +- +- if (resp_buf) +- memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); +-out: +- free_scm_command(cmd); +- return ret; +-} +-EXPORT_SYMBOL(scm_call); +- +-u32 scm_get_version(void) +-{ +- int context_id; +- static u32 version = -1; +- register u32 r0 asm("r0"); +- register u32 r1 asm("r1"); +- +- if (version != -1) +- return version; +- +- mutex_lock(&scm_lock); +- +- r0 = 0x1 << 8; +- r1 = (u32)&context_id; +- do { +- asm volatile( +- __asmeq("%0", "r0") +- __asmeq("%1", "r1") +- __asmeq("%2", "r0") +- __asmeq("%3", "r1") +-#ifdef REQUIRES_SEC +- ".arch_extension sec\n" +-#endif +- "smc #0 @ switch to secure world\n" +- : "=r" (r0), "=r" (r1) +- : "r" (r0), "r" (r1) +- : "r2", "r3"); +- } while (r0 == SCM_INTERRUPTED); +- +- version = r1; +- mutex_unlock(&scm_lock); +- +- return version; +-} +-EXPORT_SYMBOL(scm_get_version); +diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-msm/scm.h +deleted file mode 100644 +index 00b31ea..0000000 +--- a/arch/arm/mach-msm/scm.h ++++ /dev/null +@@ -1,25 +0,0 @@ +-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +-#ifndef __MACH_SCM_H +-#define __MACH_SCM_H +- +-#define SCM_SVC_BOOT 0x1 +-#define SCM_SVC_PIL 0x2 +- +-extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, +- void *resp_buf, size_t resp_len); +- +-#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) +- +-extern u32 scm_get_version(void); +- +-#endif +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +new file mode 100644 +index 0000000..a028be2 +--- /dev/null ++++ b/arch/arm/mach-qcom/Kconfig +@@ -0,0 +1,33 @@ ++config ARCH_QCOM ++ bool "Qualcomm Support" if ARCH_MULTI_V7 ++ select ARCH_REQUIRE_GPIOLIB ++ select ARM_GIC ++ select CLKSRC_OF ++ select GENERIC_CLOCKEVENTS ++ select HAVE_SMP ++ select QCOM_SCM if SMP ++ help ++ Support for Qualcomm's devicetree based systems. ++ ++if ARCH_QCOM ++ ++menu "Qualcomm SoC Selection" ++ ++config ARCH_MSM8X60 ++ bool "Enable support for MSM8X60" ++ select CLKSRC_QCOM ++ ++config ARCH_MSM8960 ++ bool "Enable support for MSM8960" ++ select CLKSRC_QCOM ++ ++config ARCH_MSM8974 ++ bool "Enable support for MSM8974" ++ select HAVE_ARM_ARCH_TIMER ++ ++endmenu ++ ++config QCOM_SCM ++ bool ++ ++endif +diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile +new file mode 100644 +index 0000000..8f756ae +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile +@@ -0,0 +1,5 @@ ++obj-y := board.o ++obj-$(CONFIG_SMP) += platsmp.o ++obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o ++ ++CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +new file mode 100644 +index 0000000..4529f6b +--- /dev/null ++++ b/arch/arm/mach-qcom/board.c +@@ -0,0 +1,40 @@ ++/* Copyright (c) 2010-2014 The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/init.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++ ++#include <asm/mach/arch.h> ++#include <asm/mach/map.h> ++ ++extern struct smp_operations msm_smp_ops; ++ ++static const char * const qcom_dt_match[] __initconst = { ++ "qcom,msm8660-surf", ++ "qcom,msm8960-cdp", ++ NULL ++}; ++ ++static const char * const apq8074_dt_match[] __initconst = { ++ "qcom,apq8074-dragonboard", ++ NULL ++}; ++ ++DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") ++ .smp = smp_ops(msm_smp_ops), ++ .dt_compat = qcom_dt_match, ++MACHINE_END ++ ++DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)") ++ .dt_compat = apq8074_dt_match, ++MACHINE_END +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +new file mode 100644 +index 0000000..67823a7 +--- /dev/null ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (C) 2002 ARM Ltd. ++ * All Rights Reserved ++ * Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * Copyright (c) 2014 The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/smp.h> ++#include <linux/io.h> ++ ++#include <asm/cputype.h> ++#include <asm/smp_plat.h> ++ ++#include "scm-boot.h" ++ ++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 ++#define SCSS_CPU1CORE_RESET 0xD80 ++#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 ++ ++extern void secondary_startup(void); ++ ++static DEFINE_SPINLOCK(boot_lock); ++ ++#ifdef CONFIG_HOTPLUG_CPU ++static void __ref msm_cpu_die(unsigned int cpu) ++{ ++ wfi(); ++} ++#endif ++ ++static inline int get_core_count(void) ++{ ++ /* 1 + the PART[1:0] field of MIDR */ ++ return ((read_cpuid_id() >> 4) & 3) + 1; ++} ++ ++static void msm_secondary_init(unsigned int cpu) ++{ ++ /* ++ * Synchronise with the boot thread. ++ */ ++ spin_lock(&boot_lock); ++ spin_unlock(&boot_lock); ++} ++ ++static void prepare_cold_cpu(unsigned int cpu) ++{ ++ int ret; ++ ret = scm_set_boot_addr(virt_to_phys(secondary_startup), ++ SCM_FLAG_COLDBOOT_CPU1); ++ if (ret == 0) { ++ void __iomem *sc1_base_ptr; ++ sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); ++ if (sc1_base_ptr) { ++ writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); ++ writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); ++ writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); ++ iounmap(sc1_base_ptr); ++ } ++ } else ++ printk(KERN_DEBUG "Failed to set secondary core boot " ++ "address\n"); ++} ++ ++static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ static int cold_boot_done; ++ ++ /* Only need to bring cpu out of reset this way once */ ++ if (cold_boot_done == false) { ++ prepare_cold_cpu(cpu); ++ cold_boot_done = true; ++ } ++ ++ /* ++ * set synchronisation state between this boot processor ++ * and the secondary one ++ */ ++ spin_lock(&boot_lock); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ ++ /* ++ * now the secondary core is starting up let it run its ++ * calibrations, then wait for it to finish ++ */ ++ spin_unlock(&boot_lock); ++ ++ return 0; ++} ++ ++/* ++ * Initialise the CPU possible map early - this describes the CPUs ++ * which may be present or become present in the system. The msm8x60 ++ * does not support the ARM SCU, so just set the possible cpu mask to ++ * NR_CPUS. ++ */ ++static void __init msm_smp_init_cpus(void) ++{ ++ unsigned int i, ncores = get_core_count(); ++ ++ if (ncores > nr_cpu_ids) { ++ pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", ++ ncores, nr_cpu_ids); ++ ncores = nr_cpu_ids; ++ } ++ ++ for (i = 0; i < ncores; i++) ++ set_cpu_possible(i, true); ++} ++ ++static void __init msm_smp_prepare_cpus(unsigned int max_cpus) ++{ ++} ++ ++struct smp_operations msm_smp_ops __initdata = { ++ .smp_init_cpus = msm_smp_init_cpus, ++ .smp_prepare_cpus = msm_smp_prepare_cpus, ++ .smp_secondary_init = msm_secondary_init, ++ .smp_boot_secondary = msm_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = msm_cpu_die, ++#endif ++}; +diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c +new file mode 100644 +index 0000000..45cee3e +--- /dev/null ++++ b/arch/arm/mach-qcom/scm-boot.c +@@ -0,0 +1,39 @@ ++/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#include "scm.h" ++#include "scm-boot.h" ++ ++/* ++ * Set the cold/warm boot address for one of the CPU cores. ++ */ ++int scm_set_boot_addr(phys_addr_t addr, int flags) ++{ ++ struct { ++ unsigned int flags; ++ phys_addr_t addr; ++ } cmd; ++ ++ cmd.addr = addr; ++ cmd.flags = flags; ++ return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, ++ &cmd, sizeof(cmd), NULL, 0); ++} ++EXPORT_SYMBOL(scm_set_boot_addr); +diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h +new file mode 100644 +index 0000000..7be32ff +--- /dev/null ++++ b/arch/arm/mach-qcom/scm-boot.h +@@ -0,0 +1,22 @@ ++/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __MACH_SCM_BOOT_H ++#define __MACH_SCM_BOOT_H ++ ++#define SCM_BOOT_ADDR 0x1 ++#define SCM_FLAG_COLDBOOT_CPU1 0x1 ++#define SCM_FLAG_WARMBOOT_CPU1 0x2 ++#define SCM_FLAG_WARMBOOT_CPU0 0x4 ++ ++int scm_set_boot_addr(phys_addr_t addr, int flags); ++ ++#endif +diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c +new file mode 100644 +index 0000000..c536fd6 +--- /dev/null ++++ b/arch/arm/mach-qcom/scm.c +@@ -0,0 +1,299 @@ ++/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++ ++#include <asm/cacheflush.h> ++ ++#include "scm.h" ++ ++/* Cache line size for msm8x60 */ ++#define CACHELINESIZE 32 ++ ++#define SCM_ENOMEM -5 ++#define SCM_EOPNOTSUPP -4 ++#define SCM_EINVAL_ADDR -3 ++#define SCM_EINVAL_ARG -2 ++#define SCM_ERROR -1 ++#define SCM_INTERRUPTED 1 ++ ++static DEFINE_MUTEX(scm_lock); ++ ++/** ++ * struct scm_command - one SCM command buffer ++ * @len: total available memory for command and response ++ * @buf_offset: start of command buffer ++ * @resp_hdr_offset: start of response buffer ++ * @id: command to be executed ++ * @buf: buffer returned from scm_get_command_buffer() ++ * ++ * An SCM command is laid out in memory as follows: ++ * ++ * ------------------- <--- struct scm_command ++ * | command header | ++ * ------------------- <--- scm_get_command_buffer() ++ * | command buffer | ++ * ------------------- <--- struct scm_response and ++ * | response header | scm_command_to_response() ++ * ------------------- <--- scm_get_response_buffer() ++ * | response buffer | ++ * ------------------- ++ * ++ * There can be arbitrary padding between the headers and buffers so ++ * you should always use the appropriate scm_get_*_buffer() routines ++ * to access the buffers in a safe manner. ++ */ ++struct scm_command { ++ u32 len; ++ u32 buf_offset; ++ u32 resp_hdr_offset; ++ u32 id; ++ u32 buf[0]; ++}; ++ ++/** ++ * struct scm_response - one SCM response buffer ++ * @len: total available memory for response ++ * @buf_offset: start of response data relative to start of scm_response ++ * @is_complete: indicates if the command has finished processing ++ */ ++struct scm_response { ++ u32 len; ++ u32 buf_offset; ++ u32 is_complete; ++}; ++ ++/** ++ * alloc_scm_command() - Allocate an SCM command ++ * @cmd_size: size of the command buffer ++ * @resp_size: size of the response buffer ++ * ++ * Allocate an SCM command, including enough room for the command ++ * and response headers as well as the command and response buffers. ++ * ++ * Returns a valid &scm_command on success or %NULL if the allocation fails. ++ */ ++static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size) ++{ ++ struct scm_command *cmd; ++ size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size + ++ resp_size; ++ ++ cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); ++ if (cmd) { ++ cmd->len = len; ++ cmd->buf_offset = offsetof(struct scm_command, buf); ++ cmd->resp_hdr_offset = cmd->buf_offset + cmd_size; ++ } ++ return cmd; ++} ++ ++/** ++ * free_scm_command() - Free an SCM command ++ * @cmd: command to free ++ * ++ * Free an SCM command. ++ */ ++static inline void free_scm_command(struct scm_command *cmd) ++{ ++ kfree(cmd); ++} ++ ++/** ++ * scm_command_to_response() - Get a pointer to a scm_response ++ * @cmd: command ++ * ++ * Returns a pointer to a response for a command. ++ */ ++static inline struct scm_response *scm_command_to_response( ++ const struct scm_command *cmd) ++{ ++ return (void *)cmd + cmd->resp_hdr_offset; ++} ++ ++/** ++ * scm_get_command_buffer() - Get a pointer to a command buffer ++ * @cmd: command ++ * ++ * Returns a pointer to the command buffer of a command. ++ */ ++static inline void *scm_get_command_buffer(const struct scm_command *cmd) ++{ ++ return (void *)cmd->buf; ++} ++ ++/** ++ * scm_get_response_buffer() - Get a pointer to a response buffer ++ * @rsp: response ++ * ++ * Returns a pointer to a response buffer of a response. ++ */ ++static inline void *scm_get_response_buffer(const struct scm_response *rsp) ++{ ++ return (void *)rsp + rsp->buf_offset; ++} ++ ++static int scm_remap_error(int err) ++{ ++ switch (err) { ++ case SCM_ERROR: ++ return -EIO; ++ case SCM_EINVAL_ADDR: ++ case SCM_EINVAL_ARG: ++ return -EINVAL; ++ case SCM_EOPNOTSUPP: ++ return -EOPNOTSUPP; ++ case SCM_ENOMEM: ++ return -ENOMEM; ++ } ++ return -EINVAL; ++} ++ ++static u32 smc(u32 cmd_addr) ++{ ++ int context_id; ++ register u32 r0 asm("r0") = 1; ++ register u32 r1 asm("r1") = (u32)&context_id; ++ register u32 r2 asm("r2") = cmd_addr; ++ do { ++ asm volatile( ++ __asmeq("%0", "r0") ++ __asmeq("%1", "r0") ++ __asmeq("%2", "r1") ++ __asmeq("%3", "r2") ++#ifdef REQUIRES_SEC ++ ".arch_extension sec\n" ++#endif ++ "smc #0 @ switch to secure world\n" ++ : "=r" (r0) ++ : "r" (r0), "r" (r1), "r" (r2) ++ : "r3"); ++ } while (r0 == SCM_INTERRUPTED); ++ ++ return r0; ++} ++ ++static int __scm_call(const struct scm_command *cmd) ++{ ++ int ret; ++ u32 cmd_addr = virt_to_phys(cmd); ++ ++ /* ++ * Flush the entire cache here so callers don't have to remember ++ * to flush the cache when passing physical addresses to the secure ++ * side in the buffer. ++ */ ++ flush_cache_all(); ++ ret = smc(cmd_addr); ++ if (ret < 0) ++ ret = scm_remap_error(ret); ++ ++ return ret; ++} ++ ++/** ++ * scm_call() - Send an SCM command ++ * @svc_id: service identifier ++ * @cmd_id: command identifier ++ * @cmd_buf: command buffer ++ * @cmd_len: length of the command buffer ++ * @resp_buf: response buffer ++ * @resp_len: length of the response buffer ++ * ++ * Sends a command to the SCM and waits for the command to finish processing. ++ */ ++int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, ++ void *resp_buf, size_t resp_len) ++{ ++ int ret; ++ struct scm_command *cmd; ++ struct scm_response *rsp; ++ ++ cmd = alloc_scm_command(cmd_len, resp_len); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->id = (svc_id << 10) | cmd_id; ++ if (cmd_buf) ++ memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len); ++ ++ mutex_lock(&scm_lock); ++ ret = __scm_call(cmd); ++ mutex_unlock(&scm_lock); ++ if (ret) ++ goto out; ++ ++ rsp = scm_command_to_response(cmd); ++ do { ++ u32 start = (u32)rsp; ++ u32 end = (u32)scm_get_response_buffer(rsp) + resp_len; ++ start &= ~(CACHELINESIZE - 1); ++ while (start < end) { ++ asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) ++ : "memory"); ++ start += CACHELINESIZE; ++ } ++ } while (!rsp->is_complete); ++ ++ if (resp_buf) ++ memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); ++out: ++ free_scm_command(cmd); ++ return ret; ++} ++EXPORT_SYMBOL(scm_call); ++ ++u32 scm_get_version(void) ++{ ++ int context_id; ++ static u32 version = -1; ++ register u32 r0 asm("r0"); ++ register u32 r1 asm("r1"); ++ ++ if (version != -1) ++ return version; ++ ++ mutex_lock(&scm_lock); ++ ++ r0 = 0x1 << 8; ++ r1 = (u32)&context_id; ++ do { ++ asm volatile( ++ __asmeq("%0", "r0") ++ __asmeq("%1", "r1") ++ __asmeq("%2", "r0") ++ __asmeq("%3", "r1") ++#ifdef REQUIRES_SEC ++ ".arch_extension sec\n" ++#endif ++ "smc #0 @ switch to secure world\n" ++ : "=r" (r0), "=r" (r1) ++ : "r" (r0), "r" (r1) ++ : "r2", "r3"); ++ } while (r0 == SCM_INTERRUPTED); ++ ++ version = r1; ++ mutex_unlock(&scm_lock); ++ ++ return version; ++} ++EXPORT_SYMBOL(scm_get_version); +diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h +new file mode 100644 +index 0000000..00b31ea +--- /dev/null ++++ b/arch/arm/mach-qcom/scm.h +@@ -0,0 +1,25 @@ ++/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __MACH_SCM_H ++#define __MACH_SCM_H ++ ++#define SCM_SVC_BOOT 0x1 ++#define SCM_SVC_PIL 0x2 ++ ++extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, ++ void *resp_buf, size_t resp_len); ++ ++#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) ++ ++extern u32 scm_get_version(void); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch new file mode 100644 index 0000000000..5cb6bf393f --- /dev/null +++ b/target/linux/ipq806x/patches/0006-clocksource-qcom-split-building-of-legacy-vs-multipl.patch @@ -0,0 +1,78 @@ +From 085d4b834dfced8580aab74707e30699b63e7c36 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 17:01:37 -0600 +Subject: [PATCH 006/182] clocksource: qcom: split building of legacy vs + multiplatform support + +The majority of the clocksource code for the Qualcomm platform is shared +between newer (multiplatform) and older platforms. However there is a bit +of code that isn't, so only build it for the appropriate config. + +Acked-by: Olof Johansson <olof@lixom.net> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/clocksource/qcom-timer.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c +index dca829e..e807acf 100644 +--- a/drivers/clocksource/qcom-timer.c ++++ b/drivers/clocksource/qcom-timer.c +@@ -106,15 +106,6 @@ static notrace cycle_t msm_read_timer_count(struct clocksource *cs) + return readl_relaxed(source_base + TIMER_COUNT_VAL); + } + +-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) +-{ +- /* +- * Shift timer count down by a constant due to unreliable lower bits +- * on some targets. +- */ +- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; +-} +- + static struct clocksource msm_clocksource = { + .name = "dg_timer", + .rating = 300, +@@ -228,7 +219,7 @@ err: + sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); + } + +-#ifdef CONFIG_OF ++#ifdef CONFIG_ARCH_QCOM + static void __init msm_dt_timer_init(struct device_node *np) + { + u32 freq; +@@ -281,7 +272,7 @@ static void __init msm_dt_timer_init(struct device_node *np) + } + CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); + CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); +-#endif ++#else + + static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, + u32 sts) +@@ -301,6 +292,15 @@ static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, + return 0; + } + ++static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) ++{ ++ /* ++ * Shift timer count down by a constant due to unreliable lower bits ++ * on some targets. ++ */ ++ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; ++} ++ + void __init msm7x01_timer_init(void) + { + struct clocksource *cs = &msm_clocksource; +@@ -327,3 +327,4 @@ void __init qsd8x50_timer_init(void) + return; + msm_timer_init(19200000 / 4, 32, 7, false); + } ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch new file mode 100644 index 0000000000..fc064b27a9 --- /dev/null +++ b/target/linux/ipq806x/patches/0007-ARM-qcom-Rename-various-msm-prefixed-functions-to-qc.patch @@ -0,0 +1,105 @@ +From 35eb6f73546d3b9475652a38fa641bd1a05a1ea1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 4 Feb 2014 15:38:45 -0600 +Subject: [PATCH 007/182] ARM: qcom: Rename various msm prefixed functions to + qcom + +As mach-qcom will support a number of different Qualcomm SoC platforms +we replace the msm prefix on function names with qcom to be a bit more +generic. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/board.c | 4 ++-- + arch/arm/mach-qcom/platsmp.c | 22 +++++++++++----------- + 2 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 4529f6b..830f69c 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -17,7 +17,7 @@ + #include <asm/mach/arch.h> + #include <asm/mach/map.h> + +-extern struct smp_operations msm_smp_ops; ++extern struct smp_operations qcom_smp_ops; + + static const char * const qcom_dt_match[] __initconst = { + "qcom,msm8660-surf", +@@ -31,7 +31,7 @@ static const char * const apq8074_dt_match[] __initconst = { + }; + + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") +- .smp = smp_ops(msm_smp_ops), ++ .smp = smp_ops(qcom_smp_ops), + .dt_compat = qcom_dt_match, + MACHINE_END + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index 67823a7..9c53ea7 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -30,7 +30,7 @@ extern void secondary_startup(void); + static DEFINE_SPINLOCK(boot_lock); + + #ifdef CONFIG_HOTPLUG_CPU +-static void __ref msm_cpu_die(unsigned int cpu) ++static void __ref qcom_cpu_die(unsigned int cpu) + { + wfi(); + } +@@ -42,7 +42,7 @@ static inline int get_core_count(void) + return ((read_cpuid_id() >> 4) & 3) + 1; + } + +-static void msm_secondary_init(unsigned int cpu) ++static void qcom_secondary_init(unsigned int cpu) + { + /* + * Synchronise with the boot thread. +@@ -70,7 +70,7 @@ static void prepare_cold_cpu(unsigned int cpu) + "address\n"); + } + +-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) ++static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) + { + static int cold_boot_done; + +@@ -108,7 +108,7 @@ static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle) + * does not support the ARM SCU, so just set the possible cpu mask to + * NR_CPUS. + */ +-static void __init msm_smp_init_cpus(void) ++static void __init qcom_smp_init_cpus(void) + { + unsigned int i, ncores = get_core_count(); + +@@ -122,16 +122,16 @@ static void __init msm_smp_init_cpus(void) + set_cpu_possible(i, true); + } + +-static void __init msm_smp_prepare_cpus(unsigned int max_cpus) ++static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + } + +-struct smp_operations msm_smp_ops __initdata = { +- .smp_init_cpus = msm_smp_init_cpus, +- .smp_prepare_cpus = msm_smp_prepare_cpus, +- .smp_secondary_init = msm_secondary_init, +- .smp_boot_secondary = msm_boot_secondary, ++struct smp_operations qcom_smp_ops __initdata = { ++ .smp_init_cpus = qcom_smp_init_cpus, ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = qcom_boot_secondary, + #ifdef CONFIG_HOTPLUG_CPU +- .cpu_die = msm_cpu_die, ++ .cpu_die = qcom_cpu_die, + #endif + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch new file mode 100644 index 0000000000..9579ac2f63 --- /dev/null +++ b/target/linux/ipq806x/patches/0008-ARM-Introduce-CPU_METHOD_OF_DECLARE-for-cpu-hotplug-.patch @@ -0,0 +1,159 @@ +From 48f17325fef9cbebc5cb39aa78f4c1caff5d7b16 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 30 Oct 2013 18:21:09 -0700 +Subject: [PATCH 008/182] ARM: Introduce CPU_METHOD_OF_DECLARE() for cpu + hotplug/smp + +The goal of multi-platform kernels is to remove the need for mach +directories and machine descriptors. To further that goal, +introduce CPU_METHOD_OF_DECLARE() to allow cpu hotplug/smp +support to be separated from the machine descriptors. +Implementers should specify an enable-method property in their +cpus node and then implement a matching set of smp_ops in their +hotplug/smp code, wiring it up with the CPU_METHOD_OF_DECLARE() +macro. When the kernel is compiled we'll collect all the +enable-method smp_ops into one section for use at boot. + +At boot time we'll look for an enable-method in each cpu node and +try to match that against all known CPU enable methods in the +kernel. If there are no enable-methods in the cpu nodes we +fallback to the cpus node and try to use any enable-method found +there. If that doesn't work we fall back to the old way of using +the machine descriptor. + +Acked-by: Mark Rutland <mark.rutland@arm.com> +Cc: Russell King <linux@arm.linux.org.uk> +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/include/asm/smp.h | 9 +++++++++ + arch/arm/kernel/devtree.c | 40 +++++++++++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 10 ++++++++++ + 3 files changed, 59 insertions(+) + +diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h +index 22a3b9b..772435b 100644 +--- a/arch/arm/include/asm/smp.h ++++ b/arch/arm/include/asm/smp.h +@@ -114,6 +114,15 @@ struct smp_operations { + #endif + }; + ++struct of_cpu_method { ++ const char *method; ++ struct smp_operations *ops; ++}; ++ ++#define CPU_METHOD_OF_DECLARE(name, _method, _ops) \ ++ static const struct of_cpu_method __cpu_method_of_table_##name \ ++ __used __section(__cpu_method_of_table) \ ++ = { .method = _method, .ops = _ops } + /* + * set platform specific SMP operations + */ +diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c +index f751714..c7419a5 100644 +--- a/arch/arm/kernel/devtree.c ++++ b/arch/arm/kernel/devtree.c +@@ -18,6 +18,7 @@ + #include <linux/of_fdt.h> + #include <linux/of_irq.h> + #include <linux/of_platform.h> ++#include <linux/smp.h> + + #include <asm/cputype.h> + #include <asm/setup.h> +@@ -63,6 +64,34 @@ void __init arm_dt_memblock_reserve(void) + } + } + ++#ifdef CONFIG_SMP ++extern struct of_cpu_method __cpu_method_of_table_begin[]; ++extern struct of_cpu_method __cpu_method_of_table_end[]; ++ ++static int __init set_smp_ops_by_method(struct device_node *node) ++{ ++ const char *method; ++ struct of_cpu_method *m = __cpu_method_of_table_begin; ++ ++ if (of_property_read_string(node, "enable-method", &method)) ++ return 0; ++ ++ for (; m < __cpu_method_of_table_end; m++) ++ if (!strcmp(m->method, method)) { ++ smp_set_ops(m->ops); ++ return 1; ++ } ++ ++ return 0; ++} ++#else ++static inline int set_smp_ops_by_method(struct device_node *node) ++{ ++ return 1; ++} ++#endif ++ ++ + /* + * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree + * and builds the cpu logical map array containing MPIDR values related to +@@ -79,6 +108,7 @@ void __init arm_dt_init_cpu_maps(void) + * read as 0. + */ + struct device_node *cpu, *cpus; ++ int found_method = 0; + u32 i, j, cpuidx = 1; + u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0; + +@@ -150,8 +180,18 @@ void __init arm_dt_init_cpu_maps(void) + } + + tmp_map[i] = hwid; ++ ++ if (!found_method) ++ found_method = set_smp_ops_by_method(cpu); + } + ++ /* ++ * Fallback to an enable-method in the cpus node if nothing found in ++ * a cpu node. ++ */ ++ if (!found_method) ++ set_smp_ops_by_method(cpus); ++ + if (!bootcpu_valid) { + pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n"); + return; +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index bc2121f..bd02ca7 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -167,6 +167,15 @@ + #define CLK_OF_TABLES() + #endif + ++#ifdef CONFIG_SMP ++#define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \ ++ *(__cpu_method_of_table) \ ++ VMLINUX_SYMBOL(__cpu_method_of_table_end) = .; ++#else ++#define CPU_METHOD_OF_TABLES() ++#endif ++ + #define KERNEL_DTB() \ + STRUCT_ALIGN(); \ + VMLINUX_SYMBOL(__dtb_start) = .; \ +@@ -491,6 +500,7 @@ + MEM_DISCARD(init.rodata) \ + CLK_OF_TABLES() \ + CLKSRC_OF_TABLES() \ ++ CPU_METHOD_OF_TABLES() \ + KERNEL_DTB() \ + IRQCHIP_OF_MATCH_TABLE() + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch new file mode 100644 index 0000000000..6601ad4e0d --- /dev/null +++ b/target/linux/ipq806x/patches/0009-ARM-qcom-Re-organize-platsmp-to-make-it-extensible.patch @@ -0,0 +1,249 @@ +From 391848e75f8b0ba14816da1ac8f2d838fd0d5744 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Tue, 21 May 2013 19:13:29 -0700 +Subject: [PATCH 009/182] ARM: qcom: Re-organize platsmp to make it extensible + +This makes it easy to add SMP support for new devices by keying +on a device node for the release sequence. We add the +enable-method property for the cpus property to specify that we +want to use the gcc-msm8660 release sequence (which is going to +look for the global clock controller device node to map some +Scorpion specific power and control registers). We also remove +the nr_cpus detection code as that is done generically in the DT +CPU detection code. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Port to CPU_METHOD_OF_DECLARE] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-msm/common.h | 2 - + arch/arm/mach-qcom/board.c | 14 ----- + arch/arm/mach-qcom/platsmp.c | 118 +++++++++++++++++++++++------------------- + 3 files changed, 65 insertions(+), 69 deletions(-) + +diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h +index 0a4899b..572479a 100644 +--- a/arch/arm/mach-msm/common.h ++++ b/arch/arm/mach-msm/common.h +@@ -23,8 +23,6 @@ extern void msm_map_qsd8x50_io(void); + extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, + unsigned int mtype, void *caller); + +-extern struct smp_operations msm_smp_ops; +- + struct msm_mmc_platform_data; + + extern void msm_add_devices(void); +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 830f69c..bae617e 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -11,30 +11,16 @@ + */ + + #include <linux/init.h> +-#include <linux/of.h> +-#include <linux/of_platform.h> + + #include <asm/mach/arch.h> +-#include <asm/mach/map.h> +- +-extern struct smp_operations qcom_smp_ops; + + static const char * const qcom_dt_match[] __initconst = { + "qcom,msm8660-surf", + "qcom,msm8960-cdp", +- NULL +-}; +- +-static const char * const apq8074_dt_match[] __initconst = { + "qcom,apq8074-dragonboard", + NULL + }; + + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") +- .smp = smp_ops(qcom_smp_ops), + .dt_compat = qcom_dt_match, + MACHINE_END +- +-DT_MACHINE_START(APQ_DT, "Qualcomm (Flattened Device Tree)") +- .dt_compat = apq8074_dt_match, +-MACHINE_END +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index 9c53ea7..ec8604d 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -13,17 +13,18 @@ + #include <linux/errno.h> + #include <linux/delay.h> + #include <linux/device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> + #include <linux/smp.h> + #include <linux/io.h> + +-#include <asm/cputype.h> + #include <asm/smp_plat.h> + + #include "scm-boot.h" + +-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 +-#define SCSS_CPU1CORE_RESET 0xD80 +-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 ++#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 ++#define SCSS_CPU1CORE_RESET 0x2d80 ++#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 + + extern void secondary_startup(void); + +@@ -36,12 +37,6 @@ static void __ref qcom_cpu_die(unsigned int cpu) + } + #endif + +-static inline int get_core_count(void) +-{ +- /* 1 + the PART[1:0] field of MIDR */ +- return ((read_cpuid_id() >> 4) & 3) + 1; +-} +- + static void qcom_secondary_init(unsigned int cpu) + { + /* +@@ -51,33 +46,41 @@ static void qcom_secondary_init(unsigned int cpu) + spin_unlock(&boot_lock); + } + +-static void prepare_cold_cpu(unsigned int cpu) ++static int scss_release_secondary(unsigned int cpu) + { +- int ret; +- ret = scm_set_boot_addr(virt_to_phys(secondary_startup), +- SCM_FLAG_COLDBOOT_CPU1); +- if (ret == 0) { +- void __iomem *sc1_base_ptr; +- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); +- if (sc1_base_ptr) { +- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); +- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); +- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); +- iounmap(sc1_base_ptr); +- } +- } else +- printk(KERN_DEBUG "Failed to set secondary core boot " +- "address\n"); ++ struct device_node *node; ++ void __iomem *base; ++ ++ node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); ++ if (!node) { ++ pr_err("%s: can't find node\n", __func__); ++ return -ENXIO; ++ } ++ ++ base = of_iomap(node, 0); ++ of_node_put(node); ++ if (!base) ++ return -ENOMEM; ++ ++ writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); ++ writel_relaxed(0, base + SCSS_CPU1CORE_RESET); ++ writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); ++ mb(); ++ iounmap(base); ++ ++ return 0; + } + +-static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) ++static DEFINE_PER_CPU(int, cold_boot_done); ++ ++static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) + { +- static int cold_boot_done; ++ int ret = 0; + +- /* Only need to bring cpu out of reset this way once */ +- if (cold_boot_done == false) { +- prepare_cold_cpu(cpu); +- cold_boot_done = true; ++ if (!per_cpu(cold_boot_done, cpu)) { ++ ret = func(cpu); ++ if (!ret) ++ per_cpu(cold_boot_done, cpu) = true; + } + + /* +@@ -99,39 +102,48 @@ static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) + */ + spin_unlock(&boot_lock); + +- return 0; ++ return ret; + } + +-/* +- * Initialise the CPU possible map early - this describes the CPUs +- * which may be present or become present in the system. The msm8x60 +- * does not support the ARM SCU, so just set the possible cpu mask to +- * NR_CPUS. +- */ +-static void __init qcom_smp_init_cpus(void) ++static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) + { +- unsigned int i, ncores = get_core_count(); +- +- if (ncores > nr_cpu_ids) { +- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", +- ncores, nr_cpu_ids); +- ncores = nr_cpu_ids; +- } +- +- for (i = 0; i < ncores; i++) +- set_cpu_possible(i, true); ++ return qcom_boot_secondary(cpu, scss_release_secondary); + } + + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { ++ int cpu, map; ++ unsigned int flags = 0; ++ static const int cold_boot_flags[] = { ++ 0, ++ SCM_FLAG_COLDBOOT_CPU1, ++ }; ++ ++ for_each_present_cpu(cpu) { ++ map = cpu_logical_map(cpu); ++ if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) { ++ set_cpu_present(cpu, false); ++ continue; ++ } ++ flags |= cold_boot_flags[map]; ++ } ++ ++ if (scm_set_boot_addr(virt_to_phys(secondary_startup), flags)) { ++ for_each_present_cpu(cpu) { ++ if (cpu == smp_processor_id()) ++ continue; ++ set_cpu_present(cpu, false); ++ } ++ pr_warn("Failed to set CPU boot address, disabling SMP\n"); ++ } + } + +-struct smp_operations qcom_smp_ops __initdata = { +- .smp_init_cpus = qcom_smp_init_cpus, ++static struct smp_operations smp_msm8660_ops __initdata = { + .smp_prepare_cpus = qcom_smp_prepare_cpus, + .smp_secondary_init = qcom_secondary_init, +- .smp_boot_secondary = qcom_boot_secondary, ++ .smp_boot_secondary = msm8660_boot_secondary, + #ifdef CONFIG_HOTPLUG_CPU + .cpu_die = qcom_cpu_die, + #endif + }; ++CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch new file mode 100644 index 0000000000..0bcd3e873b --- /dev/null +++ b/target/linux/ipq806x/patches/0010-devicetree-bindings-Document-Krait-Scorpion-cpus-and.patch @@ -0,0 +1,70 @@ +From 236d07c7bb0c758ea40ea0110d37306d2e7d9a4b Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Thu, 31 Oct 2013 17:26:33 -0700 +Subject: [PATCH 010/182] devicetree: bindings: Document Krait/Scorpion cpus + and enable-method + +Scorpion and Krait don't use the spin-table enable-method. +Instead they rely on mmio register accesses to enable power and +clocks to bring CPUs out of reset. Document their enable-methods. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Split off into separate patch, renamed methods to +match compatible nodes] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + Documentation/devicetree/bindings/arm/cpus.txt | 25 +++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt +index 9130435..333f4ae 100644 +--- a/Documentation/devicetree/bindings/arm/cpus.txt ++++ b/Documentation/devicetree/bindings/arm/cpus.txt +@@ -180,7 +180,11 @@ nodes to be present and contain the properties described below. + be one of: + "spin-table" + "psci" +- # On ARM 32-bit systems this property is optional. ++ # On ARM 32-bit systems this property is optional and ++ can be one of: ++ "qcom,gcc-msm8660" ++ "qcom,kpss-acc-v1" ++ "qcom,kpss-acc-v2" + + - cpu-release-addr + Usage: required for systems that have an "enable-method" +@@ -191,6 +195,21 @@ nodes to be present and contain the properties described below. + property identifying a 64-bit zero-initialised + memory location. + ++ - qcom,saw ++ Usage: required for systems that have an "enable-method" ++ property value of "qcom,kpss-acc-v1" or ++ "qcom,kpss-acc-v2" ++ Value type: <phandle> ++ Definition: Specifies the SAW[1] node associated with this CPU. ++ ++ - qcom,acc ++ Usage: required for systems that have an "enable-method" ++ property value of "qcom,kpss-acc-v1" or ++ "qcom,kpss-acc-v2" ++ Value type: <phandle> ++ Definition: Specifies the ACC[2] node associated with this CPU. ++ ++ + Example 1 (dual-cluster big.LITTLE system 32-bit): + + cpus { +@@ -382,3 +401,7 @@ cpus { + cpu-release-addr = <0 0x20000000>; + }; + }; ++ ++-- ++[1] arm/msm/qcom,saw2.txt ++[2] arm/msm/qcom,kpss-acc.txt +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch new file mode 100644 index 0000000000..fc57727b37 --- /dev/null +++ b/target/linux/ipq806x/patches/0011-devicetree-bindings-Document-qcom-kpss-acc.patch @@ -0,0 +1,55 @@ +From 3a4fe9a3a4aff8b1f1c3685bc9b6adbe739d7367 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 31 Oct 2013 18:20:30 -0700 +Subject: [PATCH 011/182] devicetree: bindings: Document qcom,kpss-acc + +The kpss acc binding describes the clock, reset, and power domain +controller for a Krait CPU. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 30 ++++++++++++++++++++ + 1 file changed, 30 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt + +diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +new file mode 100644 +index 0000000..1333db9 +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +@@ -0,0 +1,30 @@ ++Krait Processor Sub-system (KPSS) Application Clock Controller (ACC) ++ ++The KPSS ACC provides clock, power domain, and reset control to a Krait CPU. ++There is one ACC register region per CPU within the KPSS remapped region as ++well as an alias register region that remaps accesses to the ACC associated ++with the CPU accessing the region. ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: should be one of: ++ "qcom,kpss-acc-v1" ++ "qcom,kpss-acc-v2" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: the first element specifies the base address and size of ++ the register region. An optional second element specifies ++ the base address and size of the alias register region. ++ ++Example: ++ ++ clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0x02088000 0x1000>, ++ <0x02008000 0x1000>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch new file mode 100644 index 0000000000..5bf18c1f98 --- /dev/null +++ b/target/linux/ipq806x/patches/0012-devicetree-bindings-Document-qcom-saw2-node.patch @@ -0,0 +1,60 @@ +From 17adce4d3de1fca7761607d572f4979557f0f604 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 31 Oct 2013 18:20:30 -0700 +Subject: [PATCH 012/182] devicetree: bindings: Document qcom,saw2 node + +The saw2 binding describes the SPM/AVS wrapper hardware used to +control the regulator supplying voltage to the Krait CPUs. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/arm/msm/qcom,saw2.txt | 35 ++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt + +diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt +new file mode 100644 +index 0000000..1505fb8 +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt +@@ -0,0 +1,35 @@ ++SPM AVS Wrapper 2 (SAW2) ++ ++The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the ++Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable ++micro-controller that transitions a piece of hardware (like a processor or ++subsystem) into and out of low power modes via a direct connection to ++the PMIC. It can also be wired up to interact with other processors in the ++system, notifying them when a low power state is entered or exited. ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: shall contain "qcom,saw2". A more specific value should be ++ one of: ++ "qcom,saw2-v1" ++ "qcom,saw2-v1.1" ++ "qcom,saw2-v2" ++ "qcom,saw2-v2.1" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: the first element specifies the base address and size of ++ the register region. An optional second element specifies ++ the base address and size of the alias register region. ++ ++ ++Example: ++ ++ regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch new file mode 100644 index 0000000000..66f667c2a8 --- /dev/null +++ b/target/linux/ipq806x/patches/0013-ARM-qcom-Add-SMP-support-for-KPSSv1.patch @@ -0,0 +1,181 @@ +From 8e843640b3c4a43b963332fdc7b233948ad25a5b Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Tue, 21 May 2013 19:13:50 -0700 +Subject: [PATCH 013/182] ARM: qcom: Add SMP support for KPSSv1 + +Implement support for the Krait CPU release sequence when the +CPUs are part of the first version of the krait processor +subsystem. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/platsmp.c | 106 +++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/scm-boot.h | 8 ++-- + 2 files changed, 111 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index ec8604d..cb0783f 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -26,6 +26,16 @@ + #define SCSS_CPU1CORE_RESET 0x2d80 + #define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 + ++#define APCS_CPU_PWR_CTL 0x04 ++#define PLL_CLAMP BIT(8) ++#define CORE_PWRD_UP BIT(7) ++#define COREPOR_RST BIT(5) ++#define CORE_RST BIT(4) ++#define L2DT_SLP BIT(3) ++#define CLAMP BIT(0) ++ ++#define APCS_SAW2_VCTL 0x14 ++ + extern void secondary_startup(void); + + static DEFINE_SPINLOCK(boot_lock); +@@ -71,6 +81,85 @@ static int scss_release_secondary(unsigned int cpu) + return 0; + } + ++static int kpssv1_release_secondary(unsigned int cpu) ++{ ++ int ret = 0; ++ void __iomem *reg, *saw_reg; ++ struct device_node *cpu_node, *acc_node, *saw_node; ++ u32 val; ++ ++ cpu_node = of_get_cpu_node(cpu, NULL); ++ if (!cpu_node) ++ return -ENODEV; ++ ++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); ++ if (!acc_node) { ++ ret = -ENODEV; ++ goto out_acc; ++ } ++ ++ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); ++ if (!saw_node) { ++ ret = -ENODEV; ++ goto out_saw; ++ } ++ ++ reg = of_iomap(acc_node, 0); ++ if (!reg) { ++ ret = -ENOMEM; ++ goto out_acc_map; ++ } ++ ++ saw_reg = of_iomap(saw_node, 0); ++ if (!saw_reg) { ++ ret = -ENOMEM; ++ goto out_saw_map; ++ } ++ ++ /* Turn on CPU rail */ ++ writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); ++ mb(); ++ udelay(512); ++ ++ /* Krait bring-up sequence */ ++ val = PLL_CLAMP | L2DT_SLP | CLAMP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ val &= ~L2DT_SLP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ndelay(300); ++ ++ val |= COREPOR_RST; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ val &= ~CLAMP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ val &= ~COREPOR_RST; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(100); ++ ++ val |= CORE_PWRD_UP; ++ writel_relaxed(val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ iounmap(saw_reg); ++out_saw_map: ++ iounmap(reg); ++out_acc_map: ++ of_node_put(saw_node); ++out_saw: ++ of_node_put(acc_node); ++out_acc: ++ of_node_put(cpu_node); ++ return ret; ++} ++ + static DEFINE_PER_CPU(int, cold_boot_done); + + static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) +@@ -110,6 +199,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) + return qcom_boot_secondary(cpu, scss_release_secondary); + } + ++static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ return qcom_boot_secondary(cpu, kpssv1_release_secondary); ++} ++ + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + int cpu, map; +@@ -117,6 +211,8 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + static const int cold_boot_flags[] = { + 0, + SCM_FLAG_COLDBOOT_CPU1, ++ SCM_FLAG_COLDBOOT_CPU2, ++ SCM_FLAG_COLDBOOT_CPU3, + }; + + for_each_present_cpu(cpu) { +@@ -147,3 +243,13 @@ static struct smp_operations smp_msm8660_ops __initdata = { + #endif + }; + CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); ++ ++static struct smp_operations qcom_smp_kpssv1_ops __initdata = { ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = kpssv1_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = qcom_cpu_die, ++#endif ++}; ++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); +diff --git a/arch/arm/mach-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h +index 7be32ff..6aabb24 100644 +--- a/arch/arm/mach-qcom/scm-boot.h ++++ b/arch/arm/mach-qcom/scm-boot.h +@@ -13,9 +13,11 @@ + #define __MACH_SCM_BOOT_H + + #define SCM_BOOT_ADDR 0x1 +-#define SCM_FLAG_COLDBOOT_CPU1 0x1 +-#define SCM_FLAG_WARMBOOT_CPU1 0x2 +-#define SCM_FLAG_WARMBOOT_CPU0 0x4 ++#define SCM_FLAG_COLDBOOT_CPU1 0x01 ++#define SCM_FLAG_COLDBOOT_CPU2 0x08 ++#define SCM_FLAG_COLDBOOT_CPU3 0x20 ++#define SCM_FLAG_WARMBOOT_CPU0 0x04 ++#define SCM_FLAG_WARMBOOT_CPU1 0x02 + + int scm_set_boot_addr(phys_addr_t addr, int flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch new file mode 100644 index 0000000000..03fdaf3eef --- /dev/null +++ b/target/linux/ipq806x/patches/0014-ARM-qcom-Add-SMP-support-for-KPSSv2.patch @@ -0,0 +1,172 @@ +From eb07c23d45ddf10fa89296e6c6c6aed553d8bbf5 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 21 Jun 2013 17:09:13 -0700 +Subject: [PATCH 014/182] ARM: qcom: Add SMP support for KPSSv2 + +Implement support for the Krait CPU release sequence when the +CPUs are part of the second version of the Krait processor +subsystem. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/platsmp.c | 123 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 123 insertions(+) + +diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c +index cb0783f..d690856 100644 +--- a/arch/arm/mach-qcom/platsmp.c ++++ b/arch/arm/mach-qcom/platsmp.c +@@ -34,7 +34,15 @@ + #define L2DT_SLP BIT(3) + #define CLAMP BIT(0) + ++#define APC_PWR_GATE_CTL 0x14 ++#define BHS_CNT_SHIFT 24 ++#define LDO_PWR_DWN_SHIFT 16 ++#define LDO_BYP_SHIFT 8 ++#define BHS_SEG_SHIFT 1 ++#define BHS_EN BIT(0) ++ + #define APCS_SAW2_VCTL 0x14 ++#define APCS_SAW2_2_VCTL 0x1c + + extern void secondary_startup(void); + +@@ -160,6 +168,106 @@ out_acc: + return ret; + } + ++static int kpssv2_release_secondary(unsigned int cpu) ++{ ++ void __iomem *reg; ++ struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; ++ void __iomem *l2_saw_base; ++ unsigned reg_val; ++ int ret; ++ ++ cpu_node = of_get_cpu_node(cpu, NULL); ++ if (!cpu_node) ++ return -ENODEV; ++ ++ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); ++ if (!acc_node) { ++ ret = -ENODEV; ++ goto out_acc; ++ } ++ ++ l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); ++ if (!l2_node) { ++ ret = -ENODEV; ++ goto out_l2; ++ } ++ ++ saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); ++ if (!saw_node) { ++ ret = -ENODEV; ++ goto out_saw; ++ } ++ ++ reg = of_iomap(acc_node, 0); ++ if (!reg) { ++ ret = -ENOMEM; ++ goto out_map; ++ } ++ ++ l2_saw_base = of_iomap(saw_node, 0); ++ if (!l2_saw_base) { ++ ret = -ENOMEM; ++ goto out_saw_map; ++ } ++ ++ /* Turn on the BHS, turn off LDO Bypass and power down LDO */ ++ reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ mb(); ++ /* wait for the BHS to settle */ ++ udelay(1); ++ ++ /* Turn on BHS segments */ ++ reg_val |= 0x3f << BHS_SEG_SHIFT; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ mb(); ++ /* wait for the BHS to settle */ ++ udelay(1); ++ ++ /* Finally turn on the bypass so that BHS supplies power */ ++ reg_val |= 0x3f << LDO_BYP_SHIFT; ++ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); ++ ++ /* enable max phases */ ++ writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); ++ mb(); ++ udelay(50); ++ ++ reg_val = COREPOR_RST | CLAMP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ reg_val &= ~CLAMP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ udelay(2); ++ ++ reg_val &= ~COREPOR_RST; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ reg_val |= CORE_PWRD_UP; ++ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); ++ mb(); ++ ++ ret = 0; ++ ++ iounmap(l2_saw_base); ++out_saw_map: ++ iounmap(reg); ++out_map: ++ of_node_put(saw_node); ++out_saw: ++ of_node_put(l2_node); ++out_l2: ++ of_node_put(acc_node); ++out_acc: ++ of_node_put(cpu_node); ++ ++ return ret; ++} ++ + static DEFINE_PER_CPU(int, cold_boot_done); + + static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) +@@ -204,6 +312,11 @@ static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) + return qcom_boot_secondary(cpu, kpssv1_release_secondary); + } + ++static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ return qcom_boot_secondary(cpu, kpssv2_release_secondary); ++} ++ + static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) + { + int cpu, map; +@@ -253,3 +366,13 @@ static struct smp_operations qcom_smp_kpssv1_ops __initdata = { + #endif + }; + CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); ++ ++static struct smp_operations qcom_smp_kpssv2_ops __initdata = { ++ .smp_prepare_cpus = qcom_smp_prepare_cpus, ++ .smp_secondary_init = qcom_secondary_init, ++ .smp_boot_secondary = kpssv2_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = qcom_cpu_die, ++#endif ++}; ++CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch new file mode 100644 index 0000000000..9dbc06dc26 --- /dev/null +++ b/target/linux/ipq806x/patches/0015-tty-serial-msm-Enable-building-msm_serial-for-ARCH_Q.patch @@ -0,0 +1,32 @@ +From 17368813d4182894c6d58b66f9fccd339364de8f Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 29 Jan 2014 17:23:06 -0600 +Subject: [PATCH 015/182] tty: serial: msm: Enable building msm_serial for + ARCH_QCOM + +We've split Qualcomm MSM support into legacy and multiplatform. So add +the ability to build the serial driver on the newer ARCH_QCOM +multiplatform. + +Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/tty/serial/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig +index a3815ea..ce9b12d 100644 +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1024,7 +1024,7 @@ config SERIAL_SGI_IOC3 + + config SERIAL_MSM + bool "MSM on-chip serial port support" +- depends on ARCH_MSM ++ depends on ARCH_MSM || ARCH_QCOM + select SERIAL_CORE + + config SERIAL_MSM_CONSOLE +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch new file mode 100644 index 0000000000..46e1fb79a2 --- /dev/null +++ b/target/linux/ipq806x/patches/0016-drm-msm-drop-ARCH_MSM-Kconfig-depend.patch @@ -0,0 +1,33 @@ +From aa3cf89746c43172517378224277ba961c46e28c Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:45:05 -0600 +Subject: [PATCH 016/182] drm/msm: drop ARCH_MSM Kconfig depend + +The ARCH_MSM depend is redundant with ARCH_MSM8960, so we can remove it. +Additionally, we are splitting Qualcomm MSM support into legacy (ARCH_MSM) +and multiplatform (ARCH_QCOM). The MSM8960 with be ARCH_QCOM going forward +so dropping ARCH_MSM will work properly for the new ARCH_QCOM multiplatform +build. + +Acked-by: Rob Clark <robdclark@gmail.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/gpu/drm/msm/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig +index c69d1e0..b698497 100644 +--- a/drivers/gpu/drm/msm/Kconfig ++++ b/drivers/gpu/drm/msm/Kconfig +@@ -3,7 +3,7 @@ config DRM_MSM + tristate "MSM DRM" + depends on DRM + depends on MSM_IOMMU +- depends on (ARCH_MSM && ARCH_MSM8960) || (ARM && COMPILE_TEST) ++ depends on ARCH_MSM8960 || (ARM && COMPILE_TEST) + select DRM_KMS_HELPER + select SHMEM + select TMPFS +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..868146c40f --- /dev/null +++ b/target/linux/ipq806x/patches/0017-power-reset-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,32 @@ +From 6e8707828be07397ee8ee437a6ef1b8f73f82287 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:46:08 -0600 +Subject: [PATCH 017/182] power: reset: msm - switch Kconfig to ARCH_QCOM + depends + +We've split Qualcomm MSM support into legacy and multiplatform. The reset +driver is only relevant on the multiplatform supported SoCs so switch the +Kconfig depends to ARCH_QCOM. + +Acked-by: Dmitry Eremin-Solenikov +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/power/reset/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig +index 6d452a7..fa0e4e0 100644 +--- a/drivers/power/reset/Kconfig ++++ b/drivers/power/reset/Kconfig +@@ -22,7 +22,7 @@ config POWER_RESET_GPIO + + config POWER_RESET_MSM + bool "Qualcomm MSM power-off driver" +- depends on POWER_RESET && ARCH_MSM ++ depends on POWER_RESET && ARCH_QCOM + help + Power off and restart support for Qualcomm boards. + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..b375feca2c --- /dev/null +++ b/target/linux/ipq806x/patches/0018-hwrng-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,38 @@ +From 4aa784548190c69d946b4dfbc0592a3ed7cd18da Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 30 Jan 2014 14:43:49 -0600 +Subject: [PATCH 018/182] hwrng: msm: switch Kconfig to ARCH_QCOM depends + +We've split Qualcomm MSM support into legacy and multiplatform. The RNG +driver is only relevant on the multiplatform supported SoCs so switch the +Kconfig depends to ARCH_QCOM. + +Acked-by: Herbert Xu <herbert@gondor.apana.org.au> +CC: Stanimir Varbanov <svarbanov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/char/hw_random/Kconfig | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +index 2f2b084..244759b 100644 +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -342,11 +342,11 @@ config HW_RANDOM_TPM + If unsure, say Y. + + config HW_RANDOM_MSM +- tristate "Qualcomm MSM Random Number Generator support" +- depends on HW_RANDOM && ARCH_MSM ++ tristate "Qualcomm SoCs Random Number Generator support" ++ depends on HW_RANDOM && ARCH_QCOM + ---help--- + This driver provides kernel-side support for the Random Number +- Generator hardware found on Qualcomm MSM SoCs. ++ Generator hardware found on Qualcomm SoCs. + + To compile this driver as a module, choose M here. the + module will be called msm-rng. +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch new file mode 100644 index 0000000000..210f760b22 --- /dev/null +++ b/target/linux/ipq806x/patches/0019-gpio-msm-switch-Kconfig-to-ARCH_QCOM-depends.patch @@ -0,0 +1,32 @@ +From 87d52cc8a390c5b208b6f0bddd90a2d01c906616 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 11 Feb 2014 14:08:06 -0600 +Subject: [PATCH 019/182] gpio: msm: switch Kconfig to ARCH_QCOM depends + +We've split Qualcomm MSM support into legacy and multiplatform. The gpio +msm-v2 driver is only relevant on the multiplatform supported SoCs so +switch the Kconfig depends to ARCH_QCOM. + +CC: Linus Walleij <linus.walleij@linaro.org> +Acked-by: Alexandre Courbot <gnurou@gmail.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/gpio/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 903f24d..2c38d95 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -192,7 +192,7 @@ config GPIO_MSM_V1 + + config GPIO_MSM_V2 + tristate "Qualcomm MSM GPIO v2" +- depends on GPIOLIB && OF && ARCH_MSM ++ depends on GPIOLIB && OF && ARCH_QCOM + help + Say yes here to support the GPIO interface on ARM v7 based + Qualcomm MSM chips. Most of the pins on the MSM can be +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch new file mode 100644 index 0000000000..ba25b46e32 --- /dev/null +++ b/target/linux/ipq806x/patches/0020-ARM-qcom-Enable-basic-support-for-Qualcomm-platforms.patch @@ -0,0 +1,54 @@ +From a2f356a6d49f459a2dd681fe4a7c6a55aeb8893f Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 25 Feb 2014 14:34:05 -0600 +Subject: [PATCH 020/182] ARM: qcom: Enable basic support for Qualcomm + platforms in multi_v7_defconfig + +Enable support for the MSM8x60, MSM8960, and MSM8974 SoCs, clocks and +serial console as part of the standard multi_v7_defconfig. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +[khilman: removed non-qcom changes] +Signed-off-by: Kevin Hilman <khilman@linaro.org> +--- + arch/arm/configs/multi_v7_defconfig | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig +index ee69829..1a61bd8 100644 +--- a/arch/arm/configs/multi_v7_defconfig ++++ b/arch/arm/configs/multi_v7_defconfig +@@ -31,6 +31,10 @@ CONFIG_SOC_OMAP5=y + CONFIG_SOC_AM33XX=y + CONFIG_SOC_DRA7XX=y + CONFIG_SOC_AM43XX=y ++CONFIG_ARCH_QCOM=y ++CONFIG_ARCH_MSM8X60=y ++CONFIG_ARCH_MSM8960=y ++CONFIG_ARCH_MSM8974=y + CONFIG_ARCH_ROCKCHIP=y + CONFIG_ARCH_SOCFPGA=y + CONFIG_PLAT_SPEAR=y +@@ -146,6 +150,8 @@ CONFIG_SERIAL_SIRFSOC_CONSOLE=y + CONFIG_SERIAL_TEGRA=y + CONFIG_SERIAL_IMX=y + CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_MSM=y ++CONFIG_SERIAL_MSM_CONSOLE=y + CONFIG_SERIAL_VT8500=y + CONFIG_SERIAL_VT8500_CONSOLE=y + CONFIG_SERIAL_OF_PLATFORM=y +@@ -294,6 +300,10 @@ CONFIG_MFD_NVEC=y + CONFIG_KEYBOARD_NVEC=y + CONFIG_SERIO_NVEC_PS2=y + CONFIG_NVEC_POWER=y ++CONFIG_COMMON_CLK_QCOM=y ++CONFIG_MSM_GCC_8660=y ++CONFIG_MSM_MMCC_8960=y ++CONFIG_MSM_MMCC_8974=y + CONFIG_TEGRA_IOMMU_GART=y + CONFIG_TEGRA_IOMMU_SMMU=y + CONFIG_MEMORY=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch new file mode 100644 index 0000000000..b23ee3b154 --- /dev/null +++ b/target/linux/ipq806x/patches/0021-ARM-dts-qcom-Add-nodes-necessary-for-SMP-boot.patch @@ -0,0 +1,214 @@ +From 5a054211d9380cef5a09da7c5e815c827f330a96 Mon Sep 17 00:00:00 2001 +From: Rohit Vaswani <rvaswani@codeaurora.org> +Date: Fri, 1 Nov 2013 10:10:40 -0700 +Subject: [PATCH 021/182] ARM: dts: qcom: Add nodes necessary for SMP boot + +Add the necessary nodes to support SMP on MSM8660, MSM8960, and +MSM8974/APQ8074. While we're here also add in the error +interrupts for the Krait cache error detection. + +Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org> +[sboyd: Split into separate patch, add error interrupts] +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660.dtsi | 24 ++++++++++++ + arch/arm/boot/dts/qcom-msm8960.dtsi | 52 ++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 69 +++++++++++++++++++++++++++++++++++ + 3 files changed, 145 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +index 69d6c4e..c52a9e9 100644 +--- a/arch/arm/boot/dts/qcom-msm8660.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -9,6 +9,30 @@ + compatible = "qcom,msm8660"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ + intc: interrupt-controller@2080000 { + compatible = "qcom,msm-8660-qgic"; + interrupt-controller; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index ff00282..02231a5 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -9,6 +9,36 @@ + compatible = "qcom,msm8960"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <1 14 0x304>; ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ interrupts = <0 2 0x4>; ++ }; ++ }; ++ + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; +@@ -53,6 +83,28 @@ + #reset-cells = <1>; + }; + ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ + serial@16440000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16440000 0x1000>, +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 9e5dadb..39eebc5 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -9,6 +9,49 @@ + compatible = "qcom,msm8974"; + interrupt-parent = <&intc>; + ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <1 9 0xf04>; ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ reg = <2>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ reg = <3>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ interrupts = <0 2 0x4>; ++ qcom,saw = <&saw_l2>; ++ }; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -91,6 +134,32 @@ + }; + }; + ++ saw_l2: regulator@f9012000 { ++ compatible = "qcom,saw2"; ++ reg = <0xf9012000 0x1000>; ++ regulator; ++ }; ++ ++ acc0: clock-controller@f9088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9088000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@f9098000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9098000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@f90a8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90a8000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@f90b8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90b8000 0x1000>, <0xf9008000 0x1000>; ++ }; ++ + restart@fc4ab000 { + compatible = "qcom,pshold"; + reg = <0xfc4ab000 0x4>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch new file mode 100644 index 0000000000..23edfe7c55 --- /dev/null +++ b/target/linux/ipq806x/patches/0022-ARM-dts-qcom-Add-RNG-device-tree-node.patch @@ -0,0 +1,35 @@ +From b0f93ef8f790e77049b6149416ef0ba05cce7089 Mon Sep 17 00:00:00 2001 +From: Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Fri, 7 Feb 2014 11:23:07 +0200 +Subject: [PATCH 022/182] ARM: dts: qcom: Add RNG device tree node + +Add the necessary DT node to probe the rng driver on +msm8974 platforms. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Acked-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8974.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 39eebc5..011eb09 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -186,5 +186,12 @@ + clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; + clock-names = "core", "iface"; + }; ++ ++ rng@f9bff000 { ++ compatible = "qcom,prng"; ++ reg = <0xf9bff000 0x200>; ++ clocks = <&gcc GCC_PRNG_AHB_CLK>; ++ clock-names = "core"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch new file mode 100644 index 0000000000..a3e69f3dc6 --- /dev/null +++ b/target/linux/ipq806x/patches/0023-ARM-dts-qcom-msm8960-cdp-Add-RNG-device-tree-node.patch @@ -0,0 +1,34 @@ +From 82bedcd5ad0b3ac8fe78f4be25b2ffa0691d7804 Mon Sep 17 00:00:00 2001 +From: Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Wed, 19 Feb 2014 16:33:06 +0200 +Subject: [PATCH 023/182] ARM: dts: qcom-msm8960-cdp: Add RNG device tree node + +Add the necessary DT node to probe the rng driver on +msm8960-cdp platform. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Tested-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8960.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index 02231a5..ecfba72 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -119,4 +119,11 @@ + reg = <0x500000 0x1000>; + qcom,controller-type = "pmic-arbiter"; + }; ++ ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch new file mode 100644 index 0000000000..013d45cc41 --- /dev/null +++ b/target/linux/ipq806x/patches/0024-ARM-dts-msm-Add-krait-pmu-to-platforms-with-Krait-CP.patch @@ -0,0 +1,53 @@ +From 02c987fb8a3607ab6e0ead0e5aaa7da753ce9537 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 21 Feb 2014 11:09:50 +0000 +Subject: [PATCH 024/182] ARM: dts: msm: Add krait-pmu to platforms with Krait + CPUs + +Allows us to probe the performance counters on Krait CPUs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Will Deacon <will.deacon@arm.com> +[olof: Moved 8960 contents to the dtsi instead] +Signed-off-by: Olof Johansson <olof@lixom.net> +--- + arch/arm/boot/dts/qcom-msm8960.dtsi | 6 ++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 5 +++++ + 2 files changed, 11 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index ecfba72..997b7b9 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -39,6 +39,12 @@ + }; + }; + ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ qcom,no-pc-write; ++ }; ++ + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 011eb09..f687239 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -52,6 +52,11 @@ + }; + }; + ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 7 0xf04>; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch new file mode 100644 index 0000000000..fba5e4f9e3 --- /dev/null +++ b/target/linux/ipq806x/patches/0025-pinctrl-msm-drop-wake_irqs-bitmap.patch @@ -0,0 +1,71 @@ +From ef8eb0991f291df12c12477648235f955cc388b0 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Wed, 5 Mar 2014 13:33:08 -0600 +Subject: [PATCH 025/182] pinctrl: msm: drop wake_irqs bitmap + +Currently, the wake_irqs bitmap is used to track whether there are any +gpio's which are configured as wake irqs, and uses this to determine +whether or not to call enable_irq_wake()/disable_irq_wake() on the +summary interrupt. + +However, the genirq core already handles this case, by maintaining a +'wake_count' per irq_desc, and only calling into the controlling +irq_chip when wake_count transitions 0 <-> 1. + +Drop this bitmap, and unconditionally call irq_set_irq_wake() on the +summary interrupt. + +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 14 +------------- + 1 file changed, 1 insertion(+), 13 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index ef2bf31..0e43fdd 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -50,7 +50,6 @@ + * @enabled_irqs: Bitmap of currently enabled irqs. + * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge + * detection. +- * @wake_irqs: Bitmap of irqs with requested as wakeup source. + * @soc; Reference to soc_data of platform specific data. + * @regs: Base address for the TLMM register map. + */ +@@ -65,7 +64,6 @@ struct msm_pinctrl { + + DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); +- DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO); + + const struct msm_pinctrl_soc_data *soc; + void __iomem *regs; +@@ -783,22 +781,12 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) + { + struct msm_pinctrl *pctrl; + unsigned long flags; +- unsigned ngpio; + + pctrl = irq_data_get_irq_chip_data(d); +- ngpio = pctrl->chip.ngpio; + + spin_lock_irqsave(&pctrl->lock, flags); + +- if (on) { +- if (bitmap_empty(pctrl->wake_irqs, ngpio)) +- enable_irq_wake(pctrl->irq); +- set_bit(d->hwirq, pctrl->wake_irqs); +- } else { +- clear_bit(d->hwirq, pctrl->wake_irqs); +- if (bitmap_empty(pctrl->wake_irqs, ngpio)) +- disable_irq_wake(pctrl->irq); +- } ++ irq_set_irq_wake(pctrl->irq, on); + + spin_unlock_irqrestore(&pctrl->lock, flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch new file mode 100644 index 0000000000..fd2e10fb0f --- /dev/null +++ b/target/linux/ipq806x/patches/0026-pinctrl-msm-Silence-recursive-lockdep-warning.patch @@ -0,0 +1,73 @@ +From 9588c91936434166007c3a15ad7f4e2f3729c5e7 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:40 -0800 +Subject: [PATCH 026/182] pinctrl: msm: Silence recursive lockdep warning + +If a driver calls enable_irq_wake() on a gpio turned interrupt +from the msm pinctrl driver we'll get a lockdep warning like so: + +============================================= +[ INFO: possible recursive locking detected ] +3.14.0-rc3 #2 Not tainted +--------------------------------------------- +modprobe/52 is trying to acquire lock: + (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + +but task is already holding lock: + (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + +other info that might help us debug this: + Possible unsafe locking scenario: + + CPU0 + ---- + lock(&irq_desc_lock_class); + lock(&irq_desc_lock_class); + + *** DEADLOCK *** + + May be due to missing lock nesting notation + +4 locks held by modprobe/52: + #0: (&__lockdep_no_validate__){......}, at: [<c04f2864>] __driver_attach+0x48/0x98 + #1: (&__lockdep_no_validate__){......}, at: [<c04f2874>] __driver_attach+0x58/0x98 + #2: (&irq_desc_lock_class){-.....}, at: [<c026aea0>] __irq_get_desc_lock+0x48/0x88 + #3: (&(&pctrl->lock)->rlock){......}, at: [<c04bb4b8>] msm_gpio_irq_set_wake+0x20/0xa8 + +Silence it by putting the gpios into their own lock class. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 0e43fdd..e61b30a 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -857,6 +857,12 @@ static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) + chained_irq_exit(chip, desc); + } + ++/* ++ * This lock class tells lockdep that GPIO irqs are in a different ++ * category than their parents, so it won't report false recursion. ++ */ ++static struct lock_class_key gpio_lock_class; ++ + static int msm_gpio_init(struct msm_pinctrl *pctrl) + { + struct gpio_chip *chip; +@@ -895,6 +901,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) + + for (i = 0; i < chip->ngpio; i++) { + irq = irq_create_mapping(pctrl->domain, i); ++ irq_set_lockdep_class(irq, &gpio_lock_class); + irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, handle_edge_irq); + irq_set_chip_data(irq, pctrl); + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch new file mode 100644 index 0000000000..c2b7487540 --- /dev/null +++ b/target/linux/ipq806x/patches/0027-pinctrl-msm-Check-for-ngpios-MAX_NR_GPIO.patch @@ -0,0 +1,39 @@ +From 8341db7b05b688e8e5a93acd0e80b40be409d037 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:41 -0800 +Subject: [PATCH 027/182] pinctrl: msm: Check for ngpios > MAX_NR_GPIO + +Fail the probe and print a warning if SoC specific drivers have +more GPIOs than there can be accounted for in the static bitmaps. +This should avoid silent corruption/failures in the future. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index e61b30a..90ac995 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -870,10 +870,14 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) + int ret; + int i; + int r; ++ unsigned ngpio = pctrl->soc->ngpios; ++ ++ if (WARN_ON(ngpio > MAX_NR_GPIO)) ++ return -EINVAL; + + chip = &pctrl->chip; + chip->base = 0; +- chip->ngpio = pctrl->soc->ngpios; ++ chip->ngpio = ngpio; + chip->label = dev_name(pctrl->dev); + chip->dev = pctrl->dev; + chip->owner = THIS_MODULE; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch new file mode 100644 index 0000000000..8f864f2680 --- /dev/null +++ b/target/linux/ipq806x/patches/0028-pinctrl-msm-Drop-unused-includes.patch @@ -0,0 +1,60 @@ +From d2caa906f865d6919c511a956ae63ab62b240eba Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:42 -0800 +Subject: [PATCH 028/182] pinctrl: msm: Drop unused includes + +These includes are unused or can be handled via forward +declarations. Remove them. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 1 - + drivers/pinctrl/pinctrl-msm.h | 5 +---- + drivers/pinctrl/pinctrl-msm8x74.c | 1 - + 3 files changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 90ac995..4474e00 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -28,7 +28,6 @@ + #include <linux/interrupt.h> + #include <linux/irq.h> + #include <linux/irqchip/chained_irq.h> +-#include <linux/of_irq.h> + #include <linux/spinlock.h> + + #include "core.h" +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 206e782..8fbe9fb 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -13,10 +13,7 @@ + #ifndef __PINCTRL_MSM_H__ + #define __PINCTRL_MSM_H__ + +-#include <linux/pinctrl/pinctrl.h> +-#include <linux/pinctrl/pinmux.h> +-#include <linux/pinctrl/pinconf.h> +-#include <linux/pinctrl/machine.h> ++struct pinctrl_pin_desc; + + /** + * struct msm_function - a pinmux function +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index f944bf2..bb5ded69f 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -15,7 +15,6 @@ + #include <linux/of.h> + #include <linux/platform_device.h> + #include <linux/pinctrl/pinctrl.h> +-#include <linux/pinctrl/pinmux.h> + + #include "pinctrl-msm.h" + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch new file mode 100644 index 0000000000..6a33a9d97c --- /dev/null +++ b/target/linux/ipq806x/patches/0029-pinctrl-msm-Drop-OF_IRQ-dependency.patch @@ -0,0 +1,31 @@ +From 2cfbd01bd994bd16dbee28a99553796f12848c4c Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:43 -0800 +Subject: [PATCH 029/182] pinctrl: msm: Drop OF_IRQ dependency + +This driver doesn't rely on any functionality living in +drivers/of/irq.c to compile. Drop this dependency. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 1e4e693..06cee01 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -224,7 +224,7 @@ config PINCTRL_MSM + + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" +- depends on GPIOLIB && OF && OF_IRQ ++ depends on GPIOLIB && OF + select PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch new file mode 100644 index 0000000000..975e11a052 --- /dev/null +++ b/target/linux/ipq806x/patches/0030-pinctrl-msm-Replace-lookup-tables-with-math.patch @@ -0,0 +1,66 @@ +From e34d9fdac8182f6ce8933501fea6e84664060bf0 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:44 -0800 +Subject: [PATCH 030/182] pinctrl: msm: Replace lookup tables with math + +We don't need to waste space with these lookup tables, just do +the math directly. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 4474e00..87f6c3c 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -258,8 +258,10 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + #define MSM_PULL_DOWN 1 + #define MSM_PULL_UP 3 + +-static const unsigned msm_regval_to_drive[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; +-static const unsigned msm_drive_to_regval[] = { -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7 }; ++static unsigned msm_regval_to_drive(u32 val) ++{ ++ return (val + 1) * 2; ++} + + static int msm_config_group_get(struct pinctrl_dev *pctldev, + unsigned int group, +@@ -296,7 +298,7 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + arg = arg == MSM_PULL_UP; + break; + case PIN_CONFIG_DRIVE_STRENGTH: +- arg = msm_regval_to_drive[arg]; ++ arg = msm_regval_to_drive(arg); + break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", +@@ -349,10 +351,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* Check for invalid values */ +- if (arg >= ARRAY_SIZE(msm_drive_to_regval)) ++ if (arg > 16 || arg < 2 || (arg % 2) != 0) + arg = -1; + else +- arg = msm_drive_to_regval[arg]; ++ arg = (arg / 2) - 1; + break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", +@@ -531,7 +533,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, + pull = (ctl_reg >> g->pull_bit) & 3; + + seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); +- seq_printf(s, " %dmA", msm_regval_to_drive[drive]); ++ seq_printf(s, " %dmA", msm_regval_to_drive(drive)); + seq_printf(s, " %s", pulls[pull]); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch new file mode 100644 index 0000000000..61c8ac1b2a --- /dev/null +++ b/target/linux/ipq806x/patches/0031-pinctrl-msm-Remove-impossible-WARN_ON-s.patch @@ -0,0 +1,95 @@ +From 286113578287b9c7619b4104864cffb91820f49d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:45 -0800 +Subject: [PATCH 031/182] pinctrl: msm: Remove impossible WARN_ON()s + +All these functions are limited in what they can pass as the gpio +or irq number to whatever is setup during probe. Remove the +checks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 87f6c3c..ab46e3a 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -401,8 +401,6 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -423,8 +421,6 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -451,8 +447,6 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return -EINVAL; + + val = readl(pctrl->regs + g->io_reg); + return !!(val & BIT(g->in_bit)); +@@ -466,8 +460,6 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + u32 val; + + g = &pctrl->soc->groups[offset]; +- if (WARN_ON(g->io_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -616,8 +608,6 @@ static void msm_gpio_irq_mask(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_cfg_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -639,8 +629,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_status_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -666,8 +654,6 @@ static void msm_gpio_irq_ack(struct irq_data *d) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_status_reg < 0)) +- return; + + spin_lock_irqsave(&pctrl->lock, flags); + +@@ -692,8 +678,6 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) + + pctrl = irq_data_get_irq_chip_data(d); + g = &pctrl->soc->groups[d->hwirq]; +- if (WARN_ON(g->intr_cfg_reg < 0)) +- return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch new file mode 100644 index 0000000000..d656978236 --- /dev/null +++ b/target/linux/ipq806x/patches/0032-pinctrl-msm-Simplify-msm_config_reg-and-callers.patch @@ -0,0 +1,115 @@ +From 2d9ffb1a3f87396c3b792124870ef63fc27c568f Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 6 Mar 2014 22:44:46 -0800 +Subject: [PATCH 032/182] pinctrl: msm: Simplify msm_config_reg() and callers + +We don't need to check for a negative reg here because reg is +always the same and is always non-negative. Also, collapse the +switch statement down for the duplicate cases. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 29 +++++------------------------ + 1 file changed, 5 insertions(+), 24 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index ab46e3a..91de8bc 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -200,28 +200,17 @@ static const struct pinmux_ops msm_pinmux_ops = { + static int msm_config_reg(struct msm_pinctrl *pctrl, + const struct msm_pingroup *g, + unsigned param, +- s16 *reg, + unsigned *mask, + unsigned *bit) + { + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: +- *reg = g->ctl_reg; +- *bit = g->pull_bit; +- *mask = 3; +- break; + case PIN_CONFIG_BIAS_PULL_DOWN: +- *reg = g->ctl_reg; +- *bit = g->pull_bit; +- *mask = 3; +- break; + case PIN_CONFIG_BIAS_PULL_UP: +- *reg = g->ctl_reg; + *bit = g->pull_bit; + *mask = 3; + break; + case PIN_CONFIG_DRIVE_STRENGTH: +- *reg = g->ctl_reg; + *bit = g->drv_bit; + *mask = 7; + break; +@@ -230,12 +219,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + return -ENOTSUPP; + } + +- if (*reg < 0) { +- dev_err(pctrl->dev, "Config param %04x not supported on group %s\n", +- param, g->name); +- return -ENOTSUPP; +- } +- + return 0; + } + +@@ -273,17 +256,16 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + unsigned mask; + unsigned arg; + unsigned bit; +- s16 reg; + int ret; + u32 val; + + g = &pctrl->soc->groups[group]; + +- ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); ++ ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + +- val = readl(pctrl->regs + reg); ++ val = readl(pctrl->regs + g->ctl_reg); + arg = (val >> bit) & mask; + + /* Convert register value to pinconf value */ +@@ -323,7 +305,6 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + unsigned mask; + unsigned arg; + unsigned bit; +- s16 reg; + int ret; + u32 val; + int i; +@@ -334,7 +315,7 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + +- ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); ++ ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + +@@ -369,10 +350,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + } + + spin_lock_irqsave(&pctrl->lock, flags); +- val = readl(pctrl->regs + reg); ++ val = readl(pctrl->regs + g->ctl_reg); + val &= ~(mask << bit); + val |= arg << bit; +- writel(val, pctrl->regs + reg); ++ writel(val, pctrl->regs + g->ctl_reg); + spin_unlock_irqrestore(&pctrl->lock, flags); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch new file mode 100644 index 0000000000..18c72cc4be --- /dev/null +++ b/target/linux/ipq806x/patches/0033-pinctrl-msm-Support-output-high-low-configuration.patch @@ -0,0 +1,69 @@ +From 469f83e0ed374250be5fd6202ac535276a752fa8 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn@kryo.se> +Date: Tue, 4 Feb 2014 19:55:31 -0800 +Subject: [PATCH 033/182] pinctrl-msm: Support output-{high,low} configuration + +Add support for configuring pins as output with value as from the +pinconf-generic interface. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 91de8bc..19d2feb 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -214,6 +214,11 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + *bit = g->drv_bit; + *mask = 7; + break; ++ case PIN_CONFIG_OUTPUT: ++ *reg = g->ctl_reg; ++ *bit = g->oe_bit; ++ *mask = 1; ++ break; + default: + dev_err(pctrl->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; +@@ -282,6 +287,14 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + case PIN_CONFIG_DRIVE_STRENGTH: + arg = msm_regval_to_drive(arg); + break; ++ case PIN_CONFIG_OUTPUT: ++ /* Pin is not output */ ++ if (!arg) ++ return -EINVAL; ++ ++ val = readl(pctrl->regs + g->io_reg); ++ arg = !!(val & BIT(g->in_bit)); ++ break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", + param); +@@ -337,6 +350,20 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + else + arg = (arg / 2) - 1; + break; ++ case PIN_CONFIG_OUTPUT: ++ /* set output value */ ++ spin_lock_irqsave(&pctrl->lock, flags); ++ val = readl(pctrl->regs + g->io_reg); ++ if (arg) ++ val |= BIT(g->out_bit); ++ else ++ val &= ~BIT(g->out_bit); ++ writel(val, pctrl->regs + g->io_reg); ++ spin_unlock_irqrestore(&pctrl->lock, flags); ++ ++ /* enable output */ ++ arg = 1; ++ break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", + param); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch new file mode 100644 index 0000000000..b8df704ec0 --- /dev/null +++ b/target/linux/ipq806x/patches/0034-pinctrl-msm-Add-SPI8-pin-definitions.patch @@ -0,0 +1,63 @@ +From 77e4b572fcc4015e235f22fd93b8df35e452baf0 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 6 Feb 2014 17:28:48 +0200 +Subject: [PATCH 034/182] pinctrl-msm: Add SPI8 pin definitions + +Add pin, group and function definitions for SPI#8 +controller. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm8x74.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index bb5ded69f..dde5529 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -405,6 +405,7 @@ enum msm8x74_functions { + MSM_MUX_blsp_i2c6, + MSM_MUX_blsp_i2c11, + MSM_MUX_blsp_spi1, ++ MSM_MUX_blsp_spi8, + MSM_MUX_blsp_uart2, + MSM_MUX_blsp_uart8, + MSM_MUX_slimbus, +@@ -415,6 +416,9 @@ static const char * const blsp_i2c2_groups[] = { "gpio6", "gpio7" }; + static const char * const blsp_i2c6_groups[] = { "gpio29", "gpio30" }; + static const char * const blsp_i2c11_groups[] = { "gpio83", "gpio84" }; + static const char * const blsp_spi1_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3" }; ++static const char * const blsp_spi8_groups[] = { ++ "gpio45", "gpio46", "gpio47", "gpio48" ++}; + static const char * const blsp_uart2_groups[] = { "gpio4", "gpio5" }; + static const char * const blsp_uart8_groups[] = { "gpio45", "gpio46" }; + static const char * const slimbus_groups[] = { "gpio70", "gpio71" }; +@@ -424,6 +428,7 @@ static const struct msm_function msm8x74_functions[] = { + FUNCTION(blsp_i2c6), + FUNCTION(blsp_i2c11), + FUNCTION(blsp_spi1), ++ FUNCTION(blsp_spi8), + FUNCTION(blsp_uart2), + FUNCTION(blsp_uart8), + FUNCTION(slimbus), +@@ -475,10 +480,10 @@ static const struct msm_pingroup msm8x74_groups[] = { + PINGROUP(42, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(43, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(44, NA, NA, NA, NA, NA, NA, NA), +- PINGROUP(45, NA, blsp_uart8, NA, NA, NA, NA, NA), +- PINGROUP(46, NA, blsp_uart8, NA, NA, NA, NA, NA), +- PINGROUP(47, NA, NA, NA, NA, NA, NA, NA), +- PINGROUP(48, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), ++ PINGROUP(46, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), ++ PINGROUP(47, blsp_spi8, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, blsp_spi8, NA, NA, NA, NA, NA, NA), + PINGROUP(49, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(50, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(51, NA, NA, NA, NA, NA, NA, NA), +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch new file mode 100644 index 0000000000..c141a0b6c0 --- /dev/null +++ b/target/linux/ipq806x/patches/0035-pinctrl-msm-fix-up-out-of-order-merge-conflict.patch @@ -0,0 +1,38 @@ +From c73a138dd24049d06fe7b22518655ed9e7413cd2 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Fri, 14 Mar 2014 07:54:20 +0100 +Subject: [PATCH 035/182] pinctrl: msm: fix up out-of-order merge conflict + +Commit 051a58b4622f0e1b732acb750097c64bc00ddb93 +"pinctrl: msm: Simplify msm_config_reg() and callers" +removed the local "reg" variable in the msm_config_reg() +function, but the earlier +commit ed118a5fd951bd2def8249ee251842c4f81fe4bd +"pinctrl-msm: Support output-{high,low} configuration" +introduced a new switchclause using it. + +Fix this up by removing the offending register assignment. + +Reported-by: Kbuild test robot <fengguang.wu@intel.com> +Cc: Stephen Boyd <sboyd@codeaurora.org> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 19d2feb..343f421 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -215,7 +215,6 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + *mask = 7; + break; + case PIN_CONFIG_OUTPUT: +- *reg = g->ctl_reg; + *bit = g->oe_bit; + *mask = 1; + break; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch new file mode 100644 index 0000000000..b44fb6a0b3 --- /dev/null +++ b/target/linux/ipq806x/patches/0036-pinctrl-msm-Correct-interrupt-code-for-TLMM-v2.patch @@ -0,0 +1,55 @@ +From 32787a9bba5a1ebeea891fd7aab954e6d344892a Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:54 -0700 +Subject: [PATCH 036/182] pinctrl: msm: Correct interrupt code for TLMM v2 + +Acking interrupts are done differently between on v2 and v3, so add an extra +attribute to the pingroup struct to let the platform definitions control this. +Also make sure to start dual edge detection by detecting the rising edge. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 6 +++++- + drivers/pinctrl/pinctrl-msm.h | 1 + + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 343f421..706809e 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -665,7 +665,10 @@ static void msm_gpio_irq_ack(struct irq_data *d) + spin_lock_irqsave(&pctrl->lock, flags); + + val = readl(pctrl->regs + g->intr_status_reg); +- val &= ~BIT(g->intr_status_bit); ++ if (g->intr_ack_high) ++ val |= BIT(g->intr_status_bit); ++ else ++ val &= ~BIT(g->intr_status_bit); + writel(val, pctrl->regs + g->intr_status_reg); + + if (test_bit(d->hwirq, pctrl->dual_edge_irqs)) +@@ -744,6 +747,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) + break; + case IRQ_TYPE_EDGE_BOTH: + val |= BIT(g->intr_detection_bit); ++ val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_LEVEL_LOW: + break; +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 8fbe9fb..6e26f1b 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -84,6 +84,7 @@ struct msm_pingroup { + + unsigned intr_enable_bit:5; + unsigned intr_status_bit:5; ++ unsigned intr_ack_high:1; + + unsigned intr_target_bit:5; + unsigned intr_raw_status_bit:5; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch new file mode 100644 index 0000000000..346dcd0ac6 --- /dev/null +++ b/target/linux/ipq806x/patches/0037-pinctrl-msm-Make-number-of-functions-variable.patch @@ -0,0 +1,74 @@ +From 1c11b14dd6d740e997919f0bf789bf921548dc0f Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:55 -0700 +Subject: [PATCH 037/182] pinctrl: msm: Make number of functions variable + +The various pins may have different number of functions defined, so make this +number definable per pin instead of just increasing it to the largest one for +all of the platforms. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/pinctrl-msm.c | 4 ++-- + drivers/pinctrl/pinctrl-msm.h | 3 ++- + drivers/pinctrl/pinctrl-msm8x74.c | 3 ++- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 706809e..7d67d34 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -145,12 +145,12 @@ static int msm_pinmux_enable(struct pinctrl_dev *pctldev, + if (WARN_ON(g->mux_bit < 0)) + return -EINVAL; + +- for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { ++ for (i = 0; i < g->nfuncs; i++) { + if (g->funcs[i] == function) + break; + } + +- if (WARN_ON(i == ARRAY_SIZE(g->funcs))) ++ if (WARN_ON(i == g->nfuncs)) + return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); +diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h +index 6e26f1b..7b2a227 100644 +--- a/drivers/pinctrl/pinctrl-msm.h ++++ b/drivers/pinctrl/pinctrl-msm.h +@@ -65,7 +65,8 @@ struct msm_pingroup { + const unsigned *pins; + unsigned npins; + +- unsigned funcs[8]; ++ unsigned *funcs; ++ unsigned nfuncs; + + s16 ctl_reg; + s16 io_reg; +diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c +index dde5529..57766d5 100644 +--- a/drivers/pinctrl/pinctrl-msm8x74.c ++++ b/drivers/pinctrl/pinctrl-msm8x74.c +@@ -341,7 +341,7 @@ static const unsigned int sdc2_data_pins[] = { 151 }; + .name = "gpio" #id, \ + .pins = gpio##id##_pins, \ + .npins = ARRAY_SIZE(gpio##id##_pins), \ +- .funcs = { \ ++ .funcs = (int[]){ \ + MSM_MUX_NA, /* gpio mode */ \ + MSM_MUX_##f1, \ + MSM_MUX_##f2, \ +@@ -351,6 +351,7 @@ static const unsigned int sdc2_data_pins[] = { 151 }; + MSM_MUX_##f6, \ + MSM_MUX_##f7 \ + }, \ ++ .nfuncs = 8, \ + .ctl_reg = 0x1000 + 0x10 * id, \ + .io_reg = 0x1004 + 0x10 * id, \ + .intr_cfg_reg = 0x1008 + 0x10 * id, \ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch new file mode 100644 index 0000000000..20e5a1dc83 --- /dev/null +++ b/target/linux/ipq806x/patches/0038-pinctrl-msm-Add-definitions-for-the-APQ8064-platform.patch @@ -0,0 +1,624 @@ +From 247288012122ccfe7d5d9af00a45814c6fdd94c5 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Mon, 31 Mar 2014 14:49:57 -0700 +Subject: [PATCH 038/182] pinctrl: msm: Add definitions for the APQ8064 + platform + +This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the +Qualcomm APQ8064 platform. + +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 8 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-apq8064.c | 566 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 575 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-apq8064.c + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 06cee01..91993a6 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -222,6 +222,14 @@ config PINCTRL_MSM + select PINCONF + select GENERIC_PINCONF + ++config PINCTRL_APQ8064 ++ tristate "Qualcomm APQ8064 pin controller driver" ++ depends on GPIOLIB && OF ++ select PINCTRL_MSM ++ help ++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the ++ Qualcomm TLMM block found in the Qualcomm APQ8064 platform. ++ + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" + depends on GPIOLIB && OF +diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile +index 4b83588..9e1fb67 100644 +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o + obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o + obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o + obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o ++obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o + obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o + obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o + obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o +diff --git a/drivers/pinctrl/pinctrl-apq8064.c b/drivers/pinctrl/pinctrl-apq8064.c +new file mode 100644 +index 0000000..7c2a8ba +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-apq8064.c +@@ -0,0 +1,566 @@ ++/* ++ * Copyright (c) 2014, Sony Mobile Communications AB. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pinctrl/pinctrl.h> ++ ++#include "pinctrl-msm.h" ++ ++static const struct pinctrl_pin_desc apq8064_pins[] = { ++ PINCTRL_PIN(0, "GPIO_0"), ++ PINCTRL_PIN(1, "GPIO_1"), ++ PINCTRL_PIN(2, "GPIO_2"), ++ PINCTRL_PIN(3, "GPIO_3"), ++ PINCTRL_PIN(4, "GPIO_4"), ++ PINCTRL_PIN(5, "GPIO_5"), ++ PINCTRL_PIN(6, "GPIO_6"), ++ PINCTRL_PIN(7, "GPIO_7"), ++ PINCTRL_PIN(8, "GPIO_8"), ++ PINCTRL_PIN(9, "GPIO_9"), ++ PINCTRL_PIN(10, "GPIO_10"), ++ PINCTRL_PIN(11, "GPIO_11"), ++ PINCTRL_PIN(12, "GPIO_12"), ++ PINCTRL_PIN(13, "GPIO_13"), ++ PINCTRL_PIN(14, "GPIO_14"), ++ PINCTRL_PIN(15, "GPIO_15"), ++ PINCTRL_PIN(16, "GPIO_16"), ++ PINCTRL_PIN(17, "GPIO_17"), ++ PINCTRL_PIN(18, "GPIO_18"), ++ PINCTRL_PIN(19, "GPIO_19"), ++ PINCTRL_PIN(20, "GPIO_20"), ++ PINCTRL_PIN(21, "GPIO_21"), ++ PINCTRL_PIN(22, "GPIO_22"), ++ PINCTRL_PIN(23, "GPIO_23"), ++ PINCTRL_PIN(24, "GPIO_24"), ++ PINCTRL_PIN(25, "GPIO_25"), ++ PINCTRL_PIN(26, "GPIO_26"), ++ PINCTRL_PIN(27, "GPIO_27"), ++ PINCTRL_PIN(28, "GPIO_28"), ++ PINCTRL_PIN(29, "GPIO_29"), ++ PINCTRL_PIN(30, "GPIO_30"), ++ PINCTRL_PIN(31, "GPIO_31"), ++ PINCTRL_PIN(32, "GPIO_32"), ++ PINCTRL_PIN(33, "GPIO_33"), ++ PINCTRL_PIN(34, "GPIO_34"), ++ PINCTRL_PIN(35, "GPIO_35"), ++ PINCTRL_PIN(36, "GPIO_36"), ++ PINCTRL_PIN(37, "GPIO_37"), ++ PINCTRL_PIN(38, "GPIO_38"), ++ PINCTRL_PIN(39, "GPIO_39"), ++ PINCTRL_PIN(40, "GPIO_40"), ++ PINCTRL_PIN(41, "GPIO_41"), ++ PINCTRL_PIN(42, "GPIO_42"), ++ PINCTRL_PIN(43, "GPIO_43"), ++ PINCTRL_PIN(44, "GPIO_44"), ++ PINCTRL_PIN(45, "GPIO_45"), ++ PINCTRL_PIN(46, "GPIO_46"), ++ PINCTRL_PIN(47, "GPIO_47"), ++ PINCTRL_PIN(48, "GPIO_48"), ++ PINCTRL_PIN(49, "GPIO_49"), ++ PINCTRL_PIN(50, "GPIO_50"), ++ PINCTRL_PIN(51, "GPIO_51"), ++ PINCTRL_PIN(52, "GPIO_52"), ++ PINCTRL_PIN(53, "GPIO_53"), ++ PINCTRL_PIN(54, "GPIO_54"), ++ PINCTRL_PIN(55, "GPIO_55"), ++ PINCTRL_PIN(56, "GPIO_56"), ++ PINCTRL_PIN(57, "GPIO_57"), ++ PINCTRL_PIN(58, "GPIO_58"), ++ PINCTRL_PIN(59, "GPIO_59"), ++ PINCTRL_PIN(60, "GPIO_60"), ++ PINCTRL_PIN(61, "GPIO_61"), ++ PINCTRL_PIN(62, "GPIO_62"), ++ PINCTRL_PIN(63, "GPIO_63"), ++ PINCTRL_PIN(64, "GPIO_64"), ++ PINCTRL_PIN(65, "GPIO_65"), ++ PINCTRL_PIN(66, "GPIO_66"), ++ PINCTRL_PIN(67, "GPIO_67"), ++ PINCTRL_PIN(68, "GPIO_68"), ++ PINCTRL_PIN(69, "GPIO_69"), ++ PINCTRL_PIN(70, "GPIO_70"), ++ PINCTRL_PIN(71, "GPIO_71"), ++ PINCTRL_PIN(72, "GPIO_72"), ++ PINCTRL_PIN(73, "GPIO_73"), ++ PINCTRL_PIN(74, "GPIO_74"), ++ PINCTRL_PIN(75, "GPIO_75"), ++ PINCTRL_PIN(76, "GPIO_76"), ++ PINCTRL_PIN(77, "GPIO_77"), ++ PINCTRL_PIN(78, "GPIO_78"), ++ PINCTRL_PIN(79, "GPIO_79"), ++ PINCTRL_PIN(80, "GPIO_80"), ++ PINCTRL_PIN(81, "GPIO_81"), ++ PINCTRL_PIN(82, "GPIO_82"), ++ PINCTRL_PIN(83, "GPIO_83"), ++ PINCTRL_PIN(84, "GPIO_84"), ++ PINCTRL_PIN(85, "GPIO_85"), ++ PINCTRL_PIN(86, "GPIO_86"), ++ PINCTRL_PIN(87, "GPIO_87"), ++ PINCTRL_PIN(88, "GPIO_88"), ++ PINCTRL_PIN(89, "GPIO_89"), ++}; ++ ++#define DECLARE_APQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin } ++DECLARE_APQ_GPIO_PINS(0); ++DECLARE_APQ_GPIO_PINS(1); ++DECLARE_APQ_GPIO_PINS(2); ++DECLARE_APQ_GPIO_PINS(3); ++DECLARE_APQ_GPIO_PINS(4); ++DECLARE_APQ_GPIO_PINS(5); ++DECLARE_APQ_GPIO_PINS(6); ++DECLARE_APQ_GPIO_PINS(7); ++DECLARE_APQ_GPIO_PINS(8); ++DECLARE_APQ_GPIO_PINS(9); ++DECLARE_APQ_GPIO_PINS(10); ++DECLARE_APQ_GPIO_PINS(11); ++DECLARE_APQ_GPIO_PINS(12); ++DECLARE_APQ_GPIO_PINS(13); ++DECLARE_APQ_GPIO_PINS(14); ++DECLARE_APQ_GPIO_PINS(15); ++DECLARE_APQ_GPIO_PINS(16); ++DECLARE_APQ_GPIO_PINS(17); ++DECLARE_APQ_GPIO_PINS(18); ++DECLARE_APQ_GPIO_PINS(19); ++DECLARE_APQ_GPIO_PINS(20); ++DECLARE_APQ_GPIO_PINS(21); ++DECLARE_APQ_GPIO_PINS(22); ++DECLARE_APQ_GPIO_PINS(23); ++DECLARE_APQ_GPIO_PINS(24); ++DECLARE_APQ_GPIO_PINS(25); ++DECLARE_APQ_GPIO_PINS(26); ++DECLARE_APQ_GPIO_PINS(27); ++DECLARE_APQ_GPIO_PINS(28); ++DECLARE_APQ_GPIO_PINS(29); ++DECLARE_APQ_GPIO_PINS(30); ++DECLARE_APQ_GPIO_PINS(31); ++DECLARE_APQ_GPIO_PINS(32); ++DECLARE_APQ_GPIO_PINS(33); ++DECLARE_APQ_GPIO_PINS(34); ++DECLARE_APQ_GPIO_PINS(35); ++DECLARE_APQ_GPIO_PINS(36); ++DECLARE_APQ_GPIO_PINS(37); ++DECLARE_APQ_GPIO_PINS(38); ++DECLARE_APQ_GPIO_PINS(39); ++DECLARE_APQ_GPIO_PINS(40); ++DECLARE_APQ_GPIO_PINS(41); ++DECLARE_APQ_GPIO_PINS(42); ++DECLARE_APQ_GPIO_PINS(43); ++DECLARE_APQ_GPIO_PINS(44); ++DECLARE_APQ_GPIO_PINS(45); ++DECLARE_APQ_GPIO_PINS(46); ++DECLARE_APQ_GPIO_PINS(47); ++DECLARE_APQ_GPIO_PINS(48); ++DECLARE_APQ_GPIO_PINS(49); ++DECLARE_APQ_GPIO_PINS(50); ++DECLARE_APQ_GPIO_PINS(51); ++DECLARE_APQ_GPIO_PINS(52); ++DECLARE_APQ_GPIO_PINS(53); ++DECLARE_APQ_GPIO_PINS(54); ++DECLARE_APQ_GPIO_PINS(55); ++DECLARE_APQ_GPIO_PINS(56); ++DECLARE_APQ_GPIO_PINS(57); ++DECLARE_APQ_GPIO_PINS(58); ++DECLARE_APQ_GPIO_PINS(59); ++DECLARE_APQ_GPIO_PINS(60); ++DECLARE_APQ_GPIO_PINS(61); ++DECLARE_APQ_GPIO_PINS(62); ++DECLARE_APQ_GPIO_PINS(63); ++DECLARE_APQ_GPIO_PINS(64); ++DECLARE_APQ_GPIO_PINS(65); ++DECLARE_APQ_GPIO_PINS(66); ++DECLARE_APQ_GPIO_PINS(67); ++DECLARE_APQ_GPIO_PINS(68); ++DECLARE_APQ_GPIO_PINS(69); ++DECLARE_APQ_GPIO_PINS(70); ++DECLARE_APQ_GPIO_PINS(71); ++DECLARE_APQ_GPIO_PINS(72); ++DECLARE_APQ_GPIO_PINS(73); ++DECLARE_APQ_GPIO_PINS(74); ++DECLARE_APQ_GPIO_PINS(75); ++DECLARE_APQ_GPIO_PINS(76); ++DECLARE_APQ_GPIO_PINS(77); ++DECLARE_APQ_GPIO_PINS(78); ++DECLARE_APQ_GPIO_PINS(79); ++DECLARE_APQ_GPIO_PINS(80); ++DECLARE_APQ_GPIO_PINS(81); ++DECLARE_APQ_GPIO_PINS(82); ++DECLARE_APQ_GPIO_PINS(83); ++DECLARE_APQ_GPIO_PINS(84); ++DECLARE_APQ_GPIO_PINS(85); ++DECLARE_APQ_GPIO_PINS(86); ++DECLARE_APQ_GPIO_PINS(87); ++DECLARE_APQ_GPIO_PINS(88); ++DECLARE_APQ_GPIO_PINS(89); ++ ++#define FUNCTION(fname) \ ++ [APQ_MUX_##fname] = { \ ++ .name = #fname, \ ++ .groups = fname##_groups, \ ++ .ngroups = ARRAY_SIZE(fname##_groups), \ ++ } ++ ++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ ++ { \ ++ .name = "gpio" #id, \ ++ .pins = gpio##id##_pins, \ ++ .npins = ARRAY_SIZE(gpio##id##_pins), \ ++ .funcs = (int[]){ \ ++ APQ_MUX_NA, /* gpio mode */ \ ++ APQ_MUX_##f1, \ ++ APQ_MUX_##f2, \ ++ APQ_MUX_##f3, \ ++ APQ_MUX_##f4, \ ++ APQ_MUX_##f5, \ ++ APQ_MUX_##f6, \ ++ APQ_MUX_##f7, \ ++ APQ_MUX_##f8, \ ++ APQ_MUX_##f9, \ ++ APQ_MUX_##f10, \ ++ }, \ ++ .nfuncs = 11, \ ++ .ctl_reg = 0x1000 + 0x10 * id, \ ++ .io_reg = 0x1004 + 0x10 * id, \ ++ .intr_cfg_reg = 0x1008 + 0x10 * id, \ ++ .intr_status_reg = 0x100c + 0x10 * id, \ ++ .intr_target_reg = 0x400 + 0x4 * id, \ ++ .mux_bit = 2, \ ++ .pull_bit = 0, \ ++ .drv_bit = 6, \ ++ .oe_bit = 9, \ ++ .in_bit = 0, \ ++ .out_bit = 1, \ ++ .intr_enable_bit = 0, \ ++ .intr_status_bit = 0, \ ++ .intr_ack_high = 1, \ ++ .intr_target_bit = 0, \ ++ .intr_raw_status_bit = 3, \ ++ .intr_polarity_bit = 1, \ ++ .intr_detection_bit = 2, \ ++ .intr_detection_width = 1, \ ++ } ++ ++enum apq8064_functions { ++ APQ_MUX_cam_mclk, ++ APQ_MUX_codec_mic_i2s, ++ APQ_MUX_codec_spkr_i2s, ++ APQ_MUX_gsbi1, ++ APQ_MUX_gsbi2, ++ APQ_MUX_gsbi3, ++ APQ_MUX_gsbi4, ++ APQ_MUX_gsbi4_cam_i2c, ++ APQ_MUX_gsbi5, ++ APQ_MUX_gsbi5_spi_cs1, ++ APQ_MUX_gsbi5_spi_cs2, ++ APQ_MUX_gsbi5_spi_cs3, ++ APQ_MUX_gsbi6, ++ APQ_MUX_gsbi6_spi_cs1, ++ APQ_MUX_gsbi6_spi_cs2, ++ APQ_MUX_gsbi6_spi_cs3, ++ APQ_MUX_gsbi7, ++ APQ_MUX_gsbi7_spi_cs1, ++ APQ_MUX_gsbi7_spi_cs2, ++ APQ_MUX_gsbi7_spi_cs3, ++ APQ_MUX_gsbi_cam_i2c, ++ APQ_MUX_hdmi, ++ APQ_MUX_mi2s, ++ APQ_MUX_riva_bt, ++ APQ_MUX_riva_fm, ++ APQ_MUX_riva_wlan, ++ APQ_MUX_sdc2, ++ APQ_MUX_sdc4, ++ APQ_MUX_slimbus, ++ APQ_MUX_spkr_i2s, ++ APQ_MUX_tsif1, ++ APQ_MUX_tsif2, ++ APQ_MUX_usb2_hsic, ++ APQ_MUX_NA, ++}; ++ ++static const char * const cam_mclk_groups[] = { ++ "gpio4" "gpio5" ++}; ++static const char * const codec_mic_i2s_groups[] = { ++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38" ++}; ++static const char * const codec_spkr_i2s_groups[] = { ++ "gpio39", "gpio40", "gpio41", "gpio42" ++}; ++static const char * const gsbi1_groups[] = { ++ "gpio18", "gpio19", "gpio20", "gpio21" ++}; ++static const char * const gsbi2_groups[] = { ++ "gpio22", "gpio23", "gpio24", "gpio25" ++}; ++static const char * const gsbi3_groups[] = { ++ "gpio6", "gpio7", "gpio8", "gpio9" ++}; ++static const char * const gsbi4_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const gsbi4_cam_i2c_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const gsbi5_groups[] = { ++ "gpio51", "gpio52", "gpio53", "gpio54" ++}; ++static const char * const gsbi5_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi5_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi5_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi6_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17" ++}; ++static const char * const gsbi6_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi6_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi6_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi7_groups[] = { ++ "gpio82", "gpio83", "gpio84", "gpio85" ++}; ++static const char * const gsbi7_spi_cs1_groups[] = { ++ "gpio47" ++}; ++static const char * const gsbi7_spi_cs2_groups[] = { ++ "gpio31" ++}; ++static const char * const gsbi7_spi_cs3_groups[] = { ++ "gpio32" ++}; ++static const char * const gsbi_cam_i2c_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13" ++}; ++static const char * const hdmi_groups[] = { ++ "gpio69", "gpio70", "gpio71", "gpio72" ++}; ++static const char * const mi2s_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33" ++}; ++static const char * const riva_bt_groups[] = { ++ "gpio16", "gpio17" ++}; ++static const char * const riva_fm_groups[] = { ++ "gpio14", "gpio15" ++}; ++static const char * const riva_wlan_groups[] = { ++ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68" ++}; ++static const char * const sdc2_groups[] = { ++ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62" ++}; ++static const char * const sdc4_groups[] = { ++ "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68" ++}; ++static const char * const slimbus_groups[] = { ++ "gpio40", "gpio41" ++}; ++static const char * const spkr_i2s_groups[] = { ++ "gpio47", "gpio48", "gpio49", "gpio50" ++}; ++static const char * const tsif1_groups[] = { ++ "gpio55", "gpio56", "gpio57" ++}; ++static const char * const tsif2_groups[] = { ++ "gpio58", "gpio59", "gpio60" ++}; ++static const char * const usb2_hsic_groups[] = { ++ "gpio88", "gpio89" ++}; ++ ++static const struct msm_function apq8064_functions[] = { ++ FUNCTION(cam_mclk), ++ FUNCTION(codec_mic_i2s), ++ FUNCTION(codec_spkr_i2s), ++ FUNCTION(gsbi1), ++ FUNCTION(gsbi2), ++ FUNCTION(gsbi3), ++ FUNCTION(gsbi4), ++ FUNCTION(gsbi4_cam_i2c), ++ FUNCTION(gsbi5), ++ FUNCTION(gsbi5_spi_cs1), ++ FUNCTION(gsbi5_spi_cs2), ++ FUNCTION(gsbi5_spi_cs3), ++ FUNCTION(gsbi6), ++ FUNCTION(gsbi6_spi_cs1), ++ FUNCTION(gsbi6_spi_cs2), ++ FUNCTION(gsbi6_spi_cs3), ++ FUNCTION(gsbi7), ++ FUNCTION(gsbi7_spi_cs1), ++ FUNCTION(gsbi7_spi_cs2), ++ FUNCTION(gsbi7_spi_cs3), ++ FUNCTION(gsbi_cam_i2c), ++ FUNCTION(hdmi), ++ FUNCTION(mi2s), ++ FUNCTION(riva_bt), ++ FUNCTION(riva_fm), ++ FUNCTION(riva_wlan), ++ FUNCTION(sdc2), ++ FUNCTION(sdc4), ++ FUNCTION(slimbus), ++ FUNCTION(spkr_i2s), ++ FUNCTION(tsif1), ++ FUNCTION(tsif2), ++ FUNCTION(usb2_hsic), ++}; ++ ++static const struct msm_pingroup apq8064_groups[] = { ++ PINGROUP(0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(4, NA, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(5, NA, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(6, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(7, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(8, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(9, gsbi3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(10, gsbi4, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c, NA), ++ PINGROUP(11, gsbi4, NA, NA, NA, NA, NA, NA, NA, NA, gsbi4_cam_i2c), ++ PINGROUP(12, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA), ++ PINGROUP(13, gsbi4, NA, NA, NA, NA, gsbi4_cam_i2c, NA, NA, NA, NA), ++ PINGROUP(14, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(15, riva_fm, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(16, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(17, riva_bt, gsbi6, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(18, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(19, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(20, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(21, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(22, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(27, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(28, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(29, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(30, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(31, mi2s, NA, gsbi5_spi_cs2, gsbi6_spi_cs2, gsbi7_spi_cs2, NA, NA, NA, NA, NA), ++ PINGROUP(32, mi2s, NA, NA, NA, NA, gsbi5_spi_cs3, gsbi6_spi_cs3, gsbi7_spi_cs3, NA, NA), ++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(34, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(35, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(36, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(37, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(38, codec_mic_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(39, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(40, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(41, slimbus, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(42, codec_spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(47, spkr_i2s, gsbi5_spi_cs1, gsbi6_spi_cs1, gsbi7_spi_cs1, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(49, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(50, spkr_i2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(51, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(52, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(53, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(54, NA, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(55, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(56, tsif1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(57, tsif1, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(58, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(59, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(60, tsif2, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(61, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(62, NA, sdc2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(63, NA, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(64, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(65, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(66, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(67, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(68, riva_wlan, sdc4, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(69, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(70, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(71, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(72, hdmi, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(73, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(74, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(75, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(76, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(77, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(82, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(83, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(84, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(85, NA, NA, gsbi7, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(87, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(88, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(89, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++}; ++ ++#define NUM_GPIO_PINGROUPS 90 ++ ++static const struct msm_pinctrl_soc_data apq8064_pinctrl = { ++ .pins = apq8064_pins, ++ .npins = ARRAY_SIZE(apq8064_pins), ++ .functions = apq8064_functions, ++ .nfunctions = ARRAY_SIZE(apq8064_functions), ++ .groups = apq8064_groups, ++ .ngroups = ARRAY_SIZE(apq8064_groups), ++ .ngpios = NUM_GPIO_PINGROUPS, ++}; ++ ++static int apq8064_pinctrl_probe(struct platform_device *pdev) ++{ ++ return msm_pinctrl_probe(pdev, &apq8064_pinctrl); ++} ++ ++static const struct of_device_id apq8064_pinctrl_of_match[] = { ++ { .compatible = "qcom,apq8064-pinctrl", }, ++ { }, ++}; ++ ++static struct platform_driver apq8064_pinctrl_driver = { ++ .driver = { ++ .name = "apq8064-pinctrl", ++ .owner = THIS_MODULE, ++ .of_match_table = apq8064_pinctrl_of_match, ++ }, ++ .probe = apq8064_pinctrl_probe, ++ .remove = msm_pinctrl_remove, ++}; ++ ++static int __init apq8064_pinctrl_init(void) ++{ ++ return platform_driver_register(&apq8064_pinctrl_driver); ++} ++arch_initcall(apq8064_pinctrl_init); ++ ++static void __exit apq8064_pinctrl_exit(void) ++{ ++ platform_driver_unregister(&apq8064_pinctrl_driver); ++} ++module_exit(apq8064_pinctrl_exit); ++ ++MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); ++MODULE_DESCRIPTION("Qualcomm APQ8064 pinctrl driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DEVICE_TABLE(of, apq8064_pinctrl_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch new file mode 100644 index 0000000000..51ed6d645f --- /dev/null +++ b/target/linux/ipq806x/patches/0039-pinctrl-msm8x74-make-Kconfig-dependency-more-strict.patch @@ -0,0 +1,36 @@ +From 8605197c200786888415bf2e30d1fbde6ba8ba03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> +Date: Tue, 1 Apr 2014 22:25:59 +0200 +Subject: [PATCH 039/182] pinctrl: msm8x74: make Kconfig dependency more + strict +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This driver is only useful on MSM8x74, so let the driver depend on +ARCH_QCOM but allow compile coverage testing. +The main benefit is that the driver isn't available to be selected for +machines that don't have the matching hardware. + +Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index 91993a6..d34639d 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -232,7 +232,7 @@ config PINCTRL_APQ8064 + + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" +- depends on GPIOLIB && OF ++ depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST) + select PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch new file mode 100644 index 0000000000..6b48085289 --- /dev/null +++ b/target/linux/ipq806x/patches/0040-pinctrl-qcom-Add-definitions-for-IPQ8064.patch @@ -0,0 +1,711 @@ +From 9bbd9d7e40944ca95e07f363b68700225beb9bef Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:35 -0500 +Subject: [PATCH 040/182] pinctrl: qcom: Add definitions for IPQ8064 + +This adds pinctrl definitions for the GPIO pins of the TLMM v2 block in the +Qualcomm IPQ8064 platform. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/pinctrl/Kconfig | 8 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-ipq8064.c | 653 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 662 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-ipq8064.c + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index d34639d..232e6bc 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -230,6 +230,14 @@ config PINCTRL_APQ8064 + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm TLMM block found in the Qualcomm APQ8064 platform. + ++config PINCTRL_IPQ8064 ++ tristate "Qualcomm IPQ8064 pin controller driver" ++ depends on GPIOLIB && OF ++ select PINCTRL_MSM ++ help ++ This is the pinctrl, pinmux, pinconf and gpiolib driver for the ++ Qualcomm TLMM block found in the Qualcomm IPQ8064 platform. ++ + config PINCTRL_MSM8X74 + tristate "Qualcomm 8x74 pin controller driver" + depends on GPIOLIB && OF && (ARCH_QCOM || COMPILE_TEST) +diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile +index 9e1fb67..6b8474a 100644 +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o + obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o + obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o + obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o ++obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o + obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o + obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o + obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o +diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c +new file mode 100644 +index 0000000..1700b49 +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-ipq8064.c +@@ -0,0 +1,653 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pinctrl/pinctrl.h> ++ ++#include "pinctrl-msm.h" ++ ++static const struct pinctrl_pin_desc ipq8064_pins[] = { ++ PINCTRL_PIN(0, "GPIO_1"), ++ PINCTRL_PIN(1, "GPIO_1"), ++ PINCTRL_PIN(2, "GPIO_2"), ++ PINCTRL_PIN(3, "GPIO_3"), ++ PINCTRL_PIN(4, "GPIO_4"), ++ PINCTRL_PIN(5, "GPIO_5"), ++ PINCTRL_PIN(6, "GPIO_6"), ++ PINCTRL_PIN(7, "GPIO_7"), ++ PINCTRL_PIN(8, "GPIO_8"), ++ PINCTRL_PIN(9, "GPIO_9"), ++ PINCTRL_PIN(10, "GPIO_10"), ++ PINCTRL_PIN(11, "GPIO_11"), ++ PINCTRL_PIN(12, "GPIO_12"), ++ PINCTRL_PIN(13, "GPIO_13"), ++ PINCTRL_PIN(14, "GPIO_14"), ++ PINCTRL_PIN(15, "GPIO_15"), ++ PINCTRL_PIN(16, "GPIO_16"), ++ PINCTRL_PIN(17, "GPIO_17"), ++ PINCTRL_PIN(18, "GPIO_18"), ++ PINCTRL_PIN(19, "GPIO_19"), ++ PINCTRL_PIN(20, "GPIO_20"), ++ PINCTRL_PIN(21, "GPIO_21"), ++ PINCTRL_PIN(22, "GPIO_22"), ++ PINCTRL_PIN(23, "GPIO_23"), ++ PINCTRL_PIN(24, "GPIO_24"), ++ PINCTRL_PIN(25, "GPIO_25"), ++ PINCTRL_PIN(26, "GPIO_26"), ++ PINCTRL_PIN(27, "GPIO_27"), ++ PINCTRL_PIN(28, "GPIO_28"), ++ PINCTRL_PIN(29, "GPIO_29"), ++ PINCTRL_PIN(30, "GPIO_30"), ++ PINCTRL_PIN(31, "GPIO_31"), ++ PINCTRL_PIN(32, "GPIO_32"), ++ PINCTRL_PIN(33, "GPIO_33"), ++ PINCTRL_PIN(34, "GPIO_34"), ++ PINCTRL_PIN(35, "GPIO_35"), ++ PINCTRL_PIN(36, "GPIO_36"), ++ PINCTRL_PIN(37, "GPIO_37"), ++ PINCTRL_PIN(38, "GPIO_38"), ++ PINCTRL_PIN(39, "GPIO_39"), ++ PINCTRL_PIN(40, "GPIO_40"), ++ PINCTRL_PIN(41, "GPIO_41"), ++ PINCTRL_PIN(42, "GPIO_42"), ++ PINCTRL_PIN(43, "GPIO_43"), ++ PINCTRL_PIN(44, "GPIO_44"), ++ PINCTRL_PIN(45, "GPIO_45"), ++ PINCTRL_PIN(46, "GPIO_46"), ++ PINCTRL_PIN(47, "GPIO_47"), ++ PINCTRL_PIN(48, "GPIO_48"), ++ PINCTRL_PIN(49, "GPIO_49"), ++ PINCTRL_PIN(50, "GPIO_50"), ++ PINCTRL_PIN(51, "GPIO_51"), ++ PINCTRL_PIN(52, "GPIO_52"), ++ PINCTRL_PIN(53, "GPIO_53"), ++ PINCTRL_PIN(54, "GPIO_54"), ++ PINCTRL_PIN(55, "GPIO_55"), ++ PINCTRL_PIN(56, "GPIO_56"), ++ PINCTRL_PIN(57, "GPIO_57"), ++ PINCTRL_PIN(58, "GPIO_58"), ++ PINCTRL_PIN(59, "GPIO_59"), ++ PINCTRL_PIN(60, "GPIO_60"), ++ PINCTRL_PIN(61, "GPIO_61"), ++ PINCTRL_PIN(62, "GPIO_62"), ++ PINCTRL_PIN(63, "GPIO_63"), ++ PINCTRL_PIN(64, "GPIO_64"), ++ PINCTRL_PIN(65, "GPIO_65"), ++ PINCTRL_PIN(66, "GPIO_66"), ++ PINCTRL_PIN(67, "GPIO_67"), ++ PINCTRL_PIN(68, "GPIO_68"), ++ ++ PINCTRL_PIN(69, "SDC3_CLK"), ++ PINCTRL_PIN(70, "SDC3_CMD"), ++ PINCTRL_PIN(71, "SDC3_DATA"), ++}; ++ ++#define DECLARE_IPQ_GPIO_PINS(pin) static const unsigned int gpio##pin##_pins[] = { pin } ++DECLARE_IPQ_GPIO_PINS(0); ++DECLARE_IPQ_GPIO_PINS(1); ++DECLARE_IPQ_GPIO_PINS(2); ++DECLARE_IPQ_GPIO_PINS(3); ++DECLARE_IPQ_GPIO_PINS(4); ++DECLARE_IPQ_GPIO_PINS(5); ++DECLARE_IPQ_GPIO_PINS(6); ++DECLARE_IPQ_GPIO_PINS(7); ++DECLARE_IPQ_GPIO_PINS(8); ++DECLARE_IPQ_GPIO_PINS(9); ++DECLARE_IPQ_GPIO_PINS(10); ++DECLARE_IPQ_GPIO_PINS(11); ++DECLARE_IPQ_GPIO_PINS(12); ++DECLARE_IPQ_GPIO_PINS(13); ++DECLARE_IPQ_GPIO_PINS(14); ++DECLARE_IPQ_GPIO_PINS(15); ++DECLARE_IPQ_GPIO_PINS(16); ++DECLARE_IPQ_GPIO_PINS(17); ++DECLARE_IPQ_GPIO_PINS(18); ++DECLARE_IPQ_GPIO_PINS(19); ++DECLARE_IPQ_GPIO_PINS(20); ++DECLARE_IPQ_GPIO_PINS(21); ++DECLARE_IPQ_GPIO_PINS(22); ++DECLARE_IPQ_GPIO_PINS(23); ++DECLARE_IPQ_GPIO_PINS(24); ++DECLARE_IPQ_GPIO_PINS(25); ++DECLARE_IPQ_GPIO_PINS(26); ++DECLARE_IPQ_GPIO_PINS(27); ++DECLARE_IPQ_GPIO_PINS(28); ++DECLARE_IPQ_GPIO_PINS(29); ++DECLARE_IPQ_GPIO_PINS(30); ++DECLARE_IPQ_GPIO_PINS(31); ++DECLARE_IPQ_GPIO_PINS(32); ++DECLARE_IPQ_GPIO_PINS(33); ++DECLARE_IPQ_GPIO_PINS(34); ++DECLARE_IPQ_GPIO_PINS(35); ++DECLARE_IPQ_GPIO_PINS(36); ++DECLARE_IPQ_GPIO_PINS(37); ++DECLARE_IPQ_GPIO_PINS(38); ++DECLARE_IPQ_GPIO_PINS(39); ++DECLARE_IPQ_GPIO_PINS(40); ++DECLARE_IPQ_GPIO_PINS(41); ++DECLARE_IPQ_GPIO_PINS(42); ++DECLARE_IPQ_GPIO_PINS(43); ++DECLARE_IPQ_GPIO_PINS(44); ++DECLARE_IPQ_GPIO_PINS(45); ++DECLARE_IPQ_GPIO_PINS(46); ++DECLARE_IPQ_GPIO_PINS(47); ++DECLARE_IPQ_GPIO_PINS(48); ++DECLARE_IPQ_GPIO_PINS(49); ++DECLARE_IPQ_GPIO_PINS(50); ++DECLARE_IPQ_GPIO_PINS(51); ++DECLARE_IPQ_GPIO_PINS(52); ++DECLARE_IPQ_GPIO_PINS(53); ++DECLARE_IPQ_GPIO_PINS(54); ++DECLARE_IPQ_GPIO_PINS(55); ++DECLARE_IPQ_GPIO_PINS(56); ++DECLARE_IPQ_GPIO_PINS(57); ++DECLARE_IPQ_GPIO_PINS(58); ++DECLARE_IPQ_GPIO_PINS(59); ++DECLARE_IPQ_GPIO_PINS(60); ++DECLARE_IPQ_GPIO_PINS(61); ++DECLARE_IPQ_GPIO_PINS(62); ++DECLARE_IPQ_GPIO_PINS(63); ++DECLARE_IPQ_GPIO_PINS(64); ++DECLARE_IPQ_GPIO_PINS(65); ++DECLARE_IPQ_GPIO_PINS(66); ++DECLARE_IPQ_GPIO_PINS(67); ++DECLARE_IPQ_GPIO_PINS(68); ++ ++static const unsigned int sdc3_clk_pins[] = { 69 }; ++static const unsigned int sdc3_cmd_pins[] = { 70 }; ++static const unsigned int sdc3_data_pins[] = { 71 }; ++ ++#define FUNCTION(fname) \ ++ [IPQ_MUX_##fname] = { \ ++ .name = #fname, \ ++ .groups = fname##_groups, \ ++ .ngroups = ARRAY_SIZE(fname##_groups), \ ++ } ++ ++#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ ++ { \ ++ .name = "gpio" #id, \ ++ .pins = gpio##id##_pins, \ ++ .npins = ARRAY_SIZE(gpio##id##_pins), \ ++ .funcs = (int[]){ \ ++ IPQ_MUX_NA, /* gpio mode */ \ ++ IPQ_MUX_##f1, \ ++ IPQ_MUX_##f2, \ ++ IPQ_MUX_##f3, \ ++ IPQ_MUX_##f4, \ ++ IPQ_MUX_##f5, \ ++ IPQ_MUX_##f6, \ ++ IPQ_MUX_##f7, \ ++ IPQ_MUX_##f8, \ ++ IPQ_MUX_##f9, \ ++ IPQ_MUX_##f10, \ ++ }, \ ++ .nfuncs = 11, \ ++ .ctl_reg = 0x1000 + 0x10 * id, \ ++ .io_reg = 0x1004 + 0x10 * id, \ ++ .intr_cfg_reg = 0x1008 + 0x10 * id, \ ++ .intr_status_reg = 0x100c + 0x10 * id, \ ++ .intr_target_reg = 0x400 + 0x4 * id, \ ++ .mux_bit = 2, \ ++ .pull_bit = 0, \ ++ .drv_bit = 6, \ ++ .oe_bit = 9, \ ++ .in_bit = 0, \ ++ .out_bit = 1, \ ++ .intr_enable_bit = 0, \ ++ .intr_status_bit = 0, \ ++ .intr_ack_high = 1, \ ++ .intr_target_bit = 0, \ ++ .intr_raw_status_bit = 3, \ ++ .intr_polarity_bit = 1, \ ++ .intr_detection_bit = 2, \ ++ .intr_detection_width = 1, \ ++ } ++ ++#define SDC_PINGROUP(pg_name, ctl, pull, drv) \ ++ { \ ++ .name = #pg_name, \ ++ .pins = pg_name##_pins, \ ++ .npins = ARRAY_SIZE(pg_name##_pins), \ ++ .ctl_reg = ctl, \ ++ .io_reg = 0, \ ++ .intr_cfg_reg = 0, \ ++ .intr_status_reg = 0, \ ++ .intr_target_reg = 0, \ ++ .mux_bit = -1, \ ++ .pull_bit = pull, \ ++ .drv_bit = drv, \ ++ .oe_bit = -1, \ ++ .in_bit = -1, \ ++ .out_bit = -1, \ ++ .intr_enable_bit = -1, \ ++ .intr_status_bit = -1, \ ++ .intr_target_bit = -1, \ ++ .intr_raw_status_bit = -1, \ ++ .intr_polarity_bit = -1, \ ++ .intr_detection_bit = -1, \ ++ .intr_detection_width = -1, \ ++ } ++ ++enum ipq8064_functions { ++ IPQ_MUX_mdio, ++ IPQ_MUX_mi2s, ++ IPQ_MUX_pdm, ++ IPQ_MUX_ssbi, ++ IPQ_MUX_spmi, ++ IPQ_MUX_audio_pcm, ++ IPQ_MUX_gsbi1, ++ IPQ_MUX_gsbi2, ++ IPQ_MUX_gsbi4, ++ IPQ_MUX_gsbi5, ++ IPQ_MUX_gsbi5_spi_cs1, ++ IPQ_MUX_gsbi5_spi_cs2, ++ IPQ_MUX_gsbi5_spi_cs3, ++ IPQ_MUX_gsbi6, ++ IPQ_MUX_gsbi7, ++ IPQ_MUX_nss_spi, ++ IPQ_MUX_sdc1, ++ IPQ_MUX_spdif, ++ IPQ_MUX_nand, ++ IPQ_MUX_tsif1, ++ IPQ_MUX_tsif2, ++ IPQ_MUX_usb_fs_n, ++ IPQ_MUX_usb_fs, ++ IPQ_MUX_usb2_hsic, ++ IPQ_MUX_rgmii2, ++ IPQ_MUX_sata, ++ IPQ_MUX_pcie1_rst, ++ IPQ_MUX_pcie1_prsnt, ++ IPQ_MUX_pcie1_pwrflt, ++ IPQ_MUX_pcie1_pwren_n, ++ IPQ_MUX_pcie1_pwren, ++ IPQ_MUX_pcie1_clk_req, ++ IPQ_MUX_pcie2_rst, ++ IPQ_MUX_pcie2_prsnt, ++ IPQ_MUX_pcie2_pwrflt, ++ IPQ_MUX_pcie2_pwren_n, ++ IPQ_MUX_pcie2_pwren, ++ IPQ_MUX_pcie2_clk_req, ++ IPQ_MUX_pcie3_rst, ++ IPQ_MUX_pcie3_prsnt, ++ IPQ_MUX_pcie3_pwrflt, ++ IPQ_MUX_pcie3_pwren_n, ++ IPQ_MUX_pcie3_pwren, ++ IPQ_MUX_pcie3_clk_req, ++ IPQ_MUX_ps_hold, ++ IPQ_MUX_NA, ++}; ++ ++static const char * const mdio_groups[] = { ++ "gpio0", "gpio1", "gpio10", "gpio11", ++}; ++ ++static const char * const mi2s_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio33", "gpio55", "gpio56", "gpio57", "gpio58", ++}; ++ ++static const char * const pdm_groups[] = { ++ "gpio3", "gpio16", "gpio17", "gpio22", "gpio30", "gpio31", ++ "gpio34", "gpio35", "gpio52", "gpio55", "gpio56", "gpio58", ++ "gpio59", ++}; ++ ++static const char * const ssbi_groups[] = { ++ "gpio10", "gpio11", ++}; ++ ++static const char * const spmi_groups[] = { ++ "gpio10", "gpio11", ++}; ++ ++static const char * const audio_pcm_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17", ++}; ++ ++static const char * const gsbi1_groups[] = { ++ "gpio51", "gpio52", "gpio53", "gpio54", ++}; ++ ++static const char * const gsbi2_groups[] = { ++ "gpio22", "gpio23", "gpio24", "gpio25", ++}; ++ ++static const char * const gsbi4_groups[] = { ++ "gpio10", "gpio11", "gpio12", "gpio13", ++}; ++ ++static const char * const gsbi5_groups[] = { ++ "gpio18", "gpio19", "gpio20", "gpio21", ++}; ++ ++static const char * const gsbi5_spi_cs1_groups[] = { ++ "gpio6", "gpio61", ++}; ++ ++static const char * const gsbi5_spi_cs2_groups[] = { ++ "gpio7", "gpio62", ++}; ++ ++static const char * const gsbi5_spi_cs3_groups[] = { ++ "gpio2", ++}; ++ ++static const char * const gsbi6_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio55", "gpio56", ++ "gpio57", "gpio58", ++}; ++ ++static const char * const gsbi7_groups[] = { ++ "gpio6", "gpio7", "gpio8", "gpio9", ++}; ++ ++static const char * const nss_spi_groups[] = { ++ "gpio14", "gpio15", "gpio16", "gpio17", "gpio55", "gpio56", ++ "gpio57", "gpio58", ++}; ++ ++static const char * const sdc1_groups[] = { ++ "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", ++ "gpio44", "gpio45", "gpio46", "gpio47", ++}; ++ ++static const char * const spdif_groups[] = { ++ "gpio_10", "gpio_48", ++}; ++ ++static const char * const nand_groups[] = { ++ "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", ++ "gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47", ++}; ++ ++static const char * const tsif1_groups[] = { ++ "gpio55", "gpio56", "gpio57", "gpio58", ++}; ++ ++static const char * const tsif2_groups[] = { ++ "gpio59", "gpio60", "gpio61", "gpio62", ++}; ++ ++static const char * const usb_fs_n_groups[] = { ++ "gpio6", ++}; ++ ++static const char * const usb_fs_groups[] = { ++ "gpio6", "gpio7", "gpio8", ++}; ++ ++static const char * const usb2_hsic_groups[] = { ++ "gpio67", "gpio68", ++}; ++ ++static const char * const rgmii2_groups[] = { ++ "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62", ++}; ++ ++static const char * const sata_groups[] = { ++ "gpio10", ++}; ++ ++static const char * const pcie1_rst_groups[] = { ++ "gpio3", ++}; ++ ++static const char * const pcie1_prsnt_groups[] = { ++ "gpio3", "gpio11", ++}; ++ ++static const char * const pcie1_pwren_n_groups[] = { ++ "gpio4", "gpio12", ++}; ++ ++static const char * const pcie1_pwren_groups[] = { ++ "gpio4", "gpio12", ++}; ++ ++static const char * const pcie1_pwrflt_groups[] = { ++ "gpio5", "gpio13", ++}; ++ ++static const char * const pcie1_clk_req_groups[] = { ++ "gpio5", ++}; ++ ++static const char * const pcie2_rst_groups[] = { ++ "gpio48", ++}; ++ ++static const char * const pcie2_prsnt_groups[] = { ++ "gpio11", "gpio48", ++}; ++ ++static const char * const pcie2_pwren_n_groups[] = { ++ "gpio12", "gpio49", ++}; ++ ++static const char * const pcie2_pwren_groups[] = { ++ "gpio12", "gpio49", ++}; ++ ++static const char * const pcie2_pwrflt_groups[] = { ++ "gpio13", "gpio50", ++}; ++ ++static const char * const pcie2_clk_req_groups[] = { ++ "gpio50", ++}; ++ ++static const char * const pcie3_rst_groups[] = { ++ "gpio63", ++}; ++ ++static const char * const pcie3_prsnt_groups[] = { ++ "gpio11", ++}; ++ ++static const char * const pcie3_pwren_n_groups[] = { ++ "gpio12", ++}; ++ ++static const char * const pcie3_pwren_groups[] = { ++ "gpio12", ++}; ++ ++static const char * const pcie3_pwrflt_groups[] = { ++ "gpio13", ++}; ++ ++static const char * const pcie3_clk_req_groups[] = { ++ "gpio65", ++}; ++ ++static const char * const ps_hold_groups[] = { ++ "gpio26", ++}; ++ ++static const struct msm_function ipq8064_functions[] = { ++ FUNCTION(mdio), ++ FUNCTION(ssbi), ++ FUNCTION(spmi), ++ FUNCTION(mi2s), ++ FUNCTION(pdm), ++ FUNCTION(audio_pcm), ++ FUNCTION(gsbi1), ++ FUNCTION(gsbi2), ++ FUNCTION(gsbi4), ++ FUNCTION(gsbi5), ++ FUNCTION(gsbi5_spi_cs1), ++ FUNCTION(gsbi5_spi_cs2), ++ FUNCTION(gsbi5_spi_cs3), ++ FUNCTION(gsbi6), ++ FUNCTION(gsbi7), ++ FUNCTION(nss_spi), ++ FUNCTION(sdc1), ++ FUNCTION(spdif), ++ FUNCTION(nand), ++ FUNCTION(tsif1), ++ FUNCTION(tsif2), ++ FUNCTION(usb_fs_n), ++ FUNCTION(usb_fs), ++ FUNCTION(usb2_hsic), ++ FUNCTION(rgmii2), ++ FUNCTION(sata), ++ FUNCTION(pcie1_rst), ++ FUNCTION(pcie1_prsnt), ++ FUNCTION(pcie1_pwren_n), ++ FUNCTION(pcie1_pwren), ++ FUNCTION(pcie1_pwrflt), ++ FUNCTION(pcie1_clk_req), ++ FUNCTION(pcie2_rst), ++ FUNCTION(pcie2_prsnt), ++ FUNCTION(pcie2_pwren_n), ++ FUNCTION(pcie2_pwren), ++ FUNCTION(pcie2_pwrflt), ++ FUNCTION(pcie2_clk_req), ++ FUNCTION(pcie3_rst), ++ FUNCTION(pcie3_prsnt), ++ FUNCTION(pcie3_pwren_n), ++ FUNCTION(pcie3_pwren), ++ FUNCTION(pcie3_pwrflt), ++ FUNCTION(pcie3_clk_req), ++ FUNCTION(ps_hold), ++}; ++ ++static const struct msm_pingroup ipq8064_groups[] = { ++ PINGROUP(0, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(1, mdio, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(2, gsbi5_spi_cs3, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(3, pcie1_rst, pcie1_prsnt, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(4, pcie1_pwren_n, pcie1_pwren, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(5, pcie1_clk_req, pcie1_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(6, gsbi7, usb_fs, gsbi5_spi_cs1, usb_fs_n, NA, NA, NA, NA, NA, NA), ++ PINGROUP(7, gsbi7, usb_fs, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(8, gsbi7, usb_fs, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(9, gsbi7, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(10, gsbi4, spdif, sata, ssbi, mdio, spmi, NA, NA, NA, NA), ++ PINGROUP(11, gsbi4, pcie2_prsnt, pcie1_prsnt, pcie3_prsnt, ssbi, mdio, spmi, NA, NA, NA), ++ PINGROUP(12, gsbi4, pcie2_pwren_n, pcie1_pwren_n, pcie3_pwren_n, pcie2_pwren, pcie1_pwren, pcie3_pwren, NA, NA, NA), ++ PINGROUP(13, gsbi4, pcie2_pwrflt, pcie1_pwrflt, pcie3_pwrflt, NA, NA, NA, NA, NA, NA), ++ PINGROUP(14, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(15, audio_pcm, nss_spi, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(16, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(17, audio_pcm, nss_spi, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(18, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(19, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(20, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(21, gsbi5, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(22, gsbi2, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(23, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(24, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(25, gsbi2, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(26, ps_hold, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(27, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(28, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(29, mi2s, rgmii2, gsbi6, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(30, mi2s, rgmii2, gsbi6, pdm, NA, NA, NA, NA, NA, NA), ++ PINGROUP(31, mi2s, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(32, mi2s, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(33, mi2s, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(34, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(35, nand, pdm, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(36, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(37, nand, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(38, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(39, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(40, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(41, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(42, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(43, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(44, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(45, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(46, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(47, nand, sdc1, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(48, pcie2_rst, spdif, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(49, pcie2_pwren_n, pcie2_pwren, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(50, pcie2_clk_req, pcie2_pwrflt, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(51, gsbi1, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(52, gsbi1, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(53, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(54, gsbi1, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(55, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(56, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(57, tsif1, mi2s, gsbi6, nss_spi, NA, NA, NA, NA, NA, NA), ++ PINGROUP(58, tsif1, mi2s, gsbi6, pdm, nss_spi, NA, NA, NA, NA, NA), ++ PINGROUP(59, tsif2, rgmii2, pdm, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(60, tsif2, rgmii2, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(61, tsif2, rgmii2, gsbi5_spi_cs1, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(62, tsif2, rgmii2, gsbi5_spi_cs2, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(63, pcie3_rst, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(65, pcie3_clk_req, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(66, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(67, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ PINGROUP(68, usb2_hsic, NA, NA, NA, NA, NA, NA, NA, NA, NA), ++ SDC_PINGROUP(sdc3_clk, 0x204a, 14, 6), ++ SDC_PINGROUP(sdc3_cmd, 0x204a, 11, 3), ++ SDC_PINGROUP(sdc3_data, 0x204a, 9, 0), ++}; ++ ++#define NUM_GPIO_PINGROUPS 69 ++ ++static const struct msm_pinctrl_soc_data ipq8064_pinctrl = { ++ .pins = ipq8064_pins, ++ .npins = ARRAY_SIZE(ipq8064_pins), ++ .functions = ipq8064_functions, ++ .nfunctions = ARRAY_SIZE(ipq8064_functions), ++ .groups = ipq8064_groups, ++ .ngroups = ARRAY_SIZE(ipq8064_groups), ++ .ngpios = NUM_GPIO_PINGROUPS, ++}; ++ ++static int ipq8064_pinctrl_probe(struct platform_device *pdev) ++{ ++ return msm_pinctrl_probe(pdev, &ipq8064_pinctrl); ++} ++ ++static const struct of_device_id ipq8064_pinctrl_of_match[] = { ++ { .compatible = "qcom,ipq8064-pinctrl", }, ++ { }, ++}; ++ ++static struct platform_driver ipq8064_pinctrl_driver = { ++ .driver = { ++ .name = "ipq8064-pinctrl", ++ .owner = THIS_MODULE, ++ .of_match_table = ipq8064_pinctrl_of_match, ++ }, ++ .probe = ipq8064_pinctrl_probe, ++ .remove = msm_pinctrl_remove, ++}; ++ ++static int __init ipq8064_pinctrl_init(void) ++{ ++ return platform_driver_register(&ipq8064_pinctrl_driver); ++} ++arch_initcall(ipq8064_pinctrl_init); ++ ++static void __exit ipq8064_pinctrl_exit(void) ++{ ++ platform_driver_unregister(&ipq8064_pinctrl_driver); ++} ++module_exit(ipq8064_pinctrl_exit); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("Qualcomm IPQ8064 pinctrl driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DEVICE_TABLE(of, ipq8064_pinctrl_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch new file mode 100644 index 0000000000..2a7c870556 --- /dev/null +++ b/target/linux/ipq806x/patches/0041-dt-Document-Qualcomm-IPQ8064-pinctrl-binding.patch @@ -0,0 +1,120 @@ +From 425015979d3b1600d14403be7d6d64ba1238e58d Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:36 -0500 +Subject: [PATCH 041/182] dt: Document Qualcomm IPQ8064 pinctrl binding + +Define a new binding for the Qualcomm TLMMv2 based pin controller inside the +IPQ8064. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + .../bindings/pinctrl/qcom,ipq8064-pinctrl.txt | 95 ++++++++++++++++++++ + 1 file changed, 95 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt + +diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt +new file mode 100644 +index 0000000..e0d35a4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt +@@ -0,0 +1,95 @@ ++Qualcomm IPQ8064 TLMM block ++ ++Required properties: ++- compatible: "qcom,ipq8064-pinctrl" ++- reg: Should be the base address and length of the TLMM block. ++- interrupts: Should be the parent IRQ of the TLMM block. ++- interrupt-controller: Marks the device node as an interrupt controller. ++- #interrupt-cells: Should be two. ++- gpio-controller: Marks the device node as a GPIO controller. ++- #gpio-cells : Should be two. ++ The first cell is the gpio pin number and the ++ second cell is used for optional parameters. ++ ++Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for ++a general description of GPIO and interrupt bindings. ++ ++Please refer to pinctrl-bindings.txt in this directory for details of the ++common pinctrl bindings used by client devices, including the meaning of the ++phrase "pin configuration node". ++ ++Qualcomm's pin configuration nodes act as a container for an abitrary number of ++subnodes. Each of these subnodes represents some desired configuration for a ++pin, a group, or a list of pins or groups. This configuration can include the ++mux function to select on those pin(s)/group(s), and various pin configuration ++parameters, such as pull-up, drive strength, etc. ++ ++The name of each subnode is not important; all subnodes should be enumerated ++and processed purely based on their content. ++ ++Each subnode only affects those parameters that are explicitly listed. In ++other words, a subnode that lists a mux function but no pin configuration ++parameters implies no information about any pin configuration parameters. ++Similarly, a pin subnode that describes a pullup parameter implies no ++information about e.g. the mux function. ++ ++ ++The following generic properties as defined in pinctrl-bindings.txt are valid ++to specify in a pin configuration subnode: ++ ++ pins, function, bias-disable, bias-pull-down, bias-pull,up, drive-strength, ++ output-low, output-high. ++ ++Non-empty subnodes must specify the 'pins' property. ++ ++Valid values for qcom,pins are: ++ gpio0-gpio68 ++ Supports mux, bias, and drive-strength ++ ++ sdc3_clk, sdc3_cmd, sdc3_data ++ Supports bias and drive-strength ++ ++ ++Valid values for function are: ++ mdio, mi2s, pdm, ssbi, spmi, audio_pcm, gsbi1, gsbi2, gsbi4, gsbi5, ++ gsbi5_spi_cs1, gsbi5_spi_cs2, gsbi5_spi_cs3, gsbi6, gsbi7, nss_spi, sdc1, ++ spdif, nand, tsif1, tsif2, usb_fs_n, usb_fs, usb2_hsic, rgmii2, sata, ++ pcie1_rst, pcie1_prsnt, pcie1_pwren_n, pcie1_pwren, pcie1_pwrflt, ++ pcie1_clk_req, pcie2_rst, pcie2_prsnt, pcie2_pwren_n, pcie2_pwren, ++ pcie2_pwrflt, pcie2_clk_req, pcie3_rst, pcie3_prsnt, pcie3_pwren_n, ++ pcie3_pwren, pcie3_pwrflt, pcie3_clk_req, ps_hold ++ ++Example: ++ ++ pinmux: pinctrl@800000 { ++ compatible = "qcom,ipq8064-pinctrl"; ++ reg = <0x800000 0x4000>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 32 0x4>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gsbi5_uart_default>; ++ ++ gsbi5_uart_default: gsbi5_uart_default { ++ mux { ++ pins = "gpio18", "gpio19"; ++ function = "gsbi5"; ++ }; ++ ++ tx { ++ pins = "gpio18"; ++ drive-strength = <4>; ++ bias-disable; ++ }; ++ ++ rx { ++ pins = "gpio19"; ++ drive-strength = <2>; ++ bias-pull-up; ++ }; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch new file mode 100644 index 0000000000..c1c116f0b6 --- /dev/null +++ b/target/linux/ipq806x/patches/0042-ARM-qcom-Select-PINCTRL-by-default-for-ARCH_QCOM.patch @@ -0,0 +1,29 @@ +From add2c1451495ccc4e67ced3dd12d4286500f1672 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 14 Apr 2014 22:10:37 -0500 +Subject: [PATCH 042/182] ARM: qcom: Select PINCTRL by default for ARCH_QCOM + +Add missing PINCTRL selection. This enables selection of pinctrollers for +Qualcomm processors. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Acked-by: Linus Walleij <linus.walleij@linaro.org> +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index a028be2..6440c11 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -5,6 +5,7 @@ config ARCH_QCOM + select CLKSRC_OF + select GENERIC_CLOCKEVENTS + select HAVE_SMP ++ select PINCTRL + select QCOM_SCM if SMP + help + Support for Qualcomm's devicetree based systems. +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch new file mode 100644 index 0000000000..71e5572b67 --- /dev/null +++ b/target/linux/ipq806x/patches/0043-pinctrl-qcom-Correct-name-for-pin-0.patch @@ -0,0 +1,28 @@ +From 4cf6d61dc3441f50d1d56bfd72280dbcd2b584a6 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Fri, 25 Apr 2014 15:41:55 -0500 +Subject: [PATCH 043/182] pinctrl: qcom: Correct name for pin 0 + +Fix copy/paste error in pinctrl_pin_desc for pin 0. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/pinctrl/pinctrl-ipq8064.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-ipq8064.c b/drivers/pinctrl/pinctrl-ipq8064.c +index 1700b49..54aba9f 100644 +--- a/drivers/pinctrl/pinctrl-ipq8064.c ++++ b/drivers/pinctrl/pinctrl-ipq8064.c +@@ -20,7 +20,7 @@ + #include "pinctrl-msm.h" + + static const struct pinctrl_pin_desc ipq8064_pins[] = { +- PINCTRL_PIN(0, "GPIO_1"), ++ PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch new file mode 100644 index 0000000000..148959a9bc --- /dev/null +++ b/target/linux/ipq806x/patches/0044-dmaengine-qcom_bam_dma-Add-device-tree-binding.patch @@ -0,0 +1,65 @@ +From b5e19b657e352d565c5ddeae5f6dfd542de9d7e5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 10 Mar 2014 16:40:19 -0500 +Subject: [PATCH 044/182] dmaengine: qcom_bam_dma: Add device tree binding + +Add device tree binding support for the QCOM BAM DMA driver. + +Acked-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + .../devicetree/bindings/dma/qcom_bam_dma.txt | 41 ++++++++++++++++++++ + 1 file changed, 41 insertions(+) + create mode 100644 Documentation/devicetree/bindings/dma/qcom_bam_dma.txt + +diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +new file mode 100644 +index 0000000..d75a9d7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +@@ -0,0 +1,41 @@ ++QCOM BAM DMA controller ++ ++Required properties: ++- compatible: must contain "qcom,bam-v1.4.0" for MSM8974 ++- reg: Address range for DMA registers ++- interrupts: Should contain the one interrupt shared by all channels ++- #dma-cells: must be <1>, the cell in the dmas property of the client device ++ represents the channel number ++- clocks: required clock ++- clock-names: must contain "bam_clk" entry ++- qcom,ee : indicates the active Execution Environment identifier (0-7) used in ++ the secure world. ++ ++Example: ++ ++ uart-bam: dma@f9984000 = { ++ compatible = "qcom,bam-v1.4.0"; ++ reg = <0xf9984000 0x15000>; ++ interrupts = <0 94 0>; ++ clocks = <&gcc GCC_BAM_DMA_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <0>; ++ }; ++ ++DMA clients must use the format described in the dma.txt file, using a two cell ++specifier for each channel. ++ ++Example: ++ serial@f991e000 { ++ compatible = "qcom,msm-uart"; ++ reg = <0xf991e000 0x1000> ++ <0xf9944000 0x19000>; ++ interrupts = <0 108 0>; ++ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, ++ <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ dmas = <&uart-bam 0>, <&uart-bam 1>; ++ dma-names = "rx", "tx"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch new file mode 100644 index 0000000000..6dcba78ce5 --- /dev/null +++ b/target/linux/ipq806x/patches/0045-dmaengine-add-Qualcomm-BAM-dma-driver.patch @@ -0,0 +1,1174 @@ +From 23dd43da9c885789b3d5aceed3e401345f8e8106 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sat, 29 Mar 2014 18:53:16 +0530 +Subject: [PATCH 045/182] dmaengine: add Qualcomm BAM dma driver + +Add the DMA engine driver for the QCOM Bus Access Manager (BAM) DMA controller +found in the MSM 8x74 platforms. + +Each BAM DMA device is associated with a specific on-chip peripheral. Each +channel provides a uni-directional data transfer engine that is capable of +transferring data between the peripheral and system memory (System mode), or +between two peripherals (BAM2BAM). + +The initial release of this driver only supports slave transfers between +peripherals and system memory. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Tested-by: Stanimir Varbanov <svarbanov@mm-sol.com> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + drivers/dma/Kconfig | 9 + + drivers/dma/Makefile | 2 + + drivers/dma/qcom_bam_dma.c | 1111 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1122 insertions(+) + create mode 100644 drivers/dma/qcom_bam_dma.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index 605b016..f87cef9 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -401,4 +401,13 @@ config DMATEST + config DMA_ENGINE_RAID + bool + ++config QCOM_BAM_DMA ++ tristate "QCOM BAM DMA support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the QCOM BAM DMA controller. This controller ++ provides DMA capabilities for a variety of on-chip devices. ++ + endif +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index a029d0f4..5150c82 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -44,3 +44,5 @@ obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o + obj-$(CONFIG_TI_CPPI41) += cppi41.o + obj-$(CONFIG_K3_DMA) += k3dma.o + obj-$(CONFIG_MOXART_DMA) += moxart-dma.o ++obj-$(CONFIG_FSL_EDMA) += fsl-edma.o ++obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o +diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c +new file mode 100644 +index 0000000..82c9231 +--- /dev/null ++++ b/drivers/dma/qcom_bam_dma.c +@@ -0,0 +1,1111 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++/* ++ * QCOM BAM DMA engine driver ++ * ++ * QCOM BAM DMA blocks are distributed amongst a number of the on-chip ++ * peripherals on the MSM 8x74. The configuration of the channels are dependent ++ * on the way they are hard wired to that specific peripheral. The peripheral ++ * device tree entries specify the configuration of each channel. ++ * ++ * The DMA controller requires the use of external memory for storage of the ++ * hardware descriptors for each channel. The descriptor FIFO is accessed as a ++ * circular buffer and operations are managed according to the offset within the ++ * FIFO. After pipe/channel reset, all of the pipe registers and internal state ++ * are back to defaults. ++ * ++ * During DMA operations, we write descriptors to the FIFO, being careful to ++ * handle wrapping and then write the last FIFO offset to that channel's ++ * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register ++ * indicates the current FIFO offset that is being processed, so there is some ++ * indication of where the hardware is currently working. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/io.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/scatterlist.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/of_dma.h> ++#include <linux/clk.h> ++#include <linux/dmaengine.h> ++ ++#include "dmaengine.h" ++#include "virt-dma.h" ++ ++struct bam_desc_hw { ++ u32 addr; /* Buffer physical address */ ++ u16 size; /* Buffer size in bytes */ ++ u16 flags; ++}; ++ ++#define DESC_FLAG_INT BIT(15) ++#define DESC_FLAG_EOT BIT(14) ++#define DESC_FLAG_EOB BIT(13) ++ ++struct bam_async_desc { ++ struct virt_dma_desc vd; ++ ++ u32 num_desc; ++ u32 xfer_len; ++ struct bam_desc_hw *curr_desc; ++ ++ enum dma_transfer_direction dir; ++ size_t length; ++ struct bam_desc_hw desc[0]; ++}; ++ ++#define BAM_CTRL 0x0000 ++#define BAM_REVISION 0x0004 ++#define BAM_SW_REVISION 0x0080 ++#define BAM_NUM_PIPES 0x003C ++#define BAM_TIMER 0x0040 ++#define BAM_TIMER_CTRL 0x0044 ++#define BAM_DESC_CNT_TRSHLD 0x0008 ++#define BAM_IRQ_SRCS 0x000C ++#define BAM_IRQ_SRCS_MSK 0x0010 ++#define BAM_IRQ_SRCS_UNMASKED 0x0030 ++#define BAM_IRQ_STTS 0x0014 ++#define BAM_IRQ_CLR 0x0018 ++#define BAM_IRQ_EN 0x001C ++#define BAM_CNFG_BITS 0x007C ++#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80)) ++#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80)) ++#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000)) ++#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000)) ++#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000)) ++#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000)) ++#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000)) ++#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000)) ++#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000)) ++#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000)) ++#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000)) ++ ++/* BAM CTRL */ ++#define BAM_SW_RST BIT(0) ++#define BAM_EN BIT(1) ++#define BAM_EN_ACCUM BIT(4) ++#define BAM_TESTBUS_SEL_SHIFT 5 ++#define BAM_TESTBUS_SEL_MASK 0x3F ++#define BAM_DESC_CACHE_SEL_SHIFT 13 ++#define BAM_DESC_CACHE_SEL_MASK 0x3 ++#define BAM_CACHED_DESC_STORE BIT(15) ++#define IBC_DISABLE BIT(16) ++ ++/* BAM REVISION */ ++#define REVISION_SHIFT 0 ++#define REVISION_MASK 0xFF ++#define NUM_EES_SHIFT 8 ++#define NUM_EES_MASK 0xF ++#define CE_BUFFER_SIZE BIT(13) ++#define AXI_ACTIVE BIT(14) ++#define USE_VMIDMT BIT(15) ++#define SECURED BIT(16) ++#define BAM_HAS_NO_BYPASS BIT(17) ++#define HIGH_FREQUENCY_BAM BIT(18) ++#define INACTIV_TMRS_EXST BIT(19) ++#define NUM_INACTIV_TMRS BIT(20) ++#define DESC_CACHE_DEPTH_SHIFT 21 ++#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT) ++#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT) ++#define CMD_DESC_EN BIT(23) ++#define INACTIV_TMR_BASE_SHIFT 24 ++#define INACTIV_TMR_BASE_MASK 0xFF ++ ++/* BAM NUM PIPES */ ++#define BAM_NUM_PIPES_SHIFT 0 ++#define BAM_NUM_PIPES_MASK 0xFF ++#define PERIPH_NON_PIPE_GRP_SHIFT 16 ++#define PERIPH_NON_PIP_GRP_MASK 0xFF ++#define BAM_NON_PIPE_GRP_SHIFT 24 ++#define BAM_NON_PIPE_GRP_MASK 0xFF ++ ++/* BAM CNFG BITS */ ++#define BAM_PIPE_CNFG BIT(2) ++#define BAM_FULL_PIPE BIT(11) ++#define BAM_NO_EXT_P_RST BIT(12) ++#define BAM_IBC_DISABLE BIT(13) ++#define BAM_SB_CLK_REQ BIT(14) ++#define BAM_PSM_CSW_REQ BIT(15) ++#define BAM_PSM_P_RES BIT(16) ++#define BAM_AU_P_RES BIT(17) ++#define BAM_SI_P_RES BIT(18) ++#define BAM_WB_P_RES BIT(19) ++#define BAM_WB_BLK_CSW BIT(20) ++#define BAM_WB_CSW_ACK_IDL BIT(21) ++#define BAM_WB_RETR_SVPNT BIT(22) ++#define BAM_WB_DSC_AVL_P_RST BIT(23) ++#define BAM_REG_P_EN BIT(24) ++#define BAM_PSM_P_HD_DATA BIT(25) ++#define BAM_AU_ACCUMED BIT(26) ++#define BAM_CMD_ENABLE BIT(27) ++ ++#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \ ++ BAM_NO_EXT_P_RST | \ ++ BAM_IBC_DISABLE | \ ++ BAM_SB_CLK_REQ | \ ++ BAM_PSM_CSW_REQ | \ ++ BAM_PSM_P_RES | \ ++ BAM_AU_P_RES | \ ++ BAM_SI_P_RES | \ ++ BAM_WB_P_RES | \ ++ BAM_WB_BLK_CSW | \ ++ BAM_WB_CSW_ACK_IDL | \ ++ BAM_WB_RETR_SVPNT | \ ++ BAM_WB_DSC_AVL_P_RST | \ ++ BAM_REG_P_EN | \ ++ BAM_PSM_P_HD_DATA | \ ++ BAM_AU_ACCUMED | \ ++ BAM_CMD_ENABLE) ++ ++/* PIPE CTRL */ ++#define P_EN BIT(1) ++#define P_DIRECTION BIT(3) ++#define P_SYS_STRM BIT(4) ++#define P_SYS_MODE BIT(5) ++#define P_AUTO_EOB BIT(6) ++#define P_AUTO_EOB_SEL_SHIFT 7 ++#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT) ++#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT) ++#define P_PREFETCH_LIMIT_SHIFT 9 ++#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT) ++#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT) ++#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT) ++#define P_WRITE_NWD BIT(11) ++#define P_LOCK_GROUP_SHIFT 16 ++#define P_LOCK_GROUP_MASK 0x1F ++ ++/* BAM_DESC_CNT_TRSHLD */ ++#define CNT_TRSHLD 0xffff ++#define DEFAULT_CNT_THRSHLD 0x4 ++ ++/* BAM_IRQ_SRCS */ ++#define BAM_IRQ BIT(31) ++#define P_IRQ 0x7fffffff ++ ++/* BAM_IRQ_SRCS_MSK */ ++#define BAM_IRQ_MSK BAM_IRQ ++#define P_IRQ_MSK P_IRQ ++ ++/* BAM_IRQ_STTS */ ++#define BAM_TIMER_IRQ BIT(4) ++#define BAM_EMPTY_IRQ BIT(3) ++#define BAM_ERROR_IRQ BIT(2) ++#define BAM_HRESP_ERR_IRQ BIT(1) ++ ++/* BAM_IRQ_CLR */ ++#define BAM_TIMER_CLR BIT(4) ++#define BAM_EMPTY_CLR BIT(3) ++#define BAM_ERROR_CLR BIT(2) ++#define BAM_HRESP_ERR_CLR BIT(1) ++ ++/* BAM_IRQ_EN */ ++#define BAM_TIMER_EN BIT(4) ++#define BAM_EMPTY_EN BIT(3) ++#define BAM_ERROR_EN BIT(2) ++#define BAM_HRESP_ERR_EN BIT(1) ++ ++/* BAM_P_IRQ_EN */ ++#define P_PRCSD_DESC_EN BIT(0) ++#define P_TIMER_EN BIT(1) ++#define P_WAKE_EN BIT(2) ++#define P_OUT_OF_DESC_EN BIT(3) ++#define P_ERR_EN BIT(4) ++#define P_TRNSFR_END_EN BIT(5) ++#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN) ++ ++/* BAM_P_SW_OFSTS */ ++#define P_SW_OFSTS_MASK 0xffff ++ ++#define BAM_DESC_FIFO_SIZE SZ_32K ++#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1) ++#define BAM_MAX_DATA_SIZE (SZ_32K - 8) ++ ++struct bam_chan { ++ struct virt_dma_chan vc; ++ ++ struct bam_device *bdev; ++ ++ /* configuration from device tree */ ++ u32 id; ++ ++ struct bam_async_desc *curr_txd; /* current running dma */ ++ ++ /* runtime configuration */ ++ struct dma_slave_config slave; ++ ++ /* fifo storage */ ++ struct bam_desc_hw *fifo_virt; ++ dma_addr_t fifo_phys; ++ ++ /* fifo markers */ ++ unsigned short head; /* start of active descriptor entries */ ++ unsigned short tail; /* end of active descriptor entries */ ++ ++ unsigned int initialized; /* is the channel hw initialized? */ ++ unsigned int paused; /* is the channel paused? */ ++ unsigned int reconfigure; /* new slave config? */ ++ ++ struct list_head node; ++}; ++ ++static inline struct bam_chan *to_bam_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct bam_chan, vc.chan); ++} ++ ++struct bam_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct bam_chan *channels; ++ u32 num_channels; ++ ++ /* execution environment ID, from DT */ ++ u32 ee; ++ ++ struct clk *bamclk; ++ int irq; ++ ++ /* dma start transaction tasklet */ ++ struct tasklet_struct task; ++}; ++ ++/** ++ * bam_reset_channel - Reset individual BAM DMA channel ++ * @bchan: bam channel ++ * ++ * This function resets a specific BAM channel ++ */ ++static void bam_reset_channel(struct bam_chan *bchan) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ ++ lockdep_assert_held(&bchan->vc.lock); ++ ++ /* reset channel */ ++ writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id)); ++ writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id)); ++ ++ /* don't allow cpu to reorder BAM register accesses done after this */ ++ wmb(); ++ ++ /* make sure hw is initialized when channel is used the first time */ ++ bchan->initialized = 0; ++} ++ ++/** ++ * bam_chan_init_hw - Initialize channel hardware ++ * @bchan: bam channel ++ * ++ * This function resets and initializes the BAM channel ++ */ ++static void bam_chan_init_hw(struct bam_chan *bchan, ++ enum dma_transfer_direction dir) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ u32 val; ++ ++ /* Reset the channel to clear internal state of the FIFO */ ++ bam_reset_channel(bchan); ++ ++ /* ++ * write out 8 byte aligned address. We have enough space for this ++ * because we allocated 1 more descriptor (8 bytes) than we can use ++ */ ++ writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)), ++ bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id)); ++ writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs + ++ BAM_P_FIFO_SIZES(bchan->id)); ++ ++ /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */ ++ writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id)); ++ ++ /* unmask the specific pipe and EE combo */ ++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ val |= BIT(bchan->id); ++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ /* don't allow cpu to reorder the channel enable done below */ ++ wmb(); ++ ++ /* set fixed direction and mode, then enable channel */ ++ val = P_EN | P_SYS_MODE; ++ if (dir == DMA_DEV_TO_MEM) ++ val |= P_DIRECTION; ++ ++ writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id)); ++ ++ bchan->initialized = 1; ++ ++ /* init FIFO pointers */ ++ bchan->head = 0; ++ bchan->tail = 0; ++} ++ ++/** ++ * bam_alloc_chan - Allocate channel resources for DMA channel. ++ * @chan: specified channel ++ * ++ * This function allocates the FIFO descriptor memory ++ */ ++static int bam_alloc_chan(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ ++ if (bchan->fifo_virt) ++ return 0; ++ ++ /* allocate FIFO descriptor space, but only if necessary */ ++ bchan->fifo_virt = dma_alloc_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, ++ &bchan->fifo_phys, GFP_KERNEL); ++ ++ if (!bchan->fifo_virt) { ++ dev_err(bdev->dev, "Failed to allocate desc fifo\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * bam_free_chan - Frees dma resources associated with specific channel ++ * @chan: specified channel ++ * ++ * Free the allocated fifo descriptor memory and channel resources ++ * ++ */ ++static void bam_free_chan(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ u32 val; ++ unsigned long flags; ++ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++ ++ if (bchan->curr_txd) { ++ dev_err(bchan->bdev->dev, "Cannot free busy channel\n"); ++ return; ++ } ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ bam_reset_channel(bchan); ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ ++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt, ++ bchan->fifo_phys); ++ bchan->fifo_virt = NULL; ++ ++ /* mask irq for pipe/channel */ ++ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ val &= ~BIT(bchan->id); ++ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ /* disable irq */ ++ writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id)); ++} ++ ++/** ++ * bam_slave_config - set slave configuration for channel ++ * @chan: dma channel ++ * @cfg: slave configuration ++ * ++ * Sets slave configuration for channel ++ * ++ */ ++static void bam_slave_config(struct bam_chan *bchan, ++ struct dma_slave_config *cfg) ++{ ++ memcpy(&bchan->slave, cfg, sizeof(*cfg)); ++ bchan->reconfigure = 1; ++} ++ ++/** ++ * bam_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ struct bam_async_desc *async_desc; ++ struct scatterlist *sg; ++ u32 i; ++ struct bam_desc_hw *desc; ++ unsigned int num_alloc = 0; ++ ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(bdev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* calculate number of required entries */ ++ for_each_sg(sgl, sg, sg_len, i) ++ num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_MAX_DATA_SIZE); ++ ++ /* allocate enough room to accomodate the number of entries */ ++ async_desc = kzalloc(sizeof(*async_desc) + ++ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); ++ ++ if (!async_desc) ++ goto err_out; ++ ++ async_desc->num_desc = num_alloc; ++ async_desc->curr_desc = async_desc->desc; ++ async_desc->dir = direction; ++ ++ /* fill in temporary descriptors */ ++ desc = async_desc->desc; ++ for_each_sg(sgl, sg, sg_len, i) { ++ unsigned int remainder = sg_dma_len(sg); ++ unsigned int curr_offset = 0; ++ ++ do { ++ desc->addr = sg_dma_address(sg) + curr_offset; ++ ++ if (remainder > BAM_MAX_DATA_SIZE) { ++ desc->size = BAM_MAX_DATA_SIZE; ++ remainder -= BAM_MAX_DATA_SIZE; ++ curr_offset += BAM_MAX_DATA_SIZE; ++ } else { ++ desc->size = remainder; ++ remainder = 0; ++ } ++ ++ async_desc->length += desc->size; ++ desc++; ++ } while (remainder > 0); ++ } ++ ++ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); ++ ++err_out: ++ kfree(async_desc); ++ return NULL; ++} ++ ++/** ++ * bam_dma_terminate_all - terminate all transactions on a channel ++ * @bchan: bam dma channel ++ * ++ * Dequeues and frees all transactions ++ * No callbacks are done ++ * ++ */ ++static void bam_dma_terminate_all(struct bam_chan *bchan) ++{ ++ unsigned long flag; ++ LIST_HEAD(head); ++ ++ /* remove all transactions, including active transaction */ ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ if (bchan->curr_txd) { ++ list_add(&bchan->curr_txd->vd.node, &bchan->vc.desc_issued); ++ bchan->curr_txd = NULL; ++ } ++ ++ vchan_get_all_descriptors(&bchan->vc, &head); ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ ++ vchan_dma_desc_free_list(&bchan->vc, &head); ++} ++ ++/** ++ * bam_control - DMA device control ++ * @chan: dma channel ++ * @cmd: control cmd ++ * @arg: cmd argument ++ * ++ * Perform DMA control command ++ * ++ */ ++static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ int ret = 0; ++ unsigned long flag; ++ ++ switch (cmd) { ++ case DMA_PAUSE: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id)); ++ bchan->paused = 1; ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ case DMA_RESUME: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id)); ++ bchan->paused = 0; ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ case DMA_TERMINATE_ALL: ++ bam_dma_terminate_all(bchan); ++ break; ++ ++ case DMA_SLAVE_CONFIG: ++ spin_lock_irqsave(&bchan->vc.lock, flag); ++ bam_slave_config(bchan, (struct dma_slave_config *)arg); ++ spin_unlock_irqrestore(&bchan->vc.lock, flag); ++ break; ++ ++ default: ++ ret = -ENXIO; ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * process_channel_irqs - processes the channel interrupts ++ * @bdev: bam controller ++ * ++ * This function processes the channel interrupts ++ * ++ */ ++static u32 process_channel_irqs(struct bam_device *bdev) ++{ ++ u32 i, srcs, pipe_stts; ++ unsigned long flags; ++ struct bam_async_desc *async_desc; ++ ++ srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee)); ++ ++ /* return early if no pipe/channel interrupts are present */ ++ if (!(srcs & P_IRQ)) ++ return srcs; ++ ++ for (i = 0; i < bdev->num_channels; i++) { ++ struct bam_chan *bchan = &bdev->channels[i]; ++ ++ if (!(srcs & BIT(i))) ++ continue; ++ ++ /* clear pipe irq */ ++ pipe_stts = readl_relaxed(bdev->regs + ++ BAM_P_IRQ_STTS(i)); ++ ++ writel_relaxed(pipe_stts, bdev->regs + ++ BAM_P_IRQ_CLR(i)); ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ async_desc = bchan->curr_txd; ++ ++ if (async_desc) { ++ async_desc->num_desc -= async_desc->xfer_len; ++ async_desc->curr_desc += async_desc->xfer_len; ++ bchan->curr_txd = NULL; ++ ++ /* manage FIFO */ ++ bchan->head += async_desc->xfer_len; ++ bchan->head %= MAX_DESCRIPTORS; ++ ++ /* ++ * if complete, process cookie. Otherwise ++ * push back to front of desc_issued so that ++ * it gets restarted by the tasklet ++ */ ++ if (!async_desc->num_desc) ++ vchan_cookie_complete(&async_desc->vd); ++ else ++ list_add(&async_desc->vd.node, ++ &bchan->vc.desc_issued); ++ } ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ } ++ ++ return srcs; ++} ++ ++/** ++ * bam_dma_irq - irq handler for bam controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t bam_dma_irq(int irq, void *data) ++{ ++ struct bam_device *bdev = data; ++ u32 clr_mask = 0, srcs = 0; ++ ++ srcs |= process_channel_irqs(bdev); ++ ++ /* kick off tasklet to start next dma transfer */ ++ if (srcs & P_IRQ) ++ tasklet_schedule(&bdev->task); ++ ++ if (srcs & BAM_IRQ) ++ clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS); ++ ++ /* don't allow reorder of the various accesses to the BAM registers */ ++ mb(); ++ ++ writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * bam_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct virt_dma_desc *vd; ++ int ret; ++ size_t residue = 0; ++ unsigned int i; ++ unsigned long flags; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE) ++ return ret; ++ ++ if (!txstate) ++ return bchan->paused ? DMA_PAUSED : ret; ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ vd = vchan_find_desc(&bchan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct bam_async_desc, vd)->length; ++ else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie) ++ for (i = 0; i < bchan->curr_txd->num_desc; i++) ++ residue += bchan->curr_txd->curr_desc[i].size; ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ ++ dma_set_residue(txstate, residue); ++ ++ if (ret == DMA_IN_PROGRESS && bchan->paused) ++ ret = DMA_PAUSED; ++ ++ return ret; ++} ++ ++/** ++ * bam_apply_new_config ++ * @bchan: bam dma channel ++ * @dir: DMA direction ++ */ ++static void bam_apply_new_config(struct bam_chan *bchan, ++ enum dma_transfer_direction dir) ++{ ++ struct bam_device *bdev = bchan->bdev; ++ u32 maxburst; ++ ++ if (dir == DMA_DEV_TO_MEM) ++ maxburst = bchan->slave.src_maxburst; ++ else ++ maxburst = bchan->slave.dst_maxburst; ++ ++ writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD); ++ ++ bchan->reconfigure = 0; ++} ++ ++/** ++ * bam_start_dma - start next transaction ++ * @bchan - bam dma channel ++ */ ++static void bam_start_dma(struct bam_chan *bchan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc); ++ struct bam_device *bdev = bchan->bdev; ++ struct bam_async_desc *async_desc; ++ struct bam_desc_hw *desc; ++ struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt, ++ sizeof(struct bam_desc_hw)); ++ ++ lockdep_assert_held(&bchan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ async_desc = container_of(vd, struct bam_async_desc, vd); ++ bchan->curr_txd = async_desc; ++ ++ /* on first use, initialize the channel hardware */ ++ if (!bchan->initialized) ++ bam_chan_init_hw(bchan, async_desc->dir); ++ ++ /* apply new slave config changes, if necessary */ ++ if (bchan->reconfigure) ++ bam_apply_new_config(bchan, async_desc->dir); ++ ++ desc = bchan->curr_txd->curr_desc; ++ ++ if (async_desc->num_desc > MAX_DESCRIPTORS) ++ async_desc->xfer_len = MAX_DESCRIPTORS; ++ else ++ async_desc->xfer_len = async_desc->num_desc; ++ ++ /* set INT on last descriptor */ ++ desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT; ++ ++ if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) { ++ u32 partial = MAX_DESCRIPTORS - bchan->tail; ++ ++ memcpy(&fifo[bchan->tail], desc, ++ partial * sizeof(struct bam_desc_hw)); ++ memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) * ++ sizeof(struct bam_desc_hw)); ++ } else { ++ memcpy(&fifo[bchan->tail], desc, ++ async_desc->xfer_len * sizeof(struct bam_desc_hw)); ++ } ++ ++ bchan->tail += async_desc->xfer_len; ++ bchan->tail %= MAX_DESCRIPTORS; ++ ++ /* ensure descriptor writes and dma start not reordered */ ++ wmb(); ++ writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw), ++ bdev->regs + BAM_P_EVNT_REG(bchan->id)); ++} ++ ++/** ++ * dma_tasklet - DMA IRQ tasklet ++ * @data: tasklet argument (bam controller structure) ++ * ++ * Sets up next DMA operation and then processes all completed transactions ++ */ ++static void dma_tasklet(unsigned long data) ++{ ++ struct bam_device *bdev = (struct bam_device *)data; ++ struct bam_chan *bchan; ++ unsigned long flags; ++ unsigned int i; ++ ++ /* go through the channels and kick off transactions */ ++ for (i = 0; i < bdev->num_channels; i++) { ++ bchan = &bdev->channels[i]; ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ ++ if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd) ++ bam_start_dma(bchan); ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++ } ++} ++ ++/** ++ * bam_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Calls tasklet directly which in turn starts any pending transactions ++ */ ++static void bam_issue_pending(struct dma_chan *chan) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bchan->vc.lock, flags); ++ ++ /* if work pending and idle, start a transaction */ ++ if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd) ++ bam_start_dma(bchan); ++ ++ spin_unlock_irqrestore(&bchan->vc.lock, flags); ++} ++ ++/** ++ * bam_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void bam_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct bam_async_desc *async_desc = container_of(vd, ++ struct bam_async_desc, vd); ++ ++ kfree(async_desc); ++} ++ ++static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *of) ++{ ++ struct bam_device *bdev = container_of(of->of_dma_data, ++ struct bam_device, common); ++ unsigned int request; ++ ++ if (dma_spec->args_count != 1) ++ return NULL; ++ ++ request = dma_spec->args[0]; ++ if (request >= bdev->num_channels) ++ return NULL; ++ ++ return dma_get_slave_channel(&(bdev->channels[request].vc.chan)); ++} ++ ++/** ++ * bam_init ++ * @bdev: bam device ++ * ++ * Initialization helper for global bam registers ++ */ ++static int bam_init(struct bam_device *bdev) ++{ ++ u32 val; ++ ++ /* read revision and configuration information */ ++ val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT; ++ val &= NUM_EES_MASK; ++ ++ /* check that configured EE is within range */ ++ if (bdev->ee >= val) ++ return -EINVAL; ++ ++ val = readl_relaxed(bdev->regs + BAM_NUM_PIPES); ++ bdev->num_channels = val & BAM_NUM_PIPES_MASK; ++ ++ /* s/w reset bam */ ++ /* after reset all pipes are disabled and idle */ ++ val = readl_relaxed(bdev->regs + BAM_CTRL); ++ val |= BAM_SW_RST; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ val &= ~BAM_SW_RST; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ ++ /* make sure previous stores are visible before enabling BAM */ ++ wmb(); ++ ++ /* enable bam */ ++ val |= BAM_EN; ++ writel_relaxed(val, bdev->regs + BAM_CTRL); ++ ++ /* set descriptor threshhold, start with 4 bytes */ ++ writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD); ++ ++ /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */ ++ writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS); ++ ++ /* enable irqs for errors */ ++ writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN, ++ bdev->regs + BAM_IRQ_EN); ++ ++ /* unmask global bam interrupt */ ++ writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ return 0; ++} ++ ++static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan, ++ u32 index) ++{ ++ bchan->id = index; ++ bchan->bdev = bdev; ++ ++ vchan_init(&bchan->vc, &bdev->common); ++ bchan->vc.desc_free = bam_dma_free_desc; ++} ++ ++static int bam_dma_probe(struct platform_device *pdev) ++{ ++ struct bam_device *bdev; ++ struct resource *iores; ++ int ret, i; ++ ++ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); ++ if (!bdev) ++ return -ENOMEM; ++ ++ bdev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ bdev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(bdev->regs)) ++ return PTR_ERR(bdev->regs); ++ ++ bdev->irq = platform_get_irq(pdev, 0); ++ if (bdev->irq < 0) ++ return bdev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee); ++ if (ret) { ++ dev_err(bdev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); ++ if (IS_ERR(bdev->bamclk)) ++ return PTR_ERR(bdev->bamclk); ++ ++ ret = clk_prepare_enable(bdev->bamclk); ++ if (ret) { ++ dev_err(bdev->dev, "failed to prepare/enable clock\n"); ++ return ret; ++ } ++ ++ ret = bam_init(bdev); ++ if (ret) ++ goto err_disable_clk; ++ ++ tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev); ++ ++ bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels, ++ sizeof(*bdev->channels), GFP_KERNEL); ++ ++ if (!bdev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clk; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&bdev->common.channels); ++ ++ for (i = 0; i < bdev->num_channels; i++) ++ bam_channel_init(bdev, &bdev->channels[i], i); ++ ++ ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq, ++ IRQF_TRIGGER_HIGH, "bam_dma", bdev); ++ if (ret) ++ goto err_disable_clk; ++ ++ /* set max dma segment size */ ++ bdev->common.dev = bdev->dev; ++ bdev->common.dev->dma_parms = &bdev->dma_parms; ++ ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE); ++ if (ret) { ++ dev_err(bdev->dev, "cannot set maximum segment size\n"); ++ goto err_disable_clk; ++ } ++ ++ platform_set_drvdata(pdev, bdev); ++ ++ /* set capabilities */ ++ dma_cap_zero(bdev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, bdev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ bdev->common.device_alloc_chan_resources = bam_alloc_chan; ++ bdev->common.device_free_chan_resources = bam_free_chan; ++ bdev->common.device_prep_slave_sg = bam_prep_slave_sg; ++ bdev->common.device_control = bam_control; ++ bdev->common.device_issue_pending = bam_issue_pending; ++ bdev->common.device_tx_status = bam_tx_status; ++ bdev->common.dev = bdev->dev; ++ ++ ret = dma_async_device_register(&bdev->common); ++ if (ret) { ++ dev_err(bdev->dev, "failed to register dma async device\n"); ++ goto err_disable_clk; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate, ++ &bdev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&bdev->common); ++err_disable_clk: ++ clk_disable_unprepare(bdev->bamclk); ++ return ret; ++} ++ ++static int bam_dma_remove(struct platform_device *pdev) ++{ ++ struct bam_device *bdev = platform_get_drvdata(pdev); ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&bdev->common); ++ ++ /* mask all interrupts for this execution environment */ ++ writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); ++ ++ devm_free_irq(bdev->dev, bdev->irq, bdev); ++ ++ for (i = 0; i < bdev->num_channels; i++) { ++ bam_dma_terminate_all(&bdev->channels[i]); ++ tasklet_kill(&bdev->channels[i].vc.task); ++ ++ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, ++ bdev->channels[i].fifo_virt, ++ bdev->channels[i].fifo_phys); ++ } ++ ++ tasklet_kill(&bdev->task); ++ ++ clk_disable_unprepare(bdev->bamclk); ++ ++ return 0; ++} ++ ++static const struct of_device_id bam_of_match[] = { ++ { .compatible = "qcom,bam-v1.4.0", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, bam_of_match); ++ ++static struct platform_driver bam_dma_driver = { ++ .probe = bam_dma_probe, ++ .remove = bam_dma_remove, ++ .driver = { ++ .name = "bam-dma-engine", ++ .owner = THIS_MODULE, ++ .of_match_table = bam_of_match, ++ }, ++}; ++ ++module_platform_driver(bam_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM BAM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch new file mode 100644 index 0000000000..b173fcfc1d --- /dev/null +++ b/target/linux/ipq806x/patches/0046-mmc-sdhci-msm-Qualcomm-SDHCI-binding-documentation.patch @@ -0,0 +1,81 @@ +From 8a70c89b2fbb635a8d4fec302165343827aeed9f Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:11 +0200 +Subject: [PATCH 046/182] mmc: sdhci-msm: Qualcomm SDHCI binding documentation + +This patch adds the device-tree binding documentation for Qualcomm +SDHCI driver. It contains the differences between the core properties +in mmc.txt and the properties used by the sdhci-msm driver. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + .../devicetree/bindings/mmc/sdhci-msm.txt | 55 ++++++++++++++++++++ + 1 file changed, 55 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt + +diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +new file mode 100644 +index 0000000..81b33b5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +@@ -0,0 +1,55 @@ ++* Qualcomm SDHCI controller (sdhci-msm) ++ ++This file documents differences between the core properties in mmc.txt ++and the properties used by the sdhci-msm driver. ++ ++Required properties: ++- compatible: Should contain "qcom,sdhci-msm-v4". ++- reg: Base address and length of the register in the following order: ++ - Host controller register map (required) ++ - SD Core register map (required) ++- interrupts: Should contain an interrupt-specifiers for the interrupts: ++ - Host controller interrupt (required) ++- pinctrl-names: Should contain only one value - "default". ++- pinctrl-0: Should specify pin control groups used for this controller. ++- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names. ++- clock-names: Should contain the following: ++ "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) ++ "core" - SDC MMC clock (MCLK) (required) ++ "bus" - SDCC bus voter clock (optional) ++ ++Example: ++ ++ sdhc_1: sdhci@f9824900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; ++ interrupts = <0 123 0>; ++ bus-width = <8>; ++ non-removable; ++ ++ vmmc = <&pm8941_l20>; ++ vqmmc = <&pm8941_s3>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; ++ ++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ }; ++ ++ sdhc_2: sdhci@f98a4900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; ++ interrupts = <0 125 0>; ++ bus-width = <4>; ++ cd-gpios = <&msmgpio 62 0x1>; ++ ++ vmmc = <&pm8941_l21>; ++ vqmmc = <&pm8941_l13>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>; ++ ++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch new file mode 100644 index 0000000000..7ec97a4687 --- /dev/null +++ b/target/linux/ipq806x/patches/0047-mmc-sdhci-msm-Initial-support-for-Qualcomm-chipsets.patch @@ -0,0 +1,275 @@ +From 5a8f026acb4a7a6c6d0c868cc1790363640b9b8f Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:12 +0200 +Subject: [PATCH 047/182] mmc: sdhci-msm: Initial support for Qualcomm + chipsets + +This platform driver adds the initial support of Secure Digital Host +Controller Interface compliant controller found in Qualcomm chipsets. + +Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> +Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> +Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + drivers/mmc/host/Kconfig | 13 +++ + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/sdhci-msm.c | 208 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 222 insertions(+) + create mode 100644 drivers/mmc/host/sdhci-msm.c + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 1384f67..c0ea72a 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -334,6 +334,19 @@ config MMC_ATMELMCI + + If unsure, say N. + ++config MMC_SDHCI_MSM ++ tristate "Qualcomm SDHCI Controller Support" ++ depends on ARCH_QCOM ++ depends on MMC_SDHCI_PLTFM ++ help ++ This selects the Secure Digital Host Controller Interface (SDHCI) ++ support present in Qualcomm SOCs. The controller supports ++ SD/MMC/SDIO devices. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_MSM + tristate "Qualcomm SDCC Controller Support" + depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3483b6b..bbc8445 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -64,6 +64,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o + obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o + obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ++obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o + + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c +new file mode 100644 +index 0000000..3b0606f +--- /dev/null ++++ b/drivers/mmc/host/sdhci-msm.c +@@ -0,0 +1,208 @@ ++/* ++ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver ++ * ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/delay.h> ++ ++#include "sdhci-pltfm.h" ++ ++#define CORE_HC_MODE 0x78 ++#define HC_MODE_EN 0x1 ++#define CORE_POWER 0x0 ++#define CORE_SW_RST BIT(7) ++ ++ ++struct sdhci_msm_host { ++ struct platform_device *pdev; ++ void __iomem *core_mem; /* MSM SDCC mapped address */ ++ struct clk *clk; /* main SD/MMC bus clock */ ++ struct clk *pclk; /* SDHC peripheral bus clock */ ++ struct clk *bus_clk; /* SDHC bus voter clock */ ++ struct mmc_host *mmc; ++ struct sdhci_pltfm_data sdhci_msm_pdata; ++}; ++ ++/* Platform specific tuning */ ++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++{ ++ /* ++ * Tuning is required for SDR104, HS200 and HS400 cards and if the clock ++ * frequency greater than 100MHz in those modes. The standard tuning ++ * procedure should not be executed, but a custom implementation will be ++ * added here instead. ++ */ ++ return 0; ++} ++ ++static const struct of_device_id sdhci_msm_dt_match[] = { ++ { .compatible = "qcom,sdhci-msm-v4" }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); ++ ++static struct sdhci_ops sdhci_msm_ops = { ++ .platform_execute_tuning = sdhci_msm_execute_tuning, ++}; ++ ++static int sdhci_msm_probe(struct platform_device *pdev) ++{ ++ struct sdhci_host *host; ++ struct sdhci_pltfm_host *pltfm_host; ++ struct sdhci_msm_host *msm_host; ++ struct resource *core_memres; ++ int ret; ++ u16 host_version; ++ ++ msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); ++ if (!msm_host) ++ return -ENOMEM; ++ ++ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops; ++ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0); ++ if (IS_ERR(host)) ++ return PTR_ERR(host); ++ ++ pltfm_host = sdhci_priv(host); ++ pltfm_host->priv = msm_host; ++ msm_host->mmc = host->mmc; ++ msm_host->pdev = pdev; ++ ++ ret = mmc_of_parse(host->mmc); ++ if (ret) ++ goto pltfm_free; ++ ++ sdhci_get_of_property(pdev); ++ ++ /* Setup SDCC bus voter clock. */ ++ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); ++ if (!IS_ERR(msm_host->bus_clk)) { ++ /* Vote for max. clk rate for max. performance */ ++ ret = clk_set_rate(msm_host->bus_clk, INT_MAX); ++ if (ret) ++ goto pltfm_free; ++ ret = clk_prepare_enable(msm_host->bus_clk); ++ if (ret) ++ goto pltfm_free; ++ } ++ ++ /* Setup main peripheral bus clock */ ++ msm_host->pclk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(msm_host->pclk)) { ++ ret = PTR_ERR(msm_host->pclk); ++ dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret); ++ goto bus_clk_disable; ++ } ++ ++ ret = clk_prepare_enable(msm_host->pclk); ++ if (ret) ++ goto bus_clk_disable; ++ ++ /* Setup SDC MMC clock */ ++ msm_host->clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(msm_host->clk)) { ++ ret = PTR_ERR(msm_host->clk); ++ dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret); ++ goto pclk_disable; ++ } ++ ++ ret = clk_prepare_enable(msm_host->clk); ++ if (ret) ++ goto pclk_disable; ++ ++ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); ++ ++ if (IS_ERR(msm_host->core_mem)) { ++ dev_err(&pdev->dev, "Failed to remap registers\n"); ++ ret = PTR_ERR(msm_host->core_mem); ++ goto clk_disable; ++ } ++ ++ /* Reset the core and Enable SDHC mode */ ++ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) | ++ CORE_SW_RST, msm_host->core_mem + CORE_POWER); ++ ++ /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ ++ usleep_range(1000, 5000); ++ if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) { ++ dev_err(&pdev->dev, "Stuck in reset\n"); ++ ret = -ETIMEDOUT; ++ goto clk_disable; ++ } ++ ++ /* Set HC_MODE_EN bit in HC_MODE register */ ++ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); ++ ++ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; ++ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; ++ ++ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); ++ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", ++ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> ++ SDHCI_VENDOR_VER_SHIFT)); ++ ++ ret = sdhci_add_host(host); ++ if (ret) ++ goto clk_disable; ++ ++ return 0; ++ ++clk_disable: ++ clk_disable_unprepare(msm_host->clk); ++pclk_disable: ++ clk_disable_unprepare(msm_host->pclk); ++bus_clk_disable: ++ if (!IS_ERR(msm_host->bus_clk)) ++ clk_disable_unprepare(msm_host->bus_clk); ++pltfm_free: ++ sdhci_pltfm_free(pdev); ++ return ret; ++} ++ ++static int sdhci_msm_remove(struct platform_device *pdev) ++{ ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_msm_host *msm_host = pltfm_host->priv; ++ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == ++ 0xffffffff); ++ ++ sdhci_remove_host(host, dead); ++ sdhci_pltfm_free(pdev); ++ clk_disable_unprepare(msm_host->clk); ++ clk_disable_unprepare(msm_host->pclk); ++ if (!IS_ERR(msm_host->bus_clk)) ++ clk_disable_unprepare(msm_host->bus_clk); ++ return 0; ++} ++ ++static struct platform_driver sdhci_msm_driver = { ++ .probe = sdhci_msm_probe, ++ .remove = sdhci_msm_remove, ++ .driver = { ++ .name = "sdhci_msm", ++ .owner = THIS_MODULE, ++ .of_match_table = sdhci_msm_dt_match, ++ }, ++}; ++ ++module_platform_driver(sdhci_msm_driver); ++ ++MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch new file mode 100644 index 0000000000..07e122fa64 --- /dev/null +++ b/target/linux/ipq806x/patches/0048-mmc-sdhci-msm-Add-platform_execute_tuning-implementa.patch @@ -0,0 +1,472 @@ +From c2a237b3e467c8bb349c4624b71ec400abaf8ad1 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Mon, 10 Mar 2014 17:37:13 +0200 +Subject: [PATCH 048/182] mmc: sdhci-msm: Add platform_execute_tuning + implementation + +This patch adds implementation for platform specific tuning in order +to support HS200 bus speed mode on Qualcomm SDHCI controller. + +Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> +Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Acked-by: Ulf Hansson <ulf.hansson@linaro.org> +Signed-off-by: Chris Ball <chris@printf.net> +--- + drivers/mmc/host/sdhci-msm.c | 420 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 415 insertions(+), 5 deletions(-) + +diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c +index 3b0606f..acb0e9e 100644 +--- a/drivers/mmc/host/sdhci-msm.c ++++ b/drivers/mmc/host/sdhci-msm.c +@@ -18,6 +18,8 @@ + #include <linux/of_device.h> + #include <linux/regulator/consumer.h> + #include <linux/delay.h> ++#include <linux/mmc/mmc.h> ++#include <linux/slab.h> + + #include "sdhci-pltfm.h" + +@@ -26,6 +28,42 @@ + #define CORE_POWER 0x0 + #define CORE_SW_RST BIT(7) + ++#define MAX_PHASES 16 ++#define CORE_DLL_LOCK BIT(7) ++#define CORE_DLL_EN BIT(16) ++#define CORE_CDR_EN BIT(17) ++#define CORE_CK_OUT_EN BIT(18) ++#define CORE_CDR_EXT_EN BIT(19) ++#define CORE_DLL_PDN BIT(29) ++#define CORE_DLL_RST BIT(30) ++#define CORE_DLL_CONFIG 0x100 ++#define CORE_DLL_STATUS 0x108 ++ ++#define CORE_VENDOR_SPEC 0x10c ++#define CORE_CLK_PWRSAVE BIT(1) ++ ++#define CDR_SELEXT_SHIFT 20 ++#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT) ++#define CMUX_SHIFT_PHASE_SHIFT 24 ++#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) ++ ++static const u32 tuning_block_64[] = { ++ 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe, ++ 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777, ++ 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff, ++ 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7 ++}; ++ ++static const u32 tuning_block_128[] = { ++ 0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc, ++ 0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff, ++ 0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff, ++ 0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb, ++ 0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc, ++ 0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff, ++ 0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb, ++ 0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77 ++}; + + struct sdhci_msm_host { + struct platform_device *pdev; +@@ -38,17 +76,389 @@ struct sdhci_msm_host { + }; + + /* Platform specific tuning */ +-static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) ++{ ++ u32 wait_cnt = 50; ++ u8 ck_out_en; ++ struct mmc_host *mmc = host->mmc; ++ ++ /* Poll for CK_OUT_EN bit. max. poll time = 50us */ ++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & ++ CORE_CK_OUT_EN); ++ ++ while (ck_out_en != poll) { ++ if (--wait_cnt == 0) { ++ dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n", ++ mmc_hostname(mmc), poll); ++ return -ETIMEDOUT; ++ } ++ udelay(1); ++ ++ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & ++ CORE_CK_OUT_EN); ++ } ++ ++ return 0; ++} ++ ++static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) ++{ ++ int rc; ++ static const u8 grey_coded_phase_table[] = { ++ 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, ++ 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8 ++ }; ++ unsigned long flags; ++ u32 config; ++ struct mmc_host *mmc = host->mmc; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); ++ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ ++ rc = msm_dll_poll_ck_out_en(host, 0); ++ if (rc) ++ goto err_out; ++ ++ /* ++ * Write the selected DLL clock output phase (0 ... 15) ++ * to CDR_SELEXT bit field of DLL_CONFIG register. ++ */ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~CDR_SELEXT_MASK; ++ config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ ++ rc = msm_dll_poll_ck_out_en(host, 1); ++ if (rc) ++ goto err_out; ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config |= CORE_CDR_EN; ++ config &= ~CORE_CDR_EXT_EN; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++ goto out; ++ ++err_out: ++ dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n", ++ mmc_hostname(mmc), phase); ++out: ++ spin_unlock_irqrestore(&host->lock, flags); ++ return rc; ++} ++ ++/* ++ * Find out the greatest range of consecuitive selected ++ * DLL clock output phases that can be used as sampling ++ * setting for SD3.0 UHS-I card read operation (in SDR104 ++ * timing mode) or for eMMC4.5 card read operation (in HS200 ++ * timing mode). ++ * Select the 3/4 of the range and configure the DLL with the ++ * selected DLL clock output phase. ++ */ ++ ++static int msm_find_most_appropriate_phase(struct sdhci_host *host, ++ u8 *phase_table, u8 total_phases) ++{ ++ int ret; ++ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} }; ++ u8 phases_per_row[MAX_PHASES] = { 0 }; ++ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0; ++ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0; ++ bool phase_0_found = false, phase_15_found = false; ++ struct mmc_host *mmc = host->mmc; ++ ++ if (!total_phases || (total_phases > MAX_PHASES)) { ++ dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n", ++ mmc_hostname(mmc), total_phases); ++ return -EINVAL; ++ } ++ ++ for (cnt = 0; cnt < total_phases; cnt++) { ++ ranges[row_index][col_index] = phase_table[cnt]; ++ phases_per_row[row_index] += 1; ++ col_index++; ++ ++ if ((cnt + 1) == total_phases) { ++ continue; ++ /* check if next phase in phase_table is consecutive or not */ ++ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) { ++ row_index++; ++ col_index = 0; ++ } ++ } ++ ++ if (row_index >= MAX_PHASES) ++ return -EINVAL; ++ ++ /* Check if phase-0 is present in first valid window? */ ++ if (!ranges[0][0]) { ++ phase_0_found = true; ++ phase_0_raw_index = 0; ++ /* Check if cycle exist between 2 valid windows */ ++ for (cnt = 1; cnt <= row_index; cnt++) { ++ if (phases_per_row[cnt]) { ++ for (i = 0; i < phases_per_row[cnt]; i++) { ++ if (ranges[cnt][i] == 15) { ++ phase_15_found = true; ++ phase_15_raw_index = cnt; ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ /* If 2 valid windows form cycle then merge them as single window */ ++ if (phase_0_found && phase_15_found) { ++ /* number of phases in raw where phase 0 is present */ ++ u8 phases_0 = phases_per_row[phase_0_raw_index]; ++ /* number of phases in raw where phase 15 is present */ ++ u8 phases_15 = phases_per_row[phase_15_raw_index]; ++ ++ if (phases_0 + phases_15 >= MAX_PHASES) ++ /* ++ * If there are more than 1 phase windows then total ++ * number of phases in both the windows should not be ++ * more than or equal to MAX_PHASES. ++ */ ++ return -EINVAL; ++ ++ /* Merge 2 cyclic windows */ ++ i = phases_15; ++ for (cnt = 0; cnt < phases_0; cnt++) { ++ ranges[phase_15_raw_index][i] = ++ ranges[phase_0_raw_index][cnt]; ++ if (++i >= MAX_PHASES) ++ break; ++ } ++ ++ phases_per_row[phase_0_raw_index] = 0; ++ phases_per_row[phase_15_raw_index] = phases_15 + phases_0; ++ } ++ ++ for (cnt = 0; cnt <= row_index; cnt++) { ++ if (phases_per_row[cnt] > curr_max) { ++ curr_max = phases_per_row[cnt]; ++ selected_row_index = cnt; ++ } ++ } ++ ++ i = (curr_max * 3) / 4; ++ if (i) ++ i--; ++ ++ ret = ranges[selected_row_index][i]; ++ ++ if (ret >= MAX_PHASES) { ++ ret = -EINVAL; ++ dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n", ++ mmc_hostname(mmc), ret); ++ } ++ ++ return ret; ++} ++ ++static inline void msm_cm_dll_set_freq(struct sdhci_host *host) ++{ ++ u32 mclk_freq = 0, config; ++ ++ /* Program the MCLK value to MCLK_FREQ bit field */ ++ if (host->clock <= 112000000) ++ mclk_freq = 0; ++ else if (host->clock <= 125000000) ++ mclk_freq = 1; ++ else if (host->clock <= 137000000) ++ mclk_freq = 2; ++ else if (host->clock <= 150000000) ++ mclk_freq = 3; ++ else if (host->clock <= 162000000) ++ mclk_freq = 4; ++ else if (host->clock <= 175000000) ++ mclk_freq = 5; ++ else if (host->clock <= 187000000) ++ mclk_freq = 6; ++ else if (host->clock <= 200000000) ++ mclk_freq = 7; ++ ++ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); ++ config &= ~CMUX_SHIFT_PHASE_MASK; ++ config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; ++ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); ++} ++ ++/* Initialize the DLL (Programmable Delay Line) */ ++static int msm_init_cm_dll(struct sdhci_host *host) + { ++ struct mmc_host *mmc = host->mmc; ++ int wait_cnt = 50; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ + /* +- * Tuning is required for SDR104, HS200 and HS400 cards and if the clock +- * frequency greater than 100MHz in those modes. The standard tuning +- * procedure should not be executed, but a custom implementation will be +- * added here instead. ++ * Make sure that clock is always enabled when DLL ++ * tuning is in progress. Keeping PWRSAVE ON may ++ * turn off the clock. + */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) ++ & ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC); ++ ++ /* Write 1 to DLL_RST bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); ++ msm_cm_dll_set_freq(host); ++ ++ /* Write 0 to DLL_RST bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set DLL_EN bit to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Set CK_OUT_EN bit to 1. */ ++ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) ++ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); ++ ++ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ ++ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & ++ CORE_DLL_LOCK)) { ++ /* max. wait for 50us sec for LOCK bit to be set */ ++ if (--wait_cnt == 0) { ++ dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n", ++ mmc_hostname(mmc)); ++ spin_unlock_irqrestore(&host->lock, flags); ++ return -ETIMEDOUT; ++ } ++ udelay(1); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); + return 0; + } + ++static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ++{ ++ int tuning_seq_cnt = 3; ++ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; ++ const u32 *tuning_block_pattern = tuning_block_64; ++ int size = sizeof(tuning_block_64); /* Pattern size in bytes */ ++ int rc; ++ struct mmc_host *mmc = host->mmc; ++ struct mmc_ios ios = host->mmc->ios; ++ ++ /* ++ * Tuning is required for SDR104, HS200 and HS400 cards and ++ * if clock frequency is greater than 100MHz in these modes. ++ */ ++ if (host->clock <= 100 * 1000 * 1000 || ++ !((ios.timing == MMC_TIMING_MMC_HS200) || ++ (ios.timing == MMC_TIMING_UHS_SDR104))) ++ return 0; ++ ++ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && ++ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { ++ tuning_block_pattern = tuning_block_128; ++ size = sizeof(tuning_block_128); ++ } ++ ++ data_buf = kmalloc(size, GFP_KERNEL); ++ if (!data_buf) ++ return -ENOMEM; ++ ++retry: ++ /* First of all reset the tuning block */ ++ rc = msm_init_cm_dll(host); ++ if (rc) ++ goto out; ++ ++ phase = 0; ++ do { ++ struct mmc_command cmd = { 0 }; ++ struct mmc_data data = { 0 }; ++ struct mmc_request mrq = { ++ .cmd = &cmd, ++ .data = &data ++ }; ++ struct scatterlist sg; ++ ++ /* Set the phase in delay line hw block */ ++ rc = msm_config_cm_dll_phase(host, phase); ++ if (rc) ++ goto out; ++ ++ cmd.opcode = opcode; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = size; ++ data.blocks = 1; ++ data.flags = MMC_DATA_READ; ++ data.timeout_ns = NSEC_PER_SEC; /* 1 second */ ++ ++ data.sg = &sg; ++ data.sg_len = 1; ++ sg_init_one(&sg, data_buf, size); ++ memset(data_buf, 0, size); ++ mmc_wait_for_req(mmc, &mrq); ++ ++ if (!cmd.error && !data.error && ++ !memcmp(data_buf, tuning_block_pattern, size)) { ++ /* Tuning is successful at this tuning point */ ++ tuned_phases[tuned_phase_cnt++] = phase; ++ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", ++ mmc_hostname(mmc), phase); ++ } ++ } while (++phase < ARRAY_SIZE(tuned_phases)); ++ ++ if (tuned_phase_cnt) { ++ rc = msm_find_most_appropriate_phase(host, tuned_phases, ++ tuned_phase_cnt); ++ if (rc < 0) ++ goto out; ++ else ++ phase = rc; ++ ++ /* ++ * Finally set the selected phase in delay ++ * line hw block. ++ */ ++ rc = msm_config_cm_dll_phase(host, phase); ++ if (rc) ++ goto out; ++ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", ++ mmc_hostname(mmc), phase); ++ } else { ++ if (--tuning_seq_cnt) ++ goto retry; ++ /* Tuning failed */ ++ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", ++ mmc_hostname(mmc)); ++ rc = -EIO; ++ } ++ ++out: ++ kfree(data_buf); ++ return rc; ++} ++ + static const struct of_device_id sdhci_msm_dt_match[] = { + { .compatible = "qcom,sdhci-msm-v4" }, + {}, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch new file mode 100644 index 0000000000..f520e6cc7a --- /dev/null +++ b/target/linux/ipq806x/patches/0049-drivers-of-add-initialization-code-for-static-reserv.patch @@ -0,0 +1,216 @@ +From 60ee5b21ce14a7d5c269115b0751b904d0e005ed Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:47 +0100 +Subject: [PATCH 049/182] drivers: of: add initialization code for static + reserved memory + +This patch adds support for static (defined by 'reg' property) reserved +memory regions declared in device tree. + +Memory blocks can be reliably reserved only during early boot. This must +happen before the whole memory management subsystem is initialized, +because we need to ensure that the given contiguous blocks are not yet +allocated by kernel. Also it must happen before kernel mappings for the +whole low memory are created, to ensure that there will be no mappings +(for reserved blocks). Typically, all this happens before device tree +structures are unflattened, so we need to get reserved memory layout +directly from fdt. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/fdt.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/of_fdt.h | 4 ++ + 2 files changed, 135 insertions(+) + +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 758b4f8..819e112 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -15,6 +15,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/of_fdt.h> ++#include <linux/sizes.h> + #include <linux/string.h> + #include <linux/errno.h> + #include <linux/slab.h> +@@ -440,6 +441,118 @@ struct boot_param_header *initial_boot_params; + #ifdef CONFIG_OF_EARLY_FLATTREE + + /** ++ * res_mem_reserve_reg() - reserve all memory described in 'reg' property ++ */ ++static int __init __reserved_mem_reserve_reg(unsigned long node, ++ const char *uname) ++{ ++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); ++ phys_addr_t base, size; ++ unsigned long len; ++ __be32 *prop; ++ int nomap; ++ ++ prop = of_get_flat_dt_prop(node, "reg", &len); ++ if (!prop) ++ return -ENOENT; ++ ++ if (len && len % t_len != 0) { ++ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", ++ uname); ++ return -EINVAL; ++ } ++ ++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; ++ ++ while (len >= t_len) { ++ base = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ size = dt_mem_next_cell(dt_root_size_cells, &prop); ++ ++ if (base && size && ++ early_init_dt_reserve_memory_arch(base, size, nomap) == 0) ++ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ else ++ pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ ++ len -= t_len; ++ } ++ return 0; ++} ++ ++/** ++ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided ++ * in /reserved-memory matches the values supported by the current implementation, ++ * also check if ranges property has been provided ++ */ ++static int __reserved_mem_check_root(unsigned long node) ++{ ++ __be32 *prop; ++ ++ prop = of_get_flat_dt_prop(node, "#size-cells", NULL); ++ if (!prop || be32_to_cpup(prop) != dt_root_size_cells) ++ return -EINVAL; ++ ++ prop = of_get_flat_dt_prop(node, "#address-cells", NULL); ++ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) ++ return -EINVAL; ++ ++ prop = of_get_flat_dt_prop(node, "ranges", NULL); ++ if (!prop) ++ return -EINVAL; ++ return 0; ++} ++ ++/** ++ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory ++ */ ++static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ static int found; ++ const char *status; ++ ++ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { ++ if (__reserved_mem_check_root(node) != 0) { ++ pr_err("Reserved memory: unsupported node format, ignoring\n"); ++ /* break scan */ ++ return 1; ++ } ++ found = 1; ++ /* scan next node */ ++ return 0; ++ } else if (!found) { ++ /* scan next node */ ++ return 0; ++ } else if (found && depth < 2) { ++ /* scanning of /reserved-memory has been finished */ ++ return 1; ++ } ++ ++ status = of_get_flat_dt_prop(node, "status", NULL); ++ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) ++ return 0; ++ ++ __reserved_mem_reserve_reg(node, uname); ++ ++ /* scan next node */ ++ return 0; ++} ++ ++/** ++ * early_init_fdt_scan_reserved_mem() - create reserved memory regions ++ * ++ * This function grabs memory from early allocator for device exclusive use ++ * defined in device tree structures. It should be called by arch specific code ++ * once the early allocator (i.e. memblock) has been fully activated. ++ */ ++void __init early_init_fdt_scan_reserved_mem(void) ++{ ++ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); ++} ++ ++/** + * of_scan_flat_dt - scan flattened tree blob and call callback on each. + * @it: callback function + * @data: context data pointer +@@ -856,6 +969,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) + memblock_add(base, size); + } + ++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, ++ phys_addr_t size, bool nomap) ++{ ++ if (memblock_is_region_reserved(base, size)) ++ return -EBUSY; ++ if (nomap) ++ return memblock_remove(base, size); ++ return memblock_reserve(base, size); ++} ++ + /* + * called from unflatten_device_tree() to bootstrap devicetree itself + * Architectures can override this definition if memblock isn't used +@@ -864,6 +987,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) + { + return __va(memblock_alloc(size, align)); + } ++#else ++int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, ++ phys_addr_t size, bool nomap) ++{ ++ pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", ++ base, size, nomap ? " (nomap)" : ""); ++ return -ENOSYS; ++} + #endif + + bool __init early_init_dt_scan(void *params) +diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h +index 2b77058..ddd7219 100644 +--- a/include/linux/of_fdt.h ++++ b/include/linux/of_fdt.h +@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, + int depth, void *data); + extern int early_init_dt_scan_memory(unsigned long node, const char *uname, + int depth, void *data); ++extern void early_init_fdt_scan_reserved_mem(void); + extern void early_init_dt_add_memory_arch(u64 base, u64 size); ++extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, ++ bool no_map); + extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); + extern u64 dt_mem_next_cell(int s, __be32 **cellp); + +@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void); + extern void early_init_devtree(void *); + extern void early_get_first_memblock_info(void *, phys_addr_t *); + #else /* CONFIG_OF_FLATTREE */ ++static inline void early_init_fdt_scan_reserved_mem(void) {} + static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } + static inline void unflatten_device_tree(void) {} + static inline void unflatten_and_copy_device_tree(void) {} +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch new file mode 100644 index 0000000000..b23867ba55 --- /dev/null +++ b/target/linux/ipq806x/patches/0050-drivers-of-add-initialization-code-for-dynamic-reser.patch @@ -0,0 +1,331 @@ +From 968b497e1027ec78e986370976c76e1652aa0459 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:48 +0100 +Subject: [PATCH 050/182] drivers: of: add initialization code for dynamic + reserved memory + +This patch adds support for dynamically allocated reserved memory regions +declared in device tree. Such regions are defined by 'size', 'alignment' +and 'alloc-ranges' properties. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/Kconfig | 6 ++ + drivers/of/Makefile | 1 + + drivers/of/fdt.c | 13 ++- + drivers/of/of_reserved_mem.c | 188 +++++++++++++++++++++++++++++++++++++++ + include/linux/of_reserved_mem.h | 21 +++++ + 5 files changed, 227 insertions(+), 2 deletions(-) + create mode 100644 drivers/of/of_reserved_mem.c + create mode 100644 include/linux/of_reserved_mem.h + +diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig +index c6973f1..30a7d87 100644 +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -75,4 +75,10 @@ config OF_MTD + depends on MTD + def_bool y + ++config OF_RESERVED_MEM ++ depends on OF_EARLY_FLATTREE ++ bool ++ help ++ Helpers to allow for reservation of memory regions ++ + endmenu # OF +diff --git a/drivers/of/Makefile b/drivers/of/Makefile +index efd0510..ed9660a 100644 +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o + obj-$(CONFIG_OF_PCI) += of_pci.o + obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o + obj-$(CONFIG_OF_MTD) += of_mtd.o ++obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 819e112..510c0d8 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -15,6 +15,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/of_fdt.h> ++#include <linux/of_reserved_mem.h> + #include <linux/sizes.h> + #include <linux/string.h> + #include <linux/errno.h> +@@ -450,7 +451,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, + phys_addr_t base, size; + unsigned long len; + __be32 *prop; +- int nomap; ++ int nomap, first = 1; + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop) +@@ -477,6 +478,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, + uname, &base, (unsigned long)size / SZ_1M); + + len -= t_len; ++ if (first) { ++ fdt_reserved_mem_save_node(node, uname, base, size); ++ first = 0; ++ } + } + return 0; + } +@@ -512,6 +517,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + { + static int found; + const char *status; ++ int err; + + if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { + if (__reserved_mem_check_root(node) != 0) { +@@ -534,7 +540,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) + return 0; + +- __reserved_mem_reserve_reg(node, uname); ++ err = __reserved_mem_reserve_reg(node, uname); ++ if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) ++ fdt_reserved_mem_save_node(node, uname, 0, 0); + + /* scan next node */ + return 0; +@@ -550,6 +558,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + void __init early_init_fdt_scan_reserved_mem(void) + { + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); ++ fdt_init_reserved_mem(); + } + + /** +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +new file mode 100644 +index 0000000..69b8117 +--- /dev/null ++++ b/drivers/of/of_reserved_mem.c +@@ -0,0 +1,188 @@ ++/* ++ * Device tree based initialization code for reserved memory. ++ * ++ * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. ++ * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * Author: Marek Szyprowski <m.szyprowski@samsung.com> ++ * Author: Josh Cartwright <joshc@codeaurora.org> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License or (at your optional) any later version of the license. ++ */ ++ ++#include <linux/err.h> ++#include <linux/of.h> ++#include <linux/of_fdt.h> ++#include <linux/of_platform.h> ++#include <linux/mm.h> ++#include <linux/sizes.h> ++#include <linux/of_reserved_mem.h> ++ ++#define MAX_RESERVED_REGIONS 16 ++static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; ++static int reserved_mem_count; ++ ++#if defined(CONFIG_HAVE_MEMBLOCK) ++#include <linux/memblock.h> ++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, ++ phys_addr_t *res_base) ++{ ++ /* ++ * We use __memblock_alloc_base() because memblock_alloc_base() ++ * panic()s on allocation failure. ++ */ ++ phys_addr_t base = __memblock_alloc_base(size, align, end); ++ if (!base) ++ return -ENOMEM; ++ ++ /* ++ * Check if the allocated region fits in to start..end window ++ */ ++ if (base < start) { ++ memblock_free(base, size); ++ return -ENOMEM; ++ } ++ ++ *res_base = base; ++ if (nomap) ++ return memblock_remove(base, size); ++ return 0; ++} ++#else ++int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, ++ phys_addr_t *res_base) ++{ ++ pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", ++ size, nomap ? " (nomap)" : ""); ++ return -ENOSYS; ++} ++#endif ++ ++/** ++ * res_mem_save_node() - save fdt node for second pass initialization ++ */ ++void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, ++ phys_addr_t base, phys_addr_t size) ++{ ++ struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; ++ ++ if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { ++ pr_err("Reserved memory: not enough space all defined regions.\n"); ++ return; ++ } ++ ++ rmem->fdt_node = node; ++ rmem->name = uname; ++ rmem->base = base; ++ rmem->size = size; ++ ++ reserved_mem_count++; ++ return; ++} ++ ++/** ++ * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' ++ * and 'alloc-ranges' properties ++ */ ++static int __init __reserved_mem_alloc_size(unsigned long node, ++ const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) ++{ ++ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); ++ phys_addr_t start = 0, end = 0; ++ phys_addr_t base = 0, align = 0, size; ++ unsigned long len; ++ __be32 *prop; ++ int nomap; ++ int ret; ++ ++ prop = of_get_flat_dt_prop(node, "size", &len); ++ if (!prop) ++ return -EINVAL; ++ ++ if (len != dt_root_size_cells * sizeof(__be32)) { ++ pr_err("Reserved memory: invalid size property in '%s' node.\n", ++ uname); ++ return -EINVAL; ++ } ++ size = dt_mem_next_cell(dt_root_size_cells, &prop); ++ ++ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; ++ ++ prop = of_get_flat_dt_prop(node, "alignment", &len); ++ if (prop) { ++ if (len != dt_root_addr_cells * sizeof(__be32)) { ++ pr_err("Reserved memory: invalid alignment property in '%s' node.\n", ++ uname); ++ return -EINVAL; ++ } ++ align = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ } ++ ++ prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); ++ if (prop) { ++ ++ if (len % t_len != 0) { ++ pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", ++ uname); ++ return -EINVAL; ++ } ++ ++ base = 0; ++ ++ while (len > 0) { ++ start = dt_mem_next_cell(dt_root_addr_cells, &prop); ++ end = start + dt_mem_next_cell(dt_root_size_cells, ++ &prop); ++ ++ ret = early_init_dt_alloc_reserved_memory_arch(size, ++ align, start, end, nomap, &base); ++ if (ret == 0) { ++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", ++ uname, &base, ++ (unsigned long)size / SZ_1M); ++ break; ++ } ++ len -= t_len; ++ } ++ ++ } else { ++ ret = early_init_dt_alloc_reserved_memory_arch(size, align, ++ 0, 0, nomap, &base); ++ if (ret == 0) ++ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", ++ uname, &base, (unsigned long)size / SZ_1M); ++ } ++ ++ if (base == 0) { ++ pr_info("Reserved memory: failed to allocate memory for node '%s'\n", ++ uname); ++ return -ENOMEM; ++ } ++ ++ *res_base = base; ++ *res_size = size; ++ ++ return 0; ++} ++ ++/** ++ * fdt_init_reserved_mem - allocate and init all saved reserved memory regions ++ */ ++void __init fdt_init_reserved_mem(void) ++{ ++ int i; ++ for (i = 0; i < reserved_mem_count; i++) { ++ struct reserved_mem *rmem = &reserved_mem[i]; ++ unsigned long node = rmem->fdt_node; ++ int err = 0; ++ ++ if (rmem->size == 0) ++ err = __reserved_mem_alloc_size(node, rmem->name, ++ &rmem->base, &rmem->size); ++ } ++} +diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h +new file mode 100644 +index 0000000..89226ed +--- /dev/null ++++ b/include/linux/of_reserved_mem.h +@@ -0,0 +1,21 @@ ++#ifndef __OF_RESERVED_MEM_H ++#define __OF_RESERVED_MEM_H ++ ++struct reserved_mem { ++ const char *name; ++ unsigned long fdt_node; ++ phys_addr_t base; ++ phys_addr_t size; ++}; ++ ++#ifdef CONFIG_OF_RESERVED_MEM ++void fdt_init_reserved_mem(void); ++void fdt_reserved_mem_save_node(unsigned long node, const char *uname, ++ phys_addr_t base, phys_addr_t size); ++#else ++static inline void fdt_init_reserved_mem(void) { } ++static inline void fdt_reserved_mem_save_node(unsigned long node, ++ const char *uname, phys_addr_t base, phys_addr_t size) { } ++#endif ++ ++#endif /* __OF_RESERVED_MEM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch new file mode 100644 index 0000000000..ce01a8f084 --- /dev/null +++ b/target/linux/ipq806x/patches/0051-drivers-of-add-support-for-custom-reserved-memory-dr.patch @@ -0,0 +1,156 @@ +From fc9e25ed9db2fcfbf16a9cbbf5a1f449da78f1d9 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:49 +0100 +Subject: [PATCH 051/182] drivers: of: add support for custom reserved memory + drivers + +Add support for custom reserved memory drivers. Call their init() function +for each reserved region and prepare for using operations provided by them +with by the reserved_mem->ops array. + +Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/of_reserved_mem.c | 29 +++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 11 +++++++++++ + include/linux/of_reserved_mem.h | 32 ++++++++++++++++++++++++++++++++ + 3 files changed, 72 insertions(+) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index 69b8117..daaaf93 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -170,6 +170,33 @@ static int __init __reserved_mem_alloc_size(unsigned long node, + return 0; + } + ++static const struct of_device_id __rmem_of_table_sentinel ++ __used __section(__reservedmem_of_table_end); ++ ++/** ++ * res_mem_init_node() - call region specific reserved memory init code ++ */ ++static int __init __reserved_mem_init_node(struct reserved_mem *rmem) ++{ ++ extern const struct of_device_id __reservedmem_of_table[]; ++ const struct of_device_id *i; ++ ++ for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { ++ reservedmem_of_init_fn initfn = i->data; ++ const char *compat = i->compatible; ++ ++ if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) ++ continue; ++ ++ if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { ++ pr_info("Reserved memory: initialized node %s, compatible id %s\n", ++ rmem->name, compat); ++ return 0; ++ } ++ } ++ return -ENOENT; ++} ++ + /** + * fdt_init_reserved_mem - allocate and init all saved reserved memory regions + */ +@@ -184,5 +211,7 @@ void __init fdt_init_reserved_mem(void) + if (rmem->size == 0) + err = __reserved_mem_alloc_size(node, rmem->name, + &rmem->base, &rmem->size); ++ if (err == 0) ++ __reserved_mem_init_node(rmem); + } + } +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index bd02ca7..146e4ff 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -167,6 +167,16 @@ + #define CLK_OF_TABLES() + #endif + ++#ifdef CONFIG_OF_RESERVED_MEM ++#define RESERVEDMEM_OF_TABLES() \ ++ . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ ++ *(__reservedmem_of_table) \ ++ *(__reservedmem_of_table_end) ++#else ++#define RESERVEDMEM_OF_TABLES() ++#endif ++ + #ifdef CONFIG_SMP + #define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ + VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \ +@@ -499,6 +509,7 @@ + TRACE_SYSCALLS() \ + MEM_DISCARD(init.rodata) \ + CLK_OF_TABLES() \ ++ RESERVEDMEM_OF_TABLES() \ + CLKSRC_OF_TABLES() \ + CPU_METHOD_OF_TABLES() \ + KERNEL_DTB() \ +diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h +index 89226ed..9b1fbb7 100644 +--- a/include/linux/of_reserved_mem.h ++++ b/include/linux/of_reserved_mem.h +@@ -1,21 +1,53 @@ + #ifndef __OF_RESERVED_MEM_H + #define __OF_RESERVED_MEM_H + ++struct device; ++struct of_phandle_args; ++struct reserved_mem_ops; ++ + struct reserved_mem { + const char *name; + unsigned long fdt_node; ++ const struct reserved_mem_ops *ops; + phys_addr_t base; + phys_addr_t size; ++ void *priv; ++}; ++ ++struct reserved_mem_ops { ++ void (*device_init)(struct reserved_mem *rmem, ++ struct device *dev); ++ void (*device_release)(struct reserved_mem *rmem, ++ struct device *dev); + }; + ++typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, ++ unsigned long node, const char *uname); ++ + #ifdef CONFIG_OF_RESERVED_MEM + void fdt_init_reserved_mem(void); + void fdt_reserved_mem_save_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size); ++ ++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ ++ static const struct of_device_id __reservedmem_of_table_##name \ ++ __used __section(__reservedmem_of_table) \ ++ = { .compatible = compat, \ ++ .data = (init == (reservedmem_of_init_fn)NULL) ? \ ++ init : init } ++ + #else + static inline void fdt_init_reserved_mem(void) { } + static inline void fdt_reserved_mem_save_node(unsigned long node, + const char *uname, phys_addr_t base, phys_addr_t size) { } ++ ++#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ ++ static const struct of_device_id __reservedmem_of_table_##name \ ++ __attribute__((unused)) \ ++ = { .compatible = compat, \ ++ .data = (init == (reservedmem_of_init_fn)NULL) ? \ ++ init : init } ++ + #endif + + #endif /* __OF_RESERVED_MEM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch new file mode 100644 index 0000000000..8ff56329e0 --- /dev/null +++ b/target/linux/ipq806x/patches/0052-arm-add-support-for-reserved-memory-defined-by-devic.patch @@ -0,0 +1,43 @@ +From 95c831bca9181df62230171fd66286d08ea886a8 Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Date: Fri, 28 Feb 2014 14:42:54 +0100 +Subject: [PATCH 052/182] arm: add support for reserved memory defined by + device tree + +Enable reserved memory initialization from device tree. + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + arch/arm/Kconfig | 1 + + arch/arm/mm/init.c | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index d02ce70..4332e8d 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1922,6 +1922,7 @@ config USE_OF + select IRQ_DOMAIN + select OF + select OF_EARLY_FLATTREE ++ select OF_RESERVED_MEM + help + Include support for flattened device tree machine descriptions. + +diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c +index 804d615..2a77ba8 100644 +--- a/arch/arm/mm/init.c ++++ b/arch/arm/mm/init.c +@@ -323,6 +323,8 @@ void __init arm_memblock_init(struct meminfo *mi, + if (mdesc->reserve) + mdesc->reserve(); + ++ early_init_fdt_scan_reserved_mem(); ++ + /* + * reserve memory for DMA contigouos allocations, + * must come from DMA area inside low memory +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch new file mode 100644 index 0000000000..b499e17551 --- /dev/null +++ b/target/linux/ipq806x/patches/0053-of-document-bindings-for-reserved-memory-nodes.patch @@ -0,0 +1,164 @@ +From 060a8e3db75ef98fb296b03b8fb0cdfcbcc76c6e Mon Sep 17 00:00:00 2001 +From: Grant Likely <grant.likely@linaro.org> +Date: Fri, 28 Feb 2014 14:42:46 +0100 +Subject: [PATCH 053/182] of: document bindings for reserved-memory nodes + +Reserved memory nodes allow for the reservation of static (fixed +address) regions, or dynamically allocated regions for a specific +purpose. + +[joshc: Based on binding document proposed (in non-patch form) here: + http://lkml.kernel.org/g/20131030134702.19B57C402A0@trevor.secretlab.ca + adapted to support #memory-region-cells] +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +[mszyprow: removed #memory-region-cells property] +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +[grant.likely: removed residual #memory-region-cells example] +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + .../bindings/reserved-memory/reserved-memory.txt | 133 ++++++++++++++++++++ + 1 file changed, 133 insertions(+) + create mode 100644 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + +diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +new file mode 100644 +index 0000000..3da0ebd +--- /dev/null ++++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +@@ -0,0 +1,133 @@ ++*** Reserved memory regions *** ++ ++Reserved memory is specified as a node under the /reserved-memory node. ++The operating system shall exclude reserved memory from normal usage ++one can create child nodes describing particular reserved (excluded from ++normal use) memory regions. Such memory regions are usually designed for ++the special usage by various device drivers. ++ ++Parameters for each memory region can be encoded into the device tree ++with the following nodes: ++ ++/reserved-memory node ++--------------------- ++#address-cells, #size-cells (required) - standard definition ++ - Should use the same values as the root node ++ranges (required) - standard definition ++ - Should be empty ++ ++/reserved-memory/ child nodes ++----------------------------- ++Each child of the reserved-memory node specifies one or more regions of ++reserved memory. Each child node may either use a 'reg' property to ++specify a specific range of reserved memory, or a 'size' property with ++optional constraints to request a dynamically allocated block of memory. ++ ++Following the generic-names recommended practice, node names should ++reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit ++address (@<address>) should be appended to the name if the node is a ++static allocation. ++ ++Properties: ++Requires either a) or b) below. ++a) static allocation ++ reg (required) - standard definition ++b) dynamic allocation ++ size (required) - length based on parent's #size-cells ++ - Size in bytes of memory to reserve. ++ alignment (optional) - length based on parent's #size-cells ++ - Address boundary for alignment of allocation. ++ alloc-ranges (optional) - prop-encoded-array (address, length pairs). ++ - Specifies regions of memory that are ++ acceptable to allocate from. ++ ++If both reg and size are present, then the reg property takes precedence ++and size is ignored. ++ ++Additional properties: ++compatible (optional) - standard definition ++ - may contain the following strings: ++ - shared-dma-pool: This indicates a region of memory meant to be ++ used as a shared pool of DMA buffers for a set of devices. It can ++ be used by an operating system to instanciate the necessary pool ++ management subsystem if necessary. ++ - vendor specific string in the form <vendor>,[<device>-]<usage> ++no-map (optional) - empty property ++ - Indicates the operating system must not create a virtual mapping ++ of the region as part of its standard mapping of system memory, ++ nor permit speculative access to it under any circumstances other ++ than under the control of the device driver using the region. ++reusable (optional) - empty property ++ - The operating system can use the memory in this region with the ++ limitation that the device driver(s) owning the region need to be ++ able to reclaim it back. Typically that means that the operating ++ system can use that region to store volatile or cached data that ++ can be otherwise regenerated or migrated elsewhere. ++ ++Linux implementation note: ++- If a "linux,cma-default" property is present, then Linux will use the ++ region for the default pool of the contiguous memory allocator. ++ ++Device node references to reserved memory ++----------------------------------------- ++Regions in the /reserved-memory node may be referenced by other device ++nodes by adding a memory-region property to the device node. ++ ++memory-region (optional) - phandle, specifier pairs to children of /reserved-memory ++ ++Example ++------- ++This example defines 3 contiguous regions are defined for Linux kernel: ++one default of all device drivers (named linux,cma@72000000 and 64MiB in size), ++one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and ++one for multimedia processing (named multimedia-memory@77000000, 64MiB). ++ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ memory { ++ reg = <0x40000000 0x40000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ /* global autoconfigured region for contiguous allocations */ ++ linux,cma { ++ compatible = "shared-dma-pool"; ++ reusable; ++ size = <0x4000000>; ++ alignment = <0x2000>; ++ linux,cma-default; ++ }; ++ ++ display_reserved: framebuffer@78000000 { ++ reg = <0x78000000 0x800000>; ++ }; ++ ++ multimedia_reserved: multimedia@77000000 { ++ compatible = "acme,multimedia-memory"; ++ reg = <0x77000000 0x4000000>; ++ }; ++ }; ++ ++ /* ... */ ++ ++ fb0: video@12300000 { ++ memory-region = <&display_reserved>; ++ /* ... */ ++ }; ++ ++ scaler: scaler@12500000 { ++ memory-region = <&multimedia_reserved>; ++ /* ... */ ++ }; ++ ++ codec: codec@12600000 { ++ memory-region = <&multimedia_reserved>; ++ /* ... */ ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch new file mode 100644 index 0000000000..a5423d8446 --- /dev/null +++ b/target/linux/ipq806x/patches/0054-of-only-scan-for-reserved-mem-when-fdt-present.patch @@ -0,0 +1,35 @@ +From 884ab569aec3a0847702dff0dad133bfb67e234c Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Thu, 13 Mar 2014 16:36:36 -0500 +Subject: [PATCH 054/182] of: only scan for reserved mem when fdt present + +When the reserved memory patches hit -next, several legacy (non-DT) boot +failures were detected and bisected down to that commit. There needs to +be some sanity checking whether a DT is even present before parsing the +reserved ranges. + +Reported-by: Kevin Hilman <khilman@linaro.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Tested-by: Kevin Hilman <khilman@linaro.org> +Signed-off-by: Grant Likely <grant.likely@linaro.org> +--- + drivers/of/fdt.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 510c0d8..501bc83 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -557,6 +557,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, + */ + void __init early_init_fdt_scan_reserved_mem(void) + { ++ if (!initial_boot_params) ++ return; ++ + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); + fdt_init_reserved_mem(); + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch new file mode 100644 index 0000000000..8b5943894d --- /dev/null +++ b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch @@ -0,0 +1,943 @@ +From 1b0018dfd6295cbcc87738601b84bf49f3004419 Mon Sep 17 00:00:00 2001 +From: Kenneth Heitke <kheitke@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:22 -0600 +Subject: [PATCH 055/182] spmi: Linux driver framework for SPMI + +System Power Management Interface (SPMI) is a specification +developed by the MIPI (Mobile Industry Process Interface) Alliance +optimized for the real time control of Power Management ICs (PMIC). + +SPMI is a two-wire serial interface that supports up to 4 master +devices and up to 16 logical slaves. + +The framework supports message APIs, multiple busses (1 controller +per bus) and multiple clients/slave devices per controller. + +Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> +Signed-off-by: Michael Bohan <mbohan@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/spmi/Kconfig | 9 + + drivers/spmi/Makefile | 4 + + drivers/spmi/spmi.c | 609 +++++++++++++++++++++++++++++++++++++++ + include/dt-bindings/spmi/spmi.h | 18 ++ + include/linux/mod_devicetable.h | 8 + + include/linux/spmi.h | 191 ++++++++++++ + 8 files changed, 842 insertions(+) + create mode 100644 drivers/spmi/Kconfig + create mode 100644 drivers/spmi/Makefile + create mode 100644 drivers/spmi/spmi.c + create mode 100644 include/dt-bindings/spmi/spmi.h + create mode 100644 include/linux/spmi.h + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index b3138fb..e0a4ae6 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" + + source "drivers/spi/Kconfig" + ++source "drivers/spmi/Kconfig" ++ + source "drivers/hsi/Kconfig" + + source "drivers/pps/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 8e3b8b0..3d6de8b 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/ + obj-$(CONFIG_TARGET_CORE) += target/ + obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ ++obj-$(CONFIG_SPMI) += spmi/ + obj-y += hsi/ + obj-y += net/ + obj-$(CONFIG_ATM) += atm/ +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +new file mode 100644 +index 0000000..1dbfee0 +--- /dev/null ++++ b/drivers/spmi/Kconfig +@@ -0,0 +1,9 @@ ++# ++# SPMI driver configuration ++# ++menuconfig SPMI ++ tristate "SPMI support" ++ help ++ SPMI (System Power Management Interface) is a two-wire ++ serial interface between baseband and application processors ++ and Power Management Integrated Circuits (PMIC). +diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile +new file mode 100644 +index 0000000..1de1acd +--- /dev/null ++++ b/drivers/spmi/Makefile +@@ -0,0 +1,4 @@ ++# ++# Makefile for kernel SPMI framework. ++# ++obj-$(CONFIG_SPMI) += spmi.o +diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c +new file mode 100644 +index 0000000..6122c8f +--- /dev/null ++++ b/drivers/spmi/spmi.c +@@ -0,0 +1,609 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/idr.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/spmi.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++ ++#include <dt-bindings/spmi/spmi.h> ++ ++static DEFINE_IDA(ctrl_ida); ++ ++static void spmi_dev_release(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ kfree(sdev); ++} ++ ++static const struct device_type spmi_dev_type = { ++ .release = spmi_dev_release, ++}; ++ ++static void spmi_ctrl_release(struct device *dev) ++{ ++ struct spmi_controller *ctrl = to_spmi_controller(dev); ++ ida_simple_remove(&ctrl_ida, ctrl->nr); ++ kfree(ctrl); ++} ++ ++static const struct device_type spmi_ctrl_type = { ++ .release = spmi_ctrl_release, ++}; ++ ++#ifdef CONFIG_PM_RUNTIME ++static int spmi_runtime_suspend(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = pm_generic_runtime_suspend(dev); ++ if (err) ++ return err; ++ ++ return spmi_command_sleep(sdev); ++} ++ ++static int spmi_runtime_resume(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ return err; ++ ++ return pm_generic_runtime_resume(dev); ++} ++#endif ++ ++static const struct dev_pm_ops spmi_pm_ops = { ++ SET_RUNTIME_PM_OPS( ++ spmi_runtime_suspend, ++ spmi_runtime_resume, ++ NULL ++ ) ++}; ++ ++static int spmi_device_match(struct device *dev, struct device_driver *drv) ++{ ++ if (of_driver_match_device(dev, drv)) ++ return 1; ++ ++ if (drv->name) ++ return strncmp(dev_name(dev), drv->name, ++ SPMI_NAME_SIZE) == 0; ++ ++ return 0; ++} ++ ++/** ++ * spmi_device_add() - add a device previously constructed via spmi_device_alloc() ++ * @sdev: spmi_device to be added ++ */ ++int spmi_device_add(struct spmi_device *sdev) ++{ ++ struct spmi_controller *ctrl = sdev->ctrl; ++ int err; ++ ++ dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); ++ ++ err = device_add(&sdev->dev); ++ if (err < 0) { ++ dev_err(&sdev->dev, "Can't add %s, status %d\n", ++ dev_name(&sdev->dev), err); ++ goto err_device_add; ++ } ++ ++ dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); ++ ++err_device_add: ++ return err; ++} ++EXPORT_SYMBOL_GPL(spmi_device_add); ++ ++/** ++ * spmi_device_remove(): remove an SPMI device ++ * @sdev: spmi_device to be removed ++ */ ++void spmi_device_remove(struct spmi_device *sdev) ++{ ++ device_unregister(&sdev->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_device_remove); ++ ++static inline int ++spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) ++{ ++ if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->cmd(ctrl, opcode, sid); ++} ++ ++static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++/** ++ * spmi_register_read() - register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (5-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * ++ * Reads 1 byte of data from a Slave device register. ++ */ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr, ++ buf, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_read); ++ ++/** ++ * spmi_ext_register_read() - extended register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Reads up to 16 bytes of data from the extended register space on a ++ * Slave device. ++ */ ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_read); ++ ++/** ++ * spmi_ext_register_readl() - extended register read long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Reads up to 8 bytes of data from the extended register space on a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len) ++{ ++ /* 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_readl); ++ ++/** ++ * spmi_register_write() - register write ++ * @sdev: SPMI device ++ * @addr: slave register address (5-bit address). ++ * @data: buffer containing the data to be transferred to the Slave. ++ * ++ * Writes 1 byte of data to a Slave device register. ++ */ ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_write); ++ ++/** ++ * spmi_register_zero_write() - register zero write ++ * @sdev: SPMI device. ++ * @data: the data to be written to register 0 (7-bits). ++ * ++ * Writes data to register 0 of the Slave device. ++ */ ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data) ++{ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_zero_write); ++ ++/** ++ * spmi_ext_register_write() - extended register write ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Writes up to 16 bytes of data to the extended register space of a ++ * Slave device. ++ */ ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_write); ++ ++/** ++ * spmi_ext_register_writel() - extended register write long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Writes up to 8 bytes of data to the extended register space of a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid, ++ addr, buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_writel); ++ ++/** ++ * spmi_command_reset() - sends RESET command to the specified slave ++ * @sdev: SPMI device. ++ * ++ * The Reset command initializes the Slave and forces all registers to ++ * their reset values. The Slave shall enter the STARTUP state after ++ * receiving a Reset command. ++ */ ++int spmi_command_reset(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_reset); ++ ++/** ++ * spmi_command_sleep() - sends SLEEP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Sleep command causes the Slave to enter the user defined SLEEP state. ++ */ ++int spmi_command_sleep(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_sleep); ++ ++/** ++ * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Wakeup command causes the Slave to move from the SLEEP state to ++ * the ACTIVE state. ++ */ ++int spmi_command_wakeup(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_wakeup); ++ ++/** ++ * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Shutdown command causes the Slave to enter the SHUTDOWN state. ++ */ ++int spmi_command_shutdown(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_shutdown); ++ ++static int spmi_drv_probe(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ /* Ensure the slave is in ACTIVE state */ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ goto fail_wakeup; ++ ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ err = sdrv->probe(sdev); ++ if (err) ++ goto fail_probe; ++ ++ return 0; ++ ++fail_probe: ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++fail_wakeup: ++ return err; ++} ++ ++static int spmi_drv_remove(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ ++ pm_runtime_get_sync(dev); ++ sdrv->remove(to_spmi_device(dev)); ++ pm_runtime_put_noidle(dev); ++ ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ return 0; ++} ++ ++static struct bus_type spmi_bus_type = { ++ .name = "spmi", ++ .match = spmi_device_match, ++ .pm = &spmi_pm_ops, ++ .probe = spmi_drv_probe, ++ .remove = spmi_drv_remove, ++}; ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI device ++ * @ctrl: associated controller ++ * ++ * Caller is responsible for either calling spmi_device_add() to add the ++ * newly allocated controller, or calling spmi_device_put() to discard it. ++ */ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) ++{ ++ struct spmi_device *sdev; ++ ++ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); ++ if (!sdev) ++ return NULL; ++ ++ sdev->ctrl = ctrl; ++ device_initialize(&sdev->dev); ++ sdev->dev.parent = &ctrl->dev; ++ sdev->dev.bus = &spmi_bus_type; ++ sdev->dev.type = &spmi_dev_type; ++ return sdev; ++} ++EXPORT_SYMBOL_GPL(spmi_device_alloc); ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI controller ++ * @parent: parent device ++ * @size: size of private data ++ * ++ * Caller is responsible for either calling spmi_controller_add() to add the ++ * newly allocated controller, or calling spmi_controller_put() to discard it. ++ * The allocated private data region may be accessed via ++ * spmi_controller_get_drvdata() ++ */ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size) ++{ ++ struct spmi_controller *ctrl; ++ int id; ++ ++ if (WARN_ON(!parent)) ++ return NULL; ++ ++ ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); ++ if (!ctrl) ++ return NULL; ++ ++ device_initialize(&ctrl->dev); ++ ctrl->dev.type = &spmi_ctrl_type; ++ ctrl->dev.bus = &spmi_bus_type; ++ ctrl->dev.parent = parent; ++ ctrl->dev.of_node = parent->of_node; ++ spmi_controller_set_drvdata(ctrl, &ctrl[1]); ++ ++ id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); ++ if (id < 0) { ++ dev_err(parent, ++ "unable to allocate SPMI controller identifier.\n"); ++ spmi_controller_put(ctrl); ++ return NULL; ++ } ++ ++ ctrl->nr = id; ++ dev_set_name(&ctrl->dev, "spmi-%d", id); ++ ++ dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); ++ return ctrl; ++} ++EXPORT_SYMBOL_GPL(spmi_controller_alloc); ++ ++static void of_spmi_register_devices(struct spmi_controller *ctrl) ++{ ++ struct device_node *node; ++ int err; ++ ++ if (!ctrl->dev.of_node) ++ return; ++ ++ for_each_available_child_of_node(ctrl->dev.of_node, node) { ++ struct spmi_device *sdev; ++ u32 reg[2]; ++ ++ dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); ++ ++ err = of_property_read_u32_array(node, "reg", reg, 2); ++ if (err) { ++ dev_err(&ctrl->dev, ++ "node %s err (%d) does not have 'reg' property\n", ++ node->full_name, err); ++ continue; ++ } ++ ++ if (reg[1] != SPMI_USID) { ++ dev_err(&ctrl->dev, ++ "node %s contains unsupported 'reg' entry\n", ++ node->full_name); ++ continue; ++ } ++ ++ if (reg[0] >= SPMI_MAX_SLAVE_ID) { ++ dev_err(&ctrl->dev, ++ "invalid usid on node %s\n", ++ node->full_name); ++ continue; ++ } ++ ++ dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]); ++ ++ sdev = spmi_device_alloc(ctrl); ++ if (!sdev) ++ continue; ++ ++ sdev->dev.of_node = node; ++ sdev->usid = (u8) reg[0]; ++ ++ err = spmi_device_add(sdev); ++ if (err) { ++ dev_err(&sdev->dev, ++ "failure adding device. status %d\n", err); ++ spmi_device_put(sdev); ++ } ++ } ++} ++ ++/** ++ * spmi_controller_add() - Add an SPMI controller ++ * @ctrl: controller to be registered. ++ * ++ * Register a controller previously allocated via spmi_controller_alloc() with ++ * the SPMI core. ++ */ ++int spmi_controller_add(struct spmi_controller *ctrl) ++{ ++ int ret; ++ ++ /* Can't register until after driver model init */ ++ if (WARN_ON(!spmi_bus_type.p)) ++ return -EAGAIN; ++ ++ ret = device_add(&ctrl->dev); ++ if (ret) ++ return ret; ++ ++ if (IS_ENABLED(CONFIG_OF)) ++ of_spmi_register_devices(ctrl); ++ ++ dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n", ++ ctrl->nr, &ctrl->dev); ++ ++ return 0; ++}; ++EXPORT_SYMBOL_GPL(spmi_controller_add); ++ ++/* Remove a device associated with a controller */ ++static int spmi_ctrl_remove_device(struct device *dev, void *data) ++{ ++ struct spmi_device *spmidev = to_spmi_device(dev); ++ if (dev->type == &spmi_dev_type) ++ spmi_device_remove(spmidev); ++ return 0; ++} ++ ++/** ++ * spmi_controller_remove(): remove an SPMI controller ++ * @ctrl: controller to remove ++ * ++ * Remove a SPMI controller. Caller is responsible for calling ++ * spmi_controller_put() to discard the allocated controller. ++ */ ++void spmi_controller_remove(struct spmi_controller *ctrl) ++{ ++ int dummy; ++ ++ if (!ctrl) ++ return; ++ ++ dummy = device_for_each_child(&ctrl->dev, NULL, ++ spmi_ctrl_remove_device); ++ device_del(&ctrl->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_controller_remove); ++ ++/** ++ * spmi_driver_register() - Register client driver with SPMI core ++ * @sdrv: client driver to be associated with client-device. ++ * ++ * This API will register the client driver with the SPMI framework. ++ * It is typically called from the driver's module-init function. ++ */ ++int spmi_driver_register(struct spmi_driver *sdrv) ++{ ++ sdrv->driver.bus = &spmi_bus_type; ++ return driver_register(&sdrv->driver); ++} ++EXPORT_SYMBOL_GPL(spmi_driver_register); ++ ++static void __exit spmi_exit(void) ++{ ++ bus_unregister(&spmi_bus_type); ++} ++module_exit(spmi_exit); ++ ++static int __init spmi_init(void) ++{ ++ return bus_register(&spmi_bus_type); ++} ++postcore_initcall(spmi_init); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("SPMI module"); ++MODULE_ALIAS("platform:spmi"); +diff --git a/include/dt-bindings/spmi/spmi.h b/include/dt-bindings/spmi/spmi.h +new file mode 100644 +index 0000000..d11e1e5 +--- /dev/null ++++ b/include/dt-bindings/spmi/spmi.h +@@ -0,0 +1,18 @@ ++/* Copyright (c) 2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DT_BINDINGS_SPMI_H ++#define __DT_BINDINGS_SPMI_H ++ ++#define SPMI_USID 0 ++#define SPMI_GSID 1 ++ ++#endif +diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h +index 45e9214..677e474 100644 +--- a/include/linux/mod_devicetable.h ++++ b/include/linux/mod_devicetable.h +@@ -432,6 +432,14 @@ struct spi_device_id { + kernel_ulong_t driver_data; /* Data private to the driver */ + }; + ++#define SPMI_NAME_SIZE 32 ++#define SPMI_MODULE_PREFIX "spmi:" ++ ++struct spmi_device_id { ++ char name[SPMI_NAME_SIZE]; ++ kernel_ulong_t driver_data; /* Data private to the driver */ ++}; ++ + /* dmi */ + enum dmi_field { + DMI_NONE, +diff --git a/include/linux/spmi.h b/include/linux/spmi.h +new file mode 100644 +index 0000000..91f5eab +--- /dev/null ++++ b/include/linux/spmi.h +@@ -0,0 +1,191 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef _LINUX_SPMI_H ++#define _LINUX_SPMI_H ++ ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/mod_devicetable.h> ++ ++/* Maximum slave identifier */ ++#define SPMI_MAX_SLAVE_ID 16 ++ ++/* SPMI Commands */ ++#define SPMI_CMD_EXT_WRITE 0x00 ++#define SPMI_CMD_RESET 0x10 ++#define SPMI_CMD_SLEEP 0x11 ++#define SPMI_CMD_SHUTDOWN 0x12 ++#define SPMI_CMD_WAKEUP 0x13 ++#define SPMI_CMD_AUTHENTICATE 0x14 ++#define SPMI_CMD_MSTR_READ 0x15 ++#define SPMI_CMD_MSTR_WRITE 0x16 ++#define SPMI_CMD_TRANSFER_BUS_OWNERSHIP 0x1A ++#define SPMI_CMD_DDB_MASTER_READ 0x1B ++#define SPMI_CMD_DDB_SLAVE_READ 0x1C ++#define SPMI_CMD_EXT_READ 0x20 ++#define SPMI_CMD_EXT_WRITEL 0x30 ++#define SPMI_CMD_EXT_READL 0x38 ++#define SPMI_CMD_WRITE 0x40 ++#define SPMI_CMD_READ 0x60 ++#define SPMI_CMD_ZERO_WRITE 0x80 ++ ++/** ++ * struct spmi_device - Basic representation of an SPMI device ++ * @dev: Driver model representation of the device. ++ * @ctrl: SPMI controller managing the bus hosting this device. ++ * @usid: This devices' Unique Slave IDentifier. ++ */ ++struct spmi_device { ++ struct device dev; ++ struct spmi_controller *ctrl; ++ u8 usid; ++}; ++ ++static inline struct spmi_device *to_spmi_device(struct device *d) ++{ ++ return container_of(d, struct spmi_device, dev); ++} ++ ++static inline void *spmi_device_get_drvdata(const struct spmi_device *sdev) ++{ ++ return dev_get_drvdata(&sdev->dev); ++} ++ ++static inline void spmi_device_set_drvdata(struct spmi_device *sdev, void *data) ++{ ++ dev_set_drvdata(&sdev->dev, data); ++} ++ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl); ++ ++static inline void spmi_device_put(struct spmi_device *sdev) ++{ ++ if (sdev) ++ put_device(&sdev->dev); ++} ++ ++int spmi_device_add(struct spmi_device *sdev); ++ ++void spmi_device_remove(struct spmi_device *sdev); ++ ++/** ++ * struct spmi_controller - interface to the SPMI master controller ++ * @dev: Driver model representation of the device. ++ * @nr: board-specific number identifier for this controller/bus ++ * @cmd: sends a non-data command sequence on the SPMI bus. ++ * @read_cmd: sends a register read command sequence on the SPMI bus. ++ * @write_cmd: sends a register write command sequence on the SPMI bus. ++ */ ++struct spmi_controller { ++ struct device dev; ++ unsigned int nr; ++ int (*cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid); ++ int (*read_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len); ++ int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len); ++}; ++ ++static inline struct spmi_controller *to_spmi_controller(struct device *d) ++{ ++ return container_of(d, struct spmi_controller, dev); ++} ++ ++static inline ++void *spmi_controller_get_drvdata(const struct spmi_controller *ctrl) ++{ ++ return dev_get_drvdata(&ctrl->dev); ++} ++ ++static inline void spmi_controller_set_drvdata(struct spmi_controller *ctrl, ++ void *data) ++{ ++ dev_set_drvdata(&ctrl->dev, data); ++} ++ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size); ++ ++/** ++ * spmi_controller_put() - decrement controller refcount ++ * @ctrl SPMI controller. ++ */ ++static inline void spmi_controller_put(struct spmi_controller *ctrl) ++{ ++ if (ctrl) ++ put_device(&ctrl->dev); ++} ++ ++int spmi_controller_add(struct spmi_controller *ctrl); ++void spmi_controller_remove(struct spmi_controller *ctrl); ++ ++/** ++ * struct spmi_driver - SPMI slave device driver ++ * @driver: SPMI device drivers should initialize name and owner field of ++ * this structure. ++ * @probe: binds this driver to a SPMI device. ++ * @remove: unbinds this driver from the SPMI device. ++ * @shutdown: standard shutdown callback used during powerdown/halt. ++ * @suspend: standard suspend callback used during system suspend. ++ * @resume: standard resume callback used during system resume. ++ * ++ * If PM runtime support is desired for a slave, a device driver can call ++ * pm_runtime_put() from their probe() routine (and a balancing ++ * pm_runtime_get() in remove()). PM runtime support for a slave is ++ * implemented by issuing a SLEEP command to the slave on runtime_suspend(), ++ * transitioning the slave into the SLEEP state. On runtime_resume(), a WAKEUP ++ * command is sent to the slave to bring it back to ACTIVE. ++ */ ++struct spmi_driver { ++ struct device_driver driver; ++ int (*probe)(struct spmi_device *sdev); ++ void (*remove)(struct spmi_device *sdev); ++}; ++ ++static inline struct spmi_driver *to_spmi_driver(struct device_driver *d) ++{ ++ return container_of(d, struct spmi_driver, driver); ++} ++ ++int spmi_driver_register(struct spmi_driver *sdrv); ++ ++/** ++ * spmi_driver_unregister() - unregister an SPMI client driver ++ * @sdrv: the driver to unregister ++ */ ++static inline void spmi_driver_unregister(struct spmi_driver *sdrv) ++{ ++ if (sdrv) ++ driver_unregister(&sdrv->driver); ++} ++ ++#define module_spmi_driver(__spmi_driver) \ ++ module_driver(__spmi_driver, spmi_driver_register, \ ++ spmi_driver_unregister) ++ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf); ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len); ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len); ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data); ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data); ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, ++ const u8 *buf, size_t len); ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, ++ const u8 *buf, size_t len); ++int spmi_command_reset(struct spmi_device *sdev); ++int spmi_command_sleep(struct spmi_device *sdev); ++int spmi_command_wakeup(struct spmi_device *sdev); ++int spmi_command_shutdown(struct spmi_device *sdev); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch new file mode 100644 index 0000000000..24f841e589 --- /dev/null +++ b/target/linux/ipq806x/patches/0056-spmi-Add-MSM-PMIC-Arbiter-SPMI-controller.patch @@ -0,0 +1,477 @@ +From 1e1f53fbc2848b23af572c16d19e8004f6a7c9c1 Mon Sep 17 00:00:00 2001 +From: Kenneth Heitke <kheitke@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:24 -0600 +Subject: [PATCH 056/182] spmi: Add MSM PMIC Arbiter SPMI controller + +Qualcomm's PMIC Arbiter SPMI controller functions as a bus master and +is used to communication with one or more PMIC (slave) devices on the +SPMI bus. The PMIC Arbiter is actually a hardware wrapper around the +SPMI controller that provides concurrent and autonomous PMIC access +to various entities that need to communicate with the PMIC. + +The SPMI controller hardware handles all of the SPMI bus activity (bus +arbitration, sequence start condition, transmission of frames, etc). +This software driver uses the PMIC Arbiter register interface to +initiate command sequences on the SPMI bus. The status register is +read to determine when the command sequence has completed and whether +or not it completed successfully. + +Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 17 ++ + drivers/spmi/Makefile | 2 + + drivers/spmi/spmi-pmic-arb.c | 405 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 424 insertions(+) + create mode 100644 drivers/spmi/spmi-pmic-arb.c + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 1dbfee0..80b7901 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -7,3 +7,20 @@ menuconfig SPMI + SPMI (System Power Management Interface) is a two-wire + serial interface between baseband and application processors + and Power Management Integrated Circuits (PMIC). ++ ++if SPMI ++ ++config SPMI_MSM_PMIC_ARB ++ tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" ++ depends on ARM ++ depends on ARCH_MSM || COMPILE_TEST ++ default ARCH_MSM ++ help ++ If you say yes to this option, support will be included for the ++ built-in SPMI PMIC Arbiter interface on Qualcomm MSM family ++ processors. ++ ++ This is required for communicating with Qualcomm PMICs and ++ other devices that have the SPMI interface. ++ ++endif +diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile +index 1de1acd..fc75104 100644 +--- a/drivers/spmi/Makefile ++++ b/drivers/spmi/Makefile +@@ -2,3 +2,5 @@ + # Makefile for kernel SPMI framework. + # + obj-$(CONFIG_SPMI) += spmi.o ++ ++obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o +diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c +new file mode 100644 +index 0000000..2dd27e8 +--- /dev/null ++++ b/drivers/spmi/spmi-pmic-arb.c +@@ -0,0 +1,405 @@ ++/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spmi.h> ++ ++/* PMIC Arbiter configuration registers */ ++#define PMIC_ARB_VERSION 0x0000 ++#define PMIC_ARB_INT_EN 0x0004 ++ ++/* PMIC Arbiter channel registers */ ++#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) ++#define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N))) ++#define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N))) ++#define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N))) ++#define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N))) ++#define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N))) ++#define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N))) ++ ++/* Interrupt Controller */ ++#define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N)))) ++#define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N))) ++#define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N))) ++#define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N))) ++ ++/* Mapping Table */ ++#define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) ++#define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) ++#define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) ++#define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) ++#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) ++#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) ++ ++#define SPMI_MAPPING_TABLE_LEN 255 ++#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ ++ ++/* Ownership Table */ ++#define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) ++#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) ++ ++/* Channel Status fields */ ++enum pmic_arb_chnl_status { ++ PMIC_ARB_STATUS_DONE = (1 << 0), ++ PMIC_ARB_STATUS_FAILURE = (1 << 1), ++ PMIC_ARB_STATUS_DENIED = (1 << 2), ++ PMIC_ARB_STATUS_DROPPED = (1 << 3), ++}; ++ ++/* Command register fields */ ++#define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 ++ ++/* Command Opcodes */ ++enum pmic_arb_cmd_op_code { ++ PMIC_ARB_OP_EXT_WRITEL = 0, ++ PMIC_ARB_OP_EXT_READL = 1, ++ PMIC_ARB_OP_EXT_WRITE = 2, ++ PMIC_ARB_OP_RESET = 3, ++ PMIC_ARB_OP_SLEEP = 4, ++ PMIC_ARB_OP_SHUTDOWN = 5, ++ PMIC_ARB_OP_WAKEUP = 6, ++ PMIC_ARB_OP_AUTHENTICATE = 7, ++ PMIC_ARB_OP_MSTR_READ = 8, ++ PMIC_ARB_OP_MSTR_WRITE = 9, ++ PMIC_ARB_OP_EXT_READ = 13, ++ PMIC_ARB_OP_WRITE = 14, ++ PMIC_ARB_OP_READ = 15, ++ PMIC_ARB_OP_ZERO_WRITE = 16, ++}; ++ ++/* Maximum number of support PMIC peripherals */ ++#define PMIC_ARB_MAX_PERIPHS 256 ++#define PMIC_ARB_PERIPH_ID_VALID (1 << 15) ++#define PMIC_ARB_TIMEOUT_US 100 ++#define PMIC_ARB_MAX_TRANS_BYTES (8) ++ ++#define PMIC_ARB_APID_MASK 0xFF ++#define PMIC_ARB_PPID_MASK 0xFFF ++ ++/* interrupt enable bit */ ++#define SPMI_PIC_ACC_ENABLE_BIT BIT(0) ++ ++/** ++ * spmi_pmic_arb_dev - SPMI PMIC Arbiter object ++ * ++ * @base: address of the PMIC Arbiter core registers. ++ * @intr: address of the SPMI interrupt control registers. ++ * @cnfg: address of the PMIC Arbiter configuration registers. ++ * @lock: lock to synchronize accesses. ++ * @channel: which channel to use for accesses. ++ */ ++struct spmi_pmic_arb_dev { ++ void __iomem *base; ++ void __iomem *intr; ++ void __iomem *cnfg; ++ raw_spinlock_t lock; ++ u8 channel; ++}; ++ ++static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) ++{ ++ return readl_relaxed(dev->base + offset); ++} ++ ++static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev, ++ u32 offset, u32 val) ++{ ++ writel_relaxed(val, dev->base + offset); ++} ++ ++/** ++ * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf ++ * @bc: byte count -1. range: 0..3 ++ * @reg: register's address ++ * @buf: output parameter, length must be bc + 1 ++ */ ++static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc) ++{ ++ u32 data = pmic_arb_base_read(dev, reg); ++ memcpy(buf, &data, (bc & 3) + 1); ++} ++ ++/** ++ * pa_write_data: write 1..4 bytes from buf to pmic-arb's register ++ * @bc: byte-count -1. range: 0..3. ++ * @reg: register's address. ++ * @buf: buffer to write. length must be bc + 1. ++ */ ++static void ++pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc) ++{ ++ u32 data = 0; ++ memcpy(&data, buf, (bc & 3) + 1); ++ pmic_arb_base_write(dev, reg, data); ++} ++ ++static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) ++{ ++ struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); ++ u32 status = 0; ++ u32 timeout = PMIC_ARB_TIMEOUT_US; ++ u32 offset = PMIC_ARB_STATUS(dev->channel); ++ ++ while (timeout--) { ++ status = pmic_arb_base_read(dev, offset); ++ ++ if (status & PMIC_ARB_STATUS_DONE) { ++ if (status & PMIC_ARB_STATUS_DENIED) { ++ dev_err(&ctrl->dev, ++ "%s: transaction denied (0x%x)\n", ++ __func__, status); ++ return -EPERM; ++ } ++ ++ if (status & PMIC_ARB_STATUS_FAILURE) { ++ dev_err(&ctrl->dev, ++ "%s: transaction failed (0x%x)\n", ++ __func__, status); ++ return -EIO; ++ } ++ ++ if (status & PMIC_ARB_STATUS_DROPPED) { ++ dev_err(&ctrl->dev, ++ "%s: transaction dropped (0x%x)\n", ++ __func__, status); ++ return -EIO; ++ } ++ ++ return 0; ++ } ++ udelay(1); ++ } ++ ++ dev_err(&ctrl->dev, ++ "%s: timeout, status 0x%x\n", ++ __func__, status); ++ return -ETIMEDOUT; ++} ++ ++/* Non-data command */ ++static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u32 cmd; ++ int rc; ++ ++ /* Check for valid non-data command */ ++ if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) ++ return -EINVAL; ++ ++ cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); ++ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ ++ return rc; ++} ++ ++static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, ++ u16 addr, u8 *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u8 bc = len - 1; ++ u32 cmd; ++ int rc; ++ ++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { ++ dev_err(&ctrl->dev, ++ "pmic-arb supports 1..%d bytes per trans, but %d requested", ++ PMIC_ARB_MAX_TRANS_BYTES, len); ++ return -EINVAL; ++ } ++ ++ /* Check the opcode */ ++ if (opc >= 0x60 && opc <= 0x7F) ++ opc = PMIC_ARB_OP_READ; ++ else if (opc >= 0x20 && opc <= 0x2F) ++ opc = PMIC_ARB_OP_EXT_READ; ++ else if (opc >= 0x38 && opc <= 0x3F) ++ opc = PMIC_ARB_OP_EXT_READL; ++ else ++ return -EINVAL; ++ ++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); ++ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ if (rc) ++ goto done; ++ ++ pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), ++ min_t(u8, bc, 3)); ++ ++ if (bc > 3) ++ pa_read_data(pmic_arb, buf + 4, ++ PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4); ++ ++done: ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ return rc; ++} ++ ++static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, ++ u16 addr, const u8 *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); ++ unsigned long flags; ++ u8 bc = len - 1; ++ u32 cmd; ++ int rc; ++ ++ if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { ++ dev_err(&ctrl->dev, ++ "pmic-arb supports 1..%d bytes per trans, but:%d requested", ++ PMIC_ARB_MAX_TRANS_BYTES, len); ++ return -EINVAL; ++ } ++ ++ /* Check the opcode */ ++ if (opc >= 0x40 && opc <= 0x5F) ++ opc = PMIC_ARB_OP_WRITE; ++ else if (opc >= 0x00 && opc <= 0x0F) ++ opc = PMIC_ARB_OP_EXT_WRITE; ++ else if (opc >= 0x30 && opc <= 0x37) ++ opc = PMIC_ARB_OP_EXT_WRITEL; ++ else if (opc >= 0x80 && opc <= 0xFF) ++ opc = PMIC_ARB_OP_ZERO_WRITE; ++ else ++ return -EINVAL; ++ ++ cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); ++ ++ /* Write data to FIFOs */ ++ raw_spin_lock_irqsave(&pmic_arb->lock, flags); ++ pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel) ++ , min_t(u8, bc, 3)); ++ if (bc > 3) ++ pa_write_data(pmic_arb, buf + 4, ++ PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4); ++ ++ /* Start the transaction */ ++ pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); ++ rc = pmic_arb_wait_for_done(ctrl); ++ raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); ++ ++ return rc; ++} ++ ++static int spmi_pmic_arb_probe(struct platform_device *pdev) ++{ ++ struct spmi_pmic_arb_dev *pa; ++ struct spmi_controller *ctrl; ++ struct resource *res; ++ u32 channel; ++ int err, i; ++ ++ ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ pa = spmi_controller_get_drvdata(ctrl); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); ++ pa->base = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->base)) { ++ err = PTR_ERR(pa->base); ++ goto err_put_ctrl; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); ++ pa->intr = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->intr)) { ++ err = PTR_ERR(pa->intr); ++ goto err_put_ctrl; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); ++ pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); ++ if (IS_ERR(pa->cnfg)) { ++ err = PTR_ERR(pa->cnfg); ++ goto err_put_ctrl; ++ } ++ ++ err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); ++ if (err) { ++ dev_err(&pdev->dev, "channel unspecified.\n"); ++ goto err_put_ctrl; ++ } ++ ++ if (channel > 5) { ++ dev_err(&pdev->dev, "invalid channel (%u) specified.\n", ++ channel); ++ goto err_put_ctrl; ++ } ++ ++ pa->channel = channel; ++ ++ platform_set_drvdata(pdev, ctrl); ++ raw_spin_lock_init(&pa->lock); ++ ++ ctrl->cmd = pmic_arb_cmd; ++ ctrl->read_cmd = pmic_arb_read_cmd; ++ ctrl->write_cmd = pmic_arb_write_cmd; ++ ++ err = spmi_controller_add(ctrl); ++ if (err) ++ goto err_put_ctrl; ++ ++ dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", ++ pmic_arb_base_read(pa, PMIC_ARB_VERSION)); ++ ++ return 0; ++ ++err_put_ctrl: ++ spmi_controller_put(ctrl); ++ return err; ++} ++ ++static int spmi_pmic_arb_remove(struct platform_device *pdev) ++{ ++ struct spmi_controller *ctrl = platform_get_drvdata(pdev); ++ spmi_controller_remove(ctrl); ++ spmi_controller_put(ctrl); ++ return 0; ++} ++ ++static const struct of_device_id spmi_pmic_arb_match_table[] = { ++ { .compatible = "qcom,spmi-pmic-arb", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); ++ ++static struct platform_driver spmi_pmic_arb_driver = { ++ .probe = spmi_pmic_arb_probe, ++ .remove = spmi_pmic_arb_remove, ++ .driver = { ++ .name = "spmi_pmic_arb", ++ .owner = THIS_MODULE, ++ .of_match_table = spmi_pmic_arb_match_table, ++ }, ++}; ++module_platform_driver(spmi_pmic_arb_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:spmi_pmic_arb"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch new file mode 100644 index 0000000000..cbd49b0f76 --- /dev/null +++ b/target/linux/ipq806x/patches/0057-spmi-pmic_arb-add-support-for-interrupt-handling.patch @@ -0,0 +1,494 @@ +From b5bc51d44485c7ce0ca180a8c5de11a206f686e8 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:25 -0600 +Subject: [PATCH 057/182] spmi: pmic_arb: add support for interrupt handling + +The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller, +also implements interrupt handling for slave devices. Note, this is +outside the scope of SPMI, as SPMI leaves interrupt handling completely +unspecified. + +Extend the driver to provide a irq_chip implementation and chained irq +handling which allows for these interrupts to be used. + +Cc: Thomas Gleixner <tglx@linutronix.de> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 1 + + drivers/spmi/spmi-pmic-arb.c | 377 +++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 376 insertions(+), 2 deletions(-) + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 80b7901..075bd79 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -13,6 +13,7 @@ if SPMI + config SPMI_MSM_PMIC_ARB + tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" + depends on ARM ++ depends on IRQ_DOMAIN + depends on ARCH_MSM || COMPILE_TEST + default ARCH_MSM + help +diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c +index 2dd27e8..246e03a 100644 +--- a/drivers/spmi/spmi-pmic-arb.c ++++ b/drivers/spmi/spmi-pmic-arb.c +@@ -13,6 +13,9 @@ + #include <linux/err.h> + #include <linux/interrupt.h> + #include <linux/io.h> ++#include <linux/irqchip/chained_irq.h> ++#include <linux/irqdomain.h> ++#include <linux/irq.h> + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/of.h> +@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code { + * @cnfg: address of the PMIC Arbiter configuration registers. + * @lock: lock to synchronize accesses. + * @channel: which channel to use for accesses. ++ * @irq: PMIC ARB interrupt. ++ * @ee: the current Execution Environment ++ * @min_apid: minimum APID (used for bounding IRQ search) ++ * @max_apid: maximum APID ++ * @mapping_table: in-memory copy of PPID -> APID mapping table. ++ * @domain: irq domain object for PMIC IRQ domain ++ * @spmic: SPMI controller object ++ * @apid_to_ppid: cached mapping from APID to PPID + */ + struct spmi_pmic_arb_dev { + void __iomem *base; +@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev { + void __iomem *cnfg; + raw_spinlock_t lock; + u8 channel; ++ int irq; ++ u8 ee; ++ u8 min_apid; ++ u8 max_apid; ++ u32 mapping_table[SPMI_MAPPING_TABLE_LEN]; ++ struct irq_domain *domain; ++ struct spmi_controller *spmic; ++ u16 apid_to_ppid[256]; + }; + + static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) +@@ -306,12 +325,316 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + return rc; + } + ++enum qpnpint_regs { ++ QPNPINT_REG_RT_STS = 0x10, ++ QPNPINT_REG_SET_TYPE = 0x11, ++ QPNPINT_REG_POLARITY_HIGH = 0x12, ++ QPNPINT_REG_POLARITY_LOW = 0x13, ++ QPNPINT_REG_LATCHED_CLR = 0x14, ++ QPNPINT_REG_EN_SET = 0x15, ++ QPNPINT_REG_EN_CLR = 0x16, ++ QPNPINT_REG_LATCHED_STS = 0x18, ++}; ++ ++struct spmi_pmic_arb_qpnpint_type { ++ u8 type; /* 1 -> edge */ ++ u8 polarity_high; ++ u8 polarity_low; ++} __packed; ++ ++/* Simplified accessor functions for irqchip callbacks */ ++static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, ++ size_t len) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 sid = d->hwirq >> 24; ++ u8 per = d->hwirq >> 16; ++ ++ if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, ++ (per << 8) + reg, buf, len)) ++ dev_err_ratelimited(&pa->spmic->dev, ++ "failed irqchip transaction on %x\n", ++ d->irq); ++} ++ ++static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 sid = d->hwirq >> 24; ++ u8 per = d->hwirq >> 16; ++ ++ if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, ++ (per << 8) + reg, buf, len)) ++ dev_err_ratelimited(&pa->spmic->dev, ++ "failed irqchip transaction on %x\n", ++ d->irq); ++} ++ ++static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) ++{ ++ unsigned int irq; ++ u32 status; ++ int id; ++ ++ status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid)); ++ while (status) { ++ id = ffs(status) - 1; ++ status &= ~(1 << id); ++ irq = irq_find_mapping(pa->domain, ++ pa->apid_to_ppid[apid] << 16 ++ | id << 8 ++ | apid); ++ generic_handle_irq(irq); ++ } ++} ++ ++static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq); ++ struct irq_chip *chip = irq_get_chip(irq); ++ void __iomem *intr = pa->intr; ++ int first = pa->min_apid >> 5; ++ int last = pa->max_apid >> 5; ++ u32 status; ++ int i, id; ++ ++ chained_irq_enter(chip, desc); ++ ++ for (i = first; i <= last; ++i) { ++ status = readl_relaxed(intr + ++ SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i)); ++ while (status) { ++ id = ffs(status) - 1; ++ status &= ~(1 << id); ++ periph_interrupt(pa, id + i * 32); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void qpnpint_irq_ack(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid)); ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); ++} ++ ++static void qpnpint_irq_mask(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u32 status; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ if (status & SPMI_PIC_ACC_ENABLE_BIT) { ++ status = status & ~SPMI_PIC_ACC_ENABLE_BIT; ++ writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ } ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); ++} ++ ++static void qpnpint_irq_unmask(struct irq_data *d) ++{ ++ struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); ++ u8 irq = d->hwirq >> 8; ++ u8 apid = d->hwirq; ++ unsigned long flags; ++ u32 status; ++ u8 data; ++ ++ raw_spin_lock_irqsave(&pa->lock, flags); ++ status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) { ++ writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT, ++ pa->intr + SPMI_PIC_ACC_ENABLE(apid)); ++ } ++ raw_spin_unlock_irqrestore(&pa->lock, flags); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1); ++} ++ ++static void qpnpint_irq_enable(struct irq_data *d) ++{ ++ u8 irq = d->hwirq >> 8; ++ u8 data; ++ ++ qpnpint_irq_unmask(d); ++ ++ data = 1 << irq; ++ qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); ++} ++ ++static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) ++{ ++ struct spmi_pmic_arb_qpnpint_type type; ++ u8 irq = d->hwirq >> 8; ++ ++ qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); ++ ++ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { ++ type.type |= 1 << irq; ++ if (flow_type & IRQF_TRIGGER_RISING) ++ type.polarity_high |= 1 << irq; ++ if (flow_type & IRQF_TRIGGER_FALLING) ++ type.polarity_low |= 1 << irq; ++ } else { ++ if ((flow_type & (IRQF_TRIGGER_HIGH)) && ++ (flow_type & (IRQF_TRIGGER_LOW))) ++ return -EINVAL; ++ ++ type.type &= ~(1 << irq); /* level trig */ ++ if (flow_type & IRQF_TRIGGER_HIGH) ++ type.polarity_high |= 1 << irq; ++ else ++ type.polarity_low |= 1 << irq; ++ } ++ ++ qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); ++ return 0; ++} ++ ++static struct irq_chip pmic_arb_irqchip = { ++ .name = "pmic_arb", ++ .irq_enable = qpnpint_irq_enable, ++ .irq_ack = qpnpint_irq_ack, ++ .irq_mask = qpnpint_irq_mask, ++ .irq_unmask = qpnpint_irq_unmask, ++ .irq_set_type = qpnpint_irq_set_type, ++ .flags = IRQCHIP_MASK_ON_SUSPEND ++ | IRQCHIP_SKIP_SET_WAKE, ++}; ++ ++struct spmi_pmic_arb_irq_spec { ++ unsigned slave:4; ++ unsigned per:8; ++ unsigned irq:3; ++}; ++ ++static int search_mapping_table(struct spmi_pmic_arb_dev *pa, ++ struct spmi_pmic_arb_irq_spec *spec, ++ u8 *apid) ++{ ++ u16 ppid = spec->slave << 8 | spec->per; ++ u32 *mapping_table = pa->mapping_table; ++ int index = 0, i; ++ u32 data; ++ ++ for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { ++ data = mapping_table[index]; ++ ++ if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) { ++ if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { ++ index = SPMI_MAPPING_BIT_IS_1_RESULT(data); ++ } else { ++ *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); ++ return 0; ++ } ++ } else { ++ if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { ++ index = SPMI_MAPPING_BIT_IS_0_RESULT(data); ++ } else { ++ *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); ++ return 0; ++ } ++ } ++ } ++ ++ return -ENODEV; ++} ++ ++static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, ++ struct device_node *controller, ++ const u32 *intspec, ++ unsigned int intsize, ++ unsigned long *out_hwirq, ++ unsigned int *out_type) ++{ ++ struct spmi_pmic_arb_dev *pa = d->host_data; ++ struct spmi_pmic_arb_irq_spec spec; ++ int err; ++ u8 apid; ++ ++ dev_dbg(&pa->spmic->dev, ++ "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", ++ intspec[0], intspec[1], intspec[2]); ++ ++ if (d->of_node != controller) ++ return -EINVAL; ++ if (intsize != 4) ++ return -EINVAL; ++ if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) ++ return -EINVAL; ++ ++ spec.slave = intspec[0]; ++ spec.per = intspec[1]; ++ spec.irq = intspec[2]; ++ ++ err = search_mapping_table(pa, &spec, &apid); ++ if (err) ++ return err; ++ ++ pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per; ++ ++ /* Keep track of {max,min}_apid for bounding search during interrupt */ ++ if (apid > pa->max_apid) ++ pa->max_apid = apid; ++ if (apid < pa->min_apid) ++ pa->min_apid = apid; ++ ++ *out_hwirq = spec.slave << 24 ++ | spec.per << 16 ++ | spec.irq << 8 ++ | apid; ++ *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; ++ ++ dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); ++ ++ return 0; ++} ++ ++static int qpnpint_irq_domain_map(struct irq_domain *d, ++ unsigned int virq, ++ irq_hw_number_t hwirq) ++{ ++ struct spmi_pmic_arb_dev *pa = d->host_data; ++ ++ dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); ++ ++ irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); ++ irq_set_chip_data(virq, d->host_data); ++ irq_set_noprobe(virq); ++ return 0; ++} ++ ++static const struct irq_domain_ops pmic_arb_irq_domain_ops = { ++ .map = qpnpint_irq_domain_map, ++ .xlate = qpnpint_irq_domain_dt_translate, ++}; ++ + static int spmi_pmic_arb_probe(struct platform_device *pdev) + { + struct spmi_pmic_arb_dev *pa; + struct spmi_controller *ctrl; + struct resource *res; +- u32 channel; ++ u32 channel, ee; + int err, i; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); +@@ -319,6 +642,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + return -ENOMEM; + + pa = spmi_controller_get_drvdata(ctrl); ++ pa->spmic = ctrl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + pa->base = devm_ioremap_resource(&ctrl->dev, res); +@@ -341,6 +665,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + goto err_put_ctrl; + } + ++ pa->irq = platform_get_irq_byname(pdev, "periph_irq"); ++ if (pa->irq < 0) { ++ err = pa->irq; ++ goto err_put_ctrl; ++ } ++ + err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); + if (err) { + dev_err(&pdev->dev, "channel unspecified.\n"); +@@ -355,6 +685,29 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + + pa->channel = channel; + ++ err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); ++ if (err) { ++ dev_err(&pdev->dev, "EE unspecified.\n"); ++ goto err_put_ctrl; ++ } ++ ++ if (ee > 5) { ++ dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); ++ err = -EINVAL; ++ goto err_put_ctrl; ++ } ++ ++ pa->ee = ee; ++ ++ for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i) ++ pa->mapping_table[i] = readl_relaxed( ++ pa->cnfg + SPMI_MAPPING_TABLE_REG(i)); ++ ++ /* Initialize max_apid/min_apid to the opposite bounds, during ++ * the irq domain translation, we are sure to update these */ ++ pa->max_apid = 0; ++ pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; ++ + platform_set_drvdata(pdev, ctrl); + raw_spin_lock_init(&pa->lock); + +@@ -362,15 +715,31 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) + ctrl->read_cmd = pmic_arb_read_cmd; + ctrl->write_cmd = pmic_arb_write_cmd; + ++ dev_dbg(&pdev->dev, "adding irq domain\n"); ++ pa->domain = irq_domain_add_tree(pdev->dev.of_node, ++ &pmic_arb_irq_domain_ops, pa); ++ if (!pa->domain) { ++ dev_err(&pdev->dev, "unable to create irq_domain\n"); ++ err = -ENOMEM; ++ goto err_put_ctrl; ++ } ++ ++ irq_set_handler_data(pa->irq, pa); ++ irq_set_chained_handler(pa->irq, pmic_arb_chained_irq); ++ + err = spmi_controller_add(ctrl); + if (err) +- goto err_put_ctrl; ++ goto err_domain_remove; + + dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", + pmic_arb_base_read(pa, PMIC_ARB_VERSION)); + + return 0; + ++err_domain_remove: ++ irq_set_chained_handler(pa->irq, NULL); ++ irq_set_handler_data(pa->irq, NULL); ++ irq_domain_remove(pa->domain); + err_put_ctrl: + spmi_controller_put(ctrl); + return err; +@@ -379,7 +748,11 @@ err_put_ctrl: + static int spmi_pmic_arb_remove(struct platform_device *pdev) + { + struct spmi_controller *ctrl = platform_get_drvdata(pdev); ++ struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl); + spmi_controller_remove(ctrl); ++ irq_set_chained_handler(pa->irq, NULL); ++ irq_set_handler_data(pa->irq, NULL); ++ irq_domain_remove(pa->domain); + spmi_controller_put(ctrl); + return 0; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch new file mode 100644 index 0000000000..271e8adad4 --- /dev/null +++ b/target/linux/ipq806x/patches/0058-spmi-pmic_arb-make-selectable-on-ARCH_QCOM.patch @@ -0,0 +1,34 @@ +From 840fe915072597b5ba9599c4579f014f47b9638e Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Mon, 3 Mar 2014 10:49:43 -0600 +Subject: [PATCH 058/182] spmi: pmic_arb: make selectable on ARCH_QCOM + +With the split of Qualcomm MSM support into legacy and multiplatform, +the SPMI PMIC arb driver is only relevant on the multiplatform supported +SoCs. Switch the Kconfig depends to ARCH_QCOM. + +Acked-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +index 075bd79..bf1295e 100644 +--- a/drivers/spmi/Kconfig ++++ b/drivers/spmi/Kconfig +@@ -14,8 +14,8 @@ config SPMI_MSM_PMIC_ARB + tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" + depends on ARM + depends on IRQ_DOMAIN +- depends on ARCH_MSM || COMPILE_TEST +- default ARCH_MSM ++ depends on ARCH_QCOM || COMPILE_TEST ++ default ARCH_QCOM + help + If you say yes to this option, support will be included for the + built-in SPMI PMIC Arbiter interface on Qualcomm MSM family +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch new file mode 100644 index 0000000000..f252fa055b --- /dev/null +++ b/target/linux/ipq806x/patches/0059-spmi-pm-drop-bus-level-PM-suspend-resume-routines.patch @@ -0,0 +1,74 @@ +From 9d13f01e2ec45253adaae1a330cdc4eb881c7377 Mon Sep 17 00:00:00 2001 +From: Josh Cartwright <joshc@codeaurora.org> +Date: Mon, 3 Mar 2014 10:49:44 -0600 +Subject: [PATCH 059/182] spmi: pm: drop bus-level PM suspend/resume routines + +SPMI defines the behavior of a device in the "SLEEP" state as being +"user-defined or specified by the device manufacturer". Without +clearly-defined bus-level semantics for low-power states, push the +responsibility of transitioning a device into/out of "SLEEP" into SPMI +device drivers. + +Cc: Felipe Balbi <balbi@ti.com> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/spmi/spmi.c | 35 ----------------------------------- + 1 file changed, 35 deletions(-) + +diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c +index 6122c8f..3b57807 100644 +--- a/drivers/spmi/spmi.c ++++ b/drivers/spmi/spmi.c +@@ -46,40 +46,6 @@ static const struct device_type spmi_ctrl_type = { + .release = spmi_ctrl_release, + }; + +-#ifdef CONFIG_PM_RUNTIME +-static int spmi_runtime_suspend(struct device *dev) +-{ +- struct spmi_device *sdev = to_spmi_device(dev); +- int err; +- +- err = pm_generic_runtime_suspend(dev); +- if (err) +- return err; +- +- return spmi_command_sleep(sdev); +-} +- +-static int spmi_runtime_resume(struct device *dev) +-{ +- struct spmi_device *sdev = to_spmi_device(dev); +- int err; +- +- err = spmi_command_wakeup(sdev); +- if (err) +- return err; +- +- return pm_generic_runtime_resume(dev); +-} +-#endif +- +-static const struct dev_pm_ops spmi_pm_ops = { +- SET_RUNTIME_PM_OPS( +- spmi_runtime_suspend, +- spmi_runtime_resume, +- NULL +- ) +-}; +- + static int spmi_device_match(struct device *dev, struct device_driver *drv) + { + if (of_driver_match_device(dev, drv)) +@@ -391,7 +357,6 @@ static int spmi_drv_remove(struct device *dev) + static struct bus_type spmi_bus_type = { + .name = "spmi", + .match = spmi_device_match, +- .pm = &spmi_pm_ops, + .probe = spmi_drv_probe, + .remove = spmi_drv_remove, + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch new file mode 100644 index 0000000000..64993a3e8c --- /dev/null +++ b/target/linux/ipq806x/patches/0060-i2c-qup-New-bus-driver-for-the-Qualcomm-QUP-I2C-cont.patch @@ -0,0 +1,835 @@ +From 226eff10dc11af5d6b1bc31e5cedc079aa564fb3 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Date: Thu, 13 Mar 2014 19:07:43 -0700 +Subject: [PATCH 060/182] i2c: qup: New bus driver for the Qualcomm QUP I2C + controller + +This bus driver supports the QUP i2c hardware controller in the Qualcomm SOCs. +The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path +engine with input/output FIFOs and an embedded i2c mini-core. The driver +supports FIFO mode (for low bandwidth applications) and block mode (interrupt +generated for each block-size data transfer). + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Reviewed-by: Andy Gross <agross@codeaurora.org> +Tested-by: Philip Elcan <pelcan@codeaurora.org> +[wsa: removed needless IS_ERR_VALUE] +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/Kconfig | 10 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-qup.c | 768 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 779 insertions(+) + create mode 100644 drivers/i2c/busses/i2c-qup.c + +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index de17c55..1886315 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -648,6 +648,16 @@ config I2C_PXA_SLAVE + is necessary for systems where the PXA may be a target on the + I2C bus. + ++config I2C_QUP ++ tristate "Qualcomm QUP based I2C controller" ++ depends on ARCH_QCOM ++ help ++ If you say yes to this option, support will be included for the ++ built-in I2C interface on the Qualcomm SoCs. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-qup. ++ + config I2C_RIIC + tristate "Renesas RIIC adapter" + depends on ARCH_SHMOBILE || COMPILE_TEST +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index a08931f..bf2257b 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o + obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o + obj-$(CONFIG_I2C_PXA) += i2c-pxa.o + obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o ++obj-$(CONFIG_I2C_QUP) += i2c-qup.o + obj-$(CONFIG_I2C_RIIC) += i2c-riic.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_S6000) += i2c-s6000.o +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +new file mode 100644 +index 0000000..c9d5f78 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -0,0 +1,768 @@ ++/* ++ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2014, Sony Mobile Communications AB. ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++ ++/* QUP Registers */ ++#define QUP_CONFIG 0x000 ++#define QUP_STATE 0x004 ++#define QUP_IO_MODE 0x008 ++#define QUP_SW_RESET 0x00c ++#define QUP_OPERATIONAL 0x018 ++#define QUP_ERROR_FLAGS 0x01c ++#define QUP_ERROR_FLAGS_EN 0x020 ++#define QUP_HW_VERSION 0x030 ++#define QUP_MX_OUTPUT_CNT 0x100 ++#define QUP_OUT_FIFO_BASE 0x110 ++#define QUP_MX_WRITE_CNT 0x150 ++#define QUP_MX_INPUT_CNT 0x200 ++#define QUP_MX_READ_CNT 0x208 ++#define QUP_IN_FIFO_BASE 0x218 ++#define QUP_I2C_CLK_CTL 0x400 ++#define QUP_I2C_STATUS 0x404 ++ ++/* QUP States and reset values */ ++#define QUP_RESET_STATE 0 ++#define QUP_RUN_STATE 1 ++#define QUP_PAUSE_STATE 3 ++#define QUP_STATE_MASK 3 ++ ++#define QUP_STATE_VALID BIT(2) ++#define QUP_I2C_MAST_GEN BIT(4) ++ ++#define QUP_OPERATIONAL_RESET 0x000ff0 ++#define QUP_I2C_STATUS_RESET 0xfffffc ++ ++/* QUP OPERATIONAL FLAGS */ ++#define QUP_I2C_NACK_FLAG BIT(3) ++#define QUP_OUT_NOT_EMPTY BIT(4) ++#define QUP_IN_NOT_EMPTY BIT(5) ++#define QUP_OUT_FULL BIT(6) ++#define QUP_OUT_SVC_FLAG BIT(8) ++#define QUP_IN_SVC_FLAG BIT(9) ++#define QUP_MX_OUTPUT_DONE BIT(10) ++#define QUP_MX_INPUT_DONE BIT(11) ++ ++/* I2C mini core related values */ ++#define QUP_CLOCK_AUTO_GATE BIT(13) ++#define I2C_MINI_CORE (2 << 8) ++#define I2C_N_VAL 15 ++/* Most significant word offset in FIFO port */ ++#define QUP_MSW_SHIFT (I2C_N_VAL + 1) ++ ++/* Packing/Unpacking words in FIFOs, and IO modes */ ++#define QUP_OUTPUT_BLK_MODE (1 << 10) ++#define QUP_INPUT_BLK_MODE (1 << 12) ++#define QUP_UNPACK_EN BIT(14) ++#define QUP_PACK_EN BIT(15) ++ ++#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN) ++ ++#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03) ++#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07) ++#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03) ++#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07) ++ ++/* QUP tags */ ++#define QUP_TAG_START (1 << 8) ++#define QUP_TAG_DATA (2 << 8) ++#define QUP_TAG_STOP (3 << 8) ++#define QUP_TAG_REC (4 << 8) ++ ++/* Status, Error flags */ ++#define I2C_STATUS_WR_BUFFER_FULL BIT(0) ++#define I2C_STATUS_BUS_ACTIVE BIT(8) ++#define I2C_STATUS_ERROR_MASK 0x38000fc ++#define QUP_STATUS_ERROR_FLAGS 0x7c ++ ++#define QUP_READ_LIMIT 256 ++ ++struct qup_i2c_dev { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ struct clk *pclk; ++ struct i2c_adapter adap; ++ ++ int clk_ctl; ++ int out_fifo_sz; ++ int in_fifo_sz; ++ int out_blk_sz; ++ int in_blk_sz; ++ ++ unsigned long one_byte_t; ++ ++ struct i2c_msg *msg; ++ /* Current posion in user message buffer */ ++ int pos; ++ /* I2C protocol errors */ ++ u32 bus_err; ++ /* QUP core errors */ ++ u32 qup_err; ++ ++ struct completion xfer; ++}; ++ ++static irqreturn_t qup_i2c_interrupt(int irq, void *dev) ++{ ++ struct qup_i2c_dev *qup = dev; ++ u32 bus_err; ++ u32 qup_err; ++ u32 opflags; ++ ++ bus_err = readl(qup->base + QUP_I2C_STATUS); ++ qup_err = readl(qup->base + QUP_ERROR_FLAGS); ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ ++ if (!qup->msg) { ++ /* Clear Error interrupt */ ++ writel(QUP_RESET_STATE, qup->base + QUP_STATE); ++ return IRQ_HANDLED; ++ } ++ ++ bus_err &= I2C_STATUS_ERROR_MASK; ++ qup_err &= QUP_STATUS_ERROR_FLAGS; ++ ++ if (qup_err) { ++ /* Clear Error interrupt */ ++ writel(qup_err, qup->base + QUP_ERROR_FLAGS); ++ goto done; ++ } ++ ++ if (bus_err) { ++ /* Clear Error interrupt */ ++ writel(QUP_RESET_STATE, qup->base + QUP_STATE); ++ goto done; ++ } ++ ++ if (opflags & QUP_IN_SVC_FLAG) ++ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL); ++ ++ if (opflags & QUP_OUT_SVC_FLAG) ++ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL); ++ ++done: ++ qup->qup_err = qup_err; ++ qup->bus_err = bus_err; ++ complete(&qup->xfer); ++ return IRQ_HANDLED; ++} ++ ++static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup, ++ u32 req_state, u32 req_mask) ++{ ++ int retries = 1; ++ u32 state; ++ ++ /* ++ * State transition takes 3 AHB clocks cycles + 3 I2C master clock ++ * cycles. So retry once after a 1uS delay. ++ */ ++ do { ++ state = readl(qup->base + QUP_STATE); ++ ++ if (state & QUP_STATE_VALID && ++ (state & req_mask) == req_state) ++ return 0; ++ ++ udelay(1); ++ } while (retries--); ++ ++ return -ETIMEDOUT; ++} ++ ++static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state) ++{ ++ return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK); ++} ++ ++static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup) ++{ ++ return qup_i2c_poll_state_mask(qup, 0, 0); ++} ++ ++static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup) ++{ ++ return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN); ++} ++ ++static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state) ++{ ++ if (qup_i2c_poll_state_valid(qup) != 0) ++ return -EIO; ++ ++ writel(state, qup->base + QUP_STATE); ++ ++ if (qup_i2c_poll_state(qup, state) != 0) ++ return -EIO; ++ return 0; ++} ++ ++static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup) ++{ ++ unsigned long timeout; ++ u32 opflags; ++ u32 status; ++ ++ timeout = jiffies + HZ; ++ ++ for (;;) { ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ status = readl(qup->base + QUP_I2C_STATUS); ++ ++ if (!(opflags & QUP_OUT_NOT_EMPTY) && ++ !(status & I2C_STATUS_BUS_ACTIVE)) ++ return 0; ++ ++ if (time_after(jiffies, timeout)) ++ return -ETIMEDOUT; ++ ++ usleep_range(qup->one_byte_t, qup->one_byte_t * 2); ++ } ++} ++ ++static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ /* Number of entries to shift out, including the start */ ++ int total = msg->len + 1; ++ ++ if (total < qup->out_fifo_sz) { ++ /* FIFO mode */ ++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE); ++ writel(total, qup->base + QUP_MX_WRITE_CNT); ++ } else { ++ /* BLOCK mode (transfer data on chunks) */ ++ writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN, ++ qup->base + QUP_IO_MODE); ++ writel(total, qup->base + QUP_MX_OUTPUT_CNT); ++ } ++} ++ ++static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 addr = msg->addr << 1; ++ u32 qup_tag; ++ u32 opflags; ++ int idx; ++ u32 val; ++ ++ if (qup->pos == 0) { ++ val = QUP_TAG_START | addr; ++ idx = 1; ++ } else { ++ val = 0; ++ idx = 0; ++ } ++ ++ while (qup->pos < msg->len) { ++ /* Check that there's space in the FIFO for our pair */ ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ if (opflags & QUP_OUT_FULL) ++ break; ++ ++ if (qup->pos == msg->len - 1) ++ qup_tag = QUP_TAG_STOP; ++ else ++ qup_tag = QUP_TAG_DATA; ++ ++ if (idx & 1) ++ val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT; ++ else ++ val = qup_tag | msg->buf[qup->pos]; ++ ++ /* Write out the pair and the last odd value */ ++ if (idx & 1 || qup->pos == msg->len - 1) ++ writel(val, qup->base + QUP_OUT_FIFO_BASE); ++ ++ qup->pos++; ++ idx++; ++ } ++} ++ ++static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ unsigned long left; ++ int ret; ++ ++ qup->msg = msg; ++ qup->pos = 0; ++ ++ enable_irq(qup->irq); ++ ++ qup_i2c_set_write_mode(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); ++ ++ do { ++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); ++ if (ret) ++ goto err; ++ ++ qup_i2c_issue_write(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ left = wait_for_completion_timeout(&qup->xfer, HZ); ++ if (!left) { ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = -ETIMEDOUT; ++ goto err; ++ } ++ ++ if (qup->bus_err || qup->qup_err) { ++ if (qup->bus_err & QUP_I2C_NACK_FLAG) ++ dev_err(qup->dev, "NACK from %x\n", msg->addr); ++ ret = -EIO; ++ goto err; ++ } ++ } while (qup->pos < msg->len); ++ ++ /* Wait for the outstanding data in the fifo to drain */ ++ ret = qup_i2c_wait_writeready(qup); ++ ++err: ++ disable_irq(qup->irq); ++ qup->msg = NULL; ++ ++ return ret; ++} ++ ++static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len) ++{ ++ if (len < qup->in_fifo_sz) { ++ /* FIFO mode */ ++ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE); ++ writel(len, qup->base + QUP_MX_READ_CNT); ++ } else { ++ /* BLOCK mode (transfer data on chunks) */ ++ writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN, ++ qup->base + QUP_IO_MODE); ++ writel(len, qup->base + QUP_MX_INPUT_CNT); ++ } ++} ++ ++static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 addr, len, val; ++ ++ addr = (msg->addr << 1) | 1; ++ ++ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */ ++ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len; ++ ++ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr; ++ writel(val, qup->base + QUP_OUT_FIFO_BASE); ++} ++ ++ ++static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ u32 opflags; ++ u32 val = 0; ++ int idx; ++ ++ for (idx = 0; qup->pos < msg->len; idx++) { ++ if ((idx & 1) == 0) { ++ /* Check that FIFO have data */ ++ opflags = readl(qup->base + QUP_OPERATIONAL); ++ if (!(opflags & QUP_IN_NOT_EMPTY)) ++ break; ++ ++ /* Reading 2 words at time */ ++ val = readl(qup->base + QUP_IN_FIFO_BASE); ++ ++ msg->buf[qup->pos++] = val & 0xFF; ++ } else { ++ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT; ++ } ++ } ++} ++ ++static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg) ++{ ++ unsigned long left; ++ int ret; ++ ++ /* ++ * The QUP block will issue a NACK and STOP on the bus when reaching ++ * the end of the read, the length of the read is specified as one byte ++ * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. ++ */ ++ if (msg->len > QUP_READ_LIMIT) { ++ dev_err(qup->dev, "HW not capable of reads over %d bytes\n", ++ QUP_READ_LIMIT); ++ return -EINVAL; ++ } ++ ++ qup->msg = msg; ++ qup->pos = 0; ++ ++ enable_irq(qup->irq); ++ ++ qup_i2c_set_read_mode(qup, msg->len); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); ++ ++ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); ++ if (ret) ++ goto err; ++ ++ qup_i2c_issue_read(qup, msg); ++ ++ ret = qup_i2c_change_state(qup, QUP_RUN_STATE); ++ if (ret) ++ goto err; ++ ++ do { ++ left = wait_for_completion_timeout(&qup->xfer, HZ); ++ if (!left) { ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = -ETIMEDOUT; ++ goto err; ++ } ++ ++ if (qup->bus_err || qup->qup_err) { ++ if (qup->bus_err & QUP_I2C_NACK_FLAG) ++ dev_err(qup->dev, "NACK from %x\n", msg->addr); ++ ret = -EIO; ++ goto err; ++ } ++ ++ qup_i2c_read_fifo(qup, msg); ++ } while (qup->pos < msg->len); ++ ++err: ++ disable_irq(qup->irq); ++ qup->msg = NULL; ++ ++ return ret; ++} ++ ++static int qup_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg msgs[], ++ int num) ++{ ++ struct qup_i2c_dev *qup = i2c_get_adapdata(adap); ++ int ret, idx; ++ ++ ret = pm_runtime_get_sync(qup->dev); ++ if (ret) ++ goto out; ++ ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); ++ if (ret) ++ goto out; ++ ++ /* Configure QUP as I2C mini core */ ++ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG); ++ ++ for (idx = 0; idx < num; idx++) { ++ if (msgs[idx].len == 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (qup_i2c_poll_state_i2c_master(qup)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ if (msgs[idx].flags & I2C_M_RD) ++ ret = qup_i2c_read_one(qup, &msgs[idx]); ++ else ++ ret = qup_i2c_write_one(qup, &msgs[idx]); ++ ++ if (ret) ++ break; ++ ++ ret = qup_i2c_change_state(qup, QUP_RESET_STATE); ++ if (ret) ++ break; ++ } ++ ++ if (ret == 0) ++ ret = num; ++out: ++ ++ pm_runtime_mark_last_busy(qup->dev); ++ pm_runtime_put_autosuspend(qup->dev); ++ ++ return ret; ++} ++ ++static u32 qup_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); ++} ++ ++static const struct i2c_algorithm qup_i2c_algo = { ++ .master_xfer = qup_i2c_xfer, ++ .functionality = qup_i2c_func, ++}; ++ ++static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup) ++{ ++ clk_prepare_enable(qup->clk); ++ clk_prepare_enable(qup->pclk); ++} ++ ++static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) ++{ ++ u32 config; ++ ++ qup_i2c_change_state(qup, QUP_RESET_STATE); ++ clk_disable_unprepare(qup->clk); ++ config = readl(qup->base + QUP_CONFIG); ++ config |= QUP_CLOCK_AUTO_GATE; ++ writel(config, qup->base + QUP_CONFIG); ++ clk_disable_unprepare(qup->pclk); ++} ++ ++static int qup_i2c_probe(struct platform_device *pdev) ++{ ++ static const int blk_sizes[] = {4, 16, 32}; ++ struct device_node *node = pdev->dev.of_node; ++ struct qup_i2c_dev *qup; ++ unsigned long one_bit_t; ++ struct resource *res; ++ u32 io_mode, hw_ver, size; ++ int ret, fs_div, hs_div; ++ int src_clk_freq; ++ int clk_freq = 100000; ++ ++ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL); ++ if (!qup) ++ return -ENOMEM; ++ ++ qup->dev = &pdev->dev; ++ init_completion(&qup->xfer); ++ platform_set_drvdata(pdev, qup); ++ ++ of_property_read_u32(node, "clock-frequency", &clk_freq); ++ ++ /* We support frequencies up to FAST Mode (400KHz) */ ++ if (!clk_freq || clk_freq > 400000) { ++ dev_err(qup->dev, "clock frequency not supported %d\n", ++ clk_freq); ++ return -EINVAL; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ qup->base = devm_ioremap_resource(qup->dev, res); ++ if (IS_ERR(qup->base)) ++ return PTR_ERR(qup->base); ++ ++ qup->irq = platform_get_irq(pdev, 0); ++ if (qup->irq < 0) { ++ dev_err(qup->dev, "No IRQ defined\n"); ++ return qup->irq; ++ } ++ ++ qup->clk = devm_clk_get(qup->dev, "core"); ++ if (IS_ERR(qup->clk)) { ++ dev_err(qup->dev, "Could not get core clock\n"); ++ return PTR_ERR(qup->clk); ++ } ++ ++ qup->pclk = devm_clk_get(qup->dev, "iface"); ++ if (IS_ERR(qup->pclk)) { ++ dev_err(qup->dev, "Could not get iface clock\n"); ++ return PTR_ERR(qup->pclk); ++ } ++ ++ qup_i2c_enable_clocks(qup); ++ ++ /* ++ * Bootloaders might leave a pending interrupt on certain QUP's, ++ * so we reset the core before registering for interrupts. ++ */ ++ writel(1, qup->base + QUP_SW_RESET); ++ ret = qup_i2c_poll_state_valid(qup); ++ if (ret) ++ goto fail; ++ ++ ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt, ++ IRQF_TRIGGER_HIGH, "i2c_qup", qup); ++ if (ret) { ++ dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq); ++ goto fail; ++ } ++ disable_irq(qup->irq); ++ ++ hw_ver = readl(qup->base + QUP_HW_VERSION); ++ dev_dbg(qup->dev, "Revision %x\n", hw_ver); ++ ++ io_mode = readl(qup->base + QUP_IO_MODE); ++ ++ /* ++ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' ++ * associated with each byte written/received ++ */ ++ size = QUP_OUTPUT_BLOCK_SIZE(io_mode); ++ if (size > ARRAY_SIZE(blk_sizes)) ++ return -EIO; ++ qup->out_blk_sz = blk_sizes[size] / 2; ++ ++ size = QUP_INPUT_BLOCK_SIZE(io_mode); ++ if (size > ARRAY_SIZE(blk_sizes)) ++ return -EIO; ++ qup->in_blk_sz = blk_sizes[size] / 2; ++ ++ size = QUP_OUTPUT_FIFO_SIZE(io_mode); ++ qup->out_fifo_sz = qup->out_blk_sz * (2 << size); ++ ++ size = QUP_INPUT_FIFO_SIZE(io_mode); ++ qup->in_fifo_sz = qup->in_blk_sz * (2 << size); ++ ++ src_clk_freq = clk_get_rate(qup->clk); ++ fs_div = ((src_clk_freq / clk_freq) / 2) - 3; ++ hs_div = 3; ++ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff); ++ ++ /* ++ * Time it takes for a byte to be clocked out on the bus. ++ * Each byte takes 9 clock cycles (8 bits + 1 ack). ++ */ ++ one_bit_t = (USEC_PER_SEC / clk_freq) + 1; ++ qup->one_byte_t = one_bit_t * 9; ++ ++ dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ qup->in_blk_sz, qup->in_fifo_sz, ++ qup->out_blk_sz, qup->out_fifo_sz); ++ ++ i2c_set_adapdata(&qup->adap, qup); ++ qup->adap.algo = &qup_i2c_algo; ++ qup->adap.dev.parent = qup->dev; ++ qup->adap.dev.of_node = pdev->dev.of_node; ++ strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name)); ++ ++ ret = i2c_add_adapter(&qup->adap); ++ if (ret) ++ goto fail; ++ ++ pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC); ++ pm_runtime_use_autosuspend(qup->dev); ++ pm_runtime_set_active(qup->dev); ++ pm_runtime_enable(qup->dev); ++ return 0; ++ ++fail: ++ qup_i2c_disable_clocks(qup); ++ return ret; ++} ++ ++static int qup_i2c_remove(struct platform_device *pdev) ++{ ++ struct qup_i2c_dev *qup = platform_get_drvdata(pdev); ++ ++ disable_irq(qup->irq); ++ qup_i2c_disable_clocks(qup); ++ i2c_del_adapter(&qup->adap); ++ pm_runtime_disable(qup->dev); ++ pm_runtime_set_suspended(qup->dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int qup_i2c_pm_suspend_runtime(struct device *device) ++{ ++ struct qup_i2c_dev *qup = dev_get_drvdata(device); ++ ++ dev_dbg(device, "pm_runtime: suspending...\n"); ++ qup_i2c_disable_clocks(qup); ++ return 0; ++} ++ ++static int qup_i2c_pm_resume_runtime(struct device *device) ++{ ++ struct qup_i2c_dev *qup = dev_get_drvdata(device); ++ ++ dev_dbg(device, "pm_runtime: resuming...\n"); ++ qup_i2c_enable_clocks(qup); ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int qup_i2c_suspend(struct device *device) ++{ ++ qup_i2c_pm_suspend_runtime(device); ++ return 0; ++} ++ ++static int qup_i2c_resume(struct device *device) ++{ ++ qup_i2c_pm_resume_runtime(device); ++ pm_runtime_mark_last_busy(device); ++ pm_request_autosuspend(device); ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops qup_i2c_qup_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS( ++ qup_i2c_suspend, ++ qup_i2c_resume) ++ SET_RUNTIME_PM_OPS( ++ qup_i2c_pm_suspend_runtime, ++ qup_i2c_pm_resume_runtime, ++ NULL) ++}; ++ ++static const struct of_device_id qup_i2c_dt_match[] = { ++ { .compatible = "qcom,i2c-qup-v1.1.1" }, ++ { .compatible = "qcom,i2c-qup-v2.1.1" }, ++ { .compatible = "qcom,i2c-qup-v2.2.1" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, qup_i2c_dt_match); ++ ++static struct platform_driver qup_i2c_driver = { ++ .probe = qup_i2c_probe, ++ .remove = qup_i2c_remove, ++ .driver = { ++ .name = "i2c_qup", ++ .owner = THIS_MODULE, ++ .pm = &qup_i2c_qup_pm_ops, ++ .of_match_table = qup_i2c_dt_match, ++ }, ++}; ++ ++module_platform_driver(qup_i2c_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:i2c_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..6ddae4fe6c --- /dev/null +++ b/target/linux/ipq806x/patches/0061-i2c-qup-Add-device-tree-bindings-information.patch @@ -0,0 +1,71 @@ +From 81480a89c72d811376e9e040729721705b2a984d Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Mar 2014 19:07:42 -0700 +Subject: [PATCH 061/182] i2c: qup: Add device tree bindings information + +The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and +provide input and output FIFO's for it. I2C controller can operate +as master with supported bus speeds of 100Kbps and 400Kbps. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +[bjorn: reformulated part of binding description + added version to compatible + cleaned up example] +Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Acked-by: Rob Herring <robh@kernel.org> +[wsa: removed the dummy child node which was a confusing example] +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt + +diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +new file mode 100644 +index 0000000..dc71754 +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +@@ -0,0 +1,40 @@ ++Qualcomm Universal Peripheral (QUP) I2C controller ++ ++Required properties: ++ - compatible: Should be: ++ * "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064. ++ * "qcom,i2c-qup-v2.1.1" for 8974 v1. ++ * "qcom,i2c-qup-v2.2.1" for 8974 v2 and later. ++ - reg: Should contain QUP register address and length. ++ - interrupts: Should contain I2C interrupt. ++ ++ - clocks: A list of phandles + clock-specifiers, one for each entry in ++ clock-names. ++ - clock-names: Should contain: ++ * "core" for the core clock ++ * "iface" for the AHB clock ++ ++ - #address-cells: Should be <1> Address cells for i2c device address ++ - #size-cells: Should be <0> as i2c addresses have no size component ++ ++Optional properties: ++ - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, ++ defaults to 100kHz if omitted. ++ ++Child nodes should conform to i2c bus binding. ++ ++Example: ++ ++ i2c@f9924000 { ++ compatible = "qcom,i2c-qup-v2.2.1"; ++ reg = <0xf9924000 0x1000>; ++ interrupts = <0 96 0>; ++ ++ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ clock-frequency = <355000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch new file mode 100644 index 0000000000..a140997b82 --- /dev/null +++ b/target/linux/ipq806x/patches/0062-i2c-qup-off-by-ones-in-qup_i2c_probe.patch @@ -0,0 +1,36 @@ +From aa50d1b0d81e4a9509f15894ec64013aa5190b59 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Thu, 3 Apr 2014 10:22:54 +0300 +Subject: [PATCH 062/182] i2c: qup: off by ones in qup_i2c_probe() + +These should ">= ARRAY_SIZE()" instead of "> ARRAY_SIZE()". + +Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller') +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/i2c-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index c9d5f78..ee40980 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -633,12 +633,12 @@ static int qup_i2c_probe(struct platform_device *pdev) + * associated with each byte written/received + */ + size = QUP_OUTPUT_BLOCK_SIZE(io_mode); +- if (size > ARRAY_SIZE(blk_sizes)) ++ if (size >= ARRAY_SIZE(blk_sizes)) + return -EIO; + qup->out_blk_sz = blk_sizes[size] / 2; + + size = QUP_INPUT_BLOCK_SIZE(io_mode); +- if (size > ARRAY_SIZE(blk_sizes)) ++ if (size >= ARRAY_SIZE(blk_sizes)) + return -EIO; + qup->in_blk_sz = blk_sizes[size] / 2; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch new file mode 100644 index 0000000000..60411abbaa --- /dev/null +++ b/target/linux/ipq806x/patches/0063-i2c-qup-use-proper-type-fro-clk_freq.patch @@ -0,0 +1,30 @@ +From 62bdf6c11c9120543ba4831f0226bf51db1d1fc0 Mon Sep 17 00:00:00 2001 +From: Wolfram Sang <wsa@the-dreams.de> +Date: Thu, 3 Apr 2014 11:30:33 +0200 +Subject: [PATCH 063/182] i2c: qup: use proper type fro clk_freq + +It is used with of_property_read_u32(), so it should be u32. + +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Fixes: 10c5a8425968 ('i2c: qup: New bus driver for the Qualcomm QUP I2C controller') +--- + drivers/i2c/busses/i2c-qup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index ee40980..1b4cf14 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -562,7 +562,7 @@ static int qup_i2c_probe(struct platform_device *pdev) + u32 io_mode, hw_ver, size; + int ret, fs_div, hs_div; + int src_clk_freq; +- int clk_freq = 100000; ++ u32 clk_freq = 100000; + + qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL); + if (!qup) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch new file mode 100644 index 0000000000..eaec95084e --- /dev/null +++ b/target/linux/ipq806x/patches/0064-i2c-qup-Fix-pm_runtime_get_sync-usage.patch @@ -0,0 +1,31 @@ +From 82f2e53f207019c0d364d92515410be8dffd7524 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Fri, 2 May 2014 20:54:29 -0500 +Subject: [PATCH 064/182] i2c: qup: Fix pm_runtime_get_sync usage + +This patch corrects the error check on the call to pm_runtime_get_sync. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Reviewed-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Signed-off-by: Wolfram Sang <wsa@the-dreams.de> +--- + drivers/i2c/busses/i2c-qup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c +index 1b4cf14..2a5efb5 100644 +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -479,7 +479,7 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, + int ret, idx; + + ret = pm_runtime_get_sync(qup->dev); +- if (ret) ++ if (ret < 0) + goto out; + + writel(1, qup->base + QUP_SW_RESET); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch new file mode 100644 index 0000000000..a392e0473d --- /dev/null +++ b/target/linux/ipq806x/patches/0065-spi-Add-Qualcomm-QUP-SPI-controller-support.patch @@ -0,0 +1,907 @@ +From 24884115a6029995dba2561b1ee810f28a34271a Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Feb 2014 18:21:38 +0200 +Subject: [PATCH 065/182] spi: Add Qualcomm QUP SPI controller support + +Qualcomm Universal Peripheral (QUP) core is an AHB slave that +provides a common data path (an output FIFO and an input FIFO) +for serial peripheral interface (SPI) mini-core. SPI in master +mode supports up to 50MHz, up to four chip selects, programmable +data path from 4 bits to 32 bits and numerous protocol variants. + +Cc: Alok Chauhan <alokc@codeaurora.org> +Cc: Gilad Avidov <gavidov@codeaurora.org> +Cc: Kiran Gunda <kgunda@codeaurora.org> +Cc: Sagar Dharia <sdharia@codeaurora.org> +Cc: dsneddon@codeaurora.org +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 13 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-qup.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 851 insertions(+) + create mode 100644 drivers/spi/spi-qup.c + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 581ee2a..9e9e3ed 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -381,6 +381,19 @@ config SPI_RSPI + help + SPI driver for Renesas RSPI and QSPI blocks. + ++config SPI_QUP ++ tristate "Qualcomm SPI controller with QUP interface" ++ depends on ARCH_MSM_DT ++ help ++ Qualcomm Universal Peripheral (QUP) core is an AHB slave that ++ provides a common data path (an output FIFO and an input FIFO) ++ for serial peripheral interface (SPI) mini-core. SPI in master ++ mode supports up to 50MHz, up to four chip selects, programmable ++ data path from 4 bits to 32 bits and numerous protocol variants. ++ ++ This driver can also be built as a module. If so, the module ++ will be called spi_qup. ++ + config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on ARCH_S3C24XX +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 95af48d..e598147 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o + spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o + obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o ++obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o + spi-s3c24xx-hw-y := spi-s3c24xx.o +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +new file mode 100644 +index 0000000..b0bcc09 +--- /dev/null ++++ b/drivers/spi/spi-qup.c +@@ -0,0 +1,837 @@ ++/* ++ * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/spi/spi.h> ++ ++#define QUP_CONFIG 0x0000 ++#define QUP_STATE 0x0004 ++#define QUP_IO_M_MODES 0x0008 ++#define QUP_SW_RESET 0x000c ++#define QUP_OPERATIONAL 0x0018 ++#define QUP_ERROR_FLAGS 0x001c ++#define QUP_ERROR_FLAGS_EN 0x0020 ++#define QUP_OPERATIONAL_MASK 0x0028 ++#define QUP_HW_VERSION 0x0030 ++#define QUP_MX_OUTPUT_CNT 0x0100 ++#define QUP_OUTPUT_FIFO 0x0110 ++#define QUP_MX_WRITE_CNT 0x0150 ++#define QUP_MX_INPUT_CNT 0x0200 ++#define QUP_MX_READ_CNT 0x0208 ++#define QUP_INPUT_FIFO 0x0218 ++ ++#define SPI_CONFIG 0x0300 ++#define SPI_IO_CONTROL 0x0304 ++#define SPI_ERROR_FLAGS 0x0308 ++#define SPI_ERROR_FLAGS_EN 0x030c ++ ++/* QUP_CONFIG fields */ ++#define QUP_CONFIG_SPI_MODE (1 << 8) ++#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) ++#define QUP_CONFIG_NO_INPUT BIT(7) ++#define QUP_CONFIG_NO_OUTPUT BIT(6) ++#define QUP_CONFIG_N 0x001f ++ ++/* QUP_STATE fields */ ++#define QUP_STATE_VALID BIT(2) ++#define QUP_STATE_RESET 0 ++#define QUP_STATE_RUN 1 ++#define QUP_STATE_PAUSE 3 ++#define QUP_STATE_MASK 3 ++#define QUP_STATE_CLEAR 2 ++ ++#define QUP_HW_VERSION_2_1_1 0x20010001 ++ ++/* QUP_IO_M_MODES fields */ ++#define QUP_IO_M_PACK_EN BIT(15) ++#define QUP_IO_M_UNPACK_EN BIT(14) ++#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 ++#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 ++#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) ++#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) ++ ++#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) ++#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) ++#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) ++#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) ++ ++#define QUP_IO_M_MODE_FIFO 0 ++#define QUP_IO_M_MODE_BLOCK 1 ++#define QUP_IO_M_MODE_DMOV 2 ++#define QUP_IO_M_MODE_BAM 3 ++ ++/* QUP_OPERATIONAL fields */ ++#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) ++#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) ++#define QUP_OP_IN_SERVICE_FLAG BIT(9) ++#define QUP_OP_OUT_SERVICE_FLAG BIT(8) ++#define QUP_OP_IN_FIFO_FULL BIT(7) ++#define QUP_OP_OUT_FIFO_FULL BIT(6) ++#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) ++#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) ++ ++/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ ++#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) ++#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) ++#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) ++#define QUP_ERROR_INPUT_OVER_RUN BIT(2) ++ ++/* SPI_CONFIG fields */ ++#define SPI_CONFIG_HS_MODE BIT(10) ++#define SPI_CONFIG_INPUT_FIRST BIT(9) ++#define SPI_CONFIG_LOOPBACK BIT(8) ++ ++/* SPI_IO_CONTROL fields */ ++#define SPI_IO_C_FORCE_CS BIT(11) ++#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) ++#define SPI_IO_C_MX_CS_MODE BIT(8) ++#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) ++#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) ++#define SPI_IO_C_CS_SELECT_MASK 0x000c ++#define SPI_IO_C_TRISTATE_CS BIT(1) ++#define SPI_IO_C_NO_TRI_STATE BIT(0) ++ ++/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ ++#define SPI_ERROR_CLK_OVER_RUN BIT(1) ++#define SPI_ERROR_CLK_UNDER_RUN BIT(0) ++ ++#define SPI_NUM_CHIPSELECTS 4 ++ ++/* high speed mode is when bus rate is greater then 26MHz */ ++#define SPI_HS_MIN_RATE 26000000 ++#define SPI_MAX_RATE 50000000 ++ ++#define SPI_DELAY_THRESHOLD 1 ++#define SPI_DELAY_RETRY 10 ++ ++struct spi_qup_device { ++ int select; ++ u16 mode; ++}; ++ ++struct spi_qup { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *cclk; /* core clock */ ++ struct clk *iclk; /* interface clock */ ++ int irq; ++ u32 max_speed_hz; ++ spinlock_t lock; ++ ++ int in_fifo_sz; ++ int out_fifo_sz; ++ int in_blk_sz; ++ int out_blk_sz; ++ ++ struct spi_transfer *xfer; ++ struct completion done; ++ int error; ++ int w_size; /* bytes per SPI word */ ++ int tx_bytes; ++ int rx_bytes; ++}; ++ ++ ++static inline bool spi_qup_is_valid_state(struct spi_qup *controller) ++{ ++ u32 opstate = readl_relaxed(controller->base + QUP_STATE); ++ ++ return opstate & QUP_STATE_VALID; ++} ++ ++static int spi_qup_set_state(struct spi_qup *controller, u32 state) ++{ ++ unsigned long loop; ++ u32 cur_state; ++ ++ loop = 0; ++ while (!spi_qup_is_valid_state(controller)) { ++ ++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); ++ ++ if (++loop > SPI_DELAY_RETRY) ++ return -EIO; ++ } ++ ++ if (loop) ++ dev_dbg(controller->dev, "invalid state for %ld,us %d\n", ++ loop, state); ++ ++ cur_state = readl_relaxed(controller->base + QUP_STATE); ++ /* ++ * Per spec: for PAUSE_STATE to RESET_STATE, two writes ++ * of (b10) are required ++ */ ++ if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) && ++ (state == QUP_STATE_RESET)) { ++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); ++ writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); ++ } else { ++ cur_state &= ~QUP_STATE_MASK; ++ cur_state |= state; ++ writel_relaxed(cur_state, controller->base + QUP_STATE); ++ } ++ ++ loop = 0; ++ while (!spi_qup_is_valid_state(controller)) { ++ ++ usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); ++ ++ if (++loop > SPI_DELAY_RETRY) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++static void spi_qup_fifo_read(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ u8 *rx_buf = xfer->rx_buf; ++ u32 word, state; ++ int idx, shift, w_size; ++ ++ w_size = controller->w_size; ++ ++ while (controller->rx_bytes < xfer->len) { ++ ++ state = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) ++ break; ++ ++ word = readl_relaxed(controller->base + QUP_INPUT_FIFO); ++ ++ if (!rx_buf) { ++ controller->rx_bytes += w_size; ++ continue; ++ } ++ ++ for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { ++ /* ++ * The data format depends on bytes per SPI word: ++ * 4 bytes: 0x12345678 ++ * 2 bytes: 0x00001234 ++ * 1 byte : 0x00000012 ++ */ ++ shift = BITS_PER_BYTE; ++ shift *= (w_size - idx - 1); ++ rx_buf[controller->rx_bytes] = word >> shift; ++ } ++ } ++} ++ ++static void spi_qup_fifo_write(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ const u8 *tx_buf = xfer->tx_buf; ++ u32 word, state, data; ++ int idx, w_size; ++ ++ w_size = controller->w_size; ++ ++ while (controller->tx_bytes < xfer->len) { ++ ++ state = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ if (state & QUP_OP_OUT_FIFO_FULL) ++ break; ++ ++ word = 0; ++ for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { ++ ++ if (!tx_buf) { ++ controller->tx_bytes += w_size; ++ break; ++ } ++ ++ data = tx_buf[controller->tx_bytes]; ++ word |= data << (BITS_PER_BYTE * (3 - idx)); ++ } ++ ++ writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); ++ } ++} ++ ++static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) ++{ ++ struct spi_qup *controller = dev_id; ++ struct spi_transfer *xfer; ++ u32 opflags, qup_err, spi_err; ++ unsigned long flags; ++ int error = 0; ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ xfer = controller->xfer; ++ controller->xfer = NULL; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); ++ spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS); ++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ ++ writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); ++ writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); ++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); ++ ++ if (!xfer) { ++ dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", ++ qup_err, spi_err, opflags); ++ return IRQ_HANDLED; ++ } ++ ++ if (qup_err) { ++ if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN) ++ dev_warn(controller->dev, "OUTPUT_OVER_RUN\n"); ++ if (qup_err & QUP_ERROR_INPUT_UNDER_RUN) ++ dev_warn(controller->dev, "INPUT_UNDER_RUN\n"); ++ if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN) ++ dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n"); ++ if (qup_err & QUP_ERROR_INPUT_OVER_RUN) ++ dev_warn(controller->dev, "INPUT_OVER_RUN\n"); ++ ++ error = -EIO; ++ } ++ ++ if (spi_err) { ++ if (spi_err & SPI_ERROR_CLK_OVER_RUN) ++ dev_warn(controller->dev, "CLK_OVER_RUN\n"); ++ if (spi_err & SPI_ERROR_CLK_UNDER_RUN) ++ dev_warn(controller->dev, "CLK_UNDER_RUN\n"); ++ ++ error = -EIO; ++ } ++ ++ if (opflags & QUP_OP_IN_SERVICE_FLAG) ++ spi_qup_fifo_read(controller, xfer); ++ ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) ++ spi_qup_fifo_write(controller, xfer); ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->error = error; ++ controller->xfer = xfer; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ if (controller->rx_bytes == xfer->len || error) ++ complete(&controller->done); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* set clock freq ... bits per word */ ++static int spi_qup_io_config(struct spi_qup *controller, ++ struct spi_qup_device *chip, ++ struct spi_transfer *xfer) ++{ ++ u32 config, iomode, mode; ++ int ret, n_words, w_size; ++ ++ if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { ++ dev_err(controller->dev, "too big size for loopback %d > %d\n", ++ xfer->len, controller->in_fifo_sz); ++ return -EIO; ++ } ++ ++ ret = clk_set_rate(controller->cclk, xfer->speed_hz); ++ if (ret) { ++ dev_err(controller->dev, "fail to set frequency %d", ++ xfer->speed_hz); ++ return -EIO; ++ } ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { ++ dev_err(controller->dev, "cannot set RESET state\n"); ++ return -EIO; ++ } ++ ++ w_size = 4; ++ if (xfer->bits_per_word <= 8) ++ w_size = 1; ++ else if (xfer->bits_per_word <= 16) ++ w_size = 2; ++ ++ n_words = xfer->len / w_size; ++ controller->w_size = w_size; ++ ++ if (n_words <= controller->in_fifo_sz) { ++ mode = QUP_IO_M_MODE_FIFO; ++ writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); ++ /* must be zero for FIFO */ ++ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); ++ } else { ++ mode = QUP_IO_M_MODE_BLOCK; ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ /* must be zero for BLOCK and BAM */ ++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ } ++ ++ iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); ++ /* Set input and output transfer mode */ ++ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); ++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); ++ iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); ++ iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); ++ ++ writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); ++ ++ config = readl_relaxed(controller->base + SPI_CONFIG); ++ ++ if (chip->mode & SPI_LOOP) ++ config |= SPI_CONFIG_LOOPBACK; ++ else ++ config &= ~SPI_CONFIG_LOOPBACK; ++ ++ if (chip->mode & SPI_CPHA) ++ config &= ~SPI_CONFIG_INPUT_FIRST; ++ else ++ config |= SPI_CONFIG_INPUT_FIRST; ++ ++ /* ++ * HS_MODE improves signal stability for spi-clk high rates, ++ * but is invalid in loop back mode. ++ */ ++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) ++ config |= SPI_CONFIG_HS_MODE; ++ else ++ config &= ~SPI_CONFIG_HS_MODE; ++ ++ writel_relaxed(config, controller->base + SPI_CONFIG); ++ ++ config = readl_relaxed(controller->base + QUP_CONFIG); ++ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); ++ config |= xfer->bits_per_word - 1; ++ config |= QUP_CONFIG_SPI_MODE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ ++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ return 0; ++} ++ ++static void spi_qup_set_cs(struct spi_device *spi, bool enable) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ u32 iocontol, mask; ++ ++ iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); ++ ++ /* Disable auto CS toggle and use manual */ ++ iocontol &= ~SPI_IO_C_MX_CS_MODE; ++ iocontol |= SPI_IO_C_FORCE_CS; ++ ++ iocontol &= ~SPI_IO_C_CS_SELECT_MASK; ++ iocontol |= SPI_IO_C_CS_SELECT(chip->select); ++ ++ mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; ++ ++ if (enable) ++ iocontol |= mask; ++ else ++ iocontol &= ~mask; ++ ++ writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); ++} ++ ++static int spi_qup_transfer_one(struct spi_master *master, ++ struct spi_device *spi, ++ struct spi_transfer *xfer) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ unsigned long timeout, flags; ++ int ret = -EIO; ++ ++ ret = spi_qup_io_config(controller, chip, xfer); ++ if (ret) ++ return ret; ++ ++ timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC); ++ timeout = DIV_ROUND_UP(xfer->len * 8, timeout); ++ timeout = 100 * msecs_to_jiffies(timeout); ++ ++ reinit_completion(&controller->done); ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->xfer = xfer; ++ controller->error = 0; ++ controller->rx_bytes = 0; ++ controller->tx_bytes = 0; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set RUN state\n"); ++ goto exit; ++ } ++ ++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { ++ dev_warn(controller->dev, "cannot set PAUSE state\n"); ++ goto exit; ++ } ++ ++ spi_qup_fifo_write(controller, xfer); ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set EXECUTE state\n"); ++ goto exit; ++ } ++ ++ if (!wait_for_completion_timeout(&controller->done, timeout)) ++ ret = -ETIMEDOUT; ++exit: ++ spi_qup_set_state(controller, QUP_STATE_RESET); ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->xfer = NULL; ++ if (!ret) ++ ret = controller->error; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ return ret; ++} ++ ++static int spi_qup_setup(struct spi_device *spi) ++{ ++ struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ if (spi->chip_select >= spi->master->num_chipselect) { ++ dev_err(controller->dev, "invalid chip_select %d\n", ++ spi->chip_select); ++ return -EINVAL; ++ } ++ ++ if (spi->max_speed_hz > controller->max_speed_hz) { ++ dev_err(controller->dev, "invalid max_speed_hz %d\n", ++ spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ if (!chip) { ++ /* First setup */ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (!chip) { ++ dev_err(controller->dev, "no memory for chip data\n"); ++ return -ENOMEM; ++ } ++ ++ chip->mode = spi->mode; ++ chip->select = spi->chip_select; ++ spi_set_ctldata(spi, chip); ++ } ++ ++ return 0; ++} ++ ++static void spi_qup_cleanup(struct spi_device *spi) ++{ ++ struct spi_qup_device *chip = spi_get_ctldata(spi); ++ ++ if (!chip) ++ return; ++ ++ spi_set_ctldata(spi, NULL); ++ kfree(chip); ++} ++ ++static int spi_qup_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct clk *iclk, *cclk; ++ struct spi_qup *controller; ++ struct resource *res; ++ struct device *dev; ++ void __iomem *base; ++ u32 data, max_freq, iomode; ++ int ret, irq, size; ++ ++ dev = &pdev->dev; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ irq = platform_get_irq(pdev, 0); ++ ++ if (irq < 0) ++ return irq; ++ ++ cclk = devm_clk_get(dev, "core"); ++ if (IS_ERR(cclk)) ++ return PTR_ERR(cclk); ++ ++ iclk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(iclk)) ++ return PTR_ERR(iclk); ++ ++ /* This is optional parameter */ ++ if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) ++ max_freq = SPI_MAX_RATE; ++ ++ if (!max_freq || max_freq > SPI_MAX_RATE) { ++ dev_err(dev, "invalid clock frequency %d\n", max_freq); ++ return -ENXIO; ++ } ++ ++ ret = clk_prepare_enable(cclk); ++ if (ret) { ++ dev_err(dev, "cannot enable core clock\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(iclk); ++ if (ret) { ++ clk_disable_unprepare(cclk); ++ dev_err(dev, "cannot enable iface clock\n"); ++ return ret; ++ } ++ ++ data = readl_relaxed(base + QUP_HW_VERSION); ++ ++ if (data < QUP_HW_VERSION_2_1_1) { ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ dev_err(dev, "v.%08x is not supported\n", data); ++ return -ENXIO; ++ } ++ ++ master = spi_alloc_master(dev, sizeof(struct spi_qup)); ++ if (!master) { ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ dev_err(dev, "cannot allocate master\n"); ++ return -ENOMEM; ++ } ++ ++ master->bus_num = pdev->id; ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; ++ master->num_chipselect = SPI_NUM_CHIPSELECTS; ++ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ++ master->setup = spi_qup_setup; ++ master->cleanup = spi_qup_cleanup; ++ master->set_cs = spi_qup_set_cs; ++ master->transfer_one = spi_qup_transfer_one; ++ master->dev.of_node = pdev->dev.of_node; ++ master->auto_runtime_pm = true; ++ ++ platform_set_drvdata(pdev, master); ++ ++ controller = spi_master_get_devdata(master); ++ ++ controller->dev = dev; ++ controller->base = base; ++ controller->iclk = iclk; ++ controller->cclk = cclk; ++ controller->irq = irq; ++ controller->max_speed_hz = max_freq; ++ ++ spin_lock_init(&controller->lock); ++ init_completion(&controller->done); ++ ++ iomode = readl_relaxed(base + QUP_IO_M_MODES); ++ ++ size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); ++ if (size) ++ controller->out_blk_sz = size * 16; ++ else ++ controller->out_blk_sz = 4; ++ ++ size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode); ++ if (size) ++ controller->in_blk_sz = size * 16; ++ else ++ controller->in_blk_sz = 4; ++ ++ size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode); ++ controller->out_fifo_sz = controller->out_blk_sz * (2 << size); ++ ++ size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); ++ controller->in_fifo_sz = controller->in_blk_sz * (2 << size); ++ ++ dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ data, controller->in_blk_sz, controller->in_fifo_sz, ++ controller->out_blk_sz, controller->out_fifo_sz); ++ ++ writel_relaxed(1, base + QUP_SW_RESET); ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) { ++ dev_err(dev, "cannot set RESET state\n"); ++ goto error; ++ } ++ ++ writel_relaxed(0, base + QUP_OPERATIONAL); ++ writel_relaxed(0, base + QUP_IO_M_MODES); ++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, ++ base + SPI_ERROR_FLAGS_EN); ++ ++ writel_relaxed(0, base + SPI_CONFIG); ++ writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); ++ ++ ret = devm_request_irq(dev, irq, spi_qup_qup_irq, ++ IRQF_TRIGGER_HIGH, pdev->name, controller); ++ if (ret) ++ goto error; ++ ++ ret = devm_spi_register_master(dev, master); ++ if (ret) ++ goto error; ++ ++ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); ++ pm_runtime_use_autosuspend(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ return 0; ++ ++error: ++ clk_disable_unprepare(cclk); ++ clk_disable_unprepare(iclk); ++ spi_master_put(master); ++ return ret; ++} ++ ++#ifdef CONFIG_PM_RUNTIME ++static int spi_qup_pm_suspend_runtime(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ u32 config; ++ ++ /* Enable clocks auto gaiting */ ++ config = readl(controller->base + QUP_CONFIG); ++ config |= QUP_CLOCK_AUTO_GATE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ return 0; ++} ++ ++static int spi_qup_pm_resume_runtime(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ u32 config; ++ ++ /* Disable clocks auto gaiting */ ++ config = readl_relaxed(controller->base + QUP_CONFIG); ++ config &= ~QUP_CLOCK_AUTO_GATE; ++ writel_relaxed(config, controller->base + QUP_CONFIG); ++ return 0; ++} ++#endif /* CONFIG_PM_RUNTIME */ ++ ++#ifdef CONFIG_PM_SLEEP ++static int spi_qup_suspend(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = spi_master_suspend(master); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(controller->cclk); ++ clk_disable_unprepare(controller->iclk); ++ return 0; ++} ++ ++static int spi_qup_resume(struct device *device) ++{ ++ struct spi_master *master = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = clk_prepare_enable(controller->iclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(controller->cclk); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ return spi_master_resume(master); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static int spi_qup_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = dev_get_drvdata(&pdev->dev); ++ struct spi_qup *controller = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = pm_runtime_get_sync(&pdev->dev); ++ if (ret) ++ return ret; ++ ++ ret = spi_qup_set_state(controller, QUP_STATE_RESET); ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(controller->cclk); ++ clk_disable_unprepare(controller->iclk); ++ ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ spi_master_put(master); ++ return 0; ++} ++ ++static struct of_device_id spi_qup_dt_match[] = { ++ { .compatible = "qcom,spi-qup-v2.1.1", }, ++ { .compatible = "qcom,spi-qup-v2.2.1", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, spi_qup_dt_match); ++ ++static const struct dev_pm_ops spi_qup_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume) ++ SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime, ++ spi_qup_pm_resume_runtime, ++ NULL) ++}; ++ ++static struct platform_driver spi_qup_driver = { ++ .driver = { ++ .name = "spi_qup", ++ .owner = THIS_MODULE, ++ .pm = &spi_qup_dev_pm_ops, ++ .of_match_table = spi_qup_dt_match, ++ }, ++ .probe = spi_qup_probe, ++ .remove = spi_qup_remove, ++}; ++module_platform_driver(spi_qup_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION("0.4"); ++MODULE_ALIAS("platform:spi_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..0d8b14c62c --- /dev/null +++ b/target/linux/ipq806x/patches/0066-spi-qup-Add-device-tree-bindings-information.patch @@ -0,0 +1,111 @@ +From a8e8c90a3cc81c6a7a44ff7fb18ceb71978c9155 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 13 Feb 2014 18:21:23 +0200 +Subject: [PATCH 066/182] spi: qup: Add device tree bindings information + +The Qualcomm Universal Peripheral (QUP) core is an +AHB slave that provides a common data path (an output +FIFO and an input FIFO) for serial peripheral interface +(SPI) mini-core. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 85 ++++++++++++++++++++ + 1 file changed, 85 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-qup.txt + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +new file mode 100644 +index 0000000..b82a268 +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -0,0 +1,85 @@ ++Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) ++ ++The QUP core is an AHB slave that provides a common data path (an output FIFO ++and an input FIFO) for serial peripheral interface (SPI) mini-core. ++ ++SPI in master mode supports up to 50MHz, up to four chip selects, programmable ++data path from 4 bits to 32 bits and numerous protocol variants. ++ ++Required properties: ++- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1" ++- reg: Should contain base register location and length ++- interrupts: Interrupt number used by this controller ++ ++- clocks: Should contain the core clock and the AHB clock. ++- clock-names: Should be "core" for the core clock and "iface" for the ++ AHB clock. ++ ++- #address-cells: Number of cells required to define a chip select ++ address on the SPI bus. Should be set to 1. ++- #size-cells: Should be zero. ++ ++Optional properties: ++- spi-max-frequency: Specifies maximum SPI clock frequency, ++ Units - Hz. Definition as per ++ Documentation/devicetree/bindings/spi/spi-bus.txt ++ ++SPI slave nodes must be children of the SPI master node and can contain ++properties described in Documentation/devicetree/bindings/spi/spi-bus.txt ++ ++Example: ++ ++ spi_8: spi@f9964000 { /* BLSP2 QUP2 */ ++ ++ compatible = "qcom,spi-qup-v2"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0xf9964000 0x1000>; ++ interrupts = <0 102 0>; ++ spi-max-frequency = <19200000>; ++ ++ clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi8_default>; ++ ++ device@0 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0>; /* Chip select 0 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ }; ++ ++ device@1 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <1>; /* Chip select 1 */ ++ spi-max-frequency = <9600000>; ++ spi-cpha; ++ }; ++ ++ device@2 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <2>; /* Chip select 2 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ spi-cpha; ++ }; ++ ++ device@3 { ++ compatible = "arm,pl022-dummy"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <3>; /* Chip select 3 */ ++ spi-max-frequency = <19200000>; ++ spi-cpol; ++ spi-cpha; ++ spi-cs-high; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch new file mode 100644 index 0000000000..decf585fd7 --- /dev/null +++ b/target/linux/ipq806x/patches/0067-spi-qup-Remove-spi_master_put-in-spi_qup_remove.patch @@ -0,0 +1,30 @@ +From c6a3f7bda60cfd78c2b05e8b2ec0fbf0d39da9ea Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Fri, 21 Feb 2014 09:33:16 +0800 +Subject: [PATCH 067/182] spi: qup: Remove spi_master_put in spi_qup_remove + +This driver uses devm_spi_register_master() so don't explicitly call +spi_master_put() in spi_qup_remove(). + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index b0bcc09..5edc56f 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -802,7 +802,6 @@ static int spi_qup_remove(struct platform_device *pdev) + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); +- spi_master_put(master); + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch new file mode 100644 index 0000000000..6914d44a3a --- /dev/null +++ b/target/linux/ipq806x/patches/0068-spi-qup-Convert-ot-let-spi-core-handle-checking-tran.patch @@ -0,0 +1,69 @@ +From a4b122f945c564e47a42a9665b484785e86648de Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Fri, 21 Feb 2014 09:34:16 +0800 +Subject: [PATCH 068/182] spi: qup: Convert ot let spi core handle checking + transfer speed + +Set master->max_speed_hz then spi core will handle checking transfer speed. +So we can remove the same checking in this driver. + +Also remove checking spi->chip_select in spi_qup_setup(), the checking is done +by spi core. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 15 +-------------- + 1 file changed, 1 insertion(+), 14 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 5edc56f..dec339d 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -134,7 +134,6 @@ struct spi_qup { + struct clk *cclk; /* core clock */ + struct clk *iclk; /* interface clock */ + int irq; +- u32 max_speed_hz; + spinlock_t lock; + + int in_fifo_sz; +@@ -517,18 +516,6 @@ static int spi_qup_setup(struct spi_device *spi) + struct spi_qup *controller = spi_master_get_devdata(spi->master); + struct spi_qup_device *chip = spi_get_ctldata(spi); + +- if (spi->chip_select >= spi->master->num_chipselect) { +- dev_err(controller->dev, "invalid chip_select %d\n", +- spi->chip_select); +- return -EINVAL; +- } +- +- if (spi->max_speed_hz > controller->max_speed_hz) { +- dev_err(controller->dev, "invalid max_speed_hz %d\n", +- spi->max_speed_hz); +- return -EINVAL; +- } +- + if (!chip) { + /* First setup */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); +@@ -629,6 +616,7 @@ static int spi_qup_probe(struct platform_device *pdev) + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ++ master->max_speed_hz = max_freq; + master->setup = spi_qup_setup; + master->cleanup = spi_qup_cleanup; + master->set_cs = spi_qup_set_cs; +@@ -645,7 +633,6 @@ static int spi_qup_probe(struct platform_device *pdev) + controller->iclk = iclk; + controller->cclk = cclk; + controller->irq = irq; +- controller->max_speed_hz = max_freq; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch new file mode 100644 index 0000000000..558f8604f2 --- /dev/null +++ b/target/linux/ipq806x/patches/0069-spi-qup-Fix-build-error-due-to-a-typo.patch @@ -0,0 +1,48 @@ +From df7444fdd808111f7df507b00d357b44d3259376 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Sun, 23 Feb 2014 13:27:16 +0800 +Subject: [PATCH 069/182] spi: qup: Fix build error due to a typo + +Fix below build error when CONFIG_PM_RUNTIME=y: + +C [M] drivers/spi/spi-qup.o +drivers/spi/spi-qup.c: In function 'spi_qup_pm_suspend_runtime': +drivers/spi/spi-qup.c:712:12: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) +drivers/spi/spi-qup.c:712:12: note: each undeclared identifier is reported only once for each function it appears in +drivers/spi/spi-qup.c: In function 'spi_qup_pm_resume_runtime': +drivers/spi/spi-qup.c:725:13: error: 'QUP_CLOCK_AUTO_GATE' undeclared (first use in this function) +make[2]: *** [drivers/spi/spi-qup.o] Error 1 +make[1]: *** [drivers/spi] Error 2 +make: *** [drivers] Error 2 + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index dec339d..886edb4 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -709,7 +709,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device) + + /* Enable clocks auto gaiting */ + config = readl(controller->base + QUP_CONFIG); +- config |= QUP_CLOCK_AUTO_GATE; ++ config |= QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; + } +@@ -722,7 +722,7 @@ static int spi_qup_pm_resume_runtime(struct device *device) + + /* Disable clocks auto gaiting */ + config = readl_relaxed(controller->base + QUP_CONFIG); +- config &= ~QUP_CLOCK_AUTO_GATE; ++ config &= ~QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch new file mode 100644 index 0000000000..aee0bf488c --- /dev/null +++ b/target/linux/ipq806x/patches/0070-spi-qup-Enable-driver-compilation-with-COMPILE_TEST.patch @@ -0,0 +1,30 @@ +From 4706cb5844e0f1a08ab25e103f6415fde5328ddc Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Sun, 23 Feb 2014 13:28:33 +0800 +Subject: [PATCH 070/182] spi: qup: Enable driver compilation with + COMPILE_TEST + +This helps increasing build testing coverage. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 9e9e3ed..e6a04f8 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT ++ depends on ARCH_MSM_DT || COMPILE_TEST + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch new file mode 100644 index 0000000000..ce773da713 --- /dev/null +++ b/target/linux/ipq806x/patches/0071-spi-qup-Depend-on-ARM-COMPILE_TEST-to-avoid-build-er.patch @@ -0,0 +1,40 @@ +From 7137376bc6415ab9b2f0c5983245a1273812e8b9 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 12:07:51 +0800 +Subject: [PATCH 071/182] spi: qup: Depend on ARM && COMPILE_TEST to avoid + build error + +This driver uses writel_relaxed() which does not exist in x86, ppc, etc. +Make it depend on ARM && COMPILE_TEST to avoid below build error: + + CC [M] drivers/spi/spi-qup.o +drivers/spi/spi-qup.c: In function 'spi_qup_set_state': +drivers/spi/spi-qup.c:180:3: error: implicit declaration of function 'writel_relaxed' [-Werror=implicit-function-declaration] +cc1: some warnings being treated as errors +make[2]: *** [drivers/spi/spi-qup.o] Error 1 +make[1]: *** [drivers/spi] Error 2 +make: *** [drivers] Error 2 + +Reported-by: Stephen Rothwell <sfr@canb.auug.org.au> +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index e6a04f8..2d9111c 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT || COMPILE_TEST ++ depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch new file mode 100644 index 0000000000..fcdc517770 --- /dev/null +++ b/target/linux/ipq806x/patches/0072-spi-qup-Remove-module-version.patch @@ -0,0 +1,28 @@ +From 7b8e00404cfc652abbd538df1365a8046ed0d782 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 13:48:12 +0800 +Subject: [PATCH 072/182] spi: qup: Remove module version + +The module version is unlikely to be updated, use kernel version should be +enough. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 886edb4..203f0d4 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -819,5 +819,4 @@ static struct platform_driver spi_qup_driver = { + module_platform_driver(spi_qup_driver); + + MODULE_LICENSE("GPL v2"); +-MODULE_VERSION("0.4"); + MODULE_ALIAS("platform:spi_qup"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch new file mode 100644 index 0000000000..69b4609a5f --- /dev/null +++ b/target/linux/ipq806x/patches/0073-spi-qup-Get-rid-of-using-struct-spi_qup_device.patch @@ -0,0 +1,167 @@ +From 3027505ab8c8cb17291e53bca1d7bd770539d468 Mon Sep 17 00:00:00 2001 +From: Axel Lin <axel.lin@ingics.com> +Date: Mon, 24 Feb 2014 23:07:36 +0800 +Subject: [PATCH 073/182] spi: qup: Get rid of using struct spi_qup_device + +Current code uses struct spi_qup_device to store spi->mode and spi->chip_select +settings. We can get these settings in spi_qup_transfer_one and spi_qup_set_cs +without using struct spi_qup_device. Refactor the code a bit to remove +spi_qup_setup(), spi_qup_cleanup(), and struct spi_qup_device. + +Signed-off-by: Axel Lin <axel.lin@ingics.com> +Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 61 ++++++++----------------------------------------- + 1 file changed, 9 insertions(+), 52 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 203f0d4..b032e88 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -123,11 +123,6 @@ + #define SPI_DELAY_THRESHOLD 1 + #define SPI_DELAY_RETRY 10 + +-struct spi_qup_device { +- int select; +- u16 mode; +-}; +- + struct spi_qup { + void __iomem *base; + struct device *dev; +@@ -338,14 +333,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + + + /* set clock freq ... bits per word */ +-static int spi_qup_io_config(struct spi_qup *controller, +- struct spi_qup_device *chip, +- struct spi_transfer *xfer) ++static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + { ++ struct spi_qup *controller = spi_master_get_devdata(spi->master); + u32 config, iomode, mode; + int ret, n_words, w_size; + +- if (chip->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { ++ if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", + xfer->len, controller->in_fifo_sz); + return -EIO; +@@ -399,12 +393,12 @@ static int spi_qup_io_config(struct spi_qup *controller, + + config = readl_relaxed(controller->base + SPI_CONFIG); + +- if (chip->mode & SPI_LOOP) ++ if (spi->mode & SPI_LOOP) + config |= SPI_CONFIG_LOOPBACK; + else + config &= ~SPI_CONFIG_LOOPBACK; + +- if (chip->mode & SPI_CPHA) ++ if (spi->mode & SPI_CPHA) + config &= ~SPI_CONFIG_INPUT_FIRST; + else + config |= SPI_CONFIG_INPUT_FIRST; +@@ -413,7 +407,7 @@ static int spi_qup_io_config(struct spi_qup *controller, + * HS_MODE improves signal stability for spi-clk high rates, + * but is invalid in loop back mode. + */ +- if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(chip->mode & SPI_LOOP)) ++ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP)) + config |= SPI_CONFIG_HS_MODE; + else + config &= ~SPI_CONFIG_HS_MODE; +@@ -433,7 +427,6 @@ static int spi_qup_io_config(struct spi_qup *controller, + static void spi_qup_set_cs(struct spi_device *spi, bool enable) + { + struct spi_qup *controller = spi_master_get_devdata(spi->master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); + + u32 iocontol, mask; + +@@ -444,9 +437,9 @@ static void spi_qup_set_cs(struct spi_device *spi, bool enable) + iocontol |= SPI_IO_C_FORCE_CS; + + iocontol &= ~SPI_IO_C_CS_SELECT_MASK; +- iocontol |= SPI_IO_C_CS_SELECT(chip->select); ++ iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); + +- mask = SPI_IO_C_CS_N_POLARITY_0 << chip->select; ++ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; + + if (enable) + iocontol |= mask; +@@ -461,11 +454,10 @@ static int spi_qup_transfer_one(struct spi_master *master, + struct spi_transfer *xfer) + { + struct spi_qup *controller = spi_master_get_devdata(master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); + unsigned long timeout, flags; + int ret = -EIO; + +- ret = spi_qup_io_config(controller, chip, xfer); ++ ret = spi_qup_io_config(spi, xfer); + if (ret) + return ret; + +@@ -511,38 +503,6 @@ exit: + return ret; + } + +-static int spi_qup_setup(struct spi_device *spi) +-{ +- struct spi_qup *controller = spi_master_get_devdata(spi->master); +- struct spi_qup_device *chip = spi_get_ctldata(spi); +- +- if (!chip) { +- /* First setup */ +- chip = kzalloc(sizeof(*chip), GFP_KERNEL); +- if (!chip) { +- dev_err(controller->dev, "no memory for chip data\n"); +- return -ENOMEM; +- } +- +- chip->mode = spi->mode; +- chip->select = spi->chip_select; +- spi_set_ctldata(spi, chip); +- } +- +- return 0; +-} +- +-static void spi_qup_cleanup(struct spi_device *spi) +-{ +- struct spi_qup_device *chip = spi_get_ctldata(spi); +- +- if (!chip) +- return; +- +- spi_set_ctldata(spi, NULL); +- kfree(chip); +-} +- + static int spi_qup_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -561,7 +521,6 @@ static int spi_qup_probe(struct platform_device *pdev) + return PTR_ERR(base); + + irq = platform_get_irq(pdev, 0); +- + if (irq < 0) + return irq; + +@@ -617,8 +576,6 @@ static int spi_qup_probe(struct platform_device *pdev) + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; +- master->setup = spi_qup_setup; +- master->cleanup = spi_qup_cleanup; + master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch new file mode 100644 index 0000000000..9f279e07a3 --- /dev/null +++ b/target/linux/ipq806x/patches/0074-spi-qup-Depend-on-ARCH_QCOM.patch @@ -0,0 +1,36 @@ +From 5148138d2b879c7b024fa3865fb3980c6dc84fcc Mon Sep 17 00:00:00 2001 +From: Paul Bolle <pebolle@tiscali.nl> +Date: Mon, 7 Apr 2014 16:15:45 +0200 +Subject: [PATCH 074/182] spi: qup: Depend on ARCH_QCOM + +Commit 8fc1b0f87d9f ("ARM: qcom: Split Qualcomm support into legacy and +multiplatform") removed Kconfig symbol ARCH_MSM_DT. But that commit +left one (optional) dependency on ARCH_MSM_DT untouched. + +Three Kconfig symbols used to depend on ARCH_MSM_DT: ARCH_MSM8X60, +ARCH_MSM8960, and ARCH_MSM8974. These three symbols now depend on +ARCH_QCOM. So it appears this driver needs to depend on ARCH_QCOM too. + +Signed-off-by: Paul Bolle <pebolle@tiscali.nl> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 2d9111c..a2d3570 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -383,7 +383,7 @@ config SPI_RSPI + + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" +- depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) ++ depends on ARCH_QCOM || (ARM && COMPILE_TEST) + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch new file mode 100644 index 0000000000..f1071dbea1 --- /dev/null +++ b/target/linux/ipq806x/patches/0075-spi-qup-Correct-selection-of-FIFO-Block-mode.patch @@ -0,0 +1,39 @@ +From e8e77359524e6629bac68db4183694fccffaed8e Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Tue, 13 May 2014 16:34:42 -0500 +Subject: [PATCH 075/182] spi: qup: Correct selection of FIFO/Block mode + +This patch fixes the calculation for determining whether to use FIFO or BLOCK +mode. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Mark Brown <broonie@linaro.org> +--- + drivers/spi/spi-qup.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index b032e88..65bf18e 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -287,7 +287,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { +- dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", ++ dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; + } +@@ -366,7 +366,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + n_words = xfer->len / w_size; + controller->w_size = w_size; + +- if (n_words <= controller->in_fifo_sz) { ++ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { + mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch new file mode 100644 index 0000000000..c801ed5b35 --- /dev/null +++ b/target/linux/ipq806x/patches/0078-clk-qcom-Consolidate-common-probe-code.patch @@ -0,0 +1,755 @@ +From d79cb0c583772f38a9367af106d61bcf8bfa08e4 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 21 Mar 2014 17:59:37 -0700 +Subject: [PATCH 078/182] clk: qcom: Consolidate common probe code + +Most of the probe code is the same between all the different +clock controllers. Consolidate the code into a common.c file. +This makes changes to the common probe parts easier and reduces +chances for bugs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/common.c | 99 +++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/common.h | 34 ++++++++++++++ + drivers/clk/qcom/gcc-msm8660.c | 87 +++++----------------------------- + drivers/clk/qcom/gcc-msm8960.c | 77 +++++------------------------- + drivers/clk/qcom/gcc-msm8974.c | 77 +++++------------------------- + drivers/clk/qcom/mmcc-msm8960.c | 78 +++++------------------------- + drivers/clk/qcom/mmcc-msm8974.c | 80 +++++++------------------------ + 8 files changed, 196 insertions(+), 337 deletions(-) + create mode 100644 drivers/clk/qcom/common.c + create mode 100644 drivers/clk/qcom/common.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index f60db2e..689e05b 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -1,5 +1,6 @@ + obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o + ++clk-qcom-y += common.o + clk-qcom-y += clk-regmap.o + clk-qcom-y += clk-pll.o + clk-qcom-y += clk-rcg.o +diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c +new file mode 100644 +index 0000000..86b45fb +--- /dev/null ++++ b/drivers/clk/qcom/common.c +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/export.h> ++#include <linux/regmap.h> ++#include <linux/platform_device.h> ++#include <linux/clk-provider.h> ++#include <linux/reset-controller.h> ++ ++#include "common.h" ++#include "clk-regmap.h" ++#include "reset.h" ++ ++struct qcom_cc { ++ struct qcom_reset_controller reset; ++ struct clk_onecell_data data; ++ struct clk *clks[]; ++}; ++ ++int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) ++{ ++ void __iomem *base; ++ struct resource *res; ++ int i, ret; ++ struct device *dev = &pdev->dev; ++ struct clk *clk; ++ struct clk_onecell_data *data; ++ struct clk **clks; ++ struct regmap *regmap; ++ struct qcom_reset_controller *reset; ++ struct qcom_cc *cc; ++ size_t num_clks = desc->num_clks; ++ struct clk_regmap **rclks = desc->clks; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap = devm_regmap_init_mmio(dev, base, desc->config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, ++ GFP_KERNEL); ++ if (!cc) ++ return -ENOMEM; ++ ++ clks = cc->clks; ++ data = &cc->data; ++ data->clks = clks; ++ data->clk_num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rclks[i]) ++ continue; ++ clk = devm_clk_register_regmap(dev, rclks[i]); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[i] = clk; ++ } ++ ++ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); ++ if (ret) ++ return ret; ++ ++ reset = &cc->reset; ++ reset->rcdev.of_node = dev->of_node; ++ reset->rcdev.ops = &qcom_reset_ops; ++ reset->rcdev.owner = dev->driver->owner; ++ reset->rcdev.nr_resets = desc->num_resets; ++ reset->regmap = regmap; ++ reset->reset_map = desc->resets; ++ platform_set_drvdata(pdev, &reset->rcdev); ++ ++ ret = reset_controller_register(&reset->rcdev); ++ if (ret) ++ of_clk_del_provider(dev->of_node); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(qcom_cc_probe); ++ ++void qcom_cc_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ reset_controller_unregister(platform_get_drvdata(pdev)); ++} ++EXPORT_SYMBOL_GPL(qcom_cc_remove); +diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h +new file mode 100644 +index 0000000..2c3cfc8 +--- /dev/null ++++ b/drivers/clk/qcom/common.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __QCOM_CLK_COMMON_H__ ++#define __QCOM_CLK_COMMON_H__ ++ ++struct platform_device; ++struct regmap_config; ++struct clk_regmap; ++struct qcom_reset_map; ++ ++struct qcom_cc_desc { ++ const struct regmap_config *config; ++ struct clk_regmap **clks; ++ size_t num_clks; ++ const struct qcom_reset_map *resets; ++ size_t num_resets; ++}; ++ ++extern int qcom_cc_probe(struct platform_device *pdev, ++ const struct qcom_cc_desc *desc); ++ ++extern void qcom_cc_remove(struct platform_device *pdev); ++ ++#endif +diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c +index bc0b7f1..44bc6fa 100644 +--- a/drivers/clk/qcom/gcc-msm8660.c ++++ b/drivers/clk/qcom/gcc-msm8660.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8660.h> + #include <dt-bindings/reset/qcom,gcc-msm8660.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2701,94 +2702,28 @@ static const struct regmap_config gcc_msm8660_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8660_desc = { ++ .config = &gcc_msm8660_regmap_config, ++ .clks = gcc_msm8660_clks, ++ .num_clks = ARRAY_SIZE(gcc_msm8660_clks), ++ .resets = gcc_msm8660_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8660_resets), ++}; ++ + static const struct of_device_id gcc_msm8660_match_table[] = { + { .compatible = "qcom,gcc-msm8660" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8660_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8660_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8660_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8660_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8660_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8660_desc); + } + + static int gcc_msm8660_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index fd446ab..633b019 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8960.h> + #include <dt-bindings/reset/qcom,gcc-msm8960.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2875,51 +2876,24 @@ static const struct regmap_config gcc_msm8960_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8960_desc = { ++ .config = &gcc_msm8960_regmap_config, ++ .clks = gcc_msm8960_clks, ++ .num_clks = ARRAY_SIZE(gcc_msm8960_clks), ++ .resets = gcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets), ++}; ++ + static const struct of_device_id gcc_msm8960_match_table[] = { + { .compatible = "qcom,gcc-msm8960" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8960_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; + struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8960_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8960_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; ++ struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +@@ -2930,39 +2904,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8960_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8960_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8960_desc); + } + + static int gcc_msm8960_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 51d457e..0d1edc1 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,gcc-msm8974.h> + #include <dt-bindings/reset/qcom,gcc-msm8974.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc gcc_msm8974_desc = { ++ .config = &gcc_msm8974_regmap_config, ++ .clks = gcc_msm8974_clocks, ++ .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), ++ .resets = gcc_msm8974_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8974_resets), ++}; ++ + static const struct of_device_id gcc_msm8974_match_table[] = { + { .compatible = "qcom,gcc-msm8974" }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int gcc_msm8974_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; + struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8974_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(gcc_msm8974_clocks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; ++ struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +@@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- for (i = 0; i < num_clks; i++) { +- if (!gcc_msm8974_clocks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets), +- reset->regmap = regmap; +- reset->reset_map = gcc_msm8974_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &gcc_msm8974_desc); + } + + static int gcc_msm8974_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c +index f9b59c7..12f3c0b 100644 +--- a/drivers/clk/qcom/mmcc-msm8960.c ++++ b/drivers/clk/qcom/mmcc-msm8960.c +@@ -26,6 +26,7 @@ + #include <dt-bindings/clock/qcom,mmcc-msm8960.h> + #include <dt-bindings/reset/qcom,mmcc-msm8960.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc mmcc_msm8960_desc = { ++ .config = &mmcc_msm8960_regmap_config, ++ .clks = mmcc_msm8960_clks, ++ .num_clks = ARRAY_SIZE(mmcc_msm8960_clks), ++ .resets = mmcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(mmcc_msm8960_resets), ++}; ++ + static const struct of_device_id mmcc_msm8960_match_table[] = { + { .compatible = "qcom,mmcc-msm8960" }, + { } + }; + MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int mmcc_msm8960_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; +- struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8960_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(mmcc_msm8960_clks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- for (i = 0; i < num_clks; i++) { +- if (!mmcc_msm8960_clks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); +- if (ret) +- return ret; +- +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets), +- reset->regmap = regmap; +- reset->reset_map = mmcc_msm8960_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); +- +- return ret; ++ return qcom_cc_probe(pdev, &mmcc_msm8960_desc); + } + + static int mmcc_msm8960_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c +index c957745..60b7c24 100644 +--- a/drivers/clk/qcom/mmcc-msm8974.c ++++ b/drivers/clk/qcom/mmcc-msm8974.c +@@ -25,6 +25,7 @@ + #include <dt-bindings/clock/qcom,mmcc-msm8974.h> + #include <dt-bindings/reset/qcom,mmcc-msm8974.h> + ++#include "common.h" + #include "clk-regmap.h" + #include "clk-pll.h" + #include "clk-rcg.h" +@@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = { + .fast_io = true, + }; + ++static const struct qcom_cc_desc mmcc_msm8974_desc = { ++ .config = &mmcc_msm8974_regmap_config, ++ .clks = mmcc_msm8974_clocks, ++ .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks), ++ .resets = mmcc_msm8974_resets, ++ .num_resets = ARRAY_SIZE(mmcc_msm8974_resets), ++}; ++ + static const struct of_device_id mmcc_msm8974_match_table[] = { + { .compatible = "qcom,mmcc-msm8974" }, + { } + }; + MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table); + +-struct qcom_cc { +- struct qcom_reset_controller reset; +- struct clk_onecell_data data; +- struct clk *clks[]; +-}; +- + static int mmcc_msm8974_probe(struct platform_device *pdev) + { +- void __iomem *base; +- struct resource *res; +- int i, ret; +- struct device *dev = &pdev->dev; +- struct clk *clk; +- struct clk_onecell_data *data; +- struct clk **clks; ++ int ret; + struct regmap *regmap; +- size_t num_clks; +- struct qcom_reset_controller *reset; +- struct qcom_cc *cc; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8974_regmap_config); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- num_clks = ARRAY_SIZE(mmcc_msm8974_clocks); +- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, +- GFP_KERNEL); +- if (!cc) +- return -ENOMEM; +- +- clks = cc->clks; +- data = &cc->data; +- data->clks = clks; +- data->clk_num = num_clks; +- +- clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); +- clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); + +- for (i = 0; i < num_clks; i++) { +- if (!mmcc_msm8974_clocks[i]) +- continue; +- clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- clks[i] = clk; +- } +- +- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); ++ ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc); + if (ret) + return ret; + +- reset = &cc->reset; +- reset->rcdev.of_node = dev->of_node; +- reset->rcdev.ops = &qcom_reset_ops, +- reset->rcdev.owner = THIS_MODULE, +- reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets), +- reset->regmap = regmap; +- reset->reset_map = mmcc_msm8974_resets, +- platform_set_drvdata(pdev, &reset->rcdev); +- +- ret = reset_controller_register(&reset->rcdev); +- if (ret) +- of_clk_del_provider(dev->of_node); ++ regmap = dev_get_regmap(&pdev->dev, NULL); ++ clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); ++ clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); + +- return ret; ++ return 0; + } + + static int mmcc_msm8974_remove(struct platform_device *pdev) + { +- of_clk_del_provider(pdev->dev.of_node); +- reset_controller_unregister(platform_get_drvdata(pdev)); ++ qcom_cc_remove(pdev); + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch new file mode 100644 index 0000000000..d83d5d9696 --- /dev/null +++ b/target/linux/ipq806x/patches/0079-clk-qcom-Add-basic-support-for-APQ8064-global-clock-.patch @@ -0,0 +1,123 @@ +From 0f171b8a6e1723f0ce6f98f8b3fba7d2583088c1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 4 Apr 2014 11:31:29 -0500 +Subject: [PATCH 079/182] clk: qcom: Add basic support for APQ8064 global + clock controller clocks + +The APQ8064 and MSM8960 share a significant amount of clock data and +code between the two SoCs. Rather than duplicating the data we just add +support for a unqiue APQ8064 clock table into the MSM8960 code. + +For now add just enough clocks to get a basic serial port going on an +APQ8064 device. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +[mturquette@linaro.org: trivial conflict due to missing ipq8064 support] +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 1 + + drivers/clk/qcom/Kconfig | 4 +-- + drivers/clk/qcom/gcc-msm8960.c | 30 ++++++++++++++++++-- + 3 files changed, 30 insertions(+), 5 deletions(-) + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 767401f..7b7104e 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding + Required properties : + - compatible : shall contain only one of the following: + ++ "qcom,gcc-apq8064" + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 995bcfa..7f696b7 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -13,10 +13,10 @@ config MSM_GCC_8660 + i2c, USB, SD/eMMC, etc. + + config MSM_GCC_8960 +- tristate "MSM8960 Global Clock Controller" ++ tristate "APQ8064/MSM8960 Global Clock Controller" + depends on COMMON_CLK_QCOM + help +- Support for the global clock controller on msm8960 devices. ++ Support for the global clock controller on apq8064/msm8960 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, SATA, PCIe, etc. + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index 633b019..8e2b6dd 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2013, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and +@@ -2868,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [RIVA_RESET] = { 0x35e0 }, + }; + ++static struct clk_regmap *gcc_apq8064_clks[] = { ++ [PLL8] = &pll8.clkr, ++ [PLL8_VOTE] = &pll8_vote, ++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, ++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, ++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, ++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, ++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, ++}; ++ + static const struct regmap_config gcc_msm8960_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, +@@ -2884,8 +2894,17 @@ static const struct qcom_cc_desc gcc_msm8960_desc = { + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), + }; + ++static const struct qcom_cc_desc gcc_apq8064_desc = { ++ .config = &gcc_msm8960_regmap_config, ++ .clks = gcc_apq8064_clks, ++ .num_clks = ARRAY_SIZE(gcc_apq8064_clks), ++ .resets = gcc_msm8960_resets, ++ .num_resets = ARRAY_SIZE(gcc_msm8960_resets), ++}; ++ + static const struct of_device_id gcc_msm8960_match_table[] = { +- { .compatible = "qcom,gcc-msm8960" }, ++ { .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc }, ++ { .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); +@@ -2894,6 +2913,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ ++ match = of_match_device(gcc_msm8960_match_table, &pdev->dev); ++ if (!match) ++ return -EINVAL; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +@@ -2904,7 +2928,7 @@ static int gcc_msm8960_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- return qcom_cc_probe(pdev, &gcc_msm8960_desc); ++ return qcom_cc_probe(pdev, match->data); + } + + static int gcc_msm8960_remove(struct platform_device *pdev) +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch new file mode 100644 index 0000000000..3cc5bf828a --- /dev/null +++ b/target/linux/ipq806x/patches/0080-clk-qcom-Various-fixes-for-MSM8960-s-global-clock-co.patch @@ -0,0 +1,101 @@ +From 7456451e9df88d4c33479e3d4ea124d8a91ceb57 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 4 Apr 2014 11:32:56 -0500 +Subject: [PATCH 080/182] clk: qcom: Various fixes for MSM8960's global clock + controller + +* Remove CE2_SLEEP_CLK, doesn't exist on 8960 family SoCs +* Fix incorrect offset for PMIC_SSBI2_RESET +* Fix typo: + SIC_TIC -> SPS_TIC_H + SFAB_ADM0_M2_A_CLK -> SFAB_ADM0_M2_H_CLK +* Fix naming convention: + SFAB_CFPB_S_HCLK -> SFAB_CFPB_S_H_CLK + SATA_SRC_CLK -> SATA_CLK_SRC + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8960.c | 4 ++-- + include/dt-bindings/clock/qcom,gcc-msm8960.h | 7 +++---- + include/dt-bindings/reset/qcom,gcc-msm8960.h | 2 +- + 3 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index 8e2b6dd..f4ffd91 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -2810,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [PPSS_PROC_RESET] = { 0x2594, 1 }, + [PPSS_RESET] = { 0x2594}, + [DMA_BAM_RESET] = { 0x25c0, 7 }, +- [SIC_TIC_RESET] = { 0x2600, 7 }, ++ [SPS_TIC_H_RESET] = { 0x2600, 7 }, + [SLIMBUS_H_RESET] = { 0x2620, 7 }, + [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, + [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, +@@ -2823,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { + [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, + [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, + [RPM_PROC_RESET] = { 0x27c0, 7 }, +- [PMIC_SSBI2_RESET] = { 0x270c, 12 }, ++ [PMIC_SSBI2_RESET] = { 0x280c, 12 }, + [SDC1_RESET] = { 0x2830 }, + [SDC2_RESET] = { 0x2850 }, + [SDC3_RESET] = { 0x2870 }, +diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h +index 03bbf49..f9f5471 100644 +--- a/include/dt-bindings/clock/qcom,gcc-msm8960.h ++++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h +@@ -51,7 +51,7 @@ + #define QDSS_TSCTR_CLK 34 + #define SFAB_ADM0_M0_A_CLK 35 + #define SFAB_ADM0_M1_A_CLK 36 +-#define SFAB_ADM0_M2_A_CLK 37 ++#define SFAB_ADM0_M2_H_CLK 37 + #define ADM0_CLK 38 + #define ADM0_PBUS_CLK 39 + #define MSS_XPU_CLK 40 +@@ -99,7 +99,7 @@ + #define CFPB2_H_CLK 82 + #define SFAB_CFPB_M_H_CLK 83 + #define CFPB_MASTER_H_CLK 84 +-#define SFAB_CFPB_S_HCLK 85 ++#define SFAB_CFPB_S_H_CLK 85 + #define CFPB_SPLITTER_H_CLK 86 + #define TSIF_H_CLK 87 + #define TSIF_INACTIVITY_TIMERS_CLK 88 +@@ -110,7 +110,6 @@ + #define CE1_SLEEP_CLK 93 + #define CE2_H_CLK 94 + #define CE2_CORE_CLK 95 +-#define CE2_SLEEP_CLK 96 + #define SFPB_H_CLK_SRC 97 + #define SFPB_H_CLK 98 + #define SFAB_SFPB_M_H_CLK 99 +@@ -252,7 +251,7 @@ + #define MSS_S_H_CLK 235 + #define MSS_CXO_SRC_CLK 236 + #define SATA_H_CLK 237 +-#define SATA_SRC_CLK 238 ++#define SATA_CLK_SRC 238 + #define SATA_RXOOB_CLK 239 + #define SATA_PMALIVE_CLK 240 + #define SATA_PHY_REF_CLK 241 +diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h +index a840e68..07edd0e 100644 +--- a/include/dt-bindings/reset/qcom,gcc-msm8960.h ++++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h +@@ -58,7 +58,7 @@ + #define PPSS_PROC_RESET 41 + #define PPSS_RESET 42 + #define DMA_BAM_RESET 43 +-#define SIC_TIC_RESET 44 ++#define SPS_TIC_H_RESET 44 + #define SLIMBUS_H_RESET 45 + #define SFAB_CFPB_M_RESET 46 + #define SFAB_CFPB_S_RESET 47 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch new file mode 100644 index 0000000000..f022463d28 --- /dev/null +++ b/target/linux/ipq806x/patches/0081-ARM-config-Add-qcom_defconfig.patch @@ -0,0 +1,190 @@ +From e1f51c2d41c8936b8383cf4ac53c1ff05b0e2e2f Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 27 Feb 2014 12:54:24 -0800 +Subject: [PATCH 081/182] ARM: config: Add qcom_defconfig + +Add a defconfig for mach-qcom platforms (copied from msm_defconfig). +Although these platforms are part of the multi-platform kernel, it's +useful to have a stripped down version of the defconfig that just +selects the DT based Qualcomm platforms and drivers. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 164 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 164 insertions(+) + create mode 100644 arch/arm/configs/qcom_defconfig + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +new file mode 100644 +index 0000000..bfed753 +--- /dev/null ++++ b/arch/arm/configs/qcom_defconfig +@@ -0,0 +1,164 @@ ++CONFIG_SYSVIPC=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS_ALL=y ++CONFIG_EMBEDDED=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=y ++CONFIG_KPROBES=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODULE_FORCE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_ARCH_QCOM=y ++CONFIG_ARCH_MSM8X60=y ++CONFIG_ARCH_MSM8960=y ++CONFIG_ARCH_MSM8974=y ++CONFIG_SMP=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_HIGHMEM=y ++CONFIG_HIGHPTE=y ++CONFIG_CLEANCACHE=y ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_IPV6 is not set ++CONFIG_CFG80211=y ++CONFIG_RFKILL=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_MTD=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_M25P80=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_SCSI=y ++CONFIG_SCSI_TGT=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_SG=y ++CONFIG_CHR_DEV_SCH=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++CONFIG_NETDEVICES=y ++CONFIG_DUMMY=y ++CONFIG_MDIO_BITBANG=y ++CONFIG_MDIO_GPIO=y ++CONFIG_SLIP=y ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_MODE_SLIP6=y ++CONFIG_USB_USBNET=y ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_MOUSE_PS2 is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_UINPUT=y ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_MSM=y ++CONFIG_SERIAL_MSM_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_MSM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_QUP=y ++CONFIG_SPI=y ++CONFIG_SPI_QUP=y ++CONFIG_SPMI=y ++CONFIG_PINCTRL_APQ8064=y ++CONFIG_PINCTRL_IPQ8064=y ++CONFIG_PINCTRL_MSM8X74=y ++CONFIG_DEBUG_GPIO=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_POWER_RESET=y ++CONFIG_POWER_RESET_MSM=y ++CONFIG_THERMAL=y ++CONFIG_REGULATOR=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_FB=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_DYNAMIC_MINORS=y ++# CONFIG_SND_ARM is not set ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_SOC=y ++CONFIG_HID_BATTERY_STRENGTH=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_ACM=y ++CONFIG_USB_SERIAL=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_DEBUG_FILES=y ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=16 ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_MSM=y ++CONFIG_RTC_CLASS=y ++CONFIG_DMADEVICES=y ++CONFIG_QCOM_BAM_DMA=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_QCOM=y ++CONFIG_MSM_GCC_8660=y ++CONFIG_MSM_MMCC_8960=y ++CONFIG_MSM_MMCC_8974=y ++CONFIG_MSM_IOMMU=y ++CONFIG_GENERIC_PHY=y ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT3_FS=y ++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set ++CONFIG_EXT4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_CIFS=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_UTF8=y ++CONFIG_PRINTK_TIME=y ++CONFIG_DYNAMIC_DEBUG=y ++CONFIG_DEBUG_INFO=y ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_LOCKUP_DETECTOR=y ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++CONFIG_TIMER_STATS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch new file mode 100644 index 0000000000..7bcd2463da --- /dev/null +++ b/target/linux/ipq806x/patches/0082-ARM-qcom-Enable-GSBI-driver-in-defconfig.patch @@ -0,0 +1,25 @@ +From d247fb8a019e4cc5a607c7fff9922e8e98c2c660 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 29 May 2014 11:25:24 -0500 +Subject: [PATCH 082/182] ARM: qcom: Enable GSBI driver in defconfig + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index bfed753..42ebd72 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -131,6 +131,7 @@ CONFIG_RTC_CLASS=y + CONFIG_DMADEVICES=y + CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y ++CONFIG_QCOM_GSBI=y + CONFIG_COMMON_CLK_QCOM=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch new file mode 100644 index 0000000000..837ec9d0c5 --- /dev/null +++ b/target/linux/ipq806x/patches/0083-soc-Introduce-drivers-soc-place-holder-for-SOC-speci.patch @@ -0,0 +1,68 @@ +From 2dac9498966c79a6abb09764419ffd42a44c78cf Mon Sep 17 00:00:00 2001 +From: Santosh Shilimkar <santosh.shilimkar@ti.com> +Date: Wed, 23 Apr 2014 19:46:17 -0400 +Subject: [PATCH 083/182] soc: Introduce drivers/soc place-holder for SOC + specific drivers + +Based on earlier thread "https://lkml.org/lkml/2013/10/7/662" and +discussion at Kernel Summit'2013, it was agreed to create +'driver/soc' for drivers which are quite SOC specific. + +Further discussion on the subject is in response to +the earlier version of the patch is here: + http://lwn.net/Articles/588942/ + +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: Kumar Gala <galak@codeaurora.org> +Cc: Paul Walmsley <paul@pwsan.com> +Cc: Olof Johansson <olof@lixom.net> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Sandeep Nair <sandeep_n@ti.com> +Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/Kconfig | 2 ++ + drivers/Makefile | 3 +++ + drivers/soc/Kconfig | 3 +++ + 3 files changed, 8 insertions(+) + create mode 100644 drivers/soc/Kconfig + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index e0a4ae6..a299cbd 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -132,6 +132,8 @@ source "drivers/staging/Kconfig" + + source "drivers/platform/Kconfig" + ++source "drivers/soc/Kconfig" ++ + source "drivers/clk/Kconfig" + + source "drivers/hwspinlock/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 3d6de8b..4c2bdc1 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -33,6 +33,9 @@ obj-y += amba/ + # really early. + obj-$(CONFIG_DMADEVICES) += dma/ + ++# SOC specific infrastructure drivers. ++obj-y += soc/ ++ + obj-$(CONFIG_VIRTIO) += virtio/ + obj-$(CONFIG_XEN) += xen/ + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +new file mode 100644 +index 0000000..339baa8 +--- /dev/null ++++ b/drivers/soc/Kconfig +@@ -0,0 +1,3 @@ ++menu "SOC (System On Chip) specific Drivers" ++ ++endmenu +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch new file mode 100644 index 0000000000..f52255826c --- /dev/null +++ b/target/linux/ipq806x/patches/0084-soc-qcom-Add-GSBI-driver.patch @@ -0,0 +1,162 @@ +From a2f0fc20ea49e5dbbdbb21444683ea760fbdd38f Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:21 -0500 +Subject: [PATCH 084/182] soc: qcom: Add GSBI driver + +The GSBI (General Serial Bus Interface) driver controls the overarching +configuration of the shared serial bus infrastructure on APQ8064, IPQ8064, and +earlier QCOM processors. The GSBI supports UART, I2C, SPI, and UIM +functionality in various combinations. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/soc/Kconfig | 2 + + drivers/soc/Makefile | 5 +++ + drivers/soc/qcom/Kconfig | 11 ++++++ + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/qcom_gsbi.c | 84 ++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 103 insertions(+) + create mode 100644 drivers/soc/Makefile + create mode 100644 drivers/soc/qcom/Kconfig + create mode 100644 drivers/soc/qcom/Makefile + create mode 100644 drivers/soc/qcom/qcom_gsbi.c + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +index 339baa8..c854385 100644 +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -1,3 +1,5 @@ + menu "SOC (System On Chip) specific Drivers" + ++source "drivers/soc/qcom/Kconfig" ++ + endmenu +diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile +new file mode 100644 +index 0000000..0f7c447 +--- /dev/null ++++ b/drivers/soc/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the Linux Kernel SOC specific device drivers. ++# ++ ++obj-$(CONFIG_ARCH_QCOM) += qcom/ +diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig +new file mode 100644 +index 0000000..7bd2c94 +--- /dev/null ++++ b/drivers/soc/qcom/Kconfig +@@ -0,0 +1,11 @@ ++# ++# QCOM Soc drivers ++# ++config QCOM_GSBI ++ tristate "QCOM General Serial Bus Interface" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable GSBI support. The GSBI provides control ++ functions for connecting the underlying serial UART, SPI, and I2C ++ devices to the output pins. ++ +diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile +new file mode 100644 +index 0000000..4389012 +--- /dev/null ++++ b/drivers/soc/qcom/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o +diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c +new file mode 100644 +index 0000000..061dd06 +--- /dev/null ++++ b/drivers/soc/qcom/qcom_gsbi.c +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++ ++#define GSBI_CTRL_REG 0x0000 ++#define GSBI_PROTOCOL_SHIFT 4 ++ ++static int gsbi_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct resource *res; ++ void __iomem *base; ++ struct clk *hclk; ++ u32 mode, crci = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (of_property_read_u32(node, "qcom,mode", &mode)) { ++ dev_err(&pdev->dev, "missing mode configuration\n"); ++ return -EINVAL; ++ } ++ ++ /* not required, so default to 0 if not present */ ++ of_property_read_u32(node, "qcom,crci", &crci); ++ ++ dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n", mode, crci); ++ ++ hclk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(hclk)) ++ return PTR_ERR(hclk); ++ ++ clk_prepare_enable(hclk); ++ ++ writel_relaxed((mode << GSBI_PROTOCOL_SHIFT) | crci, ++ base + GSBI_CTRL_REG); ++ ++ /* make sure the gsbi control write is not reordered */ ++ wmb(); ++ ++ clk_disable_unprepare(hclk); ++ ++ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); ++} ++ ++static const struct of_device_id gsbi_dt_match[] = { ++ { .compatible = "qcom,gsbi-v1.0.0", }, ++}; ++ ++MODULE_DEVICE_TABLE(of, gsbi_dt_match); ++ ++static struct platform_driver gsbi_driver = { ++ .driver = { ++ .name = "gsbi", ++ .owner = THIS_MODULE, ++ .of_match_table = gsbi_dt_match, ++ }, ++ .probe = gsbi_probe, ++}; ++ ++module_platform_driver(gsbi_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM GSBI driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch new file mode 100644 index 0000000000..c619ecef9d --- /dev/null +++ b/target/linux/ipq806x/patches/0085-soc-qcom-fix-of_device_id-table.patch @@ -0,0 +1,28 @@ +From 8efaabc573e58f4b891340f4aedb34f9d0656067 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann <arnd@arndb.de> +Date: Mon, 26 May 2014 18:07:05 +0200 +Subject: [PATCH 085/182] soc: qcom: fix of_device_id table + +The match tables must be zero-terminated, and Kbuild now helpfully +fails to link the kernel if that isn't the case. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +--- + drivers/soc/qcom/qcom_gsbi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c +index 061dd06..447458e 100644 +--- a/drivers/soc/qcom/qcom_gsbi.c ++++ b/drivers/soc/qcom/qcom_gsbi.c +@@ -64,6 +64,7 @@ static int gsbi_probe(struct platform_device *pdev) + + static const struct of_device_id gsbi_dt_match[] = { + { .compatible = "qcom,gsbi-v1.0.0", }, ++ { }, + }; + + MODULE_DEVICE_TABLE(of, gsbi_dt_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch new file mode 100644 index 0000000000..afc293bfbd --- /dev/null +++ b/target/linux/ipq806x/patches/0086-msm_serial-Add-support-for-poll_-get-put-_char.patch @@ -0,0 +1,246 @@ +From 48ab619dd6e308c57dac3e5d022a3099806bf79e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 14 Jan 2014 12:34:55 -0800 +Subject: [PATCH 086/182] msm_serial: Add support for poll_{get,put}_char() + +Implement the polling functionality for the MSM serial driver. +This allows us to use KGDB on this hardware. + +Cc: David Brown <davidb@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/tty/serial/msm_serial.c | 140 ++++++++++++++++++++++++++++++++++++++- + drivers/tty/serial/msm_serial.h | 9 +++ + 2 files changed, 146 insertions(+), 3 deletions(-) + +diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c +index b5d779c..053b98e 100644 +--- a/drivers/tty/serial/msm_serial.c ++++ b/drivers/tty/serial/msm_serial.c +@@ -39,6 +39,13 @@ + + #include "msm_serial.h" + ++enum { ++ UARTDM_1P1 = 1, ++ UARTDM_1P2, ++ UARTDM_1P3, ++ UARTDM_1P4, ++}; ++ + struct msm_port { + struct uart_port uart; + char name[16]; +@@ -309,6 +316,8 @@ static unsigned int msm_get_mctrl(struct uart_port *port) + + static void msm_reset(struct uart_port *port) + { ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ + /* reset everything */ + msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); +@@ -316,6 +325,10 @@ static void msm_reset(struct uart_port *port) + msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); ++ ++ /* Disable DM modes */ ++ if (msm_port->is_uartdm) ++ msm_write(port, 0, UARTDM_DMEN); + } + + static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +@@ -711,6 +724,117 @@ static void msm_power(struct uart_port *port, unsigned int state, + } + } + ++#ifdef CONFIG_CONSOLE_POLL ++static int msm_poll_init(struct uart_port *port) ++{ ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Enable single character mode on RX FIFO */ ++ if (msm_port->is_uartdm >= UARTDM_1P4) ++ msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN); ++ ++ return 0; ++} ++ ++static int msm_poll_get_char_single(struct uart_port *port) ++{ ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : UART_RF; ++ ++ if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) ++ return NO_POLL_CHAR; ++ else ++ return msm_read(port, rf_reg) & 0xff; ++} ++ ++static int msm_poll_get_char_dm_1p3(struct uart_port *port) ++{ ++ int c; ++ static u32 slop; ++ static int count; ++ unsigned char *sp = (unsigned char *)&slop; ++ ++ /* Check if a previous read had more than one char */ ++ if (count) { ++ c = sp[sizeof(slop) - count]; ++ count--; ++ /* Or if FIFO is empty */ ++ } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) { ++ /* ++ * If RX packing buffer has less than a word, force stale to ++ * push contents into RX FIFO ++ */ ++ count = msm_read(port, UARTDM_RXFS); ++ count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK; ++ if (count) { ++ msm_write(port, UART_CR_CMD_FORCE_STALE, UART_CR); ++ slop = msm_read(port, UARTDM_RF); ++ c = sp[0]; ++ count--; ++ } else { ++ c = NO_POLL_CHAR; ++ } ++ /* FIFO has a word */ ++ } else { ++ slop = msm_read(port, UARTDM_RF); ++ c = sp[0]; ++ count = sizeof(slop) - 1; ++ } ++ ++ return c; ++} ++ ++static int msm_poll_get_char(struct uart_port *port) ++{ ++ u32 imr; ++ int c; ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Disable all interrupts */ ++ imr = msm_read(port, UART_IMR); ++ msm_write(port, 0, UART_IMR); ++ ++ if (msm_port->is_uartdm == UARTDM_1P3) ++ c = msm_poll_get_char_dm_1p3(port); ++ else ++ c = msm_poll_get_char_single(port); ++ ++ /* Enable interrupts */ ++ msm_write(port, imr, UART_IMR); ++ ++ return c; ++} ++ ++static void msm_poll_put_char(struct uart_port *port, unsigned char c) ++{ ++ u32 imr; ++ struct msm_port *msm_port = UART_TO_MSM(port); ++ ++ /* Disable all interrupts */ ++ imr = msm_read(port, UART_IMR); ++ msm_write(port, 0, UART_IMR); ++ ++ if (msm_port->is_uartdm) ++ reset_dm_count(port, 1); ++ ++ /* Wait until FIFO is empty */ ++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) ++ cpu_relax(); ++ ++ /* Write a character */ ++ msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF); ++ ++ /* Wait until FIFO is empty */ ++ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) ++ cpu_relax(); ++ ++ /* Enable interrupts */ ++ msm_write(port, imr, UART_IMR); ++ ++ return; ++} ++#endif ++ + static struct uart_ops msm_uart_pops = { + .tx_empty = msm_tx_empty, + .set_mctrl = msm_set_mctrl, +@@ -729,6 +853,11 @@ static struct uart_ops msm_uart_pops = { + .config_port = msm_config_port, + .verify_port = msm_verify_port, + .pm = msm_power, ++#ifdef CONFIG_CONSOLE_POLL ++ .poll_init = msm_poll_init, ++ .poll_get_char = msm_poll_get_char, ++ .poll_put_char = msm_poll_put_char, ++#endif + }; + + static struct msm_port msm_uart_ports[] = { +@@ -900,7 +1029,10 @@ static struct uart_driver msm_uart_driver = { + static atomic_t msm_uart_next_id = ATOMIC_INIT(0); + + static const struct of_device_id msm_uartdm_table[] = { +- { .compatible = "qcom,msm-uartdm" }, ++ { .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 }, ++ { .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 }, ++ { .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 }, ++ { .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 }, + { } + }; + +@@ -909,6 +1041,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) + struct msm_port *msm_port; + struct resource *resource; + struct uart_port *port; ++ const struct of_device_id *id; + int irq; + + if (pdev->id == -1) +@@ -923,8 +1056,9 @@ static int __init msm_serial_probe(struct platform_device *pdev) + port->dev = &pdev->dev; + msm_port = UART_TO_MSM(port); + +- if (of_match_device(msm_uartdm_table, &pdev->dev)) +- msm_port->is_uartdm = 1; ++ id = of_match_device(msm_uartdm_table, &pdev->dev); ++ if (id) ++ msm_port->is_uartdm = (unsigned long)id->data; + else + msm_port->is_uartdm = 0; + +diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h +index 469fda5..1e9b68b 100644 +--- a/drivers/tty/serial/msm_serial.h ++++ b/drivers/tty/serial/msm_serial.h +@@ -59,6 +59,7 @@ + #define UART_CR_CMD_RESET_RFR (14 << 4) + #define UART_CR_CMD_PROTECTION_EN (16 << 4) + #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) ++#define UART_CR_CMD_FORCE_STALE (4 << 8) + #define UART_CR_CMD_RESET_TX_READY (3 << 8) + #define UART_CR_TX_DISABLE (1 << 3) + #define UART_CR_TX_ENABLE (1 << 2) +@@ -113,6 +114,14 @@ + #define GSBI_PROTOCOL_UART 0x40 + #define GSBI_PROTOCOL_IDLE 0x0 + ++#define UARTDM_RXFS 0x50 ++#define UARTDM_RXFS_BUF_SHIFT 0x7 ++#define UARTDM_RXFS_BUF_MASK 0x7 ++ ++#define UARTDM_DMEN 0x3C ++#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) ++#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) ++ + #define UARTDM_DMRX 0x34 + #define UARTDM_NCF_TX 0x40 + #define UARTDM_RX_TOTAL_SNAP 0x38 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch new file mode 100644 index 0000000000..8cbb52a903 --- /dev/null +++ b/target/linux/ipq806x/patches/0087-tty-serial-msm-Remove-direct-access-to-GSBI.patch @@ -0,0 +1,151 @@ +From 025909cbf933cc20c2ff5ea9f87de8e17a739a08 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:22 -0500 +Subject: [PATCH 087/182] tty: serial: msm: Remove direct access to GSBI + +This patch removes direct access of the GSBI registers. GSBI configuration +should be done through the GSBI driver directly. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/tty/serial/msm_serial.c | 48 ++------------------------------------- + drivers/tty/serial/msm_serial.h | 5 ---- + 2 files changed, 2 insertions(+), 51 deletions(-) + +diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c +index 053b98e..778e376 100644 +--- a/drivers/tty/serial/msm_serial.c ++++ b/drivers/tty/serial/msm_serial.c +@@ -52,7 +52,6 @@ struct msm_port { + struct clk *clk; + struct clk *pclk; + unsigned int imr; +- void __iomem *gsbi_base; + int is_uartdm; + unsigned int old_snap_state; + }; +@@ -599,9 +598,7 @@ static const char *msm_type(struct uart_port *port) + static void msm_release_port(struct uart_port *port) + { + struct platform_device *pdev = to_platform_device(port->dev); +- struct msm_port *msm_port = UART_TO_MSM(port); + struct resource *uart_resource; +- struct resource *gsbi_resource; + resource_size_t size; + + uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -612,28 +609,12 @@ static void msm_release_port(struct uart_port *port) + release_mem_region(port->mapbase, size); + iounmap(port->membase); + port->membase = NULL; +- +- if (msm_port->gsbi_base) { +- writel_relaxed(GSBI_PROTOCOL_IDLE, +- msm_port->gsbi_base + GSBI_CONTROL); +- +- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- if (unlikely(!gsbi_resource)) +- return; +- +- size = resource_size(gsbi_resource); +- release_mem_region(gsbi_resource->start, size); +- iounmap(msm_port->gsbi_base); +- msm_port->gsbi_base = NULL; +- } + } + + static int msm_request_port(struct uart_port *port) + { +- struct msm_port *msm_port = UART_TO_MSM(port); + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *uart_resource; +- struct resource *gsbi_resource; + resource_size_t size; + int ret; + +@@ -652,30 +633,8 @@ static int msm_request_port(struct uart_port *port) + goto fail_release_port; + } + +- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- /* Is this a GSBI-based port? */ +- if (gsbi_resource) { +- size = resource_size(gsbi_resource); +- +- if (!request_mem_region(gsbi_resource->start, size, +- "msm_serial")) { +- ret = -EBUSY; +- goto fail_release_port_membase; +- } +- +- msm_port->gsbi_base = ioremap(gsbi_resource->start, size); +- if (!msm_port->gsbi_base) { +- ret = -EBUSY; +- goto fail_release_gsbi; +- } +- } +- + return 0; + +-fail_release_gsbi: +- release_mem_region(gsbi_resource->start, size); +-fail_release_port_membase: +- iounmap(port->membase); + fail_release_port: + release_mem_region(port->mapbase, size); + return ret; +@@ -683,7 +642,6 @@ fail_release_port: + + static void msm_config_port(struct uart_port *port, int flags) + { +- struct msm_port *msm_port = UART_TO_MSM(port); + int ret; + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MSM; +@@ -691,9 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags) + if (ret) + return; + } +- if (msm_port->gsbi_base) +- writel_relaxed(GSBI_PROTOCOL_UART, +- msm_port->gsbi_base + GSBI_CONTROL); + } + + static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) +@@ -1110,6 +1065,7 @@ static struct of_device_id msm_match_table[] = { + + static struct platform_driver msm_platform_driver = { + .remove = msm_serial_remove, ++ .probe = msm_serial_probe, + .driver = { + .name = "msm_serial", + .owner = THIS_MODULE, +@@ -1125,7 +1081,7 @@ static int __init msm_serial_init(void) + if (unlikely(ret)) + return ret; + +- ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); ++ ret = platform_driver_register(&msm_platform_driver); + if (unlikely(ret)) + uart_unregister_driver(&msm_uart_driver); + +diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h +index 1e9b68b..d98d45e 100644 +--- a/drivers/tty/serial/msm_serial.h ++++ b/drivers/tty/serial/msm_serial.h +@@ -109,11 +109,6 @@ + #define UART_ISR 0x0014 + #define UART_ISR_TX_READY (1 << 7) + +-#define GSBI_CONTROL 0x0 +-#define GSBI_PROTOCOL_CODE 0x30 +-#define GSBI_PROTOCOL_UART 0x40 +-#define GSBI_PROTOCOL_IDLE 0x0 +- + #define UARTDM_RXFS 0x50 + #define UARTDM_RXFS_BUF_SHIFT 0x7 + #define UARTDM_RXFS_BUF_MASK 0x7 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch new file mode 100644 index 0000000000..39762c1be7 --- /dev/null +++ b/target/linux/ipq806x/patches/0088-soc-qcom-Add-device-tree-binding-for-GSBI.patch @@ -0,0 +1,135 @@ +From 5a58dbf4d82c29f7e6d89abc3520bed1aa2af05c Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 24 Apr 2014 11:31:20 -0500 +Subject: [PATCH 088/182] soc: qcom: Add device tree binding for GSBI + +Add device tree binding support for the QCOM GSBI driver. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/soc/qcom/qcom,gsbi.txt | 78 ++++++++++++++++++++ + include/dt-bindings/soc/qcom,gsbi.h | 26 +++++++ + 2 files changed, 104 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt + create mode 100644 include/dt-bindings/soc/qcom,gsbi.h + +diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt +new file mode 100644 +index 0000000..4ce24d4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt +@@ -0,0 +1,78 @@ ++QCOM GSBI (General Serial Bus Interface) Driver ++ ++The GSBI controller is modeled as a node with zero or more child nodes, each ++representing a serial sub-node device that is mux'd as part of the GSBI ++configuration settings. The mode setting will govern the input/output mode of ++the 4 GSBI IOs. ++ ++Required properties: ++- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064 ++- reg: Address range for GSBI registers ++- clocks: required clock ++- clock-names: must contain "iface" entry ++- qcom,mode : indicates MUX value for configuration of the serial interface. ++ Please reference dt-bindings/soc/qcom,gsbi.h for valid mux values. ++ ++Optional properties: ++- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference ++ dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. ++ ++Required properties if child node exists: ++- #address-cells: Must be 1 ++- #size-cells: Must be 1 ++- ranges: Must be present ++ ++Properties for children: ++ ++A GSBI controller node can contain 0 or more child nodes representing serial ++devices. These serial devices can be a QCOM UART, I2C controller, spi ++controller, or some combination of aforementioned devices. ++ ++See the following for child node definitions: ++Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt ++Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt ++ ++Example for APQ8064: ++ ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++ gsbi4@16300000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16300000 0x100>; ++ clocks = <&gcc GSBI4_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ qcom,crci = <GSBI_CRCI_QUP>; ++ ++ /* child nodes go under here */ ++ ++ i2c_qup4: i2c@16380000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x16380000 0x1000>; ++ interrupts = <0 153 0>; ++ ++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ ++ clock-frequency = <200000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ }; ++ ++ uart4: serial@16340000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16340000 0x1000>, ++ <0x16300000 0x1000>; ++ interrupts = <0 152 0x0>; ++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "ok"; ++ }; ++ }; ++ +diff --git a/include/dt-bindings/soc/qcom,gsbi.h b/include/dt-bindings/soc/qcom,gsbi.h +new file mode 100644 +index 0000000..7ac4292 +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,gsbi.h +@@ -0,0 +1,26 @@ ++/* Copyright (c) 2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DT_BINDINGS_QCOM_GSBI_H ++#define __DT_BINDINGS_QCOM_GSBI_H ++ ++#define GSBI_PROT_IDLE 0 ++#define GSBI_PROT_I2C_UIM 1 ++#define GSBI_PROT_I2C 2 ++#define GSBI_PROT_SPI 3 ++#define GSBI_PROT_UART_W_FC 4 ++#define GSBI_PROT_UIM 5 ++#define GSBI_PROT_I2C_UART 6 ++ ++#define GSBI_CRCI_QUP 0 ++#define GSBI_CRCI_UART 1 ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch new file mode 100644 index 0000000000..1663fbe906 --- /dev/null +++ b/target/linux/ipq806x/patches/0089-ARM-dts-MSM8974-Add-pinctrl-node.patch @@ -0,0 +1,57 @@ +From b3a77c7cab10272988231482e2c654c5f10a910c Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Thu, 6 Feb 2014 17:28:49 +0200 +Subject: [PATCH 089/182] ARM: dts: MSM8974: Add pinctrl node + +Add the pin control node and pin definitions of SPI8. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> +Acked-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8974.dtsi | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index f687239..23aa387 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -198,5 +198,34 @@ + clocks = <&gcc GCC_PRNG_AHB_CLK>; + clock-names = "core"; + }; ++ ++ msmgpio: pinctrl@fd510000 { ++ compatible = "qcom,msm8974-pinctrl"; ++ reg = <0xfd510000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 208 0>; ++ ++ spi8_default: spi8_default { ++ mosi { ++ pins = "gpio45"; ++ function = "blsp_spi8"; ++ }; ++ miso { ++ pins = "gpio46"; ++ function = "blsp_spi8"; ++ }; ++ cs { ++ pins = "gpio47"; ++ function = "blsp_spi8"; ++ }; ++ clk { ++ pins = "gpio48"; ++ function = "blsp_spi8"; ++ }; ++ }; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch new file mode 100644 index 0000000000..c16258ce32 --- /dev/null +++ b/target/linux/ipq806x/patches/0090-ARM-dts-msm-Add-SDHC-controller-nodes-for-MSM8974-an.patch @@ -0,0 +1,75 @@ +From 6632619d49f0f90c4d74caad67749864f154cae4 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 31 Jan 2014 16:21:56 +0200 +Subject: [PATCH 090/182] ARM: dts: msm: Add SDHC controller nodes for MSM8974 + and DB8074 board + +Add support for the 2 SDHC controllers on the DB8074 board. The first +controller (at 0xf9824900) is connected to an on board soldered eMMC. +The second controller (at 0xf98a4900) is connected to a uSD card slot. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 13 +++++++++++++ + arch/arm/boot/dts/qcom-msm8974.dtsi | 22 ++++++++++++++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +index 13ac3e2..92320c4 100644 +--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts ++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +@@ -3,4 +3,17 @@ + / { + model = "Qualcomm APQ8074 Dragonboard"; + compatible = "qcom,apq8074-dragonboard", "qcom,apq8074"; ++ ++ soc: soc { ++ sdhci@f9824900 { ++ bus-width = <8>; ++ non-removable; ++ status = "ok"; ++ }; ++ ++ sdhci@f98a4900 { ++ cd-gpios = <&msmgpio 62 0x1>; ++ bus-width = <4>; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index 23aa387..c530a33 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -192,6 +192,28 @@ + clock-names = "core", "iface"; + }; + ++ sdhci@f9824900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; ++ reg-names = "hc_mem", "core_mem"; ++ interrupts = <0 123 0>, <0 138 0>; ++ interrupt-names = "hc_irq", "pwr_irq"; ++ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ ++ sdhci@f98a4900 { ++ compatible = "qcom,sdhci-msm-v4"; ++ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; ++ reg-names = "hc_mem", "core_mem"; ++ interrupts = <0 125 0>, <0 221 0>; ++ interrupt-names = "hc_irq", "pwr_irq"; ++ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ + rng@f9bff000 { + compatible = "qcom,prng"; + reg = <0xf9bff000 0x200>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch new file mode 100644 index 0000000000..dcaaa3df74 --- /dev/null +++ b/target/linux/ipq806x/patches/0091-ARM-dts-qcom-Update-msm8974-apq8074-device-trees.patch @@ -0,0 +1,186 @@ +From 63495b04141e60ceb40d4632a41b7cd4a3d23dd2 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:01:29 -0500 +Subject: [PATCH 091/182] ARM: dts: qcom: Update msm8974/apq8074 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-apq8074-dragonboard.dts) +* Move spi pinctrl into board file +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Drop interrupts property from l2-cache node as its not part of the + binding spec +* Move timer node out of SoC container + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 28 +++++++++++++- + arch/arm/boot/dts/qcom-msm8974.dtsi | 49 +++++++++--------------- + 2 files changed, 45 insertions(+), 32 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +index 92320c4..b4dfb01 100644 +--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts ++++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +@@ -4,7 +4,11 @@ + model = "Qualcomm APQ8074 Dragonboard"; + compatible = "qcom,apq8074-dragonboard", "qcom,apq8074"; + +- soc: soc { ++ soc { ++ serial@f991e000 { ++ status = "ok"; ++ }; ++ + sdhci@f9824900 { + bus-width = <8>; + non-removable; +@@ -15,5 +19,27 @@ + cd-gpios = <&msmgpio 62 0x1>; + bus-width = <4>; + }; ++ ++ ++ pinctrl@fd510000 { ++ spi8_default: spi8_default { ++ mosi { ++ pins = "gpio45"; ++ function = "blsp_spi8"; ++ }; ++ miso { ++ pins = "gpio46"; ++ function = "blsp_spi8"; ++ }; ++ cs { ++ pins = "gpio47"; ++ function = "blsp_spi8"; ++ }; ++ clk { ++ pins = "gpio48"; ++ function = "blsp_spi8"; ++ }; ++ }; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi +index c530a33..69dca2a 100644 +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -13,10 +13,10 @@ + #address-cells = <1>; + #size-cells = <0>; + interrupts = <1 9 0xf04>; +- compatible = "qcom,krait"; +- enable-method = "qcom,kpss-acc-v2"; + + cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; +@@ -24,6 +24,8 @@ + }; + + cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -31,6 +33,8 @@ + }; + + cpu@2 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <2>; + next-level-cache = <&L2>; +@@ -38,6 +42,8 @@ + }; + + cpu@3 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v2"; + device_type = "cpu"; + reg = <3>; + next-level-cache = <&L2>; +@@ -47,7 +53,6 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; +- interrupts = <0 2 0x4>; + qcom,saw = <&saw_l2>; + }; + }; +@@ -57,6 +62,15 @@ + interrupts = <1 7 0xf04>; + }; + ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 2 0xf08>, ++ <1 3 0xf08>, ++ <1 4 0xf08>, ++ <1 1 0xf08>; ++ clock-frequency = <19200000>; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -71,15 +85,6 @@ + <0xf9002000 0x1000>; + }; + +- timer { +- compatible = "arm,armv7-timer"; +- interrupts = <1 2 0xf08>, +- <1 3 0xf08>, +- <1 4 0xf08>, +- <1 1 0xf08>; +- clock-frequency = <19200000>; +- }; +- + timer@f9020000 { + #address-cells = <1>; + #size-cells = <1>; +@@ -190,6 +195,7 @@ + interrupts = <0 108 0x0>; + clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; + clock-names = "core", "iface"; ++ status = "disabled"; + }; + + sdhci@f9824900 { +@@ -229,25 +235,6 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 208 0>; +- +- spi8_default: spi8_default { +- mosi { +- pins = "gpio45"; +- function = "blsp_spi8"; +- }; +- miso { +- pins = "gpio46"; +- function = "blsp_spi8"; +- }; +- cs { +- pins = "gpio47"; +- function = "blsp_spi8"; +- }; +- clk { +- pins = "gpio48"; +- function = "blsp_spi8"; +- }; +- }; + }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch new file mode 100644 index 0000000000..5c0c7a5d2b --- /dev/null +++ b/target/linux/ipq806x/patches/0092-ARM-dts-qcom-Update-msm8960-device-trees.patch @@ -0,0 +1,268 @@ +From 881200420e6ece87d9abbb13c0653d26455cdbdd Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:09:53 -0500 +Subject: [PATCH 092/182] ARM: dts: qcom: Update msm8960 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-msm8960-cdp.dts) +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Drop interrupts property from l2-cache node as its not part of the + binding spec +* Add GSBI node and configuration of GSBI controller + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8960-cdp.dts | 10 ++ + arch/arm/boot/dts/qcom-msm8960.dtsi | 176 ++++++++++++++++++-------------- + 2 files changed, 108 insertions(+), 78 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +index a58fb88..8f75cc4 100644 +--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts ++++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts +@@ -3,4 +3,14 @@ + / { + model = "Qualcomm MSM8960 CDP"; + compatible = "qcom,msm8960-cdp", "qcom,msm8960"; ++ ++ soc { ++ gsbi@16400000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@16440000 { ++ status = "ok"; ++ }; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi +index 997b7b9..5303e53 100644 +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -3,6 +3,7 @@ + /include/ "skeleton.dtsi" + + #include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include <dt-bindings/soc/qcom,gsbi.h> + + / { + model = "Qualcomm MSM8960"; +@@ -13,10 +14,10 @@ + #address-cells = <1>; + #size-cells = <0>; + interrupts = <1 14 0x304>; +- compatible = "qcom,krait"; +- enable-method = "qcom,kpss-acc-v1"; + + cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; +@@ -25,6 +26,8 @@ + }; + + cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -35,7 +38,6 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; +- interrupts = <0 2 0x4>; + }; + }; + +@@ -45,91 +47,109 @@ + qcom,no-pc-write; + }; + +- intc: interrupt-controller@2000000 { +- compatible = "qcom,msm-qgic2"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02000000 0x1000 >, +- < 0x02002000 0x1000 >; +- }; ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; + +- timer@200a000 { +- compatible = "qcom,kpss-timer", "qcom,msm-timer"; +- interrupts = <1 1 0x301>, +- <1 2 0x301>, +- <1 3 0x301>; +- reg = <0x0200a000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x80000>; +- }; ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; + +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <150>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- reg = <0x800000 0x4000>; +- }; ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <150>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x800000 0x4000>; ++ }; + +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8960"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8960"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; + +- clock-controller@4000000 { +- compatible = "qcom,mmcc-msm8960"; +- reg = <0x4000000 0x1000>; +- #clock-cells = <1>; +- #reset-cells = <1>; +- }; ++ clock-controller@4000000 { ++ compatible = "qcom,mmcc-msm8960"; ++ reg = <0x4000000 0x1000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; + +- acc0: clock-controller@2088000 { +- compatible = "qcom,kpss-acc-v1"; +- reg = <0x02088000 0x1000>, <0x02008000 0x1000>; +- }; ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; + +- acc1: clock-controller@2098000 { +- compatible = "qcom,kpss-acc-v1"; +- reg = <0x02098000 0x1000>, <0x02008000 0x1000>; +- }; ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; + +- saw0: regulator@2089000 { +- compatible = "qcom,saw2"; +- reg = <0x02089000 0x1000>, <0x02009000 0x1000>; +- regulator; +- }; ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; + +- saw1: regulator@2099000 { +- compatible = "qcom,saw2"; +- reg = <0x02099000 0x1000>, <0x02009000 0x1000>; +- regulator; +- }; ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; + +- serial@16440000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x16440000 0x1000>, +- <0x16400000 0x1000>; +- interrupts = <0 154 0x0>; +- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; +- clock-names = "core", "iface"; +- }; ++ gsbi5: gsbi@16400000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16400000 0x100>; ++ clocks = <&gcc GSBI5_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ serial@16440000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16440000 0x1000>, ++ <0x16400000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; + +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; +- }; ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; + +- rng@1a500000 { +- compatible = "qcom,prng"; +- reg = <0x1a500000 0x200>; +- clocks = <&gcc PRNG_CLK>; +- clock-names = "core"; ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch new file mode 100644 index 0000000000..72f9939a08 --- /dev/null +++ b/target/linux/ipq806x/patches/0093-ARM-dts-qcom-Update-msm8660-device-trees.patch @@ -0,0 +1,191 @@ +From 355bf7c6410f5b6e37b5c2b28ebe59bb701c42d6 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Wed, 28 May 2014 12:12:40 -0500 +Subject: [PATCH 093/182] ARM: dts: qcom: Update msm8660 device trees + +* Move SoC peripherals into an SoC container node +* Move serial enabling into board file (qcom-msm8660-surf.dts) +* Cleanup cpu node to match binding spec, enable-method and compatible + should be per cpu, not part of the container +* Add GSBI node and configuration of GSBI controller + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-msm8660-surf.dts | 10 +++ + arch/arm/boot/dts/qcom-msm8660.dtsi | 115 ++++++++++++++++++------------- + 2 files changed, 78 insertions(+), 47 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts +index 169bad9..45180ad 100644 +--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts ++++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts +@@ -3,4 +3,14 @@ + / { + model = "Qualcomm MSM8660 SURF"; + compatible = "qcom,msm8660-surf", "qcom,msm8660"; ++ ++ soc { ++ gsbi@19c00000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@19c40000 { ++ status = "ok"; ++ }; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi +index c52a9e9..53837aaa2f 100644 +--- a/arch/arm/boot/dts/qcom-msm8660.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -3,6 +3,7 @@ + /include/ "skeleton.dtsi" + + #include <dt-bindings/clock/qcom,gcc-msm8660.h> ++#include <dt-bindings/soc/qcom,gsbi.h> + + / { + model = "Qualcomm MSM8660"; +@@ -12,16 +13,18 @@ + cpus { + #address-cells = <1>; + #size-cells = <0>; +- compatible = "qcom,scorpion"; +- enable-method = "qcom,gcc-msm8660"; + + cpu@0 { ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; + }; + + cpu@1 { ++ compatible = "qcom,scorpion"; ++ enable-method = "qcom,gcc-msm8660"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; +@@ -33,55 +36,73 @@ + }; + }; + +- intc: interrupt-controller@2080000 { +- compatible = "qcom,msm-8660-qgic"; +- interrupt-controller; +- #interrupt-cells = <3>; +- reg = < 0x02080000 0x1000 >, +- < 0x02081000 0x1000 >; +- }; ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; + +- timer@2000000 { +- compatible = "qcom,scss-timer", "qcom,msm-timer"; +- interrupts = <1 0 0x301>, +- <1 1 0x301>, +- <1 2 0x301>; +- reg = <0x02000000 0x100>; +- clock-frequency = <27000000>, +- <32768>; +- cpu-offset = <0x40000>; +- }; ++ intc: interrupt-controller@2080000 { ++ compatible = "qcom,msm-8660-qgic"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = < 0x02080000 0x1000 >, ++ < 0x02081000 0x1000 >; ++ }; + +- msmgpio: gpio@800000 { +- compatible = "qcom,msm-gpio"; +- reg = <0x00800000 0x4000>; +- gpio-controller; +- #gpio-cells = <2>; +- ngpio = <173>; +- interrupts = <0 16 0x4>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; ++ timer@2000000 { ++ compatible = "qcom,scss-timer", "qcom,msm-timer"; ++ interrupts = <1 0 0x301>, ++ <1 1 0x301>, ++ <1 2 0x301>; ++ reg = <0x02000000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x40000>; ++ }; + +- gcc: clock-controller@900000 { +- compatible = "qcom,gcc-msm8660"; +- #clock-cells = <1>; +- #reset-cells = <1>; +- reg = <0x900000 0x4000>; +- }; ++ msmgpio: gpio@800000 { ++ compatible = "qcom,msm-gpio"; ++ reg = <0x00800000 0x4000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpio = <173>; ++ interrupts = <0 16 0x4>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; + +- serial@19c40000 { +- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; +- reg = <0x19c40000 0x1000>, +- <0x19c00000 0x1000>; +- interrupts = <0 195 0x0>; +- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; +- clock-names = "core", "iface"; +- }; ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-msm8660"; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ reg = <0x900000 0x4000>; ++ }; ++ ++ gsbi12: gsbi@19c00000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x19c00000 0x100>; ++ clocks = <&gcc GSBI12_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; + +- qcom,ssbi@500000 { +- compatible = "qcom,ssbi"; +- reg = <0x500000 0x1000>; +- qcom,controller-type = "pmic-arbiter"; ++ serial@19c40000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x19c40000 0x1000>, ++ <0x19c00000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch new file mode 100644 index 0000000000..848cbc7834 --- /dev/null +++ b/target/linux/ipq806x/patches/0094-ARM-dts-qcom-Add-initial-APQ8064-SoC-and-IFC6410-boa.patch @@ -0,0 +1,265 @@ +From 07d7d95706c1bf373bd6b30c42f95c7b8dc8b9ce Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 3 Apr 2014 14:48:22 -0500 +Subject: [PATCH 094/182] ARM: dts: qcom: Add initial APQ8064 SoC and IFC6410 + board device trees + +Add basic APQ8064 SoC include device tree and support for basic booting on +the IFC6410 board. Also, keep dtb build list and qcom_dt_match in sorted +order. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 8 +- + arch/arm/boot/dts/qcom-apq8064-ifc6410.dts | 16 +++ + arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi | 1 + + arch/arm/boot/dts/qcom-apq8064.dtsi | 170 ++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 3 +- + 5 files changed, 194 insertions(+), 4 deletions(-) + create mode 100644 arch/arm/boot/dts/qcom-apq8064-ifc6410.dts + create mode 100644 arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi + create mode 100644 arch/arm/boot/dts/qcom-apq8064.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 4a89023..ee3dfea 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -231,9 +231,11 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \ + dra7-evm.dtb + dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb + dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb +-dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \ +- qcom-msm8960-cdp.dtb \ +- qcom-apq8074-dragonboard.dtb ++dtb-$(CONFIG_ARCH_QCOM) += \ ++ qcom-apq8064-ifc6410.dtb \ ++ qcom-apq8074-dragonboard.dtb \ ++ qcom-msm8660-surf.dtb \ ++ qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ + ste-hrefprev60-stuib.dtb \ + ste-hrefprev60-tvk.dtb \ +diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts +new file mode 100644 +index 0000000..7c2441d +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts +@@ -0,0 +1,16 @@ ++#include "qcom-apq8064-v2.0.dtsi" ++ ++/ { ++ model = "Qualcomm APQ8064/IFC6410"; ++ compatible = "qcom,apq8064-ifc6410", "qcom,apq8064"; ++ ++ soc { ++ gsbi@16600000 { ++ status = "ok"; ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ serial@16640000 { ++ status = "ok"; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi +new file mode 100644 +index 0000000..935c394 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064-v2.0.dtsi +@@ -0,0 +1 @@ ++#include "qcom-apq8064.dtsi" +diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi +new file mode 100644 +index 0000000..92bf793 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8064.dtsi +@@ -0,0 +1,170 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/qcom,gcc-msm8960.h> ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++/ { ++ model = "Qualcomm APQ8064"; ++ compatible = "qcom,apq8064"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ cpu@2 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <2>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ qcom,saw = <&saw2>; ++ }; ++ ++ cpu@3 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <3>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ qcom,saw = <&saw3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <27000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@20a8000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x020a8000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@20b8000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x020b8000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw2: regulator@20a9000 { ++ compatible = "qcom,saw2"; ++ reg = <0x020a9000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw3: regulator@20b9000 { ++ compatible = "qcom,saw2"; ++ reg = <0x020b9000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ gsbi7: gsbi@16600000 { ++ status = "disabled"; ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16600000 0x100>; ++ clocks = <&gcc GSBI7_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ serial@16640000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16640000 0x1000>, ++ <0x16600000 0x1000>; ++ interrupts = <0 158 0x0>; ++ clocks = <&gcc GSBI7_UART_CLK>, <&gcc GSBI7_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x00500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-apq8064"; ++ reg = <0x00900000 0x4000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index bae617e..350fa8d 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -15,9 +15,10 @@ + #include <asm/mach/arch.h> + + static const char * const qcom_dt_match[] __initconst = { ++ "qcom,apq8064", ++ "qcom,apq8074-dragonboard", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", +- "qcom,apq8074-dragonboard", + NULL + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch new file mode 100644 index 0000000000..a797266766 --- /dev/null +++ b/target/linux/ipq806x/patches/0095-ARM-dts-qcom-Add-APQ8084-MTP-board-support.patch @@ -0,0 +1,43 @@ +From 9b06bc14f21463cf687ac6fdd07ca04b99c15466 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:30 +0300 +Subject: [PATCH 095/182] ARM: dts: qcom: Add APQ8084-MTP board support + +Add device-tree file for APQ8084-MTP board, which belongs +to the Snapdragon 805 family. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-apq8084-mtp.dts | 6 ++++++ + 2 files changed, 7 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-apq8084-mtp.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index ee3dfea..f2aeb95 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -234,6 +234,7 @@ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb + dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8064-ifc6410.dtb \ + qcom-apq8074-dragonboard.dtb \ ++ qcom-apq8084-mtp.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ +diff --git a/arch/arm/boot/dts/qcom-apq8084-mtp.dts b/arch/arm/boot/dts/qcom-apq8084-mtp.dts +new file mode 100644 +index 0000000..9dae387 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8084-mtp.dts +@@ -0,0 +1,6 @@ ++#include "qcom-apq8084.dtsi" ++ ++/ { ++ model = "Qualcomm APQ 8084-MTP"; ++ compatible = "qcom,apq8084-mtp", "qcom,apq8084"; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch new file mode 100644 index 0000000000..87d70a54e5 --- /dev/null +++ b/target/linux/ipq806x/patches/0096-ARM-dts-qcom-Add-APQ8084-SoC-support.patch @@ -0,0 +1,216 @@ +From 8c52931421759b70fc37771be3390813a2a2f9f5 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:29 +0300 +Subject: [PATCH 096/182] ARM: dts: qcom: Add APQ8084 SoC support + +Add support for the Qualcomm Snapdragon 805 APQ8084 SoC. It is +used on APQ8084-MTP and other boards. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/qcom-apq8084.dtsi | 179 +++++++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 1 + + 2 files changed, 180 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-apq8084.dtsi + +diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi +new file mode 100644 +index 0000000..e3e009a +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-apq8084.dtsi +@@ -0,0 +1,179 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++ ++/ { ++ model = "Qualcomm APQ 8084"; ++ compatible = "qcom,apq8084"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <0>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <1>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <2>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "qcom,krait"; ++ reg = <3>; ++ enable-method = "qcom,kpss-acc-v2"; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc3>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "qcom,arch-cache"; ++ cache-level = <2>; ++ qcom,saw = <&saw_l2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 7 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 2 0xf08>, ++ <1 3 0xf08>, ++ <1 4 0xf08>, ++ <1 1 0xf08>; ++ clock-frequency = <19200000>; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ intc: interrupt-controller@f9000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0xf9000000 0x1000>, ++ <0xf9002000 0x1000>; ++ }; ++ ++ timer@f9020000 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "arm,armv7-timer-mem"; ++ reg = <0xf9020000 0x1000>; ++ clock-frequency = <19200000>; ++ ++ frame@f9021000 { ++ frame-number = <0>; ++ interrupts = <0 8 0x4>, ++ <0 7 0x4>; ++ reg = <0xf9021000 0x1000>, ++ <0xf9022000 0x1000>; ++ }; ++ ++ frame@f9023000 { ++ frame-number = <1>; ++ interrupts = <0 9 0x4>; ++ reg = <0xf9023000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9024000 { ++ frame-number = <2>; ++ interrupts = <0 10 0x4>; ++ reg = <0xf9024000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9025000 { ++ frame-number = <3>; ++ interrupts = <0 11 0x4>; ++ reg = <0xf9025000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9026000 { ++ frame-number = <4>; ++ interrupts = <0 12 0x4>; ++ reg = <0xf9026000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9027000 { ++ frame-number = <5>; ++ interrupts = <0 13 0x4>; ++ reg = <0xf9027000 0x1000>; ++ status = "disabled"; ++ }; ++ ++ frame@f9028000 { ++ frame-number = <6>; ++ interrupts = <0 14 0x4>; ++ reg = <0xf9028000 0x1000>; ++ status = "disabled"; ++ }; ++ }; ++ ++ saw_l2: regulator@f9012000 { ++ compatible = "qcom,saw2"; ++ reg = <0xf9012000 0x1000>; ++ regulator; ++ }; ++ ++ acc0: clock-controller@f9088000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9088000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@f9098000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf9098000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc2: clock-controller@f90a8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90a8000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ acc3: clock-controller@f90b8000 { ++ compatible = "qcom,kpss-acc-v2"; ++ reg = <0xf90b8000 0x1000>, ++ <0xf9008000 0x1000>; ++ }; ++ ++ restart@fc4ab000 { ++ compatible = "qcom,pshold"; ++ reg = <0xfc4ab000 0x4>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index 350fa8d..c437a99 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -17,6 +17,7 @@ + static const char * const qcom_dt_match[] __initconst = { + "qcom,apq8064", + "qcom,apq8074-dragonboard", ++ "qcom,apq8084", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", + NULL +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch new file mode 100644 index 0000000000..2f9cad735e --- /dev/null +++ b/target/linux/ipq806x/patches/0097-ARM-debug-qcom-make-UART-address-selection-configura.patch @@ -0,0 +1,267 @@ +From 48167d4a55890a783cc8b1590bc8071253ae4b83 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 14 Apr 2014 16:47:34 +0300 +Subject: [PATCH 097/182] ARM: debug: qcom: make UART address selection + configuration option + +Separate Qualcomm low-level debugging UART to two options. + +DEBUG_MSM_UART is used in earlier non-multi platform arches, +like MSM7X00A, QSD8X50 and MSM7X30. + +DEBUG_QCOM_UARTDM is used in multi-plafrom arches and have +embedded data mover. + +Make DEBUG_UART_PHYS and DEBUG_UART_BASE user adjustable by +Kconfig menu. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Tested-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/Kconfig.debug | 81 +++++++++++++++++------------------------- + arch/arm/include/debug/msm.S | 46 +++--------------------- + arch/arm/mach-msm/Kconfig | 3 -- + 3 files changed, 38 insertions(+), 92 deletions(-) + +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 4491c7b..1a5895d 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -353,56 +353,39 @@ choice + Say Y here if you want kernel low-level debugging support + on MMP UART3. + +- config DEBUG_MSM_UART1 +- bool "Kernel low-level debugging messages via MSM UART1" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART ++ config DEBUG_MSM_UART ++ bool "Kernel low-level debugging messages via MSM UART" ++ depends on ARCH_MSM + help + Say Y here if you want the debug print routines to direct +- their output to the first serial port on MSM devices. ++ their output to the serial port on MSM devices. + +- config DEBUG_MSM_UART2 +- bool "Kernel low-level debugging messages via MSM UART2" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the second serial port on MSM devices. ++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE # ++ MSM7X00A, QSD8X50 0xa9a00000 0xe1000000 UART1 ++ MSM7X00A, QSD8X50 0xa9b00000 0xe1000000 UART2 ++ MSM7X00A, QSD8X50 0xa9c00000 0xe1000000 UART3 + +- config DEBUG_MSM_UART3 +- bool "Kernel low-level debugging messages via MSM UART3" +- depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the third serial port on MSM devices. ++ MSM7X30 0xaca00000 0xe1000000 UART1 ++ MSM7X30 0xacb00000 0xe1000000 UART2 ++ MSM7X30 0xacc00000 0xe1000000 UART3 + +- config DEBUG_MSM8660_UART +- bool "Kernel low-level debugging messages via MSM 8660 UART" +- depends on ARCH_MSM8X60 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8660 devices. ++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration ++ options based on your needs. + +- config DEBUG_MSM8960_UART +- bool "Kernel low-level debugging messages via MSM 8960 UART" +- depends on ARCH_MSM8960 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART ++ config DEBUG_QCOM_UARTDM ++ bool "Kernel low-level debugging messages via QCOM UARTDM" ++ depends on ARCH_QCOM + help + Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8960 devices. ++ their output to the serial port on Qualcomm devices. + +- config DEBUG_MSM8974_UART +- bool "Kernel low-level debugging messages via MSM 8974 UART" +- depends on ARCH_MSM8974 +- select MSM_HAS_DEBUG_UART_HS +- select DEBUG_MSM_UART +- help +- Say Y here if you want the debug print routines to direct +- their output to the serial port on MSM 8974 devices. ++ ARCH DEBUG_UART_PHYS DEBUG_UART_BASE ++ MSM8X60 0x19c40000 0xf0040000 ++ MSM8960 0x16440000 0xf0040000 ++ MSM8974 0xf991e000 0xfa71e000 ++ ++ Please adjust DEBUG_UART_PHYS and DEBUG_UART_BASE configuration ++ options based on your needs. + + config DEBUG_MVEBU_UART + bool "Kernel low-level debugging messages via MVEBU UART (old bootloaders)" +@@ -954,10 +937,6 @@ config DEBUG_STI_UART + bool + depends on ARCH_STI + +-config DEBUG_MSM_UART +- bool +- depends on ARCH_MSM || ARCH_QCOM +- + config DEBUG_LL_INCLUDE + string + default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250 +@@ -975,7 +954,7 @@ config DEBUG_LL_INCLUDE + DEBUG_IMX53_UART ||\ + DEBUG_IMX6Q_UART || \ + DEBUG_IMX6SL_UART +- default "debug/msm.S" if DEBUG_MSM_UART ++ default "debug/msm.S" if DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART + default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1 + default "debug/sti.S" if DEBUG_STI_UART +@@ -1039,6 +1018,7 @@ config DEBUG_UART_PHYS + default 0x80074000 if DEBUG_IMX28_UART + default 0x808c0000 if ARCH_EP93XX + default 0x90020000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART ++ default 0xa9a00000 if DEBUG_MSM_UART + default 0xb0090000 if DEBUG_VEXPRESS_UART0_CRX + default 0xc0013000 if DEBUG_U300_UART + default 0xc8000000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN +@@ -1054,6 +1034,7 @@ config DEBUG_UART_PHYS + ARCH_ORION5X + default 0xf7fc9000 if DEBUG_BERLIN_UART + default 0xf8b00000 if DEBUG_HI3716_UART ++ default 0xf991e000 if DEBUG_QCOM_UARTDM + default 0xfcb00000 if DEBUG_HI3620_UART + default 0xfe800000 if ARCH_IOP32X + default 0xffc02000 if DEBUG_SOCFPGA_UART +@@ -1062,11 +1043,13 @@ config DEBUG_UART_PHYS + default 0xfffff700 if ARCH_IOP33X + depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \ + DEBUG_LL_UART_EFM32 || \ +- DEBUG_UART_8250 || DEBUG_UART_PL01X ++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \ ++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + + config DEBUG_UART_VIRT + hex "Virtual base address of debug UART" + default 0xe0010fe0 if ARCH_RPC ++ default 0xe1000000 if DEBUG_MSM_UART + default 0xf0000be0 if ARCH_EBSA110 + default 0xf0009000 if DEBUG_CNS3XXX + default 0xf01fb000 if DEBUG_NOMADIK_UART +@@ -1081,6 +1064,7 @@ config DEBUG_UART_VIRT + default 0xf7fc9000 if DEBUG_BERLIN_UART + default 0xf8009000 if DEBUG_VEXPRESS_UART0_CA9 + default 0xf8090000 if DEBUG_VEXPRESS_UART0_RS1 ++ default 0xfa71e000 if DEBUG_QCOM_UARTDM + default 0xfb009000 if DEBUG_REALVIEW_STD_PORT + default 0xfb10c000 if DEBUG_REALVIEW_PB1176_PORT + default 0xfd000000 if ARCH_SPEAR3XX || ARCH_SPEAR6XX +@@ -1120,7 +1104,8 @@ config DEBUG_UART_VIRT + default 0xff003000 if DEBUG_U300_UART + default DEBUG_UART_PHYS if !MMU + depends on DEBUG_LL_UART_8250 || DEBUG_LL_UART_PL01X || \ +- DEBUG_UART_8250 || DEBUG_UART_PL01X ++ DEBUG_UART_8250 || DEBUG_UART_PL01X || \ ++ DEBUG_MSM_UART || DEBUG_QCOM_UARTDM + + config DEBUG_UART_8250_SHIFT + int "Register offset shift for the 8250 debug UART" +diff --git a/arch/arm/include/debug/msm.S b/arch/arm/include/debug/msm.S +index 9d653d4..9ef5761 100644 +--- a/arch/arm/include/debug/msm.S ++++ b/arch/arm/include/debug/msm.S +@@ -15,51 +15,15 @@ + * + */ + +-#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_QSD8X50) +-#define MSM_UART1_PHYS 0xA9A00000 +-#define MSM_UART2_PHYS 0xA9B00000 +-#define MSM_UART3_PHYS 0xA9C00000 +-#elif defined(CONFIG_ARCH_MSM7X30) +-#define MSM_UART1_PHYS 0xACA00000 +-#define MSM_UART2_PHYS 0xACB00000 +-#define MSM_UART3_PHYS 0xACC00000 +-#endif +- +-#if defined(CONFIG_DEBUG_MSM_UART1) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS +-#elif defined(CONFIG_DEBUG_MSM_UART2) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART2_PHYS +-#elif defined(CONFIG_DEBUG_MSM_UART3) +-#define MSM_DEBUG_UART_BASE 0xE1000000 +-#define MSM_DEBUG_UART_PHYS MSM_UART3_PHYS +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8660_UART +-#define MSM_DEBUG_UART_BASE 0xF0040000 +-#define MSM_DEBUG_UART_PHYS 0x19C40000 +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8960_UART +-#define MSM_DEBUG_UART_BASE 0xF0040000 +-#define MSM_DEBUG_UART_PHYS 0x16440000 +-#endif +- +-#ifdef CONFIG_DEBUG_MSM8974_UART +-#define MSM_DEBUG_UART_BASE 0xFA71E000 +-#define MSM_DEBUG_UART_PHYS 0xF991E000 +-#endif +- + .macro addruart, rp, rv, tmp +-#ifdef MSM_DEBUG_UART_PHYS +- ldr \rp, =MSM_DEBUG_UART_PHYS +- ldr \rv, =MSM_DEBUG_UART_BASE ++#ifdef CONFIG_DEBUG_UART_PHYS ++ ldr \rp, =CONFIG_DEBUG_UART_PHYS ++ ldr \rv, =CONFIG_DEBUG_UART_VIRT + #endif + .endm + + .macro senduart, rd, rx +-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS ++#ifdef CONFIG_DEBUG_QCOM_UARTDM + @ Write the 1 character to UARTDM_TF + str \rd, [\rx, #0x70] + #else +@@ -68,7 +32,7 @@ + .endm + + .macro waituart, rd, rx +-#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS ++#ifdef CONFIG_DEBUG_QCOM_UARTDM + @ check for TX_EMT in UARTDM_SR + ldr \rd, [\rx, #0x08] + tst \rd, #0x08 +diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig +index a7f959e..9b26976 100644 +--- a/arch/arm/mach-msm/Kconfig ++++ b/arch/arm/mach-msm/Kconfig +@@ -42,9 +42,6 @@ config ARCH_QSD8X50 + + endchoice + +-config MSM_HAS_DEBUG_UART_HS +- bool +- + config MSM_SOC_REV_A + bool + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch new file mode 100644 index 0000000000..fdb5c67e22 --- /dev/null +++ b/target/linux/ipq806x/patches/0098-ARM-debug-qcom-add-UART-addresses-to-Kconfig-help-fo.patch @@ -0,0 +1,30 @@ +From 0a551e04e9a36ca1f8332071787b0e26bc5f8ecc Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Fri, 23 May 2014 18:12:32 +0300 +Subject: [PATCH 098/182] ARM: debug: qcom: add UART addresses to Kconfig help + for APQ8084 + +Add information about the APQ8084 debug UART physical and virtual +addresses in the DEBUG_QCOM_UARTDM Kconfig help section. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/Kconfig.debug | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 1a5895d..7820af1 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -380,6 +380,7 @@ choice + their output to the serial port on Qualcomm devices. + + ARCH DEBUG_UART_PHYS DEBUG_UART_BASE ++ APQ8084 0xf995e000 0xfa75e000 + MSM8X60 0x19c40000 0xf0040000 + MSM8960 0x16440000 0xf0040000 + MSM8974 0xf991e000 0xfa71e000 +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch new file mode 100644 index 0000000000..4e603eaf5e --- /dev/null +++ b/target/linux/ipq806x/patches/0099-ARM-qcom-Enable-ARM_AMBA-option-for-Qualcomm-SOCs.patch @@ -0,0 +1,31 @@ +From b6ee2071ec3af3ec90d93b8b70aa17d741590148 Mon Sep 17 00:00:00 2001 +From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> +Date: Thu, 15 May 2014 11:08:55 +0100 +Subject: [PATCH 099/182] ARM: qcom: Enable ARM_AMBA option for Qualcomm SOCs. + +As some of the IPs on Qualcomm SOCs are based on ARM PrimeCell IPs. +For example SDCC controller is PrimeCell MCI pl180. Adding this option will +give flexibility to reuse the existing drivers as it is without major +modifications. + +Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index 6440c11..63502cc 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -2,6 +2,7 @@ config ARCH_QCOM + bool "Qualcomm Support" if ARCH_MULTI_V7 + select ARCH_REQUIRE_GPIOLIB + select ARM_GIC ++ select ARM_AMBA + select CLKSRC_OF + select GENERIC_CLOCKEVENTS + select HAVE_SMP +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch new file mode 100644 index 0000000000..1fbd80602a --- /dev/null +++ b/target/linux/ipq806x/patches/0100-clk-qcom-Fix-msm8660-GCC-probe.patch @@ -0,0 +1,42 @@ +From d54996ddc69b69fe21f776e1de1eace04bca01c3 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 10:05:14 -0700 +Subject: [PATCH 100/182] clk: qcom: Fix msm8660 GCC probe + +When consolidating the msm8660 GCC probe code I forgot to keep +around these temporary clock registrations. Put them back so the +clock tree is not entirely orphaned. + +Fixes: 49fc825f0cc2 (clk: qcom: Consolidate common probe code) +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8660.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c +index 44bc6fa..0c4b727 100644 +--- a/drivers/clk/qcom/gcc-msm8660.c ++++ b/drivers/clk/qcom/gcc-msm8660.c +@@ -2718,6 +2718,18 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); + + static int gcc_msm8660_probe(struct platform_device *pdev) + { ++ struct clk *clk; ++ struct device *dev = &pdev->dev; ++ ++ /* Temporary until RPM clocks supported */ ++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ + return qcom_cc_probe(pdev, &gcc_msm8660_desc); + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch new file mode 100644 index 0000000000..9555e6b803 --- /dev/null +++ b/target/linux/ipq806x/patches/0101-clk-qcom-Fix-blsp2_ahb_clk-register-offset.patch @@ -0,0 +1,30 @@ +From b11c02becb4dadf5c375fef4b98f92af67106f18 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov <gdjakov@mm-sol.com> +Date: Tue, 20 May 2014 19:50:54 +0300 +Subject: [PATCH 101/182] clk: qcom: Fix blsp2_ahb_clk register offset + +The address of the blsp2_ahb_clk register is incorrect. Fix it. + +Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com> +Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/gcc-msm8974.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 0d1edc1..7a420fc 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -1341,7 +1341,7 @@ static struct clk_branch gcc_blsp1_uart6_apps_clk = { + }; + + static struct clk_branch gcc_blsp2_ahb_clk = { +- .halt_reg = 0x05c4, ++ .halt_reg = 0x0944, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch new file mode 100644 index 0000000000..8c959a5372 --- /dev/null +++ b/target/linux/ipq806x/patches/0104-clk-qcom-Return-highest-rate-when-round_rate-exceeds.patch @@ -0,0 +1,36 @@ +From 6417bb8469eb495d7f4e4b6b0ac08cbc1b8606cb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:10 -0700 +Subject: [PATCH 104/182] clk: qcom: Return highest rate when round_rate() + exceeds plan + +Some drivers may want to call clk_set_rate() with a very large +number to force the clock to go as fast as it possibly can +without having to know the range between the highest rate and +second highest rate. Add support for this by defaulting to the +highest rate in the frequency table if we can't find a frequency +greater than what is requested. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/clk-rcg2.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c +index 0996a3a..cbecaec 100644 +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -181,7 +181,8 @@ struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) + if (rate <= f->freq) + return f; + +- return NULL; ++ /* Default to our fastest rate */ ++ return f - 1; + } + + static long _freq_tbl_determine_rate(struct clk_hw *hw, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch new file mode 100644 index 0000000000..b95562956c --- /dev/null +++ b/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch @@ -0,0 +1,378 @@ +From 3123079878e29eb8c541111e30de4d1bb42ac6f9 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:11 -0700 +Subject: [PATCH 105/182] clk: qcom: Support display RCG clocks + +Add support for the DSI/EDP/HDMI RCG clocks. With the proper +display driver in place this should allow us to support display +clocks on msm8974 based devices. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/clk-rcg.h | 3 + + drivers/clk/qcom/clk-rcg2.c | 299 ++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 287 insertions(+), 15 deletions(-) + +diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h +index 1d6b6de..b9ec11d 100644 +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -155,5 +155,8 @@ struct clk_rcg2 { + #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) + + extern const struct clk_ops clk_rcg2_ops; ++extern const struct clk_ops clk_edp_pixel_ops; ++extern const struct clk_ops clk_byte_ops; ++extern const struct clk_ops clk_pixel_ops; + + #endif +diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c +index cbecaec..cd185d5 100644 +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -19,6 +19,7 @@ + #include <linux/clk-provider.h> + #include <linux/delay.h> + #include <linux/regmap.h> ++#include <linux/math64.h> + + #include <asm/div64.h> + +@@ -225,31 +226,25 @@ static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate, + return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p); + } + +-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) ++static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) + { +- struct clk_rcg2 *rcg = to_clk_rcg2(hw); +- const struct freq_tbl *f; + u32 cfg, mask; + int ret; + +- f = find_freq(rcg->freq_tbl, rate); +- if (!f) +- return -EINVAL; +- + if (rcg->mnd_width && f->n) { + mask = BIT(rcg->mnd_width) - 1; +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, +- mask, f->m); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + M_REG, mask, f->m); + if (ret) + return ret; + +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, +- mask, ~(f->n - f->m)); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m)); + if (ret) + return ret; + +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG, +- mask, ~f->n); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + D_REG, mask, ~f->n); + if (ret) + return ret; + } +@@ -260,14 +255,26 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) + cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT; + if (rcg->mnd_width && f->n) + cfg |= CFG_MODE_DUAL_EDGE; +- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask, +- cfg); ++ ret = regmap_update_bits(rcg->clkr.regmap, ++ rcg->cmd_rcgr + CFG_REG, mask, cfg); + if (ret) + return ret; + + return update_config(rcg); + } + ++static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f; ++ ++ f = find_freq(rcg->freq_tbl, rate); ++ if (!f) ++ return -EINVAL; ++ ++ return clk_rcg2_configure(rcg, f); ++} ++ + static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) + { +@@ -290,3 +297,265 @@ const struct clk_ops clk_rcg2_ops = { + .set_rate_and_parent = clk_rcg2_set_rate_and_parent, + }; + EXPORT_SYMBOL_GPL(clk_rcg2_ops); ++ ++struct frac_entry { ++ int num; ++ int den; ++}; ++ ++static const struct frac_entry frac_table_675m[] = { /* link rate of 270M */ ++ { 52, 295 }, /* 119 M */ ++ { 11, 57 }, /* 130.25 M */ ++ { 63, 307 }, /* 138.50 M */ ++ { 11, 50 }, /* 148.50 M */ ++ { 47, 206 }, /* 154 M */ ++ { 31, 100 }, /* 205.25 M */ ++ { 107, 269 }, /* 268.50 M */ ++ { }, ++}; ++ ++static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */ ++ { 31, 211 }, /* 119 M */ ++ { 32, 199 }, /* 130.25 M */ ++ { 63, 307 }, /* 138.50 M */ ++ { 11, 60 }, /* 148.50 M */ ++ { 50, 263 }, /* 154 M */ ++ { 31, 120 }, /* 205.25 M */ ++ { 119, 359 }, /* 268.50 M */ ++ { }, ++}; ++ ++static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ const struct frac_entry *frac; ++ int delta = 100000; ++ s64 src_rate = parent_rate; ++ s64 request; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ ++ if (src_rate == 810000000) ++ frac = frac_table_810m; ++ else ++ frac = frac_table_675m; ++ ++ for (; frac->num; frac++) { ++ request = rate; ++ request *= frac->den; ++ request = div_s64(request, frac->num); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ f.pre_div = hid_div; ++ f.pre_div >>= CFG_SRC_DIV_SHIFT; ++ f.pre_div &= mask; ++ f.m = frac->num; ++ f.n = frac->den; ++ ++ return clk_rcg2_configure(rcg, &f); ++ } ++ ++ return -EINVAL; ++} ++ ++static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_edp_pixel_set_rate(hw, rate, parent_rate); ++} ++ ++static long clk_edp_pixel_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f = rcg->freq_tbl; ++ const struct frac_entry *frac; ++ int delta = 100000; ++ s64 src_rate = *p_rate; ++ s64 request; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ ++ /* Force the correct parent */ ++ *p = clk_get_parent_by_index(hw->clk, f->src); ++ ++ if (src_rate == 810000000) ++ frac = frac_table_810m; ++ else ++ frac = frac_table_675m; ++ ++ for (; frac->num; frac++) { ++ request = rate; ++ request *= frac->den; ++ request = div_s64(request, frac->num); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ hid_div >>= CFG_SRC_DIV_SHIFT; ++ hid_div &= mask; ++ ++ return calc_rate(src_rate, frac->num, frac->den, !!frac->den, ++ hid_div); ++ } ++ ++ return -EINVAL; ++} ++ ++const struct clk_ops clk_edp_pixel_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_edp_pixel_set_rate, ++ .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent, ++ .determine_rate = clk_edp_pixel_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_edp_pixel_ops); ++ ++static long clk_byte_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ const struct freq_tbl *f = rcg->freq_tbl; ++ unsigned long parent_rate, div; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ ++ if (rate == 0) ++ return -EINVAL; ++ ++ *p = clk_get_parent_by_index(hw->clk, f->src); ++ *p_rate = parent_rate = __clk_round_rate(*p, rate); ++ ++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; ++ div = min_t(u32, div, mask); ++ ++ return calc_rate(parent_rate, 0, 0, 0, div); ++} ++ ++static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ unsigned long div; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ ++ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; ++ div = min_t(u32, div, mask); ++ ++ f.pre_div = div; ++ ++ return clk_rcg2_configure(rcg, &f); ++} ++ ++static int clk_byte_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_byte_set_rate(hw, rate, parent_rate); ++} ++ ++const struct clk_ops clk_byte_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_byte_set_rate, ++ .set_rate_and_parent = clk_byte_set_rate_and_parent, ++ .determine_rate = clk_byte_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_byte_ops); ++ ++static const struct frac_entry frac_table_pixel[] = { ++ { 3, 8 }, ++ { 2, 9 }, ++ { 4, 9 }, ++ { 1, 1 }, ++ { } ++}; ++ ++static long clk_pixel_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ unsigned long request, src_rate; ++ int delta = 100000; ++ const struct freq_tbl *f = rcg->freq_tbl; ++ const struct frac_entry *frac = frac_table_pixel; ++ struct clk *parent = *p = clk_get_parent_by_index(hw->clk, f->src); ++ ++ for (; frac->num; frac++) { ++ request = (rate * frac->den) / frac->num; ++ ++ src_rate = __clk_round_rate(parent, request); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ *p_rate = src_rate; ++ return (src_rate * frac->num) / frac->den; ++ } ++ ++ return -EINVAL; ++} ++ ++static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct freq_tbl f = *rcg->freq_tbl; ++ const struct frac_entry *frac = frac_table_pixel; ++ unsigned long request, src_rate; ++ int delta = 100000; ++ u32 mask = BIT(rcg->hid_width) - 1; ++ u32 hid_div; ++ struct clk *parent = clk_get_parent_by_index(hw->clk, f.src); ++ ++ for (; frac->num; frac++) { ++ request = (rate * frac->den) / frac->num; ++ ++ src_rate = __clk_round_rate(parent, request); ++ if ((src_rate < (request - delta)) || ++ (src_rate > (request + delta))) ++ continue; ++ ++ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, ++ &hid_div); ++ f.pre_div = hid_div; ++ f.pre_div >>= CFG_SRC_DIV_SHIFT; ++ f.pre_div &= mask; ++ f.m = frac->num; ++ f.n = frac->den; ++ ++ return clk_rcg2_configure(rcg, &f); ++ } ++ return -EINVAL; ++} ++ ++static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate, u8 index) ++{ ++ /* Parent index is set statically in frequency table */ ++ return clk_pixel_set_rate(hw, rate, parent_rate); ++} ++ ++const struct clk_ops clk_pixel_ops = { ++ .is_enabled = clk_rcg2_is_enabled, ++ .get_parent = clk_rcg2_get_parent, ++ .set_parent = clk_rcg2_set_parent, ++ .recalc_rate = clk_rcg2_recalc_rate, ++ .set_rate = clk_pixel_set_rate, ++ .set_rate_and_parent = clk_pixel_set_rate_and_parent, ++ .determine_rate = clk_pixel_determine_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_pixel_ops); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch new file mode 100644 index 0000000000..ce9499afb1 --- /dev/null +++ b/target/linux/ipq806x/patches/0106-clk-qcom-Properly-support-display-clocks-on-msm8974.patch @@ -0,0 +1,259 @@ +From 4ccbe584ecb970f86bab58c0ca93998cccc9e810 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:12 -0700 +Subject: [PATCH 106/182] clk: qcom: Properly support display clocks on + msm8974 + +The display clocks all source from dedicated phy PLLs within their +respective multimedia hardware block. Hook up these PLLs to the +display clocks with the appropriate parent mappings, clock flags, +and the appropriate clock ops. This should allow the display +clocks to work once the appropriate phy PLL driver registers their +PLL clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/mmcc-msm8974.c | 105 ++++++++++++++++++++------------------- + 1 file changed, 54 insertions(+), 51 deletions(-) + +diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c +index 62200bb..c65b905 100644 +--- a/drivers/clk/qcom/mmcc-msm8974.c ++++ b/drivers/clk/qcom/mmcc-msm8974.c +@@ -41,9 +41,11 @@ + #define P_EDPVCO 3 + #define P_GPLL1 4 + #define P_DSI0PLL 4 ++#define P_DSI0PLL_BYTE 4 + #define P_MMPLL2 4 + #define P_MMPLL3 4 + #define P_DSI1PLL 5 ++#define P_DSI1PLL_BYTE 5 + + static const u8 mmcc_xo_mmpll0_mmpll1_gpll0_map[] = { + [P_XO] = 0, +@@ -161,6 +163,24 @@ static const char *mmcc_xo_dsi_hdmi_edp_gpll0[] = { + "dsi1pll", + }; + ++static const u8 mmcc_xo_dsibyte_hdmi_edp_gpll0_map[] = { ++ [P_XO] = 0, ++ [P_EDPLINK] = 4, ++ [P_HDMIPLL] = 3, ++ [P_GPLL0] = 5, ++ [P_DSI0PLL_BYTE] = 1, ++ [P_DSI1PLL_BYTE] = 2, ++}; ++ ++static const char *mmcc_xo_dsibyte_hdmi_edp_gpll0[] = { ++ "xo", ++ "edp_link_clk", ++ "hdmipll", ++ "gpll0_vote", ++ "dsi0pllbyte", ++ "dsi1pllbyte", ++}; ++ + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + + static struct clk_pll mmpll0 = { +@@ -500,15 +520,8 @@ static struct clk_rcg2 jpeg2_clk_src = { + }, + }; + +-static struct freq_tbl ftbl_mdss_pclk0_clk[] = { +- F(125000000, P_DSI0PLL, 2, 0, 0), +- F(250000000, P_DSI0PLL, 1, 0, 0), +- { } +-}; +- +-static struct freq_tbl ftbl_mdss_pclk1_clk[] = { +- F(125000000, P_DSI1PLL, 2, 0, 0), +- F(250000000, P_DSI1PLL, 1, 0, 0), ++static struct freq_tbl pixel_freq_tbl[] = { ++ { .src = P_DSI0PLL }, + { } + }; + +@@ -517,12 +530,13 @@ static struct clk_rcg2 pclk0_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_pclk0_clk, ++ .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk0_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_pixel_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -531,12 +545,13 @@ static struct clk_rcg2 pclk1_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_pclk1_clk, ++ .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk1_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_pixel_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -754,41 +769,36 @@ static struct clk_rcg2 cpp_clk_src = { + }, + }; + +-static struct freq_tbl ftbl_mdss_byte0_clk[] = { +- F(93750000, P_DSI0PLL, 8, 0, 0), +- F(187500000, P_DSI0PLL, 4, 0, 0), +- { } +-}; +- +-static struct freq_tbl ftbl_mdss_byte1_clk[] = { +- F(93750000, P_DSI1PLL, 8, 0, 0), +- F(187500000, P_DSI1PLL, 4, 0, 0), ++static struct freq_tbl byte_freq_tbl[] = { ++ { .src = P_DSI0PLL_BYTE }, + { } + }; + + static struct clk_rcg2 byte0_clk_src = { + .cmd_rcgr = 0x2120, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_byte0_clk, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, ++ .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte0_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + + static struct clk_rcg2 byte1_clk_src = { + .cmd_rcgr = 0x2140, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_byte1_clk, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, ++ .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte1_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +@@ -826,12 +836,12 @@ static struct clk_rcg2 edplink_clk_src = { + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +-static struct freq_tbl ftbl_mdss_edppixel_clk[] = { +- F(175000000, P_EDPVCO, 2, 0, 0), +- F(350000000, P_EDPVCO, 11, 0, 0), ++static struct freq_tbl edp_pixel_freq_tbl[] = { ++ { .src = P_EDPVCO }, + { } + }; + +@@ -840,12 +850,12 @@ static struct clk_rcg2 edppixel_clk_src = { + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_map, +- .freq_tbl = ftbl_mdss_edppixel_clk, ++ .freq_tbl = edp_pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "edppixel_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_edp_pixel_ops, + }, + }; + +@@ -857,11 +867,11 @@ static struct freq_tbl ftbl_mdss_esc0_1_clk[] = { + static struct clk_rcg2 esc0_clk_src = { + .cmd_rcgr = 0x2160, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc0_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +@@ -870,26 +880,18 @@ static struct clk_rcg2 esc0_clk_src = { + static struct clk_rcg2 esc1_clk_src = { + .cmd_rcgr = 0x2180, + .hid_width = 5, +- .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, ++ .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc1_clk_src", +- .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, ++ .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, + }; + +-static struct freq_tbl ftbl_mdss_extpclk_clk[] = { +- F(25200000, P_HDMIPLL, 1, 0, 0), +- F(27000000, P_HDMIPLL, 1, 0, 0), +- F(27030000, P_HDMIPLL, 1, 0, 0), +- F(65000000, P_HDMIPLL, 1, 0, 0), +- F(74250000, P_HDMIPLL, 1, 0, 0), +- F(108000000, P_HDMIPLL, 1, 0, 0), +- F(148500000, P_HDMIPLL, 1, 0, 0), +- F(268500000, P_HDMIPLL, 1, 0, 0), +- F(297000000, P_HDMIPLL, 1, 0, 0), ++static struct freq_tbl extpclk_freq_tbl[] = { ++ { .src = P_HDMIPLL }, + { } + }; + +@@ -897,12 +899,13 @@ static struct clk_rcg2 extpclk_clk_src = { + .cmd_rcgr = 0x2060, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, +- .freq_tbl = ftbl_mdss_extpclk_clk, ++ .freq_tbl = extpclk_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "extpclk_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_byte_ops, ++ .flags = CLK_SET_RATE_PARENT, + }, + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch new file mode 100644 index 0000000000..1d0ee4573f --- /dev/null +++ b/target/linux/ipq806x/patches/0107-clk-qcom-Support-msm8974pro-global-clock-control-har.patch @@ -0,0 +1,245 @@ +From a740d2b024c5b71c6f9989976049f03b634bb13d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:13 -0700 +Subject: [PATCH 107/182] clk: qcom: Support msm8974pro global clock control + hardware + +A new PLL (gpll4) is added on msm8974 PRO devices to support a +faster sdc1 clock rate. Add support for this and the two new sdcc +cal clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 2 + + drivers/clk/qcom/gcc-msm8974.c | 130 +++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-msm8974.h | 4 + + 3 files changed, 130 insertions(+), 6 deletions(-) + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 7b7104e..9cfcb4f 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -8,6 +8,8 @@ Required properties : + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" ++ "qcom,gcc-msm8974pro" ++ "qcom,gcc-msm8974pro-ac" + + - reg : shall contain base register location and length + - #clock-cells : shall contain 1 +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index 7a420fc..7af7c18 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -35,6 +35,7 @@ + #define P_XO 0 + #define P_GPLL0 1 + #define P_GPLL1 1 ++#define P_GPLL4 2 + + static const u8 gcc_xo_gpll0_map[] = { + [P_XO] = 0, +@@ -46,6 +47,18 @@ static const char *gcc_xo_gpll0[] = { + "gpll0_vote", + }; + ++static const u8 gcc_xo_gpll0_gpll4_map[] = { ++ [P_XO] = 0, ++ [P_GPLL0] = 1, ++ [P_GPLL4] = 5, ++}; ++ ++static const char *gcc_xo_gpll0_gpll4[] = { ++ "xo", ++ "gpll0_vote", ++ "gpll4_vote", ++}; ++ + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + + static struct clk_pll gpll0 = { +@@ -138,6 +151,33 @@ static struct clk_regmap gpll1_vote = { + }, + }; + ++static struct clk_pll gpll4 = { ++ .l_reg = 0x1dc4, ++ .m_reg = 0x1dc8, ++ .n_reg = 0x1dcc, ++ .config_reg = 0x1dd4, ++ .mode_reg = 0x1dc0, ++ .status_reg = 0x1ddc, ++ .status_bit = 17, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "gpll4", ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap gpll4_vote = { ++ .enable_reg = 0x1480, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gpll4_vote", ++ .parent_names = (const char *[]){ "gpll4" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ + static const struct freq_tbl ftbl_gcc_usb30_master_clk[] = { + F(125000000, P_GPLL0, 1, 5, 24), + { } +@@ -812,18 +852,33 @@ static const struct freq_tbl ftbl_gcc_sdcc1_4_apps_clk[] = { + { } + }; + ++static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_pro[] = { ++ F(144000, P_XO, 16, 3, 25), ++ F(400000, P_XO, 12, 1, 4), ++ F(20000000, P_GPLL0, 15, 1, 2), ++ F(25000000, P_GPLL0, 12, 1, 2), ++ F(50000000, P_GPLL0, 12, 0, 0), ++ F(100000000, P_GPLL0, 6, 0, 0), ++ F(192000000, P_GPLL4, 4, 0, 0), ++ F(200000000, P_GPLL0, 3, 0, 0), ++ F(384000000, P_GPLL4, 2, 0, 0), ++ { } ++}; ++ ++static struct clk_init_data sdcc1_apps_clk_src_init = { ++ .name = "sdcc1_apps_clk_src", ++ .parent_names = gcc_xo_gpll0, ++ .num_parents = 2, ++ .ops = &clk_rcg2_ops, ++}; ++ + static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x04d0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, +- .clkr.hw.init = &(struct clk_init_data){ +- .name = "sdcc1_apps_clk_src", +- .parent_names = gcc_xo_gpll0, +- .num_parents = 2, +- .ops = &clk_rcg2_ops, +- }, ++ .clkr.hw.init = &sdcc1_apps_clk_src_init, + }; + + static struct clk_rcg2 sdcc2_apps_clk_src = { +@@ -1995,6 +2050,38 @@ static struct clk_branch gcc_sdcc1_apps_clk = { + }, + }; + ++static struct clk_branch gcc_sdcc1_cdccal_ff_clk = { ++ .halt_reg = 0x04e8, ++ .clkr = { ++ .enable_reg = 0x04e8, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_sdcc1_cdccal_ff_clk", ++ .parent_names = (const char *[]){ ++ "xo" ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_sdcc1_cdccal_sleep_clk = { ++ .halt_reg = 0x04e4, ++ .clkr = { ++ .enable_reg = 0x04e4, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_sdcc1_cdccal_sleep_clk", ++ .parent_names = (const char *[]){ ++ "sleep_clk_src" ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ + static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x0508, + .clkr = { +@@ -2484,6 +2571,10 @@ static struct clk_regmap *gcc_msm8974_clocks[] = { + [GCC_USB_HSIC_IO_CAL_SLEEP_CLK] = &gcc_usb_hsic_io_cal_sleep_clk.clkr, + [GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr, + [GCC_MMSS_GPLL0_CLK_SRC] = &gcc_mmss_gpll0_clk_src, ++ [GPLL4] = NULL, ++ [GPLL4_VOTE] = NULL, ++ [GCC_SDCC1_CDCCAL_SLEEP_CLK] = NULL, ++ [GCC_SDCC1_CDCCAL_FF_CLK] = NULL, + }; + + static const struct qcom_reset_map gcc_msm8974_resets[] = { +@@ -2585,14 +2676,41 @@ static const struct qcom_cc_desc gcc_msm8974_desc = { + + static const struct of_device_id gcc_msm8974_match_table[] = { + { .compatible = "qcom,gcc-msm8974" }, ++ { .compatible = "qcom,gcc-msm8974pro" , .data = (void *)1UL }, ++ { .compatible = "qcom,gcc-msm8974pro-ac", .data = (void *)1UL }, + { } + }; + MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); + ++static void msm8974_pro_clock_override(void) ++{ ++ sdcc1_apps_clk_src_init.parent_names = gcc_xo_gpll0_gpll4; ++ sdcc1_apps_clk_src_init.num_parents = 3; ++ sdcc1_apps_clk_src.freq_tbl = ftbl_gcc_sdcc1_apps_clk_pro; ++ sdcc1_apps_clk_src.parent_map = gcc_xo_gpll0_gpll4_map; ++ ++ gcc_msm8974_clocks[GPLL4] = &gpll4.clkr; ++ gcc_msm8974_clocks[GPLL4_VOTE] = &gpll4_vote; ++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_SLEEP_CLK] = ++ &gcc_sdcc1_cdccal_sleep_clk.clkr; ++ gcc_msm8974_clocks[GCC_SDCC1_CDCCAL_FF_CLK] = ++ &gcc_sdcc1_cdccal_ff_clk.clkr; ++} ++ + static int gcc_msm8974_probe(struct platform_device *pdev) + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ bool pro; ++ const struct of_device_id *id; ++ ++ id = of_match_device(gcc_msm8974_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ pro = !!(id->data); ++ ++ if (pro) ++ msm8974_pro_clock_override(); + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +diff --git a/include/dt-bindings/clock/qcom,gcc-msm8974.h b/include/dt-bindings/clock/qcom,gcc-msm8974.h +index 223ca17..51e51c8 100644 +--- a/include/dt-bindings/clock/qcom,gcc-msm8974.h ++++ b/include/dt-bindings/clock/qcom,gcc-msm8974.h +@@ -316,5 +316,9 @@ + #define GCC_CE2_CLK_SLEEP_ENA 299 + #define GCC_CE2_AXI_CLK_SLEEP_ENA 300 + #define GCC_CE2_AHB_CLK_SLEEP_ENA 301 ++#define GPLL4 302 ++#define GPLL4_VOTE 303 ++#define GCC_SDCC1_CDCCAL_SLEEP_CLK 304 ++#define GCC_SDCC1_CDCCAL_FF_CLK 305 + + #endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch new file mode 100644 index 0000000000..37806f154b --- /dev/null +++ b/target/linux/ipq806x/patches/0108-clk-qcom-Return-error-pointers-for-unimplemented-clo.patch @@ -0,0 +1,37 @@ +From 77b7f864b03d6b49c143a9c8c19432feef3032b5 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 16 May 2014 16:07:14 -0700 +Subject: [PATCH 108/182] clk: qcom: Return error pointers for unimplemented + clocks + +Not all clocks are implemented but client drivers can still +request them. Currently we will return a NULL pointer to them if +the clock isn't implemented in software but NULL pointers are +valid clock pointers. Return an error pointer so that driver's +don't proceed without a clock they may actually need. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/qcom/common.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c +index 86b45fb..9b5a1cf 100644 +--- a/drivers/clk/qcom/common.c ++++ b/drivers/clk/qcom/common.c +@@ -62,8 +62,10 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) + data->clk_num = num_clks; + + for (i = 0; i < num_clks; i++) { +- if (!rclks[i]) ++ if (!rclks[i]) { ++ clks[i] = ERR_PTR(-ENOENT); + continue; ++ } + clk = devm_clk_register_regmap(dev, rclks[i]); + if (IS_ERR(clk)) + return PTR_ERR(clk); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch new file mode 100644 index 0000000000..102fd4f6c3 --- /dev/null +++ b/target/linux/ipq806x/patches/0109-libahci-Allow-drivers-to-override-start_engine.patch @@ -0,0 +1,223 @@ +From 10f3c772363e549c3dbd3cc3755d270c5656d5b8 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:30 +0100 +Subject: [PATCH 109/182] libahci: Allow drivers to override start_engine + +Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a +special register to be poked before starting the DMA engine. + +This register gets reset on an ahci_stop_engine call, so there is no other +place then ahci_start_engine where this poking can be done. + +This commit allows drivers to override ahci_start_engine behavior for use by +the Allwinner AHCI driver (and potentially other drivers in the future). + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci.c | 6 ++++-- + drivers/ata/ahci.h | 6 ++++++ + drivers/ata/libahci.c | 26 +++++++++++++++++++------- + drivers/ata/sata_highbank.c | 3 ++- + 4 files changed, 31 insertions(+), 10 deletions(-) + +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index c81d809..8bfc477 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -578,6 +578,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) + { + struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + bool online; + int rc; + +@@ -588,7 +589,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + +@@ -603,6 +604,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + { + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -618,7 +620,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* The pseudo configuration device on SIMG4726 attached to + * ASUS P5W-DH Deluxe doesn't send signature FIS after +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 2289efd..64d1a99 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -323,6 +323,12 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clk; /* Only for platforms supporting clk */ + void *plat_data; /* Other platform data */ ++ /* ++ * Optional ahci_start_engine override, if not set this gets set to the ++ * default ahci_start_engine during ahci_save_initial_config, this can ++ * be overridden anytime before the host is activated. ++ */ ++ void (*start_engine)(struct ata_port *ap); + }; + + extern int ahci_ignore_sss; +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index 36605ab..f839bb3 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, + * + * If inconsistent, config values are fixed up by this function. + * ++ * If it is not set already this function sets hpriv->start_engine to ++ * ahci_start_engine. ++ * + * LOCKING: + * None. + */ +@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev, + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; ++ ++ if (!hpriv->start_engine) ++ hpriv->start_engine = ahci_start_engine; + } + EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap) + + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { +@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap) + + /* restart engine */ + out_restart: +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + return rc; + } + EXPORT_SYMBOL_GPL(ahci_kick_engine); +@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap) + + void ahci_error_handler(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + sata_pmp_error_handler(ap); +@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) + + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, +@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + + static void ahci_enable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap) + } else + dev_err(ap->host->dev, "Failed to enable FBS\n"); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_disable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap) + pp->fbs_enabled = false; + } + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_pmp_attach(struct ata_port *ap) +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index 870b11e..b3b18d1 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + break; + } while (!online && retry--); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch new file mode 100644 index 0000000000..09c4f220b4 --- /dev/null +++ b/target/linux/ipq806x/patches/0110-ahci-platform-Add-support-for-devices-with-more-then.patch @@ -0,0 +1,253 @@ +From b7ad421c184b827806c7f65be3a980cfc855b589 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:31 +0100 +Subject: [PATCH 110/182] ahci-platform: Add support for devices with more + then 1 clock + +The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the +imx AHCI controller needs 3 clocks to be enabled. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 3 +- + drivers/ata/ahci_platform.c | 113 +++++++++++++++----- + include/linux/ahci_platform.h | 4 + + 4 files changed, 93 insertions(+), 28 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 89de156..3ced07d 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -10,6 +10,7 @@ Required properties: + + Optional properties: + - dma-coherent : Present if dma operations are coherent ++- clocks : a list of phandle + clock specifier pairs + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 64d1a99..c12862b 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -51,6 +51,7 @@ + + enum { + AHCI_MAX_PORTS = 32, ++ AHCI_MAX_CLKS = 3, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +@@ -321,7 +322,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +- struct clk *clk; /* Only for platforms supporting clk */ ++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 4b231ba..2342a42 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -87,6 +87,60 @@ static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), + }; + ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++static void ahci_put_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -97,6 +151,7 @@ static int ahci_probe(struct platform_device *pdev) + struct ahci_host_priv *hpriv; + struct ata_host *host; + struct resource *mem; ++ struct clk *clk; + int irq; + int n_ports; + int i; +@@ -131,17 +186,31 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- hpriv->clk = clk_get(dev, NULL); +- if (IS_ERR(hpriv->clk)) { +- dev_err(dev, "can't get clock\n"); +- } else { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- goto free_clk; ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto free_clk; ++ break; + } ++ hpriv->clks[i] = clk; + } + ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ goto free_clk; ++ + /* + * Some platforms might need to prepare for mmio region access, + * which could be done in the following init call. So, the mmio +@@ -222,11 +291,9 @@ pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + free_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_put(hpriv->clk); ++ ahci_put_clks(hpriv); + return rc; + } + +@@ -239,10 +306,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- if (!IS_ERR(hpriv->clk)) { +- clk_disable_unprepare(hpriv->clk); +- clk_put(hpriv->clk); +- } ++ ahci_disable_clks(hpriv); ++ ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +@@ -277,8 +342,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return 0; + } +@@ -290,13 +354,9 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (!IS_ERR(hpriv->clk)) { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- return rc; +- } +- } ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -317,8 +377,7 @@ static int ahci_resume(struct device *dev) + return 0; + + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 73a2500..769d065 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -19,6 +19,7 @@ + + struct device; + struct ata_port_info; ++struct ahci_host_priv; + + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); +@@ -30,4 +31,7 @@ struct ahci_platform_data { + unsigned int mask_port_map; + }; + ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch new file mode 100644 index 0000000000..2d6ac0bd98 --- /dev/null +++ b/target/linux/ipq806x/patches/0111-ahci-platform-Add-support-for-an-optional-regulator-.patch @@ -0,0 +1,142 @@ +From 967f4a7821a3356d9d3c8b641537cd6d5eb15439 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:32 +0100 +Subject: [PATCH 111/182] ahci-platform: Add support for an optional regulator + for sata-target power + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 36 ++++++++++++++++++-- + 3 files changed, 37 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 3ced07d..1ac807f 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -11,6 +11,7 @@ Required properties: + Optional properties: + - dma-coherent : Present if dma operations are coherent + - clocks : a list of phandle + clock specifier pairs ++- target-supply : regulator for SATA target power + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index c12862b..bf8100c 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ + #define EM_CTRL_MSG_TYPE 0x000f0000 +@@ -323,6 +324,7 @@ struct ahci_host_priv { + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ ++ struct regulator *target_pwr; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 2342a42..8f18ebe 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -186,6 +186,14 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ hpriv->target_pwr = NULL; ++ } ++ + for (i = 0; i < AHCI_MAX_CLKS; i++) { + /* + * For now we must use clk_get(dev, NULL) for the first clock, +@@ -207,9 +215,15 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ goto free_clk; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- goto free_clk; ++ goto disable_regulator; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -292,6 +306,9 @@ pdata_exit: + pdata->exit(dev); + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -308,6 +325,9 @@ static void ahci_host_stop(struct ata_host *host) + + ahci_disable_clks(hpriv); + ahci_put_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -344,6 +364,9 @@ static int ahci_suspend(struct device *dev) + + ahci_disable_clks(hpriv); + ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ + return 0; + } + +@@ -354,9 +377,15 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- return rc; ++ goto disable_regulator; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -378,6 +407,9 @@ static int ahci_resume(struct device *dev) + + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + + return rc; + } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch new file mode 100644 index 0000000000..ad6adbebf5 --- /dev/null +++ b/target/linux/ipq806x/patches/0112-ahci-platform-Add-enable_-disable_resources-helper-f.patch @@ -0,0 +1,208 @@ +From 3b2df84624b38362cb84c2d4c6d1d3540c5069d3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:33 +0100 +Subject: [PATCH 112/182] ahci-platform: Add enable_ / disable_resources + helper functions + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 106 +++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 2 + + 2 files changed, 71 insertions(+), 37 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 8f18ebe..656d285 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -133,6 +133,62 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ return 0; ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Clocks (through ahci_platform_disable_clks) ++ * 2) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ + static void ahci_put_clks(struct ahci_host_priv *hpriv) + { + int c; +@@ -215,15 +271,9 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- goto free_clk; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ goto free_clk; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -234,7 +284,7 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->init) { + rc = pdata->init(dev, hpriv->mmio); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + ahci_save_initial_config(dev, hpriv, +@@ -304,11 +354,8 @@ static int ahci_probe(struct platform_device *pdev) + pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -323,11 +370,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- ahci_disable_clks(hpriv); ++ ahci_platform_disable_resources(hpriv); + ahci_put_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -362,10 +406,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- ahci_disable_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++ ahci_platform_disable_resources(hpriv); + + return 0; + } +@@ -377,26 +418,20 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- return rc; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + + ahci_init_controller(host); + } +@@ -405,11 +440,8 @@ static int ahci_resume(struct device *dev) + + return 0; + +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 769d065..b674b01 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -33,5 +33,7 @@ struct ahci_platform_data { + + int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); + + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch new file mode 100644 index 0000000000..c7db74020d --- /dev/null +++ b/target/linux/ipq806x/patches/0113-ata-delete-non-required-instances-of-include-linux-i.patch @@ -0,0 +1,904 @@ +From 01b380db3c35bbd87cb3765573fd049bce3d7640 Mon Sep 17 00:00:00 2001 +From: Paul Gortmaker <paul.gortmaker@windriver.com> +Date: Tue, 21 Jan 2014 16:22:51 -0500 +Subject: [PATCH 113/182] ata: delete non-required instances of include + <linux/init.h> + +None of these files are actually using any __init type directives +and hence don't need to include <linux/init.h>. Most are just a +left over from __devinit and __cpuinit removal, or simply due to +code getting copied from one driver to the next. + +Cc: linux-ide@vger.kernel.org +Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/acard-ahci.c | 1 - + drivers/ata/ahci.c | 1 - + drivers/ata/ahci_platform.c | 1 - + drivers/ata/ata_generic.c | 1 - + drivers/ata/libahci.c | 1 - + drivers/ata/pata_acpi.c | 1 - + drivers/ata/pata_amd.c | 1 - + drivers/ata/pata_artop.c | 1 - + drivers/ata/pata_at91.c | 1 - + drivers/ata/pata_atiixp.c | 1 - + drivers/ata/pata_atp867x.c | 1 - + drivers/ata/pata_cmd640.c | 1 - + drivers/ata/pata_cmd64x.c | 1 - + drivers/ata/pata_cs5520.c | 1 - + drivers/ata/pata_cs5530.c | 1 - + drivers/ata/pata_cs5535.c | 1 - + drivers/ata/pata_cs5536.c | 1 - + drivers/ata/pata_cypress.c | 1 - + drivers/ata/pata_efar.c | 1 - + drivers/ata/pata_ep93xx.c | 1 - + drivers/ata/pata_hpt366.c | 1 - + drivers/ata/pata_hpt37x.c | 1 - + drivers/ata/pata_hpt3x2n.c | 1 - + drivers/ata/pata_hpt3x3.c | 1 - + drivers/ata/pata_imx.c | 1 - + drivers/ata/pata_it8213.c | 1 - + drivers/ata/pata_it821x.c | 1 - + drivers/ata/pata_jmicron.c | 1 - + drivers/ata/pata_marvell.c | 1 - + drivers/ata/pata_mpiix.c | 1 - + drivers/ata/pata_netcell.c | 1 - + drivers/ata/pata_ninja32.c | 1 - + drivers/ata/pata_ns87410.c | 1 - + drivers/ata/pata_ns87415.c | 1 - + drivers/ata/pata_oldpiix.c | 1 - + drivers/ata/pata_opti.c | 1 - + drivers/ata/pata_optidma.c | 1 - + drivers/ata/pata_pcmcia.c | 1 - + drivers/ata/pata_pdc2027x.c | 1 - + drivers/ata/pata_pdc202xx_old.c | 1 - + drivers/ata/pata_piccolo.c | 1 - + drivers/ata/pata_platform.c | 1 - + drivers/ata/pata_pxa.c | 1 - + drivers/ata/pata_radisys.c | 1 - + drivers/ata/pata_rdc.c | 1 - + drivers/ata/pata_rz1000.c | 1 - + drivers/ata/pata_sc1200.c | 1 - + drivers/ata/pata_scc.c | 1 - + drivers/ata/pata_sch.c | 1 - + drivers/ata/pata_serverworks.c | 1 - + drivers/ata/pata_sil680.c | 1 - + drivers/ata/pata_sis.c | 1 - + drivers/ata/pata_sl82c105.c | 1 - + drivers/ata/pata_triflex.c | 1 - + drivers/ata/pata_via.c | 1 - + drivers/ata/pdc_adma.c | 1 - + drivers/ata/sata_dwc_460ex.c | 1 - + drivers/ata/sata_highbank.c | 1 - + drivers/ata/sata_nv.c | 1 - + drivers/ata/sata_promise.c | 1 - + drivers/ata/sata_qstor.c | 1 - + drivers/ata/sata_sil.c | 1 - + drivers/ata/sata_sis.c | 1 - + drivers/ata/sata_svw.c | 1 - + drivers/ata/sata_sx4.c | 1 - + drivers/ata/sata_uli.c | 1 - + drivers/ata/sata_via.c | 1 - + drivers/ata/sata_vsc.c | 1 - + 68 files changed, 68 deletions(-) + +diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c +index fd665d9..b51605a 100644 +--- a/drivers/ata/acard-ahci.c ++++ b/drivers/ata/acard-ahci.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index 8bfc477..a52a5b6 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 656d285..a32df31 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -17,7 +17,6 @@ + #include <linux/gfp.h> + #include <linux/module.h> + #include <linux/pm.h> +-#include <linux/init.h> + #include <linux/interrupt.h> + #include <linux/device.h> + #include <linux/platform_device.h> +diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c +index 7d19665..9498a7d 100644 +--- a/drivers/ata/ata_generic.c ++++ b/drivers/ata/ata_generic.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index f839bb3..fa02770 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/gfp.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c +index 62c9ac8..5108b87 100644 +--- a/drivers/ata/pata_acpi.c ++++ b/drivers/ata/pata_acpi.c +@@ -7,7 +7,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c +index d23e2b3..1206fa6 100644 +--- a/drivers/ata/pata_amd.c ++++ b/drivers/ata/pata_amd.c +@@ -17,7 +17,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c +index 1581dee..3aa4e65 100644 +--- a/drivers/ata/pata_artop.c ++++ b/drivers/ata/pata_artop.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c +index d63ee8f..e9c8727 100644 +--- a/drivers/ata/pata_at91.c ++++ b/drivers/ata/pata_at91.c +@@ -18,7 +18,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/gfp.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c +index 24e5105..30fa4ca 100644 +--- a/drivers/ata/pata_atiixp.c ++++ b/drivers/ata/pata_atiixp.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c +index 2ca5026..7e73a0f 100644 +--- a/drivers/ata/pata_atp867x.c ++++ b/drivers/ata/pata_atp867x.c +@@ -29,7 +29,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c +index 8fb69e5..57f1be6 100644 +--- a/drivers/ata/pata_cmd640.c ++++ b/drivers/ata/pata_cmd640.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/gfp.h> +diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c +index 1275a8d..6bca350 100644 +--- a/drivers/ata/pata_cmd64x.c ++++ b/drivers/ata/pata_cmd64x.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c +index f10baab..bcde4b7 100644 +--- a/drivers/ata/pata_cs5520.c ++++ b/drivers/ata/pata_cs5520.c +@@ -34,7 +34,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c +index f07f229..8afe854 100644 +--- a/drivers/ata/pata_cs5530.c ++++ b/drivers/ata/pata_cs5530.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c +index 997e16a..2c0986f 100644 +--- a/drivers/ata/pata_cs5535.c ++++ b/drivers/ata/pata_cs5535.c +@@ -31,7 +31,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c +index 0448860..32ddcae 100644 +--- a/drivers/ata/pata_cs5536.c ++++ b/drivers/ata/pata_cs5536.c +@@ -33,7 +33,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/libata.h> +diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c +index 810bc99..3435bd6 100644 +--- a/drivers/ata/pata_cypress.c ++++ b/drivers/ata/pata_cypress.c +@@ -11,7 +11,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c +index 3c12fd7..f440892 100644 +--- a/drivers/ata/pata_efar.c ++++ b/drivers/ata/pata_efar.c +@@ -14,7 +14,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c +index 980b88e..cad9d45 100644 +--- a/drivers/ata/pata_ep93xx.c ++++ b/drivers/ata/pata_ep93xx.c +@@ -34,7 +34,6 @@ + #include <linux/err.h> + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c +index 35b5213..8e76f79 100644 +--- a/drivers/ata/pata_hpt366.c ++++ b/drivers/ata/pata_hpt366.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c +index a9d74ef..3ba843f 100644 +--- a/drivers/ata/pata_hpt37x.c ++++ b/drivers/ata/pata_hpt37x.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c +index 4be0398..b93c0f0 100644 +--- a/drivers/ata/pata_hpt3x2n.c ++++ b/drivers/ata/pata_hpt3x2n.c +@@ -20,7 +20,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c +index 85cf286..255c5aa 100644 +--- a/drivers/ata/pata_hpt3x3.c ++++ b/drivers/ata/pata_hpt3x3.c +@@ -16,7 +16,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c +index b0b18ec..7e69797 100644 +--- a/drivers/ata/pata_imx.c ++++ b/drivers/ata/pata_imx.c +@@ -15,7 +15,6 @@ + */ + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c +index 2a8dd95..81369d1 100644 +--- a/drivers/ata/pata_it8213.c ++++ b/drivers/ata/pata_it8213.c +@@ -10,7 +10,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c +index 581e04d..dc3d787 100644 +--- a/drivers/ata/pata_it821x.c ++++ b/drivers/ata/pata_it821x.c +@@ -72,7 +72,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/slab.h> +diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c +index 76e739b0..b1cfa02 100644 +--- a/drivers/ata/pata_jmicron.c ++++ b/drivers/ata/pata_jmicron.c +@@ -10,7 +10,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c +index a4f5e78..6bad3df 100644 +--- a/drivers/ata/pata_marvell.c ++++ b/drivers/ata/pata_marvell.c +@@ -11,7 +11,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c +index 1f5f28b..f39a537 100644 +--- a/drivers/ata/pata_mpiix.c ++++ b/drivers/ata/pata_mpiix.c +@@ -28,7 +28,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c +index ad1a0fe..e3b9709 100644 +--- a/drivers/ata/pata_netcell.c ++++ b/drivers/ata/pata_netcell.c +@@ -7,7 +7,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c +index 9513e07..56201a6 100644 +--- a/drivers/ata/pata_ninja32.c ++++ b/drivers/ata/pata_ninja32.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c +index 0c424da..6154c3e 100644 +--- a/drivers/ata/pata_ns87410.c ++++ b/drivers/ata/pata_ns87410.c +@@ -20,7 +20,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c +index 16dc3a6..d44df7c 100644 +--- a/drivers/ata/pata_ns87415.c ++++ b/drivers/ata/pata_ns87415.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c +index d77b2e1..319b644 100644 +--- a/drivers/ata/pata_oldpiix.c ++++ b/drivers/ata/pata_oldpiix.c +@@ -16,7 +16,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c +index 4ea70cd..fb042e0 100644 +--- a/drivers/ata/pata_opti.c ++++ b/drivers/ata/pata_opti.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c +index 78ede3f..bb71ea2 100644 +--- a/drivers/ata/pata_optidma.c ++++ b/drivers/ata/pata_optidma.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c +index 40254f4..bcc4b96 100644 +--- a/drivers/ata/pata_pcmcia.c ++++ b/drivers/ata/pata_pcmcia.c +@@ -26,7 +26,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/slab.h> +diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c +index 9d874c8..1151f23 100644 +--- a/drivers/ata/pata_pdc2027x.c ++++ b/drivers/ata/pata_pdc2027x.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c +index c34fc50..defa050 100644 +--- a/drivers/ata/pata_pdc202xx_old.c ++++ b/drivers/ata/pata_pdc202xx_old.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c +index 2beb6b5..0b46be1 100644 +--- a/drivers/ata/pata_piccolo.c ++++ b/drivers/ata/pata_piccolo.c +@@ -18,7 +18,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c +index 0279488..a5579b5 100644 +--- a/drivers/ata/pata_platform.c ++++ b/drivers/ata/pata_platform.c +@@ -13,7 +13,6 @@ + */ + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <scsi/scsi_host.h> + #include <linux/ata.h> +diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c +index a6f05ac..73259bf 100644 +--- a/drivers/ata/pata_pxa.c ++++ b/drivers/ata/pata_pxa.c +@@ -20,7 +20,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/ata.h> + #include <linux/libata.h> +diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c +index f582ba1..be3f102 100644 +--- a/drivers/ata/pata_radisys.c ++++ b/drivers/ata/pata_radisys.c +@@ -15,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c +index 79a970f..521b213 100644 +--- a/drivers/ata/pata_rdc.c ++++ b/drivers/ata/pata_rdc.c +@@ -24,7 +24,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c +index 040b093..caedc90 100644 +--- a/drivers/ata/pata_rz1000.c ++++ b/drivers/ata/pata_rz1000.c +@@ -14,7 +14,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c +index ce2f828..96a232f 100644 +--- a/drivers/ata/pata_sc1200.c ++++ b/drivers/ata/pata_sc1200.c +@@ -32,7 +32,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c +index f35f15f..f1f5b5a 100644 +--- a/drivers/ata/pata_scc.c ++++ b/drivers/ata/pata_scc.c +@@ -35,7 +35,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c +index d3830c4..5a1cde0 100644 +--- a/drivers/ata/pata_sch.c ++++ b/drivers/ata/pata_sch.c +@@ -27,7 +27,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c +index 96c6a79..e27f31f 100644 +--- a/drivers/ata/pata_serverworks.c ++++ b/drivers/ata/pata_serverworks.c +@@ -34,7 +34,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c +index c4b0b07..73fe362 100644 +--- a/drivers/ata/pata_sil680.c ++++ b/drivers/ata/pata_sil680.c +@@ -25,7 +25,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c +index 1e83636..78d913a 100644 +--- a/drivers/ata/pata_sis.c ++++ b/drivers/ata/pata_sis.c +@@ -26,7 +26,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c +index 6816911..900f0e4 100644 +--- a/drivers/ata/pata_sl82c105.c ++++ b/drivers/ata/pata_sl82c105.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c +index 94473da..7bc78e2 100644 +--- a/drivers/ata/pata_triflex.c ++++ b/drivers/ata/pata_triflex.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <scsi/scsi_host.h> +diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c +index c3ab9a6..f6c9632 100644 +--- a/drivers/ata/pata_via.c ++++ b/drivers/ata/pata_via.c +@@ -55,7 +55,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/gfp.h> +diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c +index 8ea6e6a..f10631b 100644 +--- a/drivers/ata/pdc_adma.c ++++ b/drivers/ata/pdc_adma.c +@@ -36,7 +36,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c +index 523524b..73510d0 100644 +--- a/drivers/ata/sata_dwc_460ex.c ++++ b/drivers/ata/sata_dwc_460ex.c +@@ -29,7 +29,6 @@ + + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/device.h> + #include <linux/of_address.h> + #include <linux/of_irq.h> +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index b3b18d1..d4df0bf 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -19,7 +19,6 @@ + #include <linux/kernel.h> + #include <linux/gfp.h> + #include <linux/module.h> +-#include <linux/init.h> + #include <linux/types.h> + #include <linux/err.h> + #include <linux/io.h> +diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c +index d74def8..ba5f271 100644 +--- a/drivers/ata/sata_nv.c ++++ b/drivers/ata/sata_nv.c +@@ -40,7 +40,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c +index 97f4acb..3638887 100644 +--- a/drivers/ata/sata_promise.c ++++ b/drivers/ata/sata_promise.c +@@ -35,7 +35,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c +index 3b0dd57..9a6bd4c 100644 +--- a/drivers/ata/sata_qstor.c ++++ b/drivers/ata/sata_qstor.c +@@ -31,7 +31,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c +index b7695e8..3062f86 100644 +--- a/drivers/ata/sata_sil.c ++++ b/drivers/ata/sata_sil.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c +index 1ad2f62..b513428 100644 +--- a/drivers/ata/sata_sis.c ++++ b/drivers/ata/sata_sis.c +@@ -33,7 +33,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c +index dc4f701..c630fa8 100644 +--- a/drivers/ata/sata_svw.c ++++ b/drivers/ata/sata_svw.c +@@ -39,7 +39,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c +index 9947010..6cd0312 100644 +--- a/drivers/ata/sata_sx4.c ++++ b/drivers/ata/sata_sx4.c +@@ -82,7 +82,6 @@ + #include <linux/module.h> + #include <linux/pci.h> + #include <linux/slab.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c +index 6d64891..08f98c3 100644 +--- a/drivers/ata/sata_uli.c ++++ b/drivers/ata/sata_uli.c +@@ -28,7 +28,6 @@ + #include <linux/module.h> + #include <linux/gfp.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c +index 87f056e..f72e842 100644 +--- a/drivers/ata/sata_via.c ++++ b/drivers/ata/sata_via.c +@@ -36,7 +36,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/device.h> +diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c +index 44f304b..29e847a 100644 +--- a/drivers/ata/sata_vsc.c ++++ b/drivers/ata/sata_vsc.c +@@ -37,7 +37,6 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +-#include <linux/init.h> + #include <linux/blkdev.h> + #include <linux/delay.h> + #include <linux/interrupt.h> +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch new file mode 100644 index 0000000000..9b9c7afd47 --- /dev/null +++ b/target/linux/ipq806x/patches/0114-ahci-platform-Library-ise-ahci_probe-functionality.patch @@ -0,0 +1,341 @@ +From 8be4987340a101253fb871556894a002f1afb51f Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:34 +0100 +Subject: [PATCH 114/182] ahci-platform: "Library-ise" ahci_probe + functionality + +ahci_probe consists of 3 steps: +1) Get resources (get mmio, clks, regulator) +2) Enable resources, handled by ahci_platform_enable_resouces +3) The more or less standard ahci-host controller init sequence + +This commit refactors step 1 and 3 into separate functions, so the platform +drivers for AHCI implementations which need a specific order in step 2, +and / or need to do some custom register poking at some time, can re-use +ahci-platform.c code without needing to copy and paste it. + +Note that ahci_platform_init_host's prototype takes the 3 non function +members of ahci_platform_data as arguments, the idea is that drivers using +the new exported utility functions will not use ahci_platform_data at all, +and hopefully in the future ahci_platform_data can go away entirely. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 188 +++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 14 +++ + 2 files changed, 137 insertions(+), 65 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index a32df31..19e9eaa 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -188,64 +188,60 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +-static void ahci_put_clks(struct ahci_host_priv *hpriv) ++static void ahci_platform_put_resources(struct device *dev, void *res) + { ++ struct ahci_host_priv *hpriv = res; + int c; + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } + +-static int ahci_probe(struct platform_device *pdev) ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); +- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; +- const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; +- struct ata_host *host; +- struct resource *mem; + struct clk *clk; +- int irq; +- int n_ports; +- int i; +- int rc; ++ int i, rc = -ENOMEM; + +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!mem) { +- dev_err(dev, "no mmio space\n"); +- return -EINVAL; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); + +- if (pdata && pdata->ata_port_info) +- pi = *pdata->ata_port_info; +- +- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); +- if (!hpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); +- return -ENOMEM; +- } ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; + +- hpriv->flags |= (unsigned long)pi.private_data; ++ devres_add(dev, hpriv); + +- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (!hpriv->mmio) { +- dev_err(dev, "can't map %pR\n", mem); +- return -ENOMEM; ++ dev_err(dev, "no mmio space\n"); ++ goto err_out; + } + + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ goto err_out; + hpriv->target_pwr = NULL; + } + +@@ -264,33 +260,59 @@ static int ahci_probe(struct platform_device *pdev) + if (IS_ERR(clk)) { + rc = PTR_ERR(clk); + if (rc == -EPROBE_DEFER) +- goto free_clk; ++ goto err_out; + break; + } + hpriv->clks[i] = clk; + } + +- rc = ahci_platform_enable_resources(hpriv); +- if (rc) +- goto free_clk; ++ devres_remove_group(dev, NULL); ++ return hpriv; + +- /* +- * Some platforms might need to prepare for mmio region access, +- * which could be done in the following init call. So, the mmio +- * region shouldn't be accessed before init (if provided) has +- * returned successfully. +- */ +- if (pdata && pdata->init) { +- rc = pdata->init(dev, hpriv->mmio); +- if (rc) +- goto disable_resources; +- } ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; + +- ahci_save_initial_config(dev, hpriv, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } + + /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + +@@ -307,10 +329,8 @@ static int ahci_probe(struct platform_device *pdev) + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) { +- rc = -ENOMEM; +- goto pdata_exit; +- } ++ if (!host) ++ return -ENOMEM; + + host->private_data = hpriv; + +@@ -325,7 +345,8 @@ static int ahci_probe(struct platform_device *pdev) + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + +- ata_port_desc(ap, "mmio %pR", mem); ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ +@@ -339,13 +360,53 @@ static int ahci_probe(struct platform_device *pdev) + + rc = ahci_reset_controller(host); + if (rc) +- goto pdata_exit; ++ return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + +- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static int ahci_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ const struct platform_device_id *id = platform_get_device_id(pdev); ++ const struct ata_port_info *pi_template; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ /* ++ * Some platforms might need to prepare for mmio region access, ++ * which could be done in the following init call. So, the mmio ++ * region shouldn't be accessed before init (if provided) has ++ * returned successfully. ++ */ ++ if (pdata && pdata->init) { ++ rc = pdata->init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ if (pdata && pdata->ata_port_info) ++ pi_template = pdata->ata_port_info; ++ else ++ pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, pi_template, ++ pdata ? pdata->force_port_map : 0, ++ pdata ? pdata->mask_port_map : 0); + if (rc) + goto pdata_exit; + +@@ -355,8 +416,6 @@ pdata_exit: + pdata->exit(dev); + disable_resources: + ahci_platform_disable_resources(hpriv); +-free_clk: +- ahci_put_clks(hpriv); + return rc; + } + +@@ -370,7 +429,6 @@ static void ahci_host_stop(struct ata_host *host) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +- ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b674b01..b80c51c 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -20,7 +20,14 @@ + struct device; + struct ata_port_info; + struct ahci_host_priv; ++struct platform_device; + ++/* ++ * Note ahci_platform_data is deprecated, it is only kept around for use ++ * by the old da850 and spear13xx ahci code. ++ * New drivers should instead declare their own platform_driver struct, and ++ * use ahci_platform* functions in their own probe, suspend and resume methods. ++ */ + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); + void (*exit)(struct device *dev); +@@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); + int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev); ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map); + + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch new file mode 100644 index 0000000000..9a9cb074e8 --- /dev/null +++ b/target/linux/ipq806x/patches/0115-ahci-platform-Library-ise-suspend-resume-functionali.patch @@ -0,0 +1,180 @@ +From 8121ed9c7ea2d56f5e0a98c2db396214f85d7485 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 16:53:35 +0100 +Subject: [PATCH 115/182] ahci-platform: "Library-ise" suspend / resume + functionality + +Split suspend / resume code into host suspend / resume functionality and +resource enable / disabling phases, and export the new suspend_ / resume_host +functions. + +tj: Minor comment formatting updates. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 97 ++++++++++++++++++++++++++++++++++------- + include/linux/ahci_platform.h | 5 +++ + 2 files changed, 87 insertions(+), 15 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 19e9eaa..01f7bbe 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -432,14 +432,23 @@ static void ahci_host_stop(struct ata_host *host) + } + + #ifdef CONFIG_PM_SLEEP +-static int ahci_suspend(struct device *dev) ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) + { +- struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; +- int rc; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); +@@ -456,7 +465,58 @@ static int ahci_suspend(struct device *dev) + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + +- rc = ata_host_suspend(host, PMSG_SUSPEND); ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + +@@ -467,8 +527,19 @@ static int ahci_suspend(struct device *dev) + + return 0; + } ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-static int ahci_resume(struct device *dev) ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) + { + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); +@@ -485,15 +556,9 @@ static int ahci_resume(struct device *dev) + goto disable_resources; + } + +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- goto disable_resources; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; + + return 0; + +@@ -502,9 +567,11 @@ disable_resources: + + return rc; + } ++EXPORT_SYMBOL_GPL(ahci_platform_resume); + #endif + +-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); ++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ++ ahci_platform_resume); + + static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b80c51c..542f268 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev, + unsigned int force_port_map, + unsigned int mask_port_map); + ++int ahci_platform_suspend_host(struct device *dev); ++int ahci_platform_resume_host(struct device *dev); ++int ahci_platform_suspend(struct device *dev); ++int ahci_platform_resume(struct device *dev); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch new file mode 100644 index 0000000000..72c2c2e07d --- /dev/null +++ b/target/linux/ipq806x/patches/0116-ata-ahci_platform-Add-DT-compatible-for-Synopsis-DWC.patch @@ -0,0 +1,32 @@ +From bab3492791ade083c172a898b6f7a3692ffb89d5 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:38 +0100 +Subject: [PATCH 116/182] ata: ahci_platform: Add DT compatible for Synopsis + DWC AHCI controller + +Add compatible string "snps,dwc-ahci", which should be used +for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms. + +Signed-off-by: Roger Quadros <rogerq@ti.com> +Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 01f7bbe..968e7d9 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -577,6 +577,7 @@ static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,exynos5440-ahci", }, + { .compatible = "ibm,476gtr-ahci", }, ++ { .compatible = "snps,dwc-ahci", }, + {}, + }; + MODULE_DEVICE_TABLE(of, ahci_of_match); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch new file mode 100644 index 0000000000..fab7e57635 --- /dev/null +++ b/target/linux/ipq806x/patches/0117-ata-ahci_platform-Manage-SATA-PHY.patch @@ -0,0 +1,141 @@ +From 05b9b8d1035944af25694d2ebe53077cf1ccb067 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:40 +0100 +Subject: [PATCH 117/182] ata: ahci_platform: Manage SATA PHY + +Some platforms have a PHY hooked up to the SATA controller. The PHY +needs to be initialized and powered up for SATA to work. We do that +using the PHY framework. + +tj: Minor comment formatting updates. + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo<tj@kernel.org> +--- + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 47 insertions(+), 2 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index bf8100c..3ab7ac9 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/phy/phy.h> + #include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ +@@ -325,6 +326,7 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ ++ struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 968e7d9..243dde3 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -22,6 +22,7 @@ + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -140,6 +141,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. +@@ -161,8 +163,23 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + if (rc) + goto disable_regulator; + ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ + return 0; + ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ + disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +@@ -176,11 +193,17 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + * + * This function disables all ahci_platform managed resources in the + * following order: +- * 1) Clocks (through ahci_platform_disable_clks) +- * 2) Regulator ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator + */ + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + { ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) +@@ -208,6 +231,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value +@@ -266,6 +290,25 @@ struct ahci_host_priv *ahci_platform_get_resources( + hpriv->clks[i] = clk; + } + ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ + devres_remove_group(dev, NULL); + return hpriv; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch new file mode 100644 index 0000000000..2f3e707c31 --- /dev/null +++ b/target/linux/ipq806x/patches/0118-ata-ahci_platform-runtime-resume-the-device-before-u.patch @@ -0,0 +1,86 @@ +From abe309ab531f22b9b89329bd825606f6b68a95a1 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Sat, 22 Feb 2014 16:53:41 +0100 +Subject: [PATCH 118/182] ata: ahci_platform: runtime resume the device before + use + +On OMAP platforms the device needs to be runtime resumed before it can +be accessed. The OMAP HWMOD framework takes care of enabling the +module and its resources based on the device's runtime PM state. + +In this patch we runtime resume during .probe() and runtime suspend +after .remove(). + +We also update the runtime PM state during .resume(). + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci.h | 1 + + drivers/ata/ahci_platform.c | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 3ab7ac9..51af275 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -324,6 +324,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ ++ bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 243dde3..fc32863 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -23,6 +23,7 @@ + #include <linux/libata.h> + #include <linux/ahci_platform.h> + #include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -216,6 +217,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + struct ahci_host_priv *hpriv = res; + int c; + ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } +@@ -309,6 +315,10 @@ struct ahci_host_priv *ahci_platform_get_resources( + } + } + ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ + devres_remove_group(dev, NULL); + return hpriv; + +@@ -603,6 +613,11 @@ int ahci_platform_resume(struct device *dev) + if (rc) + goto disable_resources; + ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ + return 0; + + disable_resources: +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch new file mode 100644 index 0000000000..efccae45af --- /dev/null +++ b/target/linux/ipq806x/patches/0119-ahci_platform-Drop-support-for-ahci-strict-platform-.patch @@ -0,0 +1,54 @@ +From 474c436aebe260b0e6e7042ce5291137b7c87b57 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:53 +0100 +Subject: [PATCH 119/182] ahci_platform: Drop support for ahci-strict platform + device type + +I've done a grep over the entire kernel tree and nothing is using this +(anymore?). + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 11 ----------- + 1 file changed, 11 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index fc32863..d3d2bad 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -31,7 +31,6 @@ static void ahci_host_stop(struct ata_host *host); + enum ahci_type { + AHCI, /* standard platform ahci */ + IMX53_AHCI, /* ahci on i.mx53 */ +- STRICT_AHCI, /* delayed DMA engine start */ + }; + + static struct platform_device_id ahci_devtype[] = { +@@ -42,9 +41,6 @@ static struct platform_device_id ahci_devtype[] = { + .name = "imx53-ahci", + .driver_data = IMX53_AHCI, + }, { +- .name = "strict-ahci", +- .driver_data = STRICT_AHCI, +- }, { + /* sentinel */ + } + }; +@@ -75,13 +71,6 @@ static const struct ata_port_info ahci_port_info[] = { + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_retry_srst_ops, + }, +- [STRICT_AHCI] = { +- AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, + }; + + static struct scsi_host_template ahci_platform_sht = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch new file mode 100644 index 0000000000..8d0f64ce35 --- /dev/null +++ b/target/linux/ipq806x/patches/0120-ahci_platform-Drop-support-for-imx53-ahci-platform-d.patch @@ -0,0 +1,110 @@ +From 0d153bacb749d9291324ef0282ea9d235baca499 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:54 +0100 +Subject: [PATCH 120/182] ahci_platform: Drop support for imx53-ahci platform + device type + +Since the 3.13 release the ahci_imx driver has proper devicetree enabled +support for ahci on imx53 and that is used instead of the old board file +created imx53-ahci platform device. + +Note this patch also complete drops the id-table, an id-table is not needed +for a single id platform driver, the name field in the driver struct suffices. + +And the code already has an explicit "MODULE_ALIAS("platform:ahci");" so the +id-table is not needed for that either. + +Cc: Marek Vasut <marex@denx.de> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 46 ++++++------------------------------------- + 1 file changed, 6 insertions(+), 40 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index d3d2bad..8fab4bf 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -28,49 +28,17 @@ + + static void ahci_host_stop(struct ata_host *host); + +-enum ahci_type { +- AHCI, /* standard platform ahci */ +- IMX53_AHCI, /* ahci on i.mx53 */ +-}; +- +-static struct platform_device_id ahci_devtype[] = { +- { +- .name = "ahci", +- .driver_data = AHCI, +- }, { +- .name = "imx53-ahci", +- .driver_data = IMX53_AHCI, +- }, { +- /* sentinel */ +- } +-}; +-MODULE_DEVICE_TABLE(platform, ahci_devtype); +- + struct ata_port_operations ahci_platform_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_host_stop, + }; + EXPORT_SYMBOL_GPL(ahci_platform_ops); + +-static struct ata_port_operations ahci_platform_retry_srst_ops = { +- .inherits = &ahci_pmp_retry_srst_ops, +- .host_stop = ahci_host_stop, +-}; +- +-static const struct ata_port_info ahci_port_info[] = { +- /* by features */ +- [AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, +- [IMX53_AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_retry_srst_ops, +- }, ++static const struct ata_port_info ahci_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, + }; + + static struct scsi_host_template ahci_platform_sht = { +@@ -416,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); + const struct ata_port_info *pi_template; + struct ahci_host_priv *hpriv; + int rc; +@@ -444,7 +411,7 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->ata_port_info) + pi_template = pdata->ata_port_info; + else +- pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ pi_template = &ahci_port_info; + + rc = ahci_platform_init_host(pdev, hpriv, pi_template, + pdata ? pdata->force_port_map : 0, +@@ -638,7 +605,6 @@ static struct platform_driver ahci_driver = { + .of_match_table = ahci_of_match, + .pm = &ahci_pm_ops, + }, +- .id_table = ahci_devtype, + }; + module_platform_driver(ahci_driver); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch new file mode 100644 index 0000000000..e8f2378b02 --- /dev/null +++ b/target/linux/ipq806x/patches/0121-ahci_platform-Drop-unused-ahci_platform_data-members.patch @@ -0,0 +1,62 @@ +From 438df60c6bff1b12a407fefcfb8a2e5d9b29ac6a Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 22 Feb 2014 17:22:55 +0100 +Subject: [PATCH 121/182] ahci_platform: Drop unused ahci_platform_data + members + +These members are not used anywhere, and in the future we want +ahci_platform_data to go away entirely so there is no reason to keep these +around. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 10 +--------- + include/linux/ahci_platform.h | 3 --- + 2 files changed, 1 insertion(+), 12 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 8fab4bf..db24d2a 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -384,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct ata_port_info *pi_template; + struct ahci_host_priv *hpriv; + int rc; + +@@ -408,14 +407,7 @@ static int ahci_probe(struct platform_device *pdev) + goto disable_resources; + } + +- if (pdata && pdata->ata_port_info) +- pi_template = pdata->ata_port_info; +- else +- pi_template = &ahci_port_info; +- +- rc = ahci_platform_init_host(pdev, hpriv, pi_template, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0); + if (rc) + goto pdata_exit; + +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 542f268..1f16d50 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -33,9 +33,6 @@ struct ahci_platform_data { + void (*exit)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); +- const struct ata_port_info *ata_port_info; +- unsigned int force_port_map; +- unsigned int mask_port_map; + }; + + int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch new file mode 100644 index 0000000000..1a5925b48a --- /dev/null +++ b/target/linux/ipq806x/patches/0122-ata-ahci_platform-fix-devm_ioremap_resource-return-v.patch @@ -0,0 +1,46 @@ +From 0db91c83bcb5f7307aaa16c093b8c6bb3f44be52 Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Fri, 14 Mar 2014 18:22:09 +0100 +Subject: [PATCH 122/182] ata: ahci_platform: fix devm_ioremap_resource() + return value checking + +devm_ioremap_resource() returns a pointer to the remapped memory or +an ERR_PTR() encoded error code on failure. Fix the check inside +ahci_platform_get_resources() accordingly. + +Also while at it remove a needless line break. + +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index db24d2a..70fbf66 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -199,8 +199,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +-struct ahci_host_priv *ahci_platform_get_resources( +- struct platform_device *pdev) ++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; +@@ -219,8 +218,9 @@ struct ahci_host_priv *ahci_platform_get_resources( + + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- if (!hpriv->mmio) { ++ if (IS_ERR(hpriv->mmio)) { + dev_err(dev, "no mmio space\n"); ++ rc = PTR_ERR(hpriv->mmio); + goto err_out; + } + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch new file mode 100644 index 0000000000..ce1d840b90 --- /dev/null +++ b/target/linux/ipq806x/patches/0123-ata-ahci_platform-fix-ahci_platform_data-suspend-met.patch @@ -0,0 +1,50 @@ +From bc26554ffa0223015edb474a4877ec544167dfba Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Tue, 25 Mar 2014 19:51:38 +0100 +Subject: [PATCH 123/182] ata: ahci_platform: fix ahci_platform_data->suspend + method handling + +Looking at ST SPEAr1340 AHCI code (the only user of the deprecated +pdata->suspend and pdata->resume) it is obvious the we should return +after calling pdata->suspend() only if the function have returned +non-zero return value. The code has been broken since commit 1e70c2 +("ata/ahci_platform: Add clock framework support"). Fix it. + +Cc: Viresh Kumar <viresh.linux@gmail.com> +Cc: Shiraz Hashim <shiraz.hashim@st.com> +Acked-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> +--- + drivers/ata/ahci_platform.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 70fbf66..7bd6adf 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -521,12 +521,19 @@ int ahci_platform_suspend(struct device *dev) + if (rc) + return rc; + +- if (pdata && pdata->suspend) +- return pdata->suspend(dev); ++ if (pdata && pdata->suspend) { ++ rc = pdata->suspend(dev); ++ if (rc) ++ goto resume_host; ++ } + + ahci_platform_disable_resources(hpriv); + + return 0; ++ ++resume_host: ++ ahci_platform_resume_host(dev); ++ return rc; + } + EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch new file mode 100644 index 0000000000..bd45720661 --- /dev/null +++ b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch @@ -0,0 +1,1157 @@ +From 04800db1047afbf6701379435bff1a6fa64215f7 Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Tue, 25 Mar 2014 19:51:39 +0100 +Subject: [PATCH 124/182] ata: move library code from ahci_platform.c to + libahci_platform.c + +Move AHCI platform library code from ahci_platform.c to +libahci_platform.c and fix dependencies for ahci_st, +ahci_imx and ahci_sunxi drivers. + +Acked-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> + +Conflicts: + drivers/ata/Kconfig + drivers/ata/Makefile +--- + drivers/ata/Kconfig | 2 +- + drivers/ata/Makefile | 4 +- + drivers/ata/ahci_platform.c | 515 -------------------------------------- + drivers/ata/libahci_platform.c | 541 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 544 insertions(+), 518 deletions(-) + create mode 100644 drivers/ata/libahci_platform.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 868429a..dc950f3 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -99,7 +99,7 @@ config SATA_AHCI_PLATFORM + + config AHCI_IMX + tristate "Freescale i.MX AHCI SATA support" +- depends on SATA_AHCI_PLATFORM && MFD_SYSCON ++ depends on MFD_SYSCON + help + This option enables support for the Freescale i.MX SoC's + onboard AHCI SATA. +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 46518c6..366b743 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -4,13 +4,13 @@ obj-$(CONFIG_ATA) += libata.o + # non-SFF interface + obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o + obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o +-obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o ++obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o + obj-$(CONFIG_SATA_FSL) += sata_fsl.o + obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o + obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +-obj-$(CONFIG_AHCI_IMX) += ahci_imx.o ++obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 7bd6adf..ef67e79 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -12,28 +12,15 @@ + * any later version. + */ + +-#include <linux/clk.h> + #include <linux/kernel.h> +-#include <linux/gfp.h> + #include <linux/module.h> + #include <linux/pm.h> +-#include <linux/interrupt.h> + #include <linux/device.h> + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> +-#include <linux/phy/phy.h> +-#include <linux/pm_runtime.h> + #include "ahci.h" + +-static void ahci_host_stop(struct ata_host *host); +- +-struct ata_port_operations ahci_platform_ops = { +- .inherits = &ahci_ops, +- .host_stop = ahci_host_stop, +-}; +-EXPORT_SYMBOL_GPL(ahci_platform_ops); +- + static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, +@@ -41,345 +28,6 @@ static const struct ata_port_info ahci_port_info = { + .port_ops = &ahci_platform_ops, + }; + +-static struct scsi_host_template ahci_platform_sht = { +- AHCI_SHT("ahci_platform"), +-}; +- +-/** +- * ahci_platform_enable_clks - Enable platform clocks +- * @hpriv: host private area to store config values +- * +- * This function enables all the clks found in hpriv->clks, starting at +- * index 0. If any clk fails to enable it disables all the clks already +- * enabled in reverse order, and then returns an error. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +-{ +- int c, rc; +- +- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { +- rc = clk_prepare_enable(hpriv->clks[c]); +- if (rc) +- goto disable_unprepare_clk; +- } +- return 0; +- +-disable_unprepare_clk: +- while (--c >= 0) +- clk_disable_unprepare(hpriv->clks[c]); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); +- +-/** +- * ahci_platform_disable_clks - Disable platform clocks +- * @hpriv: host private area to store config values +- * +- * This function disables all the clks found in hpriv->clks, in reverse +- * order of ahci_platform_enable_clks (starting at the end of the array). +- */ +-void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) +-{ +- int c; +- +- for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) +- if (hpriv->clks[c]) +- clk_disable_unprepare(hpriv->clks[c]); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); +- +-/** +- * ahci_platform_enable_resources - Enable platform resources +- * @hpriv: host private area to store config values +- * +- * This function enables all ahci_platform managed resources in the +- * following order: +- * 1) Regulator +- * 2) Clocks (through ahci_platform_enable_clks) +- * 3) Phy +- * +- * If resource enabling fails at any point the previous enabled resources +- * are disabled in reverse order. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) +-{ +- int rc; +- +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- return rc; +- } +- +- rc = ahci_platform_enable_clks(hpriv); +- if (rc) +- goto disable_regulator; +- +- if (hpriv->phy) { +- rc = phy_init(hpriv->phy); +- if (rc) +- goto disable_clks; +- +- rc = phy_power_on(hpriv->phy); +- if (rc) { +- phy_exit(hpriv->phy); +- goto disable_clks; +- } +- } +- +- return 0; +- +-disable_clks: +- ahci_platform_disable_clks(hpriv); +- +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); +- +-/** +- * ahci_platform_disable_resources - Disable platform resources +- * @hpriv: host private area to store config values +- * +- * This function disables all ahci_platform managed resources in the +- * following order: +- * 1) Phy +- * 2) Clocks (through ahci_platform_disable_clks) +- * 3) Regulator +- */ +-void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +-{ +- if (hpriv->phy) { +- phy_power_off(hpriv->phy); +- phy_exit(hpriv->phy); +- } +- +- ahci_platform_disable_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); +- +-static void ahci_platform_put_resources(struct device *dev, void *res) +-{ +- struct ahci_host_priv *hpriv = res; +- int c; +- +- if (hpriv->got_runtime_pm) { +- pm_runtime_put_sync(dev); +- pm_runtime_disable(dev); +- } +- +- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) +- clk_put(hpriv->clks[c]); +-} +- +-/** +- * ahci_platform_get_resources - Get platform resources +- * @pdev: platform device to get resources for +- * +- * This function allocates an ahci_host_priv struct, and gets the following +- * resources, storing a reference to them inside the returned struct: +- * +- * 1) mmio registers (IORESOURCE_MEM 0, mandatory) +- * 2) regulator for controlling the targets power (optional) +- * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, +- * or for non devicetree enabled platforms a single clock +- * 4) phy (optional) +- * +- * RETURNS: +- * The allocated ahci_host_priv on success, otherwise an ERR_PTR value +- */ +-struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct ahci_host_priv *hpriv; +- struct clk *clk; +- int i, rc = -ENOMEM; +- +- if (!devres_open_group(dev, NULL, GFP_KERNEL)) +- return ERR_PTR(-ENOMEM); +- +- hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), +- GFP_KERNEL); +- if (!hpriv) +- goto err_out; +- +- devres_add(dev, hpriv); +- +- hpriv->mmio = devm_ioremap_resource(dev, +- platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- if (IS_ERR(hpriv->mmio)) { +- dev_err(dev, "no mmio space\n"); +- rc = PTR_ERR(hpriv->mmio); +- goto err_out; +- } +- +- hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); +- if (IS_ERR(hpriv->target_pwr)) { +- rc = PTR_ERR(hpriv->target_pwr); +- if (rc == -EPROBE_DEFER) +- goto err_out; +- hpriv->target_pwr = NULL; +- } +- +- for (i = 0; i < AHCI_MAX_CLKS; i++) { +- /* +- * For now we must use clk_get(dev, NULL) for the first clock, +- * because some platforms (da850, spear13xx) are not yet +- * converted to use devicetree for clocks. For new platforms +- * this is equivalent to of_clk_get(dev->of_node, 0). +- */ +- if (i == 0) +- clk = clk_get(dev, NULL); +- else +- clk = of_clk_get(dev->of_node, i); +- +- if (IS_ERR(clk)) { +- rc = PTR_ERR(clk); +- if (rc == -EPROBE_DEFER) +- goto err_out; +- break; +- } +- hpriv->clks[i] = clk; +- } +- +- hpriv->phy = devm_phy_get(dev, "sata-phy"); +- if (IS_ERR(hpriv->phy)) { +- rc = PTR_ERR(hpriv->phy); +- switch (rc) { +- case -ENODEV: +- case -ENOSYS: +- /* continue normally */ +- hpriv->phy = NULL; +- break; +- +- case -EPROBE_DEFER: +- goto err_out; +- +- default: +- dev_err(dev, "couldn't get sata-phy\n"); +- goto err_out; +- } +- } +- +- pm_runtime_enable(dev); +- pm_runtime_get_sync(dev); +- hpriv->got_runtime_pm = true; +- +- devres_remove_group(dev, NULL); +- return hpriv; +- +-err_out: +- devres_release_group(dev, NULL); +- return ERR_PTR(rc); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_get_resources); +- +-/** +- * ahci_platform_init_host - Bring up an ahci-platform host +- * @pdev: platform device pointer for the host +- * @hpriv: ahci-host private data for the host +- * @pi_template: template for the ata_port_info to use +- * @force_port_map: param passed to ahci_save_initial_config +- * @mask_port_map: param passed to ahci_save_initial_config +- * +- * This function does all the usual steps needed to bring up an +- * ahci-platform host, note any necessary resources (ie clks, phy, etc.) +- * must be initialized / enabled before calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_init_host(struct platform_device *pdev, +- struct ahci_host_priv *hpriv, +- const struct ata_port_info *pi_template, +- unsigned int force_port_map, +- unsigned int mask_port_map) +-{ +- struct device *dev = &pdev->dev; +- struct ata_port_info pi = *pi_template; +- const struct ata_port_info *ppi[] = { &pi, NULL }; +- struct ata_host *host; +- int i, irq, n_ports, rc; +- +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } +- +- /* prepare host */ +- hpriv->flags |= (unsigned long)pi.private_data; +- +- ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); +- +- if (hpriv->cap & HOST_CAP_NCQ) +- pi.flags |= ATA_FLAG_NCQ; +- +- if (hpriv->cap & HOST_CAP_PMP) +- pi.flags |= ATA_FLAG_PMP; +- +- ahci_set_em_messages(hpriv, &pi); +- +- /* CAP.NP sometimes indicate the index of the last enabled +- * port, at other times, that of the last possible port, so +- * determining the maximum port number requires looking at +- * both CAP.NP and port_map. +- */ +- n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); +- +- host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) +- return -ENOMEM; +- +- host->private_data = hpriv; +- +- if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) +- host->flags |= ATA_HOST_PARALLEL_SCAN; +- else +- dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); +- +- if (pi.flags & ATA_FLAG_EM) +- ahci_reset_em(host); +- +- for (i = 0; i < host->n_ports; i++) { +- struct ata_port *ap = host->ports[i]; +- +- ata_port_desc(ap, "mmio %pR", +- platform_get_resource(pdev, IORESOURCE_MEM, 0)); +- ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); +- +- /* set enclosure management message type */ +- if (ap->flags & ATA_FLAG_EM) +- ap->em_message_type = hpriv->em_msg_type; +- +- /* disabled/not-implemented port */ +- if (!(hpriv->port_map & (1 << i))) +- ap->ops = &ata_dummy_port_ops; +- } +- +- rc = ahci_reset_controller(host); +- if (rc) +- return rc; +- +- ahci_init_controller(host); +- ahci_print_info(host, "platform"); +- +- return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_init_host); +- + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -420,169 +68,6 @@ disable_resources: + return rc; + } + +-static void ahci_host_stop(struct ata_host *host) +-{ +- struct device *dev = host->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- +- if (pdata && pdata->exit) +- pdata->exit(dev); +- +- ahci_platform_disable_resources(hpriv); +-} +- +-#ifdef CONFIG_PM_SLEEP +-/** +- * ahci_platform_suspend_host - Suspend an ahci-platform host +- * @dev: device pointer for the host +- * +- * This function does all the usual steps needed to suspend an +- * ahci-platform host, note any necessary resources (ie clks, phy, etc.) +- * must be disabled after calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_suspend_host(struct device *dev) +-{ +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- void __iomem *mmio = hpriv->mmio; +- u32 ctl; +- +- if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { +- dev_err(dev, "firmware update required for suspend/resume\n"); +- return -EIO; +- } +- +- /* +- * AHCI spec rev1.1 section 8.3.3: +- * Software must disable interrupts prior to requesting a +- * transition of the HBA to D3 state. +- */ +- ctl = readl(mmio + HOST_CTL); +- ctl &= ~HOST_IRQ_EN; +- writel(ctl, mmio + HOST_CTL); +- readl(mmio + HOST_CTL); /* flush */ +- +- return ata_host_suspend(host, PMSG_SUSPEND); +-} +-EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); +- +-/** +- * ahci_platform_resume_host - Resume an ahci-platform host +- * @dev: device pointer for the host +- * +- * This function does all the usual steps needed to resume an ahci-platform +- * host, note any necessary resources (ie clks, phy, etc.) must be +- * initialized / enabled before calling this. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_resume_host(struct device *dev) +-{ +- struct ata_host *host = dev_get_drvdata(dev); +- int rc; +- +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- return rc; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_resume_host); +- +-/** +- * ahci_platform_suspend - Suspend an ahci-platform device +- * @dev: the platform device to suspend +- * +- * This function suspends the host associated with the device, followed by +- * disabling all the resources of the device. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_suspend(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- int rc; +- +- rc = ahci_platform_suspend_host(dev); +- if (rc) +- return rc; +- +- if (pdata && pdata->suspend) { +- rc = pdata->suspend(dev); +- if (rc) +- goto resume_host; +- } +- +- ahci_platform_disable_resources(hpriv); +- +- return 0; +- +-resume_host: +- ahci_platform_resume_host(dev); +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_suspend); +- +-/** +- * ahci_platform_resume - Resume an ahci-platform device +- * @dev: the platform device to resume +- * +- * This function enables all the resources of the device followed by +- * resuming the host associated with the device. +- * +- * RETURNS: +- * 0 on success otherwise a negative error code +- */ +-int ahci_platform_resume(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- int rc; +- +- rc = ahci_platform_enable_resources(hpriv); +- if (rc) +- return rc; +- +- if (pdata && pdata->resume) { +- rc = pdata->resume(dev); +- if (rc) +- goto disable_resources; +- } +- +- rc = ahci_platform_resume_host(dev); +- if (rc) +- goto disable_resources; +- +- /* We resumed so update PM runtime state */ +- pm_runtime_disable(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); +- +- return 0; +- +-disable_resources: +- ahci_platform_disable_resources(hpriv); +- +- return rc; +-} +-EXPORT_SYMBOL_GPL(ahci_platform_resume); +-#endif +- + static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); + +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +new file mode 100644 +index 0000000..7cb3a85 +--- /dev/null ++++ b/drivers/ata/libahci_platform.c +@@ -0,0 +1,541 @@ ++/* ++ * AHCI SATA platform library ++ * ++ * Copyright 2004-2005 Red Hat, Inc. ++ * Jeff Garzik <jgarzik@pobox.com> ++ * Copyright 2010 MontaVista Software, LLC. ++ * Anton Vorontsov <avorontsov@ru.mvista.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/kernel.h> ++#include <linux/gfp.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/libata.h> ++#include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> ++#include "ahci.h" ++ ++static void ahci_host_stop(struct ata_host *host); ++ ++struct ata_port_operations ahci_platform_ops = { ++ .inherits = &ahci_ops, ++ .host_stop = ahci_host_stop, ++}; ++EXPORT_SYMBOL_GPL(ahci_platform_ops); ++ ++static struct scsi_host_template ahci_platform_sht = { ++ AHCI_SHT("ahci_platform"), ++}; ++ ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ ++ return 0; ++ ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ ++static void ahci_platform_put_resources(struct device *dev, void *res) ++{ ++ struct ahci_host_priv *hpriv = res; ++ int c; ++ ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ struct clk *clk; ++ int i, rc = -ENOMEM; ++ ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); ++ ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; ++ ++ devres_add(dev, hpriv); ++ ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(hpriv->mmio)) { ++ dev_err(dev, "no mmio space\n"); ++ rc = PTR_ERR(hpriv->mmio); ++ goto err_out; ++ } ++ ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ hpriv->target_pwr = NULL; ++ } ++ ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ break; ++ } ++ hpriv->clks[i] = clk; ++ } ++ ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ ++ devres_remove_group(dev, NULL); ++ return hpriv; ++ ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } ++ ++ /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ ++ if (hpriv->cap & HOST_CAP_NCQ) ++ pi.flags |= ATA_FLAG_NCQ; ++ ++ if (hpriv->cap & HOST_CAP_PMP) ++ pi.flags |= ATA_FLAG_PMP; ++ ++ ahci_set_em_messages(hpriv, &pi); ++ ++ /* CAP.NP sometimes indicate the index of the last enabled ++ * port, at other times, that of the last possible port, so ++ * determining the maximum port number requires looking at ++ * both CAP.NP and port_map. ++ */ ++ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); ++ ++ host = ata_host_alloc_pinfo(dev, ppi, n_ports); ++ if (!host) ++ return -ENOMEM; ++ ++ host->private_data = hpriv; ++ ++ if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) ++ host->flags |= ATA_HOST_PARALLEL_SCAN; ++ else ++ dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); ++ ++ if (pi.flags & ATA_FLAG_EM) ++ ahci_reset_em(host); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); ++ ++ /* set enclosure management message type */ ++ if (ap->flags & ATA_FLAG_EM) ++ ap->em_message_type = hpriv->em_msg_type; ++ ++ /* disabled/not-implemented port */ ++ if (!(hpriv->port_map & (1 << i))) ++ ap->ops = &ata_dummy_port_ops; ++ } ++ ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ ahci_print_info(host, "platform"); ++ ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static void ahci_host_stop(struct ata_host *host) ++{ ++ struct device *dev = host->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ ++ if (pdata && pdata->exit) ++ pdata->exit(dev); ++ ++ ahci_platform_disable_resources(hpriv); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ void __iomem *mmio = hpriv->mmio; ++ u32 ctl; ++ ++ if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { ++ dev_err(dev, "firmware update required for suspend/resume\n"); ++ return -EIO; ++ } ++ ++ /* ++ * AHCI spec rev1.1 section 8.3.3: ++ * Software must disable interrupts prior to requesting a ++ * transition of the HBA to D3 state. ++ */ ++ ctl = readl(mmio + HOST_CTL); ++ ctl &= ~HOST_IRQ_EN; ++ writel(ctl, mmio + HOST_CTL); ++ readl(mmio + HOST_CTL); /* flush */ ++ ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->suspend) { ++ rc = pdata->suspend(dev); ++ if (rc) ++ goto resume_host; ++ } ++ ++ ahci_platform_disable_resources(hpriv); ++ ++ return 0; ++ ++resume_host: ++ ahci_platform_resume_host(dev); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); ++ ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->resume) { ++ rc = pdata->resume(dev); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; ++ ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume); ++#endif ++ ++MODULE_DESCRIPTION("AHCI SATA platform library"); ++MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch new file mode 100644 index 0000000000..250ca6e85d --- /dev/null +++ b/target/linux/ipq806x/patches/0125-clk-qcom-Add-support-for-IPQ8064-s-global-clock-cont.patch @@ -0,0 +1,2939 @@ +From 2e6dfaa714ba4bd70fa5dda07c525b6c15e44552 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 3 Apr 2014 13:47:07 -0500 +Subject: [PATCH 125/182] clk: qcom: Add support for IPQ8064's global clock + controller (GCC) + +Add a driver for the global clock controller found on IPQ8064 based +platforms. This should allow most non-multimedia device drivers to probe +and control their clocks. + +This is currently missing clocks for USB HSIC and networking devices. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + .../devicetree/bindings/clock/qcom,gcc.txt | 1 + + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/gcc-ipq806x.c | 2424 ++++++++++++++++++++ + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 293 +++ + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 132 ++ + 6 files changed, 2859 insertions(+) + create mode 100644 drivers/clk/qcom/gcc-ipq806x.c + create mode 100644 include/dt-bindings/clock/qcom,gcc-ipq806x.h + create mode 100644 include/dt-bindings/reset/qcom,gcc-ipq806x.h + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +index 9cfcb4f..0171509 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt +@@ -5,6 +5,7 @@ Required properties : + - compatible : shall contain only one of the following: + + "qcom,gcc-apq8064" ++ "qcom,gcc-ipq8064" + "qcom,gcc-msm8660" + "qcom,gcc-msm8960" + "qcom,gcc-msm8974" +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 7f696b7..cfaa54c 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -4,6 +4,14 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config IPQ_GCC_806X ++ tristate "IPQ806x Global Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the global clock controller on ipq806x devices. ++ Say Y if you want to use peripheral devices such as UART, SPI, ++ i2c, USB, SD/eMMC, etc. ++ + config MSM_GCC_8660 + tristate "MSM8660 Global Clock Controller" + depends on COMMON_CLK_QCOM +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 689e05b..df2a1b3 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += reset.o + ++obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o + obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o + obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +new file mode 100644 +index 0000000..278c5fe +--- /dev/null ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -0,0 +1,2424 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/bitops.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk-provider.h> ++#include <linux/regmap.h> ++#include <linux/reset-controller.h> ++ ++#include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/reset/qcom,gcc-ipq806x.h> ++ ++#include "common.h" ++#include "clk-regmap.h" ++#include "clk-pll.h" ++#include "clk-rcg.h" ++#include "clk-branch.h" ++#include "reset.h" ++ ++static struct clk_pll pll3 = { ++ .l_reg = 0x3164, ++ .m_reg = 0x3168, ++ .n_reg = 0x316c, ++ .config_reg = 0x3174, ++ .mode_reg = 0x3160, ++ .status_reg = 0x3178, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll3", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_pll pll8 = { ++ .l_reg = 0x3144, ++ .m_reg = 0x3148, ++ .n_reg = 0x314c, ++ .config_reg = 0x3154, ++ .mode_reg = 0x3140, ++ .status_reg = 0x3158, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll8", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll8_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(8), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll8_vote", ++ .parent_names = (const char *[]){ "pll8" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ ++static struct clk_pll pll14 = { ++ .l_reg = 0x31c4, ++ .m_reg = 0x31c8, ++ .n_reg = 0x31cc, ++ .config_reg = 0x31d4, ++ .mode_reg = 0x31c0, ++ .status_reg = 0x31d8, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll14", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll14_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(14), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll14_vote", ++ .parent_names = (const char *[]){ "pll14" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ ++#define P_PXO 0 ++#define P_PLL8 1 ++#define P_PLL3 1 ++#define P_PLL0 2 ++#define P_CXO 2 ++ ++static const u8 gcc_pxo_pll8_map[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++}; ++ ++static const char *gcc_pxo_pll8[] = { ++ "pxo", ++ "pll8_vote", ++}; ++ ++static const u8 gcc_pxo_pll8_cxo_map[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++ [P_CXO] = 5, ++}; ++ ++static const char *gcc_pxo_pll8_cxo[] = { ++ "pxo", ++ "pll8_vote", ++ "cxo", ++}; ++ ++static const u8 gcc_pxo_pll3_map[] = { ++ [P_PXO] = 0, ++ [P_PLL3] = 1, ++}; ++ ++static const u8 gcc_pxo_pll3_sata_map[] = { ++ [P_PXO] = 0, ++ [P_PLL3] = 6, ++}; ++ ++static const char *gcc_pxo_pll3[] = { ++ "pxo", ++ "pll3", ++}; ++ ++static const u8 gcc_pxo_pll8_pll0[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 3, ++ [P_PLL0] = 2, ++}; ++ ++static const char *gcc_pxo_pll8_pll0_map[] = { ++ "pxo", ++ "pll8_vote", ++ "pll0", ++}; ++ ++static struct freq_tbl clk_tbl_gsbi_uart[] = { ++ { 1843200, P_PLL8, 2, 6, 625 }, ++ { 3686400, P_PLL8, 2, 12, 625 }, ++ { 7372800, P_PLL8, 2, 24, 625 }, ++ { 14745600, P_PLL8, 2, 48, 625 }, ++ { 16000000, P_PLL8, 4, 1, 6 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 32000000, P_PLL8, 4, 1, 3 }, ++ { 40000000, P_PLL8, 1, 5, 48 }, ++ { 46400000, P_PLL8, 1, 29, 240 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 51200000, P_PLL8, 1, 2, 15 }, ++ { 56000000, P_PLL8, 1, 7, 48 }, ++ { 58982400, P_PLL8, 1, 96, 625 }, ++ { 64000000, P_PLL8, 2, 1, 3 }, ++ { } ++}; ++ ++static struct clk_rcg gsbi1_uart_src = { ++ .ns_reg = 0x29d4, ++ .md_reg = 0x29d0, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x29d4, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_uart_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x29d4, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi1_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi2_uart_src = { ++ .ns_reg = 0x29f4, ++ .md_reg = 0x29f0, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x29f4, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_uart_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 8, ++ .clkr = { ++ .enable_reg = 0x29f4, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi2_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi4_uart_src = { ++ .ns_reg = 0x2a34, ++ .md_reg = 0x2a30, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a34, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 26, ++ .clkr = { ++ .enable_reg = 0x2a34, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi4_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi5_uart_src = { ++ .ns_reg = 0x2a54, ++ .md_reg = 0x2a50, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a54, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x2a54, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi5_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi6_uart_src = { ++ .ns_reg = 0x2a74, ++ .md_reg = 0x2a70, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a74, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x2a74, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi6_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi7_uart_src = { ++ .ns_reg = 0x2a94, ++ .md_reg = 0x2a90, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_uart, ++ .clkr = { ++ .enable_reg = 0x2a94, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_uart_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_uart_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2a94, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_uart_clk", ++ .parent_names = (const char *[]){ ++ "gsbi7_uart_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct freq_tbl clk_tbl_gsbi_qup[] = { ++ { 1100000, P_PXO, 1, 2, 49 }, ++ { 5400000, P_PXO, 1, 1, 5 }, ++ { 10800000, P_PXO, 1, 2, 5 }, ++ { 15060000, P_PLL8, 1, 2, 51 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 25600000, P_PLL8, 1, 1, 15 }, ++ { 27000000, P_PXO, 1, 0, 0 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 51200000, P_PLL8, 1, 2, 15 }, ++ { } ++}; ++ ++static struct clk_rcg gsbi1_qup_src = { ++ .ns_reg = 0x29cc, ++ .md_reg = 0x29c8, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x29cc, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_qup_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x29cc, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_qup_clk", ++ .parent_names = (const char *[]){ "gsbi1_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi2_qup_src = { ++ .ns_reg = 0x29ec, ++ .md_reg = 0x29e8, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x29ec, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_qup_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x29ec, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_qup_clk", ++ .parent_names = (const char *[]){ "gsbi2_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi4_qup_src = { ++ .ns_reg = 0x2a2c, ++ .md_reg = 0x2a28, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a2c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 24, ++ .clkr = { ++ .enable_reg = 0x2a2c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_qup_clk", ++ .parent_names = (const char *[]){ "gsbi4_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi5_qup_src = { ++ .ns_reg = 0x2a4c, ++ .md_reg = 0x2a48, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a4c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x2a4c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_qup_clk", ++ .parent_names = (const char *[]){ "gsbi5_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi6_qup_src = { ++ .ns_reg = 0x2a6c, ++ .md_reg = 0x2a68, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a6c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 16, ++ .clkr = { ++ .enable_reg = 0x2a6c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_qup_clk", ++ .parent_names = (const char *[]){ "gsbi6_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gsbi7_qup_src = { ++ .ns_reg = 0x2a8c, ++ .md_reg = 0x2a88, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_gsbi_qup, ++ .clkr = { ++ .enable_reg = 0x2a8c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_qup_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_qup_clk = { ++ .halt_reg = 0x2fd0, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x2a8c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_qup_clk", ++ .parent_names = (const char *[]){ "gsbi7_qup_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi1_h_clk = { ++ .hwcg_reg = 0x29c0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fcc, ++ .halt_bit = 13, ++ .clkr = { ++ .enable_reg = 0x29c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi2_h_clk = { ++ .hwcg_reg = 0x29e0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fcc, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x29e0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi2_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi4_h_clk = { ++ .hwcg_reg = 0x2a20, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 27, ++ .clkr = { ++ .enable_reg = 0x2a20, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi4_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi5_h_clk = { ++ .hwcg_reg = 0x2a40, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x2a40, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi5_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi6_h_clk = { ++ .hwcg_reg = 0x2a60, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2a60, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi6_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch gsbi7_h_clk = { ++ .hwcg_reg = 0x2a80, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd0, ++ .halt_bit = 15, ++ .clkr = { ++ .enable_reg = 0x2a80, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gsbi7_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_gp[] = { ++ { 12500000, P_PXO, 2, 0, 0 }, ++ { 25000000, P_PXO, 1, 0, 0 }, ++ { 64000000, P_PLL8, 2, 1, 3 }, ++ { 76800000, P_PLL8, 1, 1, 5 }, ++ { 96000000, P_PLL8, 4, 0, 0 }, ++ { 128000000, P_PLL8, 3, 0, 0 }, ++ { 192000000, P_PLL8, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg gp0_src = { ++ .ns_reg = 0x2d24, ++ .md_reg = 0x2d00, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d24, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp0_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_PARENT_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp0_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 7, ++ .clkr = { ++ .enable_reg = 0x2d24, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp0_clk", ++ .parent_names = (const char *[]){ "gp0_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gp1_src = { ++ .ns_reg = 0x2d44, ++ .md_reg = 0x2d40, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d44, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp1_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp1_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x2d44, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp1_clk", ++ .parent_names = (const char *[]){ "gp1_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg gp2_src = { ++ .ns_reg = 0x2d64, ++ .md_reg = 0x2d60, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_cxo_map, ++ }, ++ .freq_tbl = clk_tbl_gp, ++ .clkr = { ++ .enable_reg = 0x2d64, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp2_src", ++ .parent_names = gcc_pxo_pll8_cxo, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch gp2_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_bit = 5, ++ .clkr = { ++ .enable_reg = 0x2d64, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gp2_clk", ++ .parent_names = (const char *[]){ "gp2_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmem_clk = { ++ .hwcg_reg = 0x25a0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x25a0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmem_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg prng_src = { ++ .ns_reg = 0x2e80, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .clkr = { ++ .hw.init = &(struct clk_init_data){ ++ .name = "prng_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch prng_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(10), ++ .hw.init = &(struct clk_init_data){ ++ .name = "prng_clk", ++ .parent_names = (const char *[]){ "prng_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_sdc[] = { ++ { 144000, P_PXO, 5, 18,625 }, ++ { 400000, P_PLL8, 4, 1, 240 }, ++ { 16000000, P_PLL8, 4, 1, 6 }, ++ { 17070000, P_PLL8, 1, 2, 45 }, ++ { 20210000, P_PLL8, 1, 1, 19 }, ++ { 24000000, P_PLL8, 4, 1, 4 }, ++ { 48000000, P_PLL8, 4, 1, 2 }, ++ { 64000000, P_PLL8, 3, 1, 2 }, ++ { 96000000, P_PLL8, 4, 0, 0 }, ++ { 192000000, P_PLL8, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg sdc1_src = { ++ .ns_reg = 0x282c, ++ .md_reg = 0x2828, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_sdc, ++ .clkr = { ++ .enable_reg = 0x282c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch sdc1_clk = { ++ .halt_reg = 0x2fc8, ++ .halt_bit = 6, ++ .clkr = { ++ .enable_reg = 0x282c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_clk", ++ .parent_names = (const char *[]){ "sdc1_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg sdc3_src = { ++ .ns_reg = 0x286c, ++ .md_reg = 0x2868, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_sdc, ++ .clkr = { ++ .enable_reg = 0x286c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch sdc3_clk = { ++ .halt_reg = 0x2fc8, ++ .halt_bit = 4, ++ .clkr = { ++ .enable_reg = 0x286c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_clk", ++ .parent_names = (const char *[]){ "sdc3_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sdc1_h_clk = { ++ .hwcg_reg = 0x2820, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x2820, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sdc3_h_clk = { ++ .hwcg_reg = 0x2860, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x2860, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sdc3_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_tsif_ref[] = { ++ { 105000, P_PXO, 1, 1, 256 }, ++ { } ++}; ++ ++static struct clk_rcg tsif_ref_src = { ++ .ns_reg = 0x2710, ++ .md_reg = 0x270c, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 16, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_map, ++ }, ++ .freq_tbl = clk_tbl_tsif_ref, ++ .clkr = { ++ .enable_reg = 0x2710, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_ref_src", ++ .parent_names = gcc_pxo_pll8, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ } ++}; ++ ++static struct clk_branch tsif_ref_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 5, ++ .clkr = { ++ .enable_reg = 0x2710, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_ref_clk", ++ .parent_names = (const char *[]){ "tsif_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch tsif_h_clk = { ++ .hwcg_reg = 0x2700, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd4, ++ .halt_bit = 7, ++ .clkr = { ++ .enable_reg = 0x2700, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "tsif_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch dma_bam_h_clk = { ++ .hwcg_reg = 0x25c0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x25c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "dma_bam_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch adm0_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(2), ++ .hw.init = &(struct clk_init_data){ ++ .name = "adm0_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch adm0_pbus_clk = { ++ .hwcg_reg = 0x2208, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fdc, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 11, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(3), ++ .hw.init = &(struct clk_init_data){ ++ .name = "adm0_pbus_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_arb0_h_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(8), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_arb0_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_arb1_h_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_arb1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pmic_ssbi2_clk = { ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(7), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pmic_ssbi2_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch rpm_msg_ram_h_clk = { ++ .hwcg_reg = 0x27e0, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fd8, ++ .halt_check = BRANCH_HALT_VOTED, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x3080, ++ .enable_mask = BIT(6), ++ .hw.init = &(struct clk_init_data){ ++ .name = "rpm_msg_ram_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_pcie_ref[] = { ++ { 100000000, P_PLL3, 12, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg pcie_ref_src = { ++ .ns_reg = 0x3860, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3860, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 30, ++ .clkr = { ++ .enable_reg = 0x3860, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 13, ++ .clkr = { ++ .enable_reg = 0x22c0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x22c8, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 8, ++ .clkr = { ++ .enable_reg = 0x22cc, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 29, ++ .clkr = { ++ .enable_reg = 0x22d0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg pcie1_ref_src = { ++ .ns_reg = 0x3aa0, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3aa0, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 27, ++ .clkr = { ++ .enable_reg = 0x3aa0, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie1_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3a80, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 28, ++ .clkr = { ++ .enable_reg = 0x3a88, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x3a8c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie1_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 26, ++ .clkr = { ++ .enable_reg = 0x3a90, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie1_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg pcie2_ref_src = { ++ .ns_reg = 0x3ae0, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_map, ++ }, ++ .freq_tbl = clk_tbl_pcie_ref, ++ .clkr = { ++ .enable_reg = 0x3ae0, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_ref_src_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 24, ++ .clkr = { ++ .enable_reg = 0x3ae0, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_ref_src_clk", ++ .parent_names = (const char *[]){ "pcie2_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 9, ++ .clkr = { ++ .enable_reg = 0x3ac0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_aux_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 25, ++ .clkr = { ++ .enable_reg = 0x3ac8, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_aux_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_h_clk = { ++ .halt_reg = 0x2fd4, ++ .halt_bit = 10, ++ .clkr = { ++ .enable_reg = 0x3acc, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch pcie2_phy_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 23, ++ .clkr = { ++ .enable_reg = 0x3ad0, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pcie2_phy_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_sata_ref[] = { ++ { 100000000, P_PLL3, 12, 0, 0 }, ++ { } ++}; ++ ++static struct clk_rcg sata_ref_src = { ++ .ns_reg = 0x2c08, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_sata_map, ++ }, ++ .freq_tbl = clk_tbl_sata_ref, ++ .clkr = { ++ .enable_reg = 0x2c08, ++ .enable_mask = BIT(7), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_ref_src", ++ .parent_names = gcc_pxo_pll3, ++ .num_parents = 2, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_rxoob_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 20, ++ .clkr = { ++ .enable_reg = 0x2c0c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_rxoob_clk", ++ .parent_names = (const char *[]){ "sata_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_pmalive_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2c10, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_pmalive_clk", ++ .parent_names = (const char *[]){ "sata_ref_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_phy_ref_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x2c14, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_phy_ref_clk", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_a_clk = { ++ .halt_reg = 0x2fc0, ++ .halt_bit = 12, ++ .clkr = { ++ .enable_reg = 0x2c20, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_a_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_h_clk = { ++ .halt_reg = 0x2fdc, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x2c00, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sfab_sata_s_h_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2480, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sfab_sata_s_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_branch sata_phy_cfg_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x2c40, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "sata_phy_cfg_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb30_master[] = { ++ { 125000000, P_PLL0, 1, 5, 32 }, ++ { } ++}; ++ ++static struct clk_rcg usb30_master_clk_src = { ++ .ns_reg = 0x3b2c, ++ .md_reg = 0x3b28, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb30_master, ++ .clkr = { ++ .enable_reg = 0x3b2c, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_master_ref_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_0_branch_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 22, ++ .clkr = { ++ .enable_reg = 0x3b24, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_0_branch_clk", ++ .parent_names = (const char *[]){ "usb30_master_ref_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_1_branch_clk = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x3b34, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_1_branch_clk", ++ .parent_names = (const char *[]){ "usb30_master_ref_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb30_utmi[] = { ++ { 60000000, P_PLL0, 1, 1, 40 }, ++ { } ++}; ++ ++static struct clk_rcg usb30_utmi_clk = { ++ .ns_reg = 0x3b44, ++ .md_reg = 0x3b40, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb30_utmi, ++ .clkr = { ++ .enable_reg = 0x3b44, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_utmi_clk", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_0_utmi_clk_ctl = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 21, ++ .clkr = { ++ .enable_reg = 0x3b48, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_0_utmi_clk_ctl", ++ .parent_names = (const char *[]){ "usb30_utmi_clk", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb30_1_utmi_clk_ctl = { ++ .halt_reg = 0x2fc4, ++ .halt_bit = 15, ++ .clkr = { ++ .enable_reg = 0x3b4c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb30_1_utmi_clk_ctl", ++ .parent_names = (const char *[]){ "usb30_utmi_clk", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_usb[] = { ++ { 60000000, P_PLL8, 1, 5, 32 }, ++ { } ++}; ++ ++static struct clk_rcg usb_hs1_xcvr_clk_src = { ++ .ns_reg = 0x290C, ++ .md_reg = 0x2908, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_xcvr_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_hs1_xcvr_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x290c, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_xcvr_clk", ++ .parent_names = (const char *[]){ "usb_hs1_xcvr_src" }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_hs1_h_clk = { ++ .hwcg_reg = 0x2900, ++ .hwcg_bit = 6, ++ .halt_reg = 0x2fc8, ++ .halt_bit = 1, ++ .clkr = { ++ .enable_reg = 0x2900, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_hs1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_rcg usb_fs1_xcvr_clk_src = { ++ .ns_reg = 0x2968, ++ .md_reg = 0x2964, ++ .mn = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .p = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .s = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0, ++ }, ++ .freq_tbl = clk_tbl_usb, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(11), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_xcvr_src", ++ .parent_names = gcc_pxo_pll8_pll0_map, ++ .num_parents = 3, ++ .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_xcvr_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 17, ++ .clkr = { ++ .enable_reg = 0x2968, ++ .enable_mask = BIT(9), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_xcvr_clk", ++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_sys_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 18, ++ .clkr = { ++ .enable_reg = 0x296c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_sys_clk", ++ .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_branch usb_fs1_h_clk = { ++ .halt_reg = 0x2fcc, ++ .halt_bit = 19, ++ .clkr = { ++ .enable_reg = 0x2960, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "usb_fs1_h_clk", ++ .ops = &clk_branch_ops, ++ .flags = CLK_IS_ROOT, ++ }, ++ }, ++}; ++ ++static struct clk_regmap *gcc_ipq806x_clks[] = { ++ [PLL3] = &pll3.clkr, ++ [PLL8] = &pll8.clkr, ++ [PLL8_VOTE] = &pll8_vote, ++ [PLL14] = &pll14.clkr, ++ [PLL14_VOTE] = &pll14_vote, ++ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, ++ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, ++ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, ++ [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr, ++ [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr, ++ [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr, ++ [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr, ++ [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr, ++ [GSBI6_UART_SRC] = &gsbi6_uart_src.clkr, ++ [GSBI6_UART_CLK] = &gsbi6_uart_clk.clkr, ++ [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, ++ [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, ++ [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr, ++ [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr, ++ [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr, ++ [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr, ++ [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr, ++ [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr, ++ [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr, ++ [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr, ++ [GSBI6_QUP_SRC] = &gsbi6_qup_src.clkr, ++ [GSBI6_QUP_CLK] = &gsbi6_qup_clk.clkr, ++ [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, ++ [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, ++ [GP0_SRC] = &gp0_src.clkr, ++ [GP0_CLK] = &gp0_clk.clkr, ++ [GP1_SRC] = &gp1_src.clkr, ++ [GP1_CLK] = &gp1_clk.clkr, ++ [GP2_SRC] = &gp2_src.clkr, ++ [GP2_CLK] = &gp2_clk.clkr, ++ [PMEM_A_CLK] = &pmem_clk.clkr, ++ [PRNG_SRC] = &prng_src.clkr, ++ [PRNG_CLK] = &prng_clk.clkr, ++ [SDC1_SRC] = &sdc1_src.clkr, ++ [SDC1_CLK] = &sdc1_clk.clkr, ++ [SDC3_SRC] = &sdc3_src.clkr, ++ [SDC3_CLK] = &sdc3_clk.clkr, ++ [TSIF_REF_SRC] = &tsif_ref_src.clkr, ++ [TSIF_REF_CLK] = &tsif_ref_clk.clkr, ++ [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr, ++ [GSBI1_H_CLK] = &gsbi1_h_clk.clkr, ++ [GSBI2_H_CLK] = &gsbi2_h_clk.clkr, ++ [GSBI4_H_CLK] = &gsbi4_h_clk.clkr, ++ [GSBI5_H_CLK] = &gsbi5_h_clk.clkr, ++ [GSBI6_H_CLK] = &gsbi6_h_clk.clkr, ++ [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, ++ [TSIF_H_CLK] = &tsif_h_clk.clkr, ++ [SDC1_H_CLK] = &sdc1_h_clk.clkr, ++ [SDC3_H_CLK] = &sdc3_h_clk.clkr, ++ [ADM0_CLK] = &adm0_clk.clkr, ++ [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr, ++ [PCIE_A_CLK] = &pcie_a_clk.clkr, ++ [PCIE_AUX_CLK] = &pcie_aux_clk.clkr, ++ [PCIE_H_CLK] = &pcie_h_clk.clkr, ++ [PCIE_PHY_CLK] = &pcie_phy_clk.clkr, ++ [SFAB_SATA_S_H_CLK] = &sfab_sata_s_h_clk.clkr, ++ [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr, ++ [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, ++ [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, ++ [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, ++ [SATA_H_CLK] = &sata_h_clk.clkr, ++ [SATA_CLK_SRC] = &sata_ref_src.clkr, ++ [SATA_RXOOB_CLK] = &sata_rxoob_clk.clkr, ++ [SATA_PMALIVE_CLK] = &sata_pmalive_clk.clkr, ++ [SATA_PHY_REF_CLK] = &sata_phy_ref_clk.clkr, ++ [SATA_A_CLK] = &sata_a_clk.clkr, ++ [SATA_PHY_CFG_CLK] = &sata_phy_cfg_clk.clkr, ++ [PCIE_ALT_REF_SRC] = &pcie_ref_src.clkr, ++ [PCIE_ALT_REF_CLK] = &pcie_ref_src_clk.clkr, ++ [PCIE_1_A_CLK] = &pcie1_a_clk.clkr, ++ [PCIE_1_AUX_CLK] = &pcie1_aux_clk.clkr, ++ [PCIE_1_H_CLK] = &pcie1_h_clk.clkr, ++ [PCIE_1_PHY_CLK] = &pcie1_phy_clk.clkr, ++ [PCIE_1_ALT_REF_SRC] = &pcie1_ref_src.clkr, ++ [PCIE_1_ALT_REF_CLK] = &pcie1_ref_src_clk.clkr, ++ [PCIE_2_A_CLK] = &pcie2_a_clk.clkr, ++ [PCIE_2_AUX_CLK] = &pcie2_aux_clk.clkr, ++ [PCIE_2_H_CLK] = &pcie2_h_clk.clkr, ++ [PCIE_2_PHY_CLK] = &pcie2_phy_clk.clkr, ++ [PCIE_2_ALT_REF_SRC] = &pcie2_ref_src.clkr, ++ [PCIE_2_ALT_REF_CLK] = &pcie2_ref_src_clk.clkr, ++ [USB30_MASTER_SRC] = &usb30_master_clk_src.clkr, ++ [USB30_0_MASTER_CLK] = &usb30_0_branch_clk.clkr, ++ [USB30_1_MASTER_CLK] = &usb30_1_branch_clk.clkr, ++ [USB30_UTMI_SRC] = &usb30_utmi_clk.clkr, ++ [USB30_0_UTMI_CLK] = &usb30_0_utmi_clk_ctl.clkr, ++ [USB30_1_UTMI_CLK] = &usb30_1_utmi_clk_ctl.clkr, ++ [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr, ++ [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_clk_src.clkr, ++ [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr, ++ [USB_FS1_H_CLK] = &usb_fs1_h_clk.clkr, ++ [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, ++ [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, ++ [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, ++}; ++ ++static const struct qcom_reset_map gcc_ipq806x_resets[] = { ++ [QDSS_STM_RESET] = { 0x2060, 6 }, ++ [AFAB_SMPSS_S_RESET] = { 0x20b8, 2 }, ++ [AFAB_SMPSS_M1_RESET] = { 0x20b8, 1 }, ++ [AFAB_SMPSS_M0_RESET] = { 0x20b8, 0 }, ++ [AFAB_EBI1_CH0_RESET] = { 0x20c0, 7 }, ++ [AFAB_EBI1_CH1_RESET] = { 0x20c4, 7 }, ++ [SFAB_ADM0_M0_RESET] = { 0x21e0, 7 }, ++ [SFAB_ADM0_M1_RESET] = { 0x21e4, 7 }, ++ [SFAB_ADM0_M2_RESET] = { 0x21e8, 7 }, ++ [ADM0_C2_RESET] = { 0x220c, 4 }, ++ [ADM0_C1_RESET] = { 0x220c, 3 }, ++ [ADM0_C0_RESET] = { 0x220c, 2 }, ++ [ADM0_PBUS_RESET] = { 0x220c, 1 }, ++ [ADM0_RESET] = { 0x220c, 0 }, ++ [QDSS_CLKS_SW_RESET] = { 0x2260, 5 }, ++ [QDSS_POR_RESET] = { 0x2260, 4 }, ++ [QDSS_TSCTR_RESET] = { 0x2260, 3 }, ++ [QDSS_HRESET_RESET] = { 0x2260, 2 }, ++ [QDSS_AXI_RESET] = { 0x2260, 1 }, ++ [QDSS_DBG_RESET] = { 0x2260, 0 }, ++ [SFAB_PCIE_M_RESET] = { 0x22d8, 1 }, ++ [SFAB_PCIE_S_RESET] = { 0x22d8, 0 }, ++ [PCIE_EXT_RESET] = { 0x22dc, 6 }, ++ [PCIE_PHY_RESET] = { 0x22dc, 5 }, ++ [PCIE_PCI_RESET] = { 0x22dc, 4 }, ++ [PCIE_POR_RESET] = { 0x22dc, 3 }, ++ [PCIE_HCLK_RESET] = { 0x22dc, 2 }, ++ [PCIE_ACLK_RESET] = { 0x22dc, 0 }, ++ [SFAB_LPASS_RESET] = { 0x23a0, 7 }, ++ [SFAB_AFAB_M_RESET] = { 0x23e0, 7 }, ++ [AFAB_SFAB_M0_RESET] = { 0x2420, 7 }, ++ [AFAB_SFAB_M1_RESET] = { 0x2424, 7 }, ++ [SFAB_SATA_S_RESET] = { 0x2480, 7 }, ++ [SFAB_DFAB_M_RESET] = { 0x2500, 7 }, ++ [DFAB_SFAB_M_RESET] = { 0x2520, 7 }, ++ [DFAB_SWAY0_RESET] = { 0x2540, 7 }, ++ [DFAB_SWAY1_RESET] = { 0x2544, 7 }, ++ [DFAB_ARB0_RESET] = { 0x2560, 7 }, ++ [DFAB_ARB1_RESET] = { 0x2564, 7 }, ++ [PPSS_PROC_RESET] = { 0x2594, 1 }, ++ [PPSS_RESET] = { 0x2594, 0 }, ++ [DMA_BAM_RESET] = { 0x25c0, 7 }, ++ [SPS_TIC_H_RESET] = { 0x2600, 7 }, ++ [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, ++ [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, ++ [TSIF_H_RESET] = { 0x2700, 7 }, ++ [CE1_H_RESET] = { 0x2720, 7 }, ++ [CE1_CORE_RESET] = { 0x2724, 7 }, ++ [CE1_SLEEP_RESET] = { 0x2728, 7 }, ++ [CE2_H_RESET] = { 0x2740, 7 }, ++ [CE2_CORE_RESET] = { 0x2744, 7 }, ++ [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, ++ [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, ++ [RPM_PROC_RESET] = { 0x27c0, 7 }, ++ [PMIC_SSBI2_RESET] = { 0x280c, 12 }, ++ [SDC1_RESET] = { 0x2830, 0 }, ++ [SDC2_RESET] = { 0x2850, 0 }, ++ [SDC3_RESET] = { 0x2870, 0 }, ++ [SDC4_RESET] = { 0x2890, 0 }, ++ [USB_HS1_RESET] = { 0x2910, 0 }, ++ [USB_HSIC_RESET] = { 0x2934, 0 }, ++ [USB_FS1_XCVR_RESET] = { 0x2974, 1 }, ++ [USB_FS1_RESET] = { 0x2974, 0 }, ++ [GSBI1_RESET] = { 0x29dc, 0 }, ++ [GSBI2_RESET] = { 0x29fc, 0 }, ++ [GSBI3_RESET] = { 0x2a1c, 0 }, ++ [GSBI4_RESET] = { 0x2a3c, 0 }, ++ [GSBI5_RESET] = { 0x2a5c, 0 }, ++ [GSBI6_RESET] = { 0x2a7c, 0 }, ++ [GSBI7_RESET] = { 0x2a9c, 0 }, ++ [SPDM_RESET] = { 0x2b6c, 0 }, ++ [SEC_CTRL_RESET] = { 0x2b80, 7 }, ++ [TLMM_H_RESET] = { 0x2ba0, 7 }, ++ [SFAB_SATA_M_RESET] = { 0x2c18, 0 }, ++ [SATA_RESET] = { 0x2c1c, 0 }, ++ [TSSC_RESET] = { 0x2ca0, 7 }, ++ [PDM_RESET] = { 0x2cc0, 12 }, ++ [MPM_H_RESET] = { 0x2da0, 7 }, ++ [MPM_RESET] = { 0x2da4, 0 }, ++ [SFAB_SMPSS_S_RESET] = { 0x2e00, 7 }, ++ [PRNG_RESET] = { 0x2e80, 12 }, ++ [SFAB_CE3_M_RESET] = { 0x36c8, 1 }, ++ [SFAB_CE3_S_RESET] = { 0x36c8, 0 }, ++ [CE3_SLEEP_RESET] = { 0x36d0, 7 }, ++ [PCIE_1_M_RESET] = { 0x3a98, 1 }, ++ [PCIE_1_S_RESET] = { 0x3a98, 0 }, ++ [PCIE_1_EXT_RESET] = { 0x3a9c, 6 }, ++ [PCIE_1_PHY_RESET] = { 0x3a9c, 5 }, ++ [PCIE_1_PCI_RESET] = { 0x3a9c, 4 }, ++ [PCIE_1_POR_RESET] = { 0x3a9c, 3 }, ++ [PCIE_1_HCLK_RESET] = { 0x3a9c, 2 }, ++ [PCIE_1_ACLK_RESET] = { 0x3a9c, 0 }, ++ [PCIE_2_M_RESET] = { 0x3ad8, 1 }, ++ [PCIE_2_S_RESET] = { 0x3ad8, 0 }, ++ [PCIE_2_EXT_RESET] = { 0x3adc, 6 }, ++ [PCIE_2_PHY_RESET] = { 0x3adc, 5 }, ++ [PCIE_2_PCI_RESET] = { 0x3adc, 4 }, ++ [PCIE_2_POR_RESET] = { 0x3adc, 3 }, ++ [PCIE_2_HCLK_RESET] = { 0x3adc, 2 }, ++ [PCIE_2_ACLK_RESET] = { 0x3adc, 0 }, ++ [SFAB_USB30_S_RESET] = { 0x3b54, 1 }, ++ [SFAB_USB30_M_RESET] = { 0x3b54, 0 }, ++ [USB30_0_PORT2_HS_PHY_RESET] = { 0x3b50, 5 }, ++ [USB30_0_MASTER_RESET] = { 0x3b50, 4 }, ++ [USB30_0_SLEEP_RESET] = { 0x3b50, 3 }, ++ [USB30_0_UTMI_PHY_RESET] = { 0x3b50, 2 }, ++ [USB30_0_POWERON_RESET] = { 0x3b50, 1 }, ++ [USB30_0_PHY_RESET] = { 0x3b50, 0 }, ++ [USB30_1_MASTER_RESET] = { 0x3b58, 4 }, ++ [USB30_1_SLEEP_RESET] = { 0x3b58, 3 }, ++ [USB30_1_UTMI_PHY_RESET] = { 0x3b58, 2 }, ++ [USB30_1_POWERON_RESET] = { 0x3b58, 1 }, ++ [USB30_1_PHY_RESET] = { 0x3b58, 0 }, ++ [NSSFB0_RESET] = { 0x3b60, 6 }, ++ [NSSFB1_RESET] = { 0x3b60, 7 }, ++}; ++ ++static const struct regmap_config gcc_ipq806x_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x3e40, ++ .fast_io = true, ++}; ++ ++static const struct qcom_cc_desc gcc_ipq806x_desc = { ++ .config = &gcc_ipq806x_regmap_config, ++ .clks = gcc_ipq806x_clks, ++ .num_clks = ARRAY_SIZE(gcc_ipq806x_clks), ++ .resets = gcc_ipq806x_resets, ++ .num_resets = ARRAY_SIZE(gcc_ipq806x_resets), ++}; ++ ++static const struct of_device_id gcc_ipq806x_match_table[] = { ++ { .compatible = "qcom,gcc-ipq8064" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, gcc_ipq806x_match_table); ++ ++static int gcc_ipq806x_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct device *dev = &pdev->dev; ++ ++ /* Temporary until RPM clocks supported */ ++ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ return qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++} ++ ++static int gcc_ipq806x_remove(struct platform_device *pdev) ++{ ++ qcom_cc_remove(pdev); ++ return 0; ++} ++ ++static struct platform_driver gcc_ipq806x_driver = { ++ .probe = gcc_ipq806x_probe, ++ .remove = gcc_ipq806x_remove, ++ .driver = { ++ .name = "gcc-ipq806x", ++ .owner = THIS_MODULE, ++ .of_match_table = gcc_ipq806x_match_table, ++ }, ++}; ++ ++static int __init gcc_ipq806x_init(void) ++{ ++ return platform_driver_register(&gcc_ipq806x_driver); ++} ++core_initcall(gcc_ipq806x_init); ++ ++static void __exit gcc_ipq806x_exit(void) ++{ ++ platform_driver_unregister(&gcc_ipq806x_driver); ++} ++module_exit(gcc_ipq806x_exit); ++ ++MODULE_DESCRIPTION("QCOM GCC IPQ806x Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:gcc-ipq806x"); +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +new file mode 100644 +index 0000000..3b0f8e7 +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -0,0 +1,293 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_GCC_IPQ806X_H ++#define _DT_BINDINGS_CLK_GCC_IPQ806X_H ++ ++#define AFAB_CLK_SRC 0 ++#define QDSS_STM_CLK 1 ++#define SCSS_A_CLK 2 ++#define SCSS_H_CLK 3 ++#define AFAB_CORE_CLK 4 ++#define SCSS_XO_SRC_CLK 5 ++#define AFAB_EBI1_CH0_A_CLK 6 ++#define AFAB_EBI1_CH1_A_CLK 7 ++#define AFAB_AXI_S0_FCLK 8 ++#define AFAB_AXI_S1_FCLK 9 ++#define AFAB_AXI_S2_FCLK 10 ++#define AFAB_AXI_S3_FCLK 11 ++#define AFAB_AXI_S4_FCLK 12 ++#define SFAB_CORE_CLK 13 ++#define SFAB_AXI_S0_FCLK 14 ++#define SFAB_AXI_S1_FCLK 15 ++#define SFAB_AXI_S2_FCLK 16 ++#define SFAB_AXI_S3_FCLK 17 ++#define SFAB_AXI_S4_FCLK 18 ++#define SFAB_AXI_S5_FCLK 19 ++#define SFAB_AHB_S0_FCLK 20 ++#define SFAB_AHB_S1_FCLK 21 ++#define SFAB_AHB_S2_FCLK 22 ++#define SFAB_AHB_S3_FCLK 23 ++#define SFAB_AHB_S4_FCLK 24 ++#define SFAB_AHB_S5_FCLK 25 ++#define SFAB_AHB_S6_FCLK 26 ++#define SFAB_AHB_S7_FCLK 27 ++#define QDSS_AT_CLK_SRC 28 ++#define QDSS_AT_CLK 29 ++#define QDSS_TRACECLKIN_CLK_SRC 30 ++#define QDSS_TRACECLKIN_CLK 31 ++#define QDSS_TSCTR_CLK_SRC 32 ++#define QDSS_TSCTR_CLK 33 ++#define SFAB_ADM0_M0_A_CLK 34 ++#define SFAB_ADM0_M1_A_CLK 35 ++#define SFAB_ADM0_M2_H_CLK 36 ++#define ADM0_CLK 37 ++#define ADM0_PBUS_CLK 38 ++#define IMEM0_A_CLK 39 ++#define QDSS_H_CLK 40 ++#define PCIE_A_CLK 41 ++#define PCIE_AUX_CLK 42 ++#define PCIE_H_CLK 43 ++#define PCIE_PHY_CLK 44 ++#define SFAB_CLK_SRC 45 ++#define SFAB_LPASS_Q6_A_CLK 46 ++#define SFAB_AFAB_M_A_CLK 47 ++#define AFAB_SFAB_M0_A_CLK 48 ++#define AFAB_SFAB_M1_A_CLK 49 ++#define SFAB_SATA_S_H_CLK 50 ++#define DFAB_CLK_SRC 51 ++#define DFAB_CLK 52 ++#define SFAB_DFAB_M_A_CLK 53 ++#define DFAB_SFAB_M_A_CLK 54 ++#define DFAB_SWAY0_H_CLK 55 ++#define DFAB_SWAY1_H_CLK 56 ++#define DFAB_ARB0_H_CLK 57 ++#define DFAB_ARB1_H_CLK 58 ++#define PPSS_H_CLK 59 ++#define PPSS_PROC_CLK 60 ++#define PPSS_TIMER0_CLK 61 ++#define PPSS_TIMER1_CLK 62 ++#define PMEM_A_CLK 63 ++#define DMA_BAM_H_CLK 64 ++#define SIC_H_CLK 65 ++#define SPS_TIC_H_CLK 66 ++#define CFPB_2X_CLK_SRC 67 ++#define CFPB_CLK 68 ++#define CFPB0_H_CLK 69 ++#define CFPB1_H_CLK 70 ++#define CFPB2_H_CLK 71 ++#define SFAB_CFPB_M_H_CLK 72 ++#define CFPB_MASTER_H_CLK 73 ++#define SFAB_CFPB_S_H_CLK 74 ++#define CFPB_SPLITTER_H_CLK 75 ++#define TSIF_H_CLK 76 ++#define TSIF_INACTIVITY_TIMERS_CLK 77 ++#define TSIF_REF_SRC 78 ++#define TSIF_REF_CLK 79 ++#define CE1_H_CLK 80 ++#define CE1_CORE_CLK 81 ++#define CE1_SLEEP_CLK 82 ++#define CE2_H_CLK 83 ++#define CE2_CORE_CLK 84 ++#define SFPB_H_CLK_SRC 85 ++#define SFPB_H_CLK 86 ++#define SFAB_SFPB_M_H_CLK 87 ++#define SFAB_SFPB_S_H_CLK 88 ++#define RPM_PROC_CLK 89 ++#define RPM_BUS_H_CLK 90 ++#define RPM_SLEEP_CLK 91 ++#define RPM_TIMER_CLK 92 ++#define RPM_MSG_RAM_H_CLK 93 ++#define PMIC_ARB0_H_CLK 94 ++#define PMIC_ARB1_H_CLK 95 ++#define PMIC_SSBI2_SRC 96 ++#define PMIC_SSBI2_CLK 97 ++#define SDC1_H_CLK 98 ++#define SDC2_H_CLK 99 ++#define SDC3_H_CLK 100 ++#define SDC4_H_CLK 101 ++#define SDC1_SRC 102 ++#define SDC1_CLK 103 ++#define SDC2_SRC 104 ++#define SDC2_CLK 105 ++#define SDC3_SRC 106 ++#define SDC3_CLK 107 ++#define SDC4_SRC 108 ++#define SDC4_CLK 109 ++#define USB_HS1_H_CLK 110 ++#define USB_HS1_XCVR_SRC 111 ++#define USB_HS1_XCVR_CLK 112 ++#define USB_HSIC_H_CLK 113 ++#define USB_HSIC_XCVR_SRC 114 ++#define USB_HSIC_XCVR_CLK 115 ++#define USB_HSIC_SYSTEM_CLK_SRC 116 ++#define USB_HSIC_SYSTEM_CLK 117 ++#define CFPB0_C0_H_CLK 118 ++#define CFPB0_D0_H_CLK 119 ++#define CFPB0_C1_H_CLK 120 ++#define CFPB0_D1_H_CLK 121 ++#define USB_FS1_H_CLK 122 ++#define USB_FS1_XCVR_SRC 123 ++#define USB_FS1_XCVR_CLK 124 ++#define USB_FS1_SYSTEM_CLK 125 ++#define GSBI_COMMON_SIM_SRC 126 ++#define GSBI1_H_CLK 127 ++#define GSBI2_H_CLK 128 ++#define GSBI3_H_CLK 129 ++#define GSBI4_H_CLK 130 ++#define GSBI5_H_CLK 131 ++#define GSBI6_H_CLK 132 ++#define GSBI7_H_CLK 133 ++#define GSBI1_QUP_SRC 134 ++#define GSBI1_QUP_CLK 135 ++#define GSBI2_QUP_SRC 136 ++#define GSBI2_QUP_CLK 137 ++#define GSBI3_QUP_SRC 138 ++#define GSBI3_QUP_CLK 139 ++#define GSBI4_QUP_SRC 140 ++#define GSBI4_QUP_CLK 141 ++#define GSBI5_QUP_SRC 142 ++#define GSBI5_QUP_CLK 143 ++#define GSBI6_QUP_SRC 144 ++#define GSBI6_QUP_CLK 145 ++#define GSBI7_QUP_SRC 146 ++#define GSBI7_QUP_CLK 147 ++#define GSBI1_UART_SRC 148 ++#define GSBI1_UART_CLK 149 ++#define GSBI2_UART_SRC 150 ++#define GSBI2_UART_CLK 151 ++#define GSBI3_UART_SRC 152 ++#define GSBI3_UART_CLK 153 ++#define GSBI4_UART_SRC 154 ++#define GSBI4_UART_CLK 155 ++#define GSBI5_UART_SRC 156 ++#define GSBI5_UART_CLK 157 ++#define GSBI6_UART_SRC 158 ++#define GSBI6_UART_CLK 159 ++#define GSBI7_UART_SRC 160 ++#define GSBI7_UART_CLK 161 ++#define GSBI1_SIM_CLK 162 ++#define GSBI2_SIM_CLK 163 ++#define GSBI3_SIM_CLK 164 ++#define GSBI4_SIM_CLK 165 ++#define GSBI5_SIM_CLK 166 ++#define GSBI6_SIM_CLK 167 ++#define GSBI7_SIM_CLK 168 ++#define USB_HSIC_HSIC_CLK_SRC 169 ++#define USB_HSIC_HSIC_CLK 170 ++#define USB_HSIC_HSIO_CAL_CLK 171 ++#define SPDM_CFG_H_CLK 172 ++#define SPDM_MSTR_H_CLK 173 ++#define SPDM_FF_CLK_SRC 174 ++#define SPDM_FF_CLK 175 ++#define SEC_CTRL_CLK 176 ++#define SEC_CTRL_ACC_CLK_SRC 177 ++#define SEC_CTRL_ACC_CLK 178 ++#define TLMM_H_CLK 179 ++#define TLMM_CLK 180 ++#define SATA_H_CLK 181 ++#define SATA_CLK_SRC 182 ++#define SATA_RXOOB_CLK 183 ++#define SATA_PMALIVE_CLK 184 ++#define SATA_PHY_REF_CLK 185 ++#define SATA_A_CLK 186 ++#define SATA_PHY_CFG_CLK 187 ++#define TSSC_CLK_SRC 188 ++#define TSSC_CLK 189 ++#define PDM_SRC 190 ++#define PDM_CLK 191 ++#define GP0_SRC 192 ++#define GP0_CLK 193 ++#define GP1_SRC 194 ++#define GP1_CLK 195 ++#define GP2_SRC 196 ++#define GP2_CLK 197 ++#define MPM_CLK 198 ++#define EBI1_CLK_SRC 199 ++#define EBI1_CH0_CLK 200 ++#define EBI1_CH1_CLK 201 ++#define EBI1_2X_CLK 202 ++#define EBI1_CH0_DQ_CLK 203 ++#define EBI1_CH1_DQ_CLK 204 ++#define EBI1_CH0_CA_CLK 205 ++#define EBI1_CH1_CA_CLK 206 ++#define EBI1_XO_CLK 207 ++#define SFAB_SMPSS_S_H_CLK 208 ++#define PRNG_SRC 209 ++#define PRNG_CLK 210 ++#define PXO_SRC 211 ++#define SPDM_CY_PORT0_CLK 212 ++#define SPDM_CY_PORT1_CLK 213 ++#define SPDM_CY_PORT2_CLK 214 ++#define SPDM_CY_PORT3_CLK 215 ++#define SPDM_CY_PORT4_CLK 216 ++#define SPDM_CY_PORT5_CLK 217 ++#define SPDM_CY_PORT6_CLK 218 ++#define SPDM_CY_PORT7_CLK 219 ++#define PLL0 220 ++#define PLL0_VOTE 221 ++#define PLL3 222 ++#define PLL3_VOTE 223 ++#define PLL4 224 ++#define PLL4_VOTE 225 ++#define PLL8 226 ++#define PLL8_VOTE 227 ++#define PLL9 228 ++#define PLL10 229 ++#define PLL11 230 ++#define PLL12 231 ++#define PLL14 232 ++#define PLL14_VOTE 233 ++#define PLL18 234 ++#define CE5_SRC 235 ++#define CE5_H_CLK 236 ++#define CE5_CORE_CLK 237 ++#define CE3_SLEEP_CLK 238 ++#define SFAB_AHB_S8_FCLK 239 ++#define SPDM_CY_PORT8_CLK 246 ++#define PCIE_ALT_REF_SRC 247 ++#define PCIE_ALT_REF_CLK 248 ++#define PCIE_1_A_CLK 249 ++#define PCIE_1_AUX_CLK 250 ++#define PCIE_1_H_CLK 251 ++#define PCIE_1_PHY_CLK 252 ++#define PCIE_1_ALT_REF_SRC 253 ++#define PCIE_1_ALT_REF_CLK 254 ++#define PCIE_2_A_CLK 255 ++#define PCIE_2_AUX_CLK 256 ++#define PCIE_2_H_CLK 257 ++#define PCIE_2_PHY_CLK 258 ++#define PCIE_2_ALT_REF_SRC 259 ++#define PCIE_2_ALT_REF_CLK 260 ++#define EBI2_CLK 261 ++#define USB30_SLEEP_CLK 262 ++#define USB30_UTMI_SRC 263 ++#define USB30_0_UTMI_CLK 264 ++#define USB30_1_UTMI_CLK 264 ++#define USB30_MASTER_SRC 265 ++#define USB30_0_MASTER_CLK 266 ++#define USB30_1_MASTER_CLK 267 ++#define GMAC_CORE1_CLK_SRC 268 ++#define GMAC_CORE2_CLK_SRC 269 ++#define GMAC_CORE3_CLK_SRC 270 ++#define GMAC_CORE4_CLK_SRC 271 ++#define GMAC_CORE1_CLK 272 ++#define GMAC_CORE2_CLK 273 ++#define GMAC_CORE3_CLK 274 ++#define GMAC_CORE4_CLK 275 ++#define UBI32_CORE1_CLK_SRC 276 ++#define UBI32_CORE2_CLK_SRC 277 ++#define UBI32_CORE1_CLK 278 ++#define UBI32_CORE2_CLK 279 ++ ++#endif +diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +new file mode 100644 +index 0000000..0ad5ef9 +--- /dev/null ++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_RESET_IPQ_806X_H ++#define _DT_BINDINGS_RESET_IPQ_806X_H ++ ++#define QDSS_STM_RESET 0 ++#define AFAB_SMPSS_S_RESET 1 ++#define AFAB_SMPSS_M1_RESET 2 ++#define AFAB_SMPSS_M0_RESET 3 ++#define AFAB_EBI1_CH0_RESET 4 ++#define AFAB_EBI1_CH1_RESET 5 ++#define SFAB_ADM0_M0_RESET 6 ++#define SFAB_ADM0_M1_RESET 7 ++#define SFAB_ADM0_M2_RESET 8 ++#define ADM0_C2_RESET 9 ++#define ADM0_C1_RESET 10 ++#define ADM0_C0_RESET 11 ++#define ADM0_PBUS_RESET 12 ++#define ADM0_RESET 13 ++#define QDSS_CLKS_SW_RESET 14 ++#define QDSS_POR_RESET 15 ++#define QDSS_TSCTR_RESET 16 ++#define QDSS_HRESET_RESET 17 ++#define QDSS_AXI_RESET 18 ++#define QDSS_DBG_RESET 19 ++#define SFAB_PCIE_M_RESET 20 ++#define SFAB_PCIE_S_RESET 21 ++#define PCIE_EXT_RESET 22 ++#define PCIE_PHY_RESET 23 ++#define PCIE_PCI_RESET 24 ++#define PCIE_POR_RESET 25 ++#define PCIE_HCLK_RESET 26 ++#define PCIE_ACLK_RESET 27 ++#define SFAB_LPASS_RESET 28 ++#define SFAB_AFAB_M_RESET 29 ++#define AFAB_SFAB_M0_RESET 30 ++#define AFAB_SFAB_M1_RESET 31 ++#define SFAB_SATA_S_RESET 32 ++#define SFAB_DFAB_M_RESET 33 ++#define DFAB_SFAB_M_RESET 34 ++#define DFAB_SWAY0_RESET 35 ++#define DFAB_SWAY1_RESET 36 ++#define DFAB_ARB0_RESET 37 ++#define DFAB_ARB1_RESET 38 ++#define PPSS_PROC_RESET 39 ++#define PPSS_RESET 40 ++#define DMA_BAM_RESET 41 ++#define SPS_TIC_H_RESET 42 ++#define SFAB_CFPB_M_RESET 43 ++#define SFAB_CFPB_S_RESET 44 ++#define TSIF_H_RESET 45 ++#define CE1_H_RESET 46 ++#define CE1_CORE_RESET 47 ++#define CE1_SLEEP_RESET 48 ++#define CE2_H_RESET 49 ++#define CE2_CORE_RESET 50 ++#define SFAB_SFPB_M_RESET 51 ++#define SFAB_SFPB_S_RESET 52 ++#define RPM_PROC_RESET 53 ++#define PMIC_SSBI2_RESET 54 ++#define SDC1_RESET 55 ++#define SDC2_RESET 56 ++#define SDC3_RESET 57 ++#define SDC4_RESET 58 ++#define USB_HS1_RESET 59 ++#define USB_HSIC_RESET 60 ++#define USB_FS1_XCVR_RESET 61 ++#define USB_FS1_RESET 62 ++#define GSBI1_RESET 63 ++#define GSBI2_RESET 64 ++#define GSBI3_RESET 65 ++#define GSBI4_RESET 66 ++#define GSBI5_RESET 67 ++#define GSBI6_RESET 68 ++#define GSBI7_RESET 69 ++#define SPDM_RESET 70 ++#define SEC_CTRL_RESET 71 ++#define TLMM_H_RESET 72 ++#define SFAB_SATA_M_RESET 73 ++#define SATA_RESET 74 ++#define TSSC_RESET 75 ++#define PDM_RESET 76 ++#define MPM_H_RESET 77 ++#define MPM_RESET 78 ++#define SFAB_SMPSS_S_RESET 79 ++#define PRNG_RESET 80 ++#define SFAB_CE3_M_RESET 81 ++#define SFAB_CE3_S_RESET 82 ++#define CE3_SLEEP_RESET 83 ++#define PCIE_1_M_RESET 84 ++#define PCIE_1_S_RESET 85 ++#define PCIE_1_EXT_RESET 86 ++#define PCIE_1_PHY_RESET 87 ++#define PCIE_1_PCI_RESET 88 ++#define PCIE_1_POR_RESET 89 ++#define PCIE_1_HCLK_RESET 90 ++#define PCIE_1_ACLK_RESET 91 ++#define PCIE_2_M_RESET 92 ++#define PCIE_2_S_RESET 93 ++#define PCIE_2_EXT_RESET 94 ++#define PCIE_2_PHY_RESET 95 ++#define PCIE_2_PCI_RESET 96 ++#define PCIE_2_POR_RESET 97 ++#define PCIE_2_HCLK_RESET 98 ++#define PCIE_2_ACLK_RESET 99 ++#define SFAB_USB30_S_RESET 100 ++#define SFAB_USB30_M_RESET 101 ++#define USB30_0_PORT2_HS_PHY_RESET 102 ++#define USB30_0_MASTER_RESET 103 ++#define USB30_0_SLEEP_RESET 104 ++#define USB30_0_UTMI_PHY_RESET 105 ++#define USB30_0_POWERON_RESET 106 ++#define USB30_0_PHY_RESET 107 ++#define USB30_1_MASTER_RESET 108 ++#define USB30_1_SLEEP_RESET 109 ++#define USB30_1_UTMI_PHY_RESET 110 ++#define USB30_1_POWERON_RESET 111 ++#define USB30_1_PHY_RESET 112 ++#define NSSFB0_RESET 113 ++#define NSSFB1_RESET 114 ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..bda409363e --- /dev/null +++ b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch @@ -0,0 +1,155 @@ +From b7b3ceec506179d59e46e3a8ea8b370872cc7fcb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 31 Mar 2014 16:52:29 -0700 +Subject: [PATCH 126/182] clk: Add safe switch hook + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/clk.c | 53 ++++++++++++++++++++++++++++++++++++------ + include/linux/clk-private.h | 2 ++ + include/linux/clk-provider.h | 1 + + 3 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index b94a311..0582068 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1356,6 +1356,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) + { + struct clk *child; ++ struct clk *parent; + + clk->new_rate = new_rate; + clk->new_parent = new_parent; +@@ -1365,6 +1366,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; + ++ if (clk->ops->get_safe_parent) { ++ parent = clk->ops->get_safe_parent(clk->hw); ++ if (parent) { ++ p_index = clk_fetch_parent_index(clk, parent); ++ clk->safe_parent_index = p_index; ++ clk->safe_parent = parent; ++ } ++ } else { ++ clk->safe_parent = NULL; ++ } ++ + hlist_for_each_entry(child, &clk->children, child_node) { + if (child->ops->recalc_rate) + child->new_rate = child->ops->recalc_rate(child->hw, new_rate); +@@ -1450,14 +1462,42 @@ out: + static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) + { + struct clk *child, *tmp_clk, *fail_clk = NULL; ++ struct clk *old_parent; + int ret = NOTIFY_DONE; + +- if (clk->rate == clk->new_rate) ++ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE) + return NULL; + ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ if (clk->safe_parent) ++ clk->ops->set_parent(clk->hw, clk->safe_parent_index); ++ break; ++ case POST_RATE_CHANGE: ++ if (clk->safe_parent) { ++ old_parent = __clk_set_parent_before(clk, ++ clk->new_parent); ++ if (clk->ops->set_rate_and_parent) { ++ clk->ops->set_rate_and_parent(clk->hw, ++ clk->new_rate, ++ clk->new_parent ? ++ clk->new_parent->rate : 0, ++ clk->new_parent_index); ++ } else if (clk->ops->set_parent) { ++ clk->ops->set_parent(clk->hw, ++ clk->new_parent_index); ++ } ++ __clk_set_parent_after(clk, clk->new_parent, ++ old_parent); ++ } ++ break; ++ } ++ + if (clk->notifier_count) { +- ret = __clk_notify(clk, event, clk->rate, clk->new_rate); +- if (ret & NOTIFY_STOP_MASK) ++ if (event != POST_RATE_CHANGE) ++ ret = __clk_notify(clk, event, clk->rate, ++ clk->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = clk; + } + +@@ -1499,7 +1539,8 @@ static void clk_change_rate(struct clk *clk) + else if (clk->parent) + best_parent_rate = clk->parent->rate; + +- if (clk->new_parent && clk->new_parent != clk->parent) { ++ if (clk->new_parent && clk->new_parent != clk->parent && ++ !clk->safe_parent) { + old_parent = __clk_set_parent_before(clk, clk->new_parent); + + if (clk->ops->set_rate_and_parent) { +@@ -1522,9 +1563,6 @@ static void clk_change_rate(struct clk *clk) + else + clk->rate = best_parent_rate; + +- if (clk->notifier_count && old_rate != clk->rate) +- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); +- + hlist_for_each_entry(child, &clk->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) +@@ -1598,6 +1636,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) + /* change the rates */ + clk_change_rate(top); + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); + out: + clk_prepare_unlock(); + +diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h +index efbf70b..f48684a 100644 +--- a/include/linux/clk-private.h ++++ b/include/linux/clk-private.h +@@ -38,8 +38,10 @@ struct clk { + struct clk **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long new_rate; ++ struct clk *safe_parent; + struct clk *new_parent; + struct clk *new_child; + unsigned long flags; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index 939533d..300fcb8 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -157,6 +157,7 @@ struct clk_ops { + struct clk **best_parent_clk); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long, + unsigned long); + int (*set_rate_and_parent)(struct clk_hw *hw, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch new file mode 100644 index 0000000000..2983b8444b --- /dev/null +++ b/target/linux/ipq806x/patches/0127-clk-qcom-Add-support-for-setting-rates-on-PLLs.patch @@ -0,0 +1,155 @@ +From fd06a2cc719296f65a280cb1533b125f63cfcb34 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 15:58:11 -0700 +Subject: [PATCH 127/182] clk: qcom: Add support for setting rates on PLLs + +Some PLLs may require changing their rate at runtime. Add support +for these PLLs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/clk-pll.c | 68 +++++++++++++++++++++++++++++++++++++++++++- + drivers/clk/qcom/clk-pll.h | 20 +++++++++++++ + 2 files changed, 87 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c +index 0f927c5..80c7a76 100644 +--- a/drivers/clk/qcom/clk-pll.c ++++ b/drivers/clk/qcom/clk-pll.c +@@ -97,7 +97,7 @@ static unsigned long + clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + { + struct clk_pll *pll = to_clk_pll(hw); +- u32 l, m, n; ++ u32 l, m, n, config; + unsigned long rate; + u64 tmp; + +@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + do_div(tmp, n); + rate += tmp; + } ++ if (pll->post_div_width) { ++ regmap_read(pll->clkr.regmap, pll->config_reg, &config); ++ config >>= pll->post_div_shift; ++ config &= BIT(pll->post_div_width) - 1; ++ rate /= config + 1; ++ } ++ + return rate; + } + ++static const ++struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate) ++{ ++ if (!f) ++ return NULL; ++ ++ for (; f->freq; f++) ++ if (rate <= f->freq) ++ return f; ++ ++ return NULL; ++} ++ ++static long ++clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ const struct pll_freq_tbl *f; ++ ++ f = find_freq(pll->freq_tbl, rate); ++ if (!f) ++ return clk_pll_recalc_rate(hw, *p_rate); ++ ++ return f->freq; ++} ++ ++static int ++clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) ++{ ++ struct clk_pll *pll = to_clk_pll(hw); ++ const struct pll_freq_tbl *f; ++ bool enabled; ++ u32 mode; ++ u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; ++ ++ f = find_freq(pll->freq_tbl, rate); ++ if (!f) ++ return -EINVAL; ++ ++ regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); ++ enabled = (mode & enable_mask) == enable_mask; ++ ++ if (enabled) ++ clk_pll_disable(hw); ++ ++ regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l); ++ regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m); ++ regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n); ++ regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits); ++ ++ if (enabled) ++ clk_pll_enable(hw); ++ ++ return 0; ++} ++ + const struct clk_ops clk_pll_ops = { + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .set_rate = clk_pll_set_rate, + }; + EXPORT_SYMBOL_GPL(clk_pll_ops); + +diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h +index 0775a99..5f9928b 100644 +--- a/drivers/clk/qcom/clk-pll.h ++++ b/drivers/clk/qcom/clk-pll.h +@@ -18,6 +18,21 @@ + #include "clk-regmap.h" + + /** ++ * struct pll_freq_tbl - PLL frequency table ++ * @l: L value ++ * @m: M value ++ * @n: N value ++ * @ibits: internal values ++ */ ++struct pll_freq_tbl { ++ unsigned long freq; ++ u16 l; ++ u16 m; ++ u16 n; ++ u32 ibits; ++}; ++ ++/** + * struct clk_pll - phase locked loop (PLL) + * @l_reg: L register + * @m_reg: M register +@@ -26,6 +41,7 @@ + * @mode_reg: mode register + * @status_reg: status register + * @status_bit: ANDed with @status_reg to determine if PLL is enabled ++ * @freq_tbl: PLL frequency table + * @hw: handle between common and hardware-specific interfaces + */ + struct clk_pll { +@@ -36,6 +52,10 @@ struct clk_pll { + u32 mode_reg; + u32 status_reg; + u8 status_bit; ++ u8 post_div_width; ++ u8 post_div_shift; ++ ++ const struct pll_freq_tbl *freq_tbl; + + struct clk_regmap clkr; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch new file mode 100644 index 0000000000..0b9e237c71 --- /dev/null +++ b/target/linux/ipq806x/patches/0128-clk-qcom-Add-support-for-banked-MD-RCGs.patch @@ -0,0 +1,317 @@ +From 856324d2daa3246ac62d7920d6a274e1fa35bcf5 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 15:59:16 -0700 +Subject: [PATCH 128/182] clk: qcom: Add support for banked MD RCGs + +The banked MD RCGs in global clock control have a different +register layout than the ones implemented in multimedia clock +control. Add support for these types of clocks so we can change +the rates of the UBI32 clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/clk-rcg.c | 99 ++++++++++++++++++++------------------- + drivers/clk/qcom/clk-rcg.h | 5 +- + drivers/clk/qcom/mmcc-msm8960.c | 24 +++++++--- + 3 files changed, 73 insertions(+), 55 deletions(-) + +diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c +index abfc2b6..7bce729 100644 +--- a/drivers/clk/qcom/clk-rcg.c ++++ b/drivers/clk/qcom/clk-rcg.c +@@ -67,16 +67,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw) + { + struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); + int num_parents = __clk_get_num_parents(hw->clk); +- u32 ns, ctl; ++ u32 ns, reg; + int bank; + int i; + struct src_sel *s; + +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- bank = reg_to_bank(rcg, ctl); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ bank = reg_to_bank(rcg, reg); + s = &rcg->s[bank]; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + ns = ns_to_src(s, ns); + + for (i = 0; i < num_parents; i++) +@@ -192,90 +192,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val) + + static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) + { +- u32 ns, md, ctl, *regp; ++ u32 ns, md, reg; + int bank, new_bank; + struct mn *mn; + struct pre_div *p; + struct src_sel *s; + bool enabled; +- u32 md_reg; +- u32 bank_reg; ++ u32 md_reg, ns_reg; + bool banked_mn = !!rcg->mn[1].width; ++ bool banked_p = !!rcg->p[1].pre_div_width; + struct clk_hw *hw = &rcg->clkr.hw; + + enabled = __clk_is_enabled(hw->clk); + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- +- if (banked_mn) { +- regp = &ctl; +- bank_reg = rcg->clkr.enable_reg; +- } else { +- regp = &ns; +- bank_reg = rcg->ns_reg; +- } +- +- bank = reg_to_bank(rcg, *regp); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ bank = reg_to_bank(rcg, reg); + new_bank = enabled ? !bank : bank; + ++ ns_reg = rcg->ns_reg[new_bank]; ++ regmap_read(rcg->clkr.regmap, ns_reg, &ns); ++ + if (banked_mn) { + mn = &rcg->mn[new_bank]; + md_reg = rcg->md_reg[new_bank]; + + ns |= BIT(mn->mnctr_reset_bit); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + + regmap_read(rcg->clkr.regmap, md_reg, &md); + md = mn_to_md(mn, f->m, f->n, md); + regmap_write(rcg->clkr.regmap, md_reg, md); + + ns = mn_to_ns(mn, f->m, f->n, ns); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + +- ctl = mn_to_reg(mn, f->m, f->n, ctl); +- regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl); ++ /* Two NS registers means mode control is in NS register */ ++ if (rcg->ns_reg[0] != rcg->ns_reg[1]) { ++ ns = mn_to_reg(mn, f->m, f->n, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); ++ } else { ++ reg = mn_to_reg(mn, f->m, f->n, reg); ++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); ++ } + + ns &= ~BIT(mn->mnctr_reset_bit); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); +- } else { ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); ++ } ++ ++ if (banked_p) { + p = &rcg->p[new_bank]; + ns = pre_div_to_ns(p, f->pre_div - 1, ns); + } + + s = &rcg->s[new_bank]; + ns = src_to_ns(s, s->parent_map[f->src], ns); +- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); ++ regmap_write(rcg->clkr.regmap, ns_reg, ns); + + if (enabled) { +- *regp ^= BIT(rcg->mux_sel_bit); +- regmap_write(rcg->clkr.regmap, bank_reg, *regp); ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); ++ reg ^= BIT(rcg->mux_sel_bit); ++ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); + } + } + + static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) + { + struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); +- u32 ns, ctl, md, reg; ++ u32 ns, md, reg; + int bank; + struct freq_tbl f = { 0 }; + bool banked_mn = !!rcg->mn[1].width; ++ bool banked_p = !!rcg->p[1].pre_div_width; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); +- reg = banked_mn ? ctl : ns; +- ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); + ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ++ + if (banked_mn) { + regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); + f.m = md_to_m(&rcg->mn[bank], md); + f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); +- } else { +- f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; + } +- f.src = index; + ++ if (banked_p) ++ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; ++ ++ f.src = index; + configure_bank(rcg, &f); + + return 0; +@@ -336,28 +339,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) + u32 m, n, pre_div, ns, md, mode, reg; + int bank; + struct mn *mn; ++ bool banked_p = !!rcg->p[1].pre_div_width; + bool banked_mn = !!rcg->mn[1].width; + +- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); +- +- if (banked_mn) +- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®); +- else +- reg = ns; +- ++ regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); + ++ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ++ m = n = pre_div = mode = 0; ++ + if (banked_mn) { + mn = &rcg->mn[bank]; + regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); + m = md_to_m(mn, md); + n = ns_m_to_n(mn, ns, m); ++ /* Two NS registers means mode control is in NS register */ ++ if (rcg->ns_reg[0] != rcg->ns_reg[1]) ++ reg = ns; + mode = reg_to_mnctr_mode(mn, reg); +- return calc_rate(parent_rate, m, n, mode, 0); +- } else { +- pre_div = ns_to_pre_div(&rcg->p[bank], ns); +- return calc_rate(parent_rate, 0, 0, 0, pre_div); + } ++ ++ if (banked_p) ++ pre_div = ns_to_pre_div(&rcg->p[bank], ns); ++ ++ return calc_rate(parent_rate, m, n, mode, pre_div); + } + + static const +diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h +index b9ec11d..5f8b06d 100644 +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -102,7 +102,7 @@ extern const struct clk_ops clk_rcg_ops; + * struct clk_dyn_rcg - root clock generator with glitch free mux + * + * @mux_sel_bit: bit to switch glitch free mux +- * @ns_reg: NS register ++ * @ns_reg: NS0 and NS1 register + * @md_reg: MD0 and MD1 register + * @mn: mn counter (banked) + * @s: source selector (banked) +@@ -112,8 +112,9 @@ extern const struct clk_ops clk_rcg_ops; + * + */ + struct clk_dyn_rcg { +- u32 ns_reg; ++ u32 ns_reg[2]; + u32 md_reg[2]; ++ u32 bank_reg; + + u8 mux_sel_bit; + +diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c +index 12f3c0b..ce48ad1 100644 +--- a/drivers/clk/qcom/mmcc-msm8960.c ++++ b/drivers/clk/qcom/mmcc-msm8960.c +@@ -726,9 +726,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = { + }; + + static struct clk_dyn_rcg gfx2d0_src = { +- .ns_reg = 0x0070, ++ .ns_reg[0] = 0x0070, ++ .ns_reg[1] = 0x0070, + .md_reg[0] = 0x0064, + .md_reg[1] = 0x0068, ++ .bank_reg = 0x0060, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -784,9 +786,11 @@ static struct clk_branch gfx2d0_clk = { + }; + + static struct clk_dyn_rcg gfx2d1_src = { +- .ns_reg = 0x007c, ++ .ns_reg[0] = 0x007c, ++ .ns_reg[1] = 0x007c, + .md_reg[0] = 0x0078, + .md_reg[1] = 0x006c, ++ .bank_reg = 0x0074, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -862,9 +866,11 @@ static struct freq_tbl clk_tbl_gfx3d[] = { + }; + + static struct clk_dyn_rcg gfx3d_src = { +- .ns_reg = 0x008c, ++ .ns_reg[0] = 0x008c, ++ .ns_reg[1] = 0x008c, + .md_reg[0] = 0x0084, + .md_reg[1] = 0x0088, ++ .bank_reg = 0x0080, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 25, +@@ -1051,9 +1057,11 @@ static struct freq_tbl clk_tbl_mdp[] = { + }; + + static struct clk_dyn_rcg mdp_src = { +- .ns_reg = 0x00d0, ++ .ns_reg[0] = 0x00d0, ++ .ns_reg[1] = 0x00d0, + .md_reg[0] = 0x00c4, + .md_reg[1] = 0x00c8, ++ .bank_reg = 0x00c0, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 31, +@@ -1158,7 +1166,9 @@ static struct freq_tbl clk_tbl_rot[] = { + }; + + static struct clk_dyn_rcg rot_src = { +- .ns_reg = 0x00e8, ++ .ns_reg[0] = 0x00e8, ++ .ns_reg[1] = 0x00e8, ++ .bank_reg = 0x00e8, + .p[0] = { + .pre_div_shift = 22, + .pre_div_width = 4, +@@ -1355,9 +1365,11 @@ static struct freq_tbl clk_tbl_vcodec[] = { + }; + + static struct clk_dyn_rcg vcodec_src = { +- .ns_reg = 0x0100, ++ .ns_reg[0] = 0x0100, ++ .ns_reg[1] = 0x0100, + .md_reg[0] = 0x00fc, + .md_reg[1] = 0x0128, ++ .bank_reg = 0x00f8, + .mn[0] = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 31, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch new file mode 100644 index 0000000000..4c59580372 --- /dev/null +++ b/target/linux/ipq806x/patches/0129-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch @@ -0,0 +1,869 @@ +From 92fde23153240a6173645dabe4d99f4aa6570bb7 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 28 Apr 2014 16:01:03 -0700 +Subject: [PATCH 129/182] clk: qcom: Add support for NSS/GMAC clocks and + resets + +The NSS driver expects one virtual clock that actually represents +two clocks (one for each UBI32 core). Register the ubi32 core +clocks and also make a wrapper virtual clock called nss_core_clk +to be used by the driver. This will properly handle switching the +rates of both clocks at the same time like how the NSS driver +expects it. Also add the TCM clock and the NSS resets. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 710 +++++++++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 3 + + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++ + 3 files changed, 755 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index 278c5fe..f7916be 100644 +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -32,6 +32,33 @@ + #include "clk-branch.h" + #include "reset.h" + ++static struct clk_pll pll0 = { ++ .l_reg = 0x30c4, ++ .m_reg = 0x30c8, ++ .n_reg = 0x30cc, ++ .config_reg = 0x30d4, ++ .mode_reg = 0x30c0, ++ .status_reg = 0x30d8, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll0", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ ++static struct clk_regmap pll0_vote = { ++ .enable_reg = 0x34c0, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "pll0_vote", ++ .parent_names = (const char *[]){ "pll0" }, ++ .num_parents = 1, ++ .ops = &clk_pll_vote_ops, ++ }, ++}; ++ + static struct clk_pll pll3 = { + .l_reg = 0x3164, + .m_reg = 0x3168, +@@ -102,11 +129,46 @@ static struct clk_regmap pll14_vote = { + }, + }; + ++#define NSS_PLL_RATE(f, _l, _m, _n, i) \ ++ { \ ++ .freq = f, \ ++ .l = _l, \ ++ .m = _m, \ ++ .n = _n, \ ++ .ibits = i, \ ++ } ++ ++static struct pll_freq_tbl pll18_freq_tbl[] = { ++ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625), ++ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625), ++}; ++ ++static struct clk_pll pll18 = { ++ .l_reg = 0x31a4, ++ .m_reg = 0x31a8, ++ .n_reg = 0x31ac, ++ .config_reg = 0x31b4, ++ .mode_reg = 0x31a0, ++ .status_reg = 0x31b8, ++ .status_bit = 16, ++ .post_div_shift = 16, ++ .post_div_width = 1, ++ .freq_tbl = pll18_freq_tbl, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll18", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ + #define P_PXO 0 + #define P_PLL8 1 + #define P_PLL3 1 + #define P_PLL0 2 + #define P_CXO 2 ++#define P_PLL14 3 ++#define P_PLL18 4 + + static const u8 gcc_pxo_pll8_map[] = { + [P_PXO] = 0, +@@ -157,6 +219,22 @@ static const char *gcc_pxo_pll8_pll0_map[] = { + "pll0", + }; + ++static const u8 gcc_pxo_pll8_pll14_pll18_pll0_map[] = { ++ [P_PXO] = 0, ++ [P_PLL8] = 4, ++ [P_PLL0] = 2, ++ [P_PLL14] = 5, ++ [P_PLL18] = 1, ++}; ++ ++static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = { ++ "pxo", ++ "pll8_vote", ++ "pll0_vote", ++ "pll14", ++ "pll18", ++}; ++ + static struct freq_tbl clk_tbl_gsbi_uart[] = { + { 1843200, P_PLL8, 2, 6, 625 }, + { 3686400, P_PLL8, 2, 12, 625 }, +@@ -2132,12 +2210,567 @@ static struct clk_branch usb_fs1_h_clk = { + }, + }; + ++static const struct freq_tbl clk_tbl_gmac[] = { ++ { 133000000, P_PLL0, 1, 50, 301 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg gmac_core1_src = { ++ .ns_reg[0] = 0x3cac, ++ .ns_reg[1] = 0x3cb0, ++ .md_reg[0] = 0x3ca4, ++ .md_reg[1] = 0x3ca8, ++ .bank_reg = 0x3ca0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3ca0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core1_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core1_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 4, ++ .hwcg_reg = 0x3cb4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cb4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core1_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core1_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core2_src = { ++ .ns_reg[0] = 0x3ccc, ++ .ns_reg[1] = 0x3cd0, ++ .md_reg[0] = 0x3cc4, ++ .md_reg[1] = 0x3cc8, ++ .bank_reg = 0x3ca0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3cc0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core2_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core2_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 5, ++ .hwcg_reg = 0x3cd4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cd4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core2_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core2_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core3_src = { ++ .ns_reg[0] = 0x3cec, ++ .ns_reg[1] = 0x3cf0, ++ .md_reg[0] = 0x3ce4, ++ .md_reg[1] = 0x3ce8, ++ .bank_reg = 0x3ce0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3ce0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core3_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core3_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 6, ++ .hwcg_reg = 0x3cf4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cf4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core3_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core3_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core4_src = { ++ .ns_reg[0] = 0x3d0c, ++ .ns_reg[1] = 0x3d10, ++ .md_reg[0] = 0x3d04, ++ .md_reg[1] = 0x3d08, ++ .bank_reg = 0x3d00, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3d00, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core4_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core4_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 7, ++ .hwcg_reg = 0x3d14, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3d14, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core4_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core4_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_nss_tcm[] = { ++ { 266000000, P_PLL0, 3, 0, 0 }, ++ { 400000000, P_PLL0, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg nss_tcm_src = { ++ .ns_reg[0] = 0x3dc4, ++ .ns_reg[1] = 0x3dc8, ++ .bank_reg = 0x3dc0, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss_tcm, ++ .clkr = { ++ .enable_reg = 0x3dc0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_tcm_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch nss_tcm_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x3dd0, ++ .enable_mask = BIT(6) | BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_tcm_clk", ++ .parent_names = (const char *[]){ ++ "nss_tcm_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_nss[] = { ++ { 110000000, P_PLL18, 1, 1, 5 }, ++ { 275000000, P_PLL18, 2, 0, 0 }, ++ { 550000000, P_PLL18, 1, 0, 0 }, ++ { 733000000, P_PLL18, 1, 0, 0 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg ubi32_core1_src_clk = { ++ .ns_reg[0] = 0x3d2c, ++ .ns_reg[1] = 0x3d30, ++ .md_reg[0] = 0x3d24, ++ .md_reg[1] = 0x3d28, ++ .bank_reg = 0x3d20, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss, ++ .clkr = { ++ .enable_reg = 0x3d20, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ubi32_core1_src_clk", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg ubi32_core2_src_clk = { ++ .ns_reg[0] = 0x3d4c, ++ .ns_reg[1] = 0x3d50, ++ .md_reg[0] = 0x3d44, ++ .md_reg[1] = 0x3d48, ++ .bank_reg = 0x3d40, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss, ++ .clkr = { ++ .enable_reg = 0x3d40, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ubi32_core2_src_clk", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ }, ++ }, ++}; ++ ++static int nss_core_clk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate(&ubi32_core1_src_clk.clkr.hw, rate, ++ parent_rate); ++ if (ret) ++ return ret; ++ ++ return clk_dyn_rcg_ops.set_rate(&ubi32_core2_src_clk.clkr.hw, rate, ++ parent_rate); ++} ++ ++static int ++nss_core_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate, u8 index) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate_and_parent( ++ &ubi32_core1_src_clk.clkr.hw, rate, parent_rate, index); ++ if (ret) ++ return ret; ++ ++ ret = clk_dyn_rcg_ops.set_rate_and_parent( ++ &ubi32_core2_src_clk.clkr.hw, rate, parent_rate, index); ++ return ret; ++} ++ ++static long nss_core_clk_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *p_rate, struct clk **p) ++{ ++ return clk_dyn_rcg_ops.determine_rate(&ubi32_core1_src_clk.clkr.hw, ++ rate, p_rate, p); ++} ++ ++static unsigned long ++nss_core_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ return clk_dyn_rcg_ops.recalc_rate(&ubi32_core1_src_clk.clkr.hw, ++ parent_rate); ++} ++ ++static u8 nss_core_clk_get_parent(struct clk_hw *hw) ++{ ++ return clk_dyn_rcg_ops.get_parent(&ubi32_core1_src_clk.clkr.hw); ++} ++ ++static int nss_core_clk_set_parent(struct clk_hw *hw, u8 i) ++{ ++ int ret; ++ ++ ret = clk_dyn_rcg_ops.set_parent(&ubi32_core1_src_clk.clkr.hw, i); ++ if (ret) ++ return ret; ++ ++ return clk_dyn_rcg_ops.set_parent(&ubi32_core2_src_clk.clkr.hw, i); ++} ++ ++static struct clk *nss_core_clk_get_safe_parent(struct clk_hw *hw) ++{ ++ return clk_get_parent_by_index(hw->clk, ++ ubi32_core2_src_clk.s[0].parent_map[P_PLL8]); ++} ++ ++static const struct clk_ops clk_ops_nss_core = { ++ .set_rate = nss_core_clk_set_rate, ++ .set_rate_and_parent = nss_core_clk_set_rate_and_parent, ++ .determine_rate = nss_core_clk_determine_rate, ++ .recalc_rate = nss_core_clk_recalc_rate, ++ .get_parent = nss_core_clk_get_parent, ++ .set_parent = nss_core_clk_set_parent, ++ .get_safe_parent = nss_core_clk_get_safe_parent, ++}; ++ ++/* Virtual clock for nss core clocks */ ++static struct clk_regmap nss_core_clk = { ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_core_clk", ++ .ops = &clk_ops_nss_core, ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++}; ++ + static struct clk_regmap *gcc_ipq806x_clks[] = { ++ [PLL0] = &pll0.clkr, ++ [PLL0_VOTE] = &pll0_vote, + [PLL3] = &pll3.clkr, + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, ++ [PLL18] = &pll18.clkr, + [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, + [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, + [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, +@@ -2232,6 +2865,19 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { + [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, + [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, + [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, ++ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr, ++ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr, ++ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr, ++ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr, ++ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr, ++ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr, ++ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr, ++ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr, ++ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr, ++ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, ++ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, ++ [NSSTCM_CLK] = &nss_tcm_clk.clkr, ++ [NSS_CORE_CLK] = &nss_core_clk, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +@@ -2350,6 +2996,48 @@ static const struct qcom_reset_map gcc_ipq806x_resets[] = { + [USB30_1_PHY_RESET] = { 0x3b58, 0 }, + [NSSFB0_RESET] = { 0x3b60, 6 }, + [NSSFB1_RESET] = { 0x3b60, 7 }, ++ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3}, ++ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 }, ++ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 }, ++ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 }, ++ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 }, ++ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 }, ++ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 }, ++ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 }, ++ [GMAC_CORE1_RESET] = { 0x3cbc, 0 }, ++ [GMAC_CORE2_RESET] = { 0x3cdc, 0 }, ++ [GMAC_CORE3_RESET] = { 0x3cfc, 0 }, ++ [GMAC_CORE4_RESET] = { 0x3d1c, 0 }, ++ [GMAC_AHB_RESET] = { 0x3e24, 0 }, ++ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 }, ++ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 }, ++ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 }, ++ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 }, ++ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 }, ++ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 }, ++ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 }, ++ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 }, ++ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 }, ++ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 }, ++ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 }, ++ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 }, ++ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 }, ++ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 }, ++ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 }, ++ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 }, ++ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 }, ++ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 }, ++ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 }, ++ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 }, ++ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 }, ++ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 }, ++ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 }, ++ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 }, ++ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 }, ++ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 }, ++ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 }, ++ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 }, ++ [NSS_SRDS_N_RESET] = { 0x3b60, 28 }, + }; + + static const struct regmap_config gcc_ipq806x_regmap_config = { +@@ -2378,6 +3066,8 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ struct regmap *regmap; ++ int ret; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); +@@ -2388,7 +3078,25 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- return qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++ if (ret) ++ return ret; ++ ++ regmap = dev_get_regmap(dev, NULL); ++ if (!regmap) ++ return -ENODEV; ++ ++ /* Setup PLL18 static bits */ ++ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400); ++ regmap_write(regmap, 0x31b0, 0x3080); ++ ++ /* Set GMAC footswitch sleep/wakeup values */ ++ regmap_write(regmap, 0x3cb8, 8); ++ regmap_write(regmap, 0x3cd8, 8); ++ regmap_write(regmap, 0x3cf8, 8); ++ regmap_write(regmap, 0x3d18, 8); ++ ++ return 0; + } + + static int gcc_ipq806x_remove(struct platform_device *pdev) +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +index 3b0f8e7..0fd3e8a 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -289,5 +289,8 @@ + #define UBI32_CORE2_CLK_SRC 277 + #define UBI32_CORE1_CLK 278 + #define UBI32_CORE2_CLK 279 ++#define NSSTCM_CLK_SRC 280 ++#define NSSTCM_CLK 281 ++#define NSS_CORE_CLK 282 /* Virtual */ + + #endif +diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +index 0ad5ef9..de9c814 100644 +--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +@@ -129,4 +129,47 @@ + #define USB30_1_PHY_RESET 112 + #define NSSFB0_RESET 113 + #define NSSFB1_RESET 114 ++#define UBI32_CORE1_CLKRST_CLAMP_RESET 115 ++#define UBI32_CORE1_CLAMP_RESET 116 ++#define UBI32_CORE1_AHB_RESET 117 ++#define UBI32_CORE1_AXI_RESET 118 ++#define UBI32_CORE2_CLKRST_CLAMP_RESET 119 ++#define UBI32_CORE2_CLAMP_RESET 120 ++#define UBI32_CORE2_AHB_RESET 121 ++#define UBI32_CORE2_AXI_RESET 122 ++#define GMAC_CORE1_RESET 123 ++#define GMAC_CORE2_RESET 124 ++#define GMAC_CORE3_RESET 125 ++#define GMAC_CORE4_RESET 126 ++#define GMAC_AHB_RESET 127 ++#define NSS_CH0_RST_RX_CLK_N_RESET 128 ++#define NSS_CH0_RST_TX_CLK_N_RESET 129 ++#define NSS_CH0_RST_RX_125M_N_RESET 130 ++#define NSS_CH0_HW_RST_RX_125M_N_RESET 131 ++#define NSS_CH0_RST_TX_125M_N_RESET 132 ++#define NSS_CH1_RST_RX_CLK_N_RESET 133 ++#define NSS_CH1_RST_TX_CLK_N_RESET 134 ++#define NSS_CH1_RST_RX_125M_N_RESET 135 ++#define NSS_CH1_HW_RST_RX_125M_N_RESET 136 ++#define NSS_CH1_RST_TX_125M_N_RESET 137 ++#define NSS_CH2_RST_RX_CLK_N_RESET 138 ++#define NSS_CH2_RST_TX_CLK_N_RESET 139 ++#define NSS_CH2_RST_RX_125M_N_RESET 140 ++#define NSS_CH2_HW_RST_RX_125M_N_RESET 141 ++#define NSS_CH2_RST_TX_125M_N_RESET 142 ++#define NSS_CH3_RST_RX_CLK_N_RESET 143 ++#define NSS_CH3_RST_TX_CLK_N_RESET 144 ++#define NSS_CH3_RST_RX_125M_N_RESET 145 ++#define NSS_CH3_HW_RST_RX_125M_N_RESET 146 ++#define NSS_CH3_RST_TX_125M_N_RESET 147 ++#define NSS_RST_RX_250M_125M_N_RESET 148 ++#define NSS_RST_TX_250M_125M_N_RESET 149 ++#define NSS_QSGMII_TXPI_RST_N_RESET 150 ++#define NSS_QSGMII_CDR_RST_N_RESET 151 ++#define NSS_SGMII2_CDR_RST_N_RESET 152 ++#define NSS_SGMII3_CDR_RST_N_RESET 153 ++#define NSS_CAL_PRBS_RST_N_RESET 154 ++#define NSS_LCKDT_RST_N_RESET 155 ++#define NSS_SRDS_N_RESET 156 ++ + #endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch new file mode 100644 index 0000000000..4c0eed85ca --- /dev/null +++ b/target/linux/ipq806x/patches/0130-ARM-qcom-Add-initial-IPQ8064-SoC-and-AP148-device-tr.patch @@ -0,0 +1,269 @@ +From 1c6e51ffb10f5bf93a3018c7c1e04d7ed93f944e Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 7 Mar 2014 10:56:59 -0600 +Subject: [PATCH 130/182] ARM: qcom: Add initial IPQ8064 SoC and AP148 device + trees + +Add basic IPQ8064 SoC include device tree and support for basic booting on +the AP148 Reference board. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 25 +++++ + arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi | 1 + + arch/arm/boot/dts/qcom-ipq8064.dtsi | 176 ++++++++++++++++++++++++++++++ + arch/arm/mach-qcom/board.c | 2 + + 5 files changed, 205 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-ap148.dts + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi + create mode 100644 arch/arm/boot/dts/qcom-ipq8064.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index f2aeb95..f22c51d 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -235,6 +235,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8064-ifc6410.dtb \ + qcom-apq8074-dragonboard.dtb \ + qcom-apq8084-mtp.dtb \ ++ qcom-ipq8064-ap148.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb + dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \ +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +new file mode 100644 +index 0000000..100b6eb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -0,0 +1,25 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++/ { ++ model = "Qualcomm IPQ8064/AP148"; ++ compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ rsvd@41200000 { ++ reg = <0x41200000 0x300000>; ++ no-map; ++ }; ++ }; ++ ++ soc { ++ gsbi@16300000 { ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ status = "ok"; ++ serial@16340000 { ++ status = "ok"; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +new file mode 100644 +index 0000000..7093b07 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +@@ -0,0 +1 @@ ++#include "qcom-ipq8064.dtsi" +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +new file mode 100644 +index 0000000..952afb7 +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -0,0 +1,176 @@ ++/dts-v1/; ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/soc/qcom,gsbi.h> ++ ++/ { ++ model = "Qualcomm IPQ8064"; ++ compatible = "qcom,ipq8064"; ++ interrupt-parent = <&intc>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ }; ++ ++ cpu@1 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc1>; ++ qcom,saw = <&saw1>; ++ }; ++ ++ L2: l2-cache { ++ compatible = "cache"; ++ cache-level = <2>; ++ }; ++ }; ++ ++ cpu-pmu { ++ compatible = "qcom,krait-pmu"; ++ interrupts = <1 10 0x304>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ nss@40000000 { ++ reg = <0x40000000 0x1000000>; ++ no-map; ++ }; ++ ++ smem@41000000 { ++ reg = <0x41000000 0x200000>; ++ no-map; ++ }; ++ }; ++ ++ soc: soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ compatible = "simple-bus"; ++ ++ qcom_pinmux: pinmux@800000 { ++ compatible = "qcom,ipq8064-pinctrl"; ++ reg = <0x800000 0x4000>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <0 32 0x4>; ++ }; ++ ++ intc: interrupt-controller@2000000 { ++ compatible = "qcom,msm-qgic2"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ reg = <0x02000000 0x1000>, ++ <0x02002000 0x1000>; ++ }; ++ ++ timer@200a000 { ++ compatible = "qcom,kpss-timer", "qcom,msm-timer"; ++ interrupts = <1 1 0x301>, ++ <1 2 0x301>, ++ <1 3 0x301>; ++ reg = <0x0200a000 0x100>; ++ clock-frequency = <25000000>, ++ <32768>; ++ cpu-offset = <0x80000>; ++ }; ++ ++ acc0: clock-controller@2088000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ acc1: clock-controller@2098000 { ++ compatible = "qcom,kpss-acc-v1"; ++ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ }; ++ ++ saw0: regulator@2089000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ saw1: regulator@2099000 { ++ compatible = "qcom,saw2"; ++ reg = <0x02099000 0x1000>, <0x02009000 0x1000>; ++ regulator; ++ }; ++ ++ gsbi2: gsbi@12480000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x12480000 0x100>; ++ clocks = <&gcc GSBI2_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@12490000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x12490000 0x1000>, ++ <0x12480000 0x1000>; ++ interrupts = <0 195 0x0>; ++ clocks = <&gcc GSBI2_UART_CLK>, <&gcc GSBI2_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ gsbi4: gsbi@16300000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x16300000 0x100>; ++ clocks = <&gcc GSBI4_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@16340000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x16340000 0x1000>, ++ <0x16300000 0x1000>; ++ interrupts = <0 152 0x0>; ++ clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ }; ++ ++ qcom,ssbi@500000 { ++ compatible = "qcom,ssbi"; ++ reg = <0x00500000 0x1000>; ++ qcom,controller-type = "pmic-arbiter"; ++ }; ++ ++ gcc: clock-controller@900000 { ++ compatible = "qcom,gcc-ipq8064"; ++ reg = <0x00900000 0x4000>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c +index c437a99..6d8bbf7 100644 +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -18,6 +18,8 @@ static const char * const qcom_dt_match[] __initconst = { + "qcom,apq8064", + "qcom,apq8074-dragonboard", + "qcom,apq8084", ++ "qcom,ipq8062", ++ "qcom,ipq8064", + "qcom,msm8660-surf", + "qcom,msm8960-cdp", + NULL +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch new file mode 100644 index 0000000000..8b20c4b68d --- /dev/null +++ b/target/linux/ipq806x/patches/0131-ARM-qcom-config-Enable-IPQ806x-support.patch @@ -0,0 +1,25 @@ +From df1564829c4abe206d354f7c1b745cc1e864f4bd Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 11 Apr 2014 14:18:29 -0500 +Subject: [PATCH 131/182] ARM: qcom: config: Enable IPQ806x support + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 42ebd72..1752a4d 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -133,6 +133,7 @@ CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y + CONFIG_QCOM_GSBI=y + CONFIG_COMMON_CLK_QCOM=y ++CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch new file mode 100644 index 0000000000..c94c75abe2 --- /dev/null +++ b/target/linux/ipq806x/patches/0132-XXX-Add-boot-support-for-u-boot.ipq-image.patch @@ -0,0 +1,55 @@ +From 776fff11c1bf57e564a386521e56c277287a568c Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 8 May 2014 13:40:16 -0500 +Subject: [PATCH 132/182] XXX: Add boot support for u-boot.ipq image + +--- + arch/arm/Makefile | 2 +- + arch/arm/boot/Makefile | 11 ++++++++++- + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 51e5bed..e9fc731 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -297,7 +297,7 @@ archprepare: + # Convert bzImage to zImage + bzImage: zImage + +-BOOT_TARGETS = zImage Image xipImage bootpImage uImage ++BOOT_TARGETS = zImage Image xipImage bootpImage uImage uImage.ipq Image.gz + INSTALL_TARGETS = zinstall uinstall install + + PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS) +diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile +index ec2f806..3cbc83e 100644 +--- a/arch/arm/boot/Makefile ++++ b/arch/arm/boot/Makefile +@@ -25,7 +25,7 @@ INITRD_PHYS := $(initrd_phys-y) + + export ZRELADDR INITRD_PHYS PARAMS_PHYS + +-targets := Image zImage xipImage bootpImage uImage ++targets := Image zImage xipImage bootpImage uImage uImage.ipq Image.gz + + ifeq ($(CONFIG_XIP_KERNEL),y) + +@@ -80,6 +80,15 @@ $(obj)/uImage: $(obj)/zImage FORCE + $(call if_changed,uimage) + @$(kecho) ' Image $@ is ready' + ++$(obj)/uImage.ipq: $(obj)/Image.gz FORCE ++ @$(check_for_multiple_loadaddr) ++ $(call if_changed,uimage,gzip) ++ @$(kecho) ' Image $@ is ready' ++ ++$(obj)/Image.gz: $(obj)/Image FORCE ++ $(call if_changed,gzip) ++ @$(kecho) ' Image $@ is ready' ++ + $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE + $(Q)$(MAKE) $(build)=$(obj)/bootp $@ + @: +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch new file mode 100644 index 0000000000..3881064b6b --- /dev/null +++ b/target/linux/ipq806x/patches/0133-spi-qup-Remove-chip-select-function.patch @@ -0,0 +1,90 @@ +From 9bc674f40f22596ef8c2ff6d7f9e53da0baa57e9 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:10 -0500 +Subject: [PATCH 133/182] spi: qup: Remove chip select function + +This patch removes the chip select function. Chip select should instead be +supported using GPIOs, defining the DT entry "cs-gpios", and letting the SPI +core assert/deassert the chip select as it sees fit. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 ++++ + drivers/spi/spi-qup.c | 33 ++++---------------- + 2 files changed, 12 insertions(+), 27 deletions(-) + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +index b82a268..bee6ff2 100644 +--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -23,6 +23,12 @@ Optional properties: + - spi-max-frequency: Specifies maximum SPI clock frequency, + Units - Hz. Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt ++- num-cs: total number of chipselects ++- cs-gpios: should specify GPIOs used for chipselects. ++ The gpios will be referred to as reg = <index> in the SPI child ++ nodes. If unspecified, a single SPI device without a chip ++ select can be used. ++ + + SPI slave nodes must be children of the SPI master node and can contain + properties described in Documentation/devicetree/bindings/spi/spi-bus.txt +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 65bf18e..dc128ac 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -424,31 +424,6 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + return 0; + } + +-static void spi_qup_set_cs(struct spi_device *spi, bool enable) +-{ +- struct spi_qup *controller = spi_master_get_devdata(spi->master); +- +- u32 iocontol, mask; +- +- iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); +- +- /* Disable auto CS toggle and use manual */ +- iocontol &= ~SPI_IO_C_MX_CS_MODE; +- iocontol |= SPI_IO_C_FORCE_CS; +- +- iocontol &= ~SPI_IO_C_CS_SELECT_MASK; +- iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); +- +- mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; +- +- if (enable) +- iocontol |= mask; +- else +- iocontol &= ~mask; +- +- writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); +-} +- + static int spi_qup_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +@@ -571,12 +546,16 @@ static int spi_qup_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ /* use num-cs unless not present or out of range */ ++ if (of_property_read_u16(dev->of_node, "num-cs", ++ &master->num_chipselect) || ++ (master->num_chipselect > SPI_NUM_CHIPSELECTS)) ++ master->num_chipselect = SPI_NUM_CHIPSELECTS; ++ + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; +- master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; +- master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch new file mode 100644 index 0000000000..c8738ccac8 --- /dev/null +++ b/target/linux/ipq806x/patches/0134-spi-qup-Fix-order-of-spi_register_master.patch @@ -0,0 +1,46 @@ +From a7357be131e30fd1fb987b2cb343bc81db487f96 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:11 -0500 +Subject: [PATCH 134/182] spi: qup: Fix order of spi_register_master + +This patch moves the devm_spi_register_master below the initialization of the +runtime_pm. If done in the wrong order, the spi_register_master fails if any +probed slave devices issue SPI transactions. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +Acked-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/spi/spi-qup.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index dc128ac..a404298 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -619,16 +619,19 @@ static int spi_qup_probe(struct platform_device *pdev) + if (ret) + goto error; + +- ret = devm_spi_register_master(dev, master); +- if (ret) +- goto error; +- + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); ++ ++ ret = devm_spi_register_master(dev, master); ++ if (ret) ++ goto disable_pm; ++ + return 0; + ++disable_pm: ++ pm_runtime_disable(&pdev->dev); + error: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch new file mode 100644 index 0000000000..c1ccc21197 --- /dev/null +++ b/target/linux/ipq806x/patches/0135-spi-qup-Add-support-for-v1.1.1.patch @@ -0,0 +1,132 @@ +From 8b9de04ef3aaa154f30baf1ac703a2d3b474ad4e Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 14:34:12 -0500 +Subject: [PATCH 135/182] spi: qup: Add support for v1.1.1 + +This patch adds support for v1.1.1 of the SPI QUP controller. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/spi/qcom,spi-qup.txt | 6 +++- + drivers/spi/spi-qup.c | 36 ++++++++++++-------- + 2 files changed, 27 insertions(+), 15 deletions(-) + +diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +index bee6ff2..e2c88df 100644 +--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -7,7 +7,11 @@ SPI in master mode supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol variants. + + Required properties: +-- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1" ++- compatible: Should contain: ++ "qcom,spi-qup-v1.1.1" for 8660, 8960 and 8064. ++ "qcom,spi-qup-v2.1.1" for 8974 and later ++ "qcom,spi-qup-v2.2.1" for 8974 v2 and later. ++ + - reg: Should contain base register location and length + - interrupts: Interrupt number used by this controller + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index a404298..c137226 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -142,6 +142,7 @@ struct spi_qup { + int w_size; /* bytes per SPI word */ + int tx_bytes; + int rx_bytes; ++ int qup_v1; + }; + + +@@ -420,7 +421,9 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + config |= QUP_CONFIG_SPI_MODE; + writel_relaxed(config, controller->base + QUP_CONFIG); + +- writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ /* only write to OPERATIONAL_MASK when register is present */ ++ if (!controller->qup_v1) ++ writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); + return 0; + } + +@@ -486,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev) + struct resource *res; + struct device *dev; + void __iomem *base; +- u32 data, max_freq, iomode; ++ u32 max_freq, iomode; + int ret, irq, size; + + dev = &pdev->dev; +@@ -529,15 +532,6 @@ static int spi_qup_probe(struct platform_device *pdev) + return ret; + } + +- data = readl_relaxed(base + QUP_HW_VERSION); +- +- if (data < QUP_HW_VERSION_2_1_1) { +- clk_disable_unprepare(cclk); +- clk_disable_unprepare(iclk); +- dev_err(dev, "v.%08x is not supported\n", data); +- return -ENXIO; +- } +- + master = spi_alloc_master(dev, sizeof(struct spi_qup)); + if (!master) { + clk_disable_unprepare(cclk); +@@ -570,6 +564,10 @@ static int spi_qup_probe(struct platform_device *pdev) + controller->cclk = cclk; + controller->irq = irq; + ++ /* set v1 flag if device is version 1 */ ++ if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) ++ controller->qup_v1 = 1; ++ + spin_lock_init(&controller->lock); + init_completion(&controller->done); + +@@ -593,8 +591,8 @@ static int spi_qup_probe(struct platform_device *pdev) + size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); + controller->in_fifo_sz = controller->in_blk_sz * (2 << size); + +- dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", +- data, controller->in_blk_sz, controller->in_fifo_sz, ++ dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", ++ controller->in_blk_sz, controller->in_fifo_sz, + controller->out_blk_sz, controller->out_fifo_sz); + + writel_relaxed(1, base + QUP_SW_RESET); +@@ -607,10 +605,19 @@ static int spi_qup_probe(struct platform_device *pdev) + + writel_relaxed(0, base + QUP_OPERATIONAL); + writel_relaxed(0, base + QUP_IO_M_MODES); +- writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ ++ if (!controller->qup_v1) ++ writel_relaxed(0, base + QUP_OPERATIONAL_MASK); ++ + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_ERROR_FLAGS_EN); + ++ /* if earlier version of the QUP, disable INPUT_OVERRUN */ ++ if (controller->qup_v1) ++ writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN | ++ QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, ++ base + QUP_ERROR_FLAGS_EN); ++ + writel_relaxed(0, base + SPI_CONFIG); + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); + +@@ -732,6 +739,7 @@ static int spi_qup_remove(struct platform_device *pdev) + } + + static struct of_device_id spi_qup_dt_match[] = { ++ { .compatible = "qcom,spi-qup-v1.1.1", }, + { .compatible = "qcom,spi-qup-v2.1.1", }, + { .compatible = "qcom,spi-qup-v2.2.1", }, + { } +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch new file mode 100644 index 0000000000..7c4757da99 --- /dev/null +++ b/target/linux/ipq806x/patches/0136-ARM-ipq8064-ap148-Add-i2c-pinctrl-nodes.patch @@ -0,0 +1,93 @@ +From e93b9480667cbd0e3a4276e8749279693fe239f4 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 22:49:03 -0500 +Subject: [PATCH 136/182] ARM: ipq8064-ap148: Add i2c pinctrl nodes + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 17 +++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 27 +++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 100b6eb..dbb546d 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -14,12 +14,29 @@ + }; + + soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ bias-disable; ++ }; ++ }; ++ + gsbi@16300000 { + qcom,mode = <GSBI_PROT_I2C_UART>; + status = "ok"; + serial@16340000 { + status = "ok"; + }; ++ ++ i2c4: i2c@16380000 { ++ status = "ok"; ++ ++ clock-frequency = <200000>; ++ ++ pinctrl-0 = <&i2c4_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 952afb7..b39c1ef 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -137,6 +137,20 @@ + clock-names = "core", "iface"; + status = "disabled"; + }; ++ ++ i2c@124a0000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x124a0000 0x1000>; ++ interrupts = <0 196 0>; ++ ++ clocks = <&gcc GSBI2_QUP_CLK>, <&gcc GSBI2_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + }; + + gsbi4: gsbi@16300000 { +@@ -158,6 +172,19 @@ + clock-names = "core", "iface"; + status = "disabled"; + }; ++ ++ i2c@16380000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x16380000 0x1000>; ++ interrupts = <0 153 0>; ++ ++ clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; + }; + + qcom,ssbi@500000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch new file mode 100644 index 0000000000..4a0385db27 --- /dev/null +++ b/target/linux/ipq806x/patches/0137-ARM-qcom-ipq8064-ap148-Add-SPI-related-bindings.patch @@ -0,0 +1,131 @@ +From b9eaa80146abb09bcc7e6d8b33fca476453c839c Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 22:01:16 -0500 +Subject: [PATCH 137/182] ARM: qcom-ipq8064-ap148: Add SPI related bindings + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 42 ++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 47 ++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index dbb546d..158a09f 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -20,6 +20,15 @@ + function = "gsbi4"; + bias-disable; + }; ++ ++ spi_pins: spi_pins { ++ mux { ++ pins = "gpio18", "gpio19", "gpio21"; ++ function = "gsbi5"; ++ drive-strength = <10>; ++ bias-none; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -38,5 +47,38 @@ + pinctrl-names = "default"; + }; + }; ++ ++ gsbi5: gsbi@1a200000 { ++ qcom,mode = <GSBI_PROT_SPI>; ++ status = "ok"; ++ ++ spi4: spi@1a280000 { ++ status = "ok"; ++ spi-max-frequency = <50000000>; ++ ++ pinctrl-0 = <&spi_pins>; ++ pinctrl-names = "default"; ++ ++ cs-gpios = <&qcom_pinmux 20 0>; ++ ++ flash: m25p80@0 { ++ compatible = "s25fl256s1"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <50000000>; ++ reg = <0>; ++ ++ partition@0 { ++ label = "rootfs"; ++ reg = <0x0 0x1000000>; ++ }; ++ ++ partition@1 { ++ label = "scratch"; ++ reg = <0x1000000 0x1000000>; ++ }; ++ }; ++ }; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index b39c1ef..244f857 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -187,6 +187,53 @@ + }; + }; + ++ gsbi5: gsbi@1a200000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ reg = <0x1a200000 0x100>; ++ clocks = <&gcc GSBI5_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ serial@1a240000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x1a240000 0x1000>, ++ <0x1a200000 0x1000>; ++ interrupts = <0 154 0x0>; ++ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ ++ i2c@1a280000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x1a280000 0x1000>; ++ interrupts = <0 155 0>; ++ ++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi@1a280000 { ++ compatible = "qcom,spi-qup-v1.1.1"; ++ reg = <0x1a280000 0x1000>; ++ interrupts = <0 155 0>; ++ ++ clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ + qcom,ssbi@500000 { + compatible = "qcom,ssbi"; + reg = <0x00500000 0x1000>; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch new file mode 100644 index 0000000000..b1ff04b90d --- /dev/null +++ b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch @@ -0,0 +1,726 @@ +From 98567c99b4dcd80fc9e5dd97229ebb9a7f6dab03 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 16 May 2014 11:53:23 -0500 +Subject: [PATCH 138/182] PCI: qcom: Add support for pcie controllers on + IPQ8064 + +--- + arch/arm/mach-qcom/Kconfig | 2 + + drivers/pci/host/Makefile | 1 + + drivers/pci/host/pci-qcom.c | 682 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 685 insertions(+) + create mode 100644 drivers/pci/host/pci-qcom.c + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index 63502cc..4d242e5 100644 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -7,6 +7,8 @@ config ARCH_QCOM + select GENERIC_CLOCKEVENTS + select HAVE_SMP + select PINCTRL ++ select MIGHT_HAVE_PCI ++ select PCI_DOMAINS if PCI + select QCOM_SCM if SMP + help + Support for Qualcomm's devicetree based systems. +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 13fb333..be80744 100644 +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o + obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o + obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o + obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o ++obj-$(CONFIG_ARCH_QCOM) += pci-qcom.o +diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c +new file mode 100644 +index 0000000..76d7b88 +--- /dev/null ++++ b/drivers/pci/host/pci-qcom.c +@@ -0,0 +1,682 @@ ++/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* ++ * QCOM MSM PCIe controller driver. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/pci.h> ++#include <linux/gpio.h> ++#include <linux/of_gpio.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <linux/delay.h> ++ ++/* Root Complex Port vendor/device IDs */ ++#define PCIE_VENDOR_ID_RCP 0x17cb ++#define PCIE_DEVICE_ID_RCP 0x0101 ++ ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++ ++#define PCIE20_PARF_PCS_DEEMPH 0x34 ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16) ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8) ++#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0) ++ ++#define PCIE20_PARF_PCS_SWING 0x38 ++#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8) ++#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0) ++ ++#define PCIE20_PARF_PHY_CTRL 0x40 ++#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16) ++#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8) ++#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1) ++#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0) ++ ++#define PCIE20_PARF_PHY_REFCLK 0x4C ++#define PCIE20_PARF_CONFIG_BITS 0x50 ++ ++#define PCIE20_ELBI_SYS_CTRL 0x04 ++#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01 ++ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PCIE20_COMMAND_STATUS 0x04 ++#define PCIE20_BUSNUMBERS 0x18 ++#define PCIE20_MEMORY_BASE_LIMIT 0x20 ++ ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c ++#define PCIE20_PLR_IATU_VIEWPORT 0x900 ++#define PCIE20_PLR_IATU_CTRL1 0x904 ++#define PCIE20_PLR_IATU_CTRL2 0x908 ++#define PCIE20_PLR_IATU_LBAR 0x90C ++#define PCIE20_PLR_IATU_UBAR 0x910 ++#define PCIE20_PLR_IATU_LAR 0x914 ++#define PCIE20_PLR_IATU_LTAR 0x918 ++#define PCIE20_PLR_IATU_UTAR 0x91c ++ ++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 ++ ++#define RD 0 ++#define WR 1 ++ ++#define MAX_RC_NUM 3 ++#define PCIE_BUS_PRIV_DATA(pdev) \ ++ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data) ++ ++/* PCIe TLP types that we are interested in */ ++#define PCI_CFG0_RDWR 0x4 ++#define PCI_CFG1_RDWR 0x5 ++ ++#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++({ \ ++ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \ ++ might_sleep_if(timeout_us); \ ++ for (;;) { \ ++ (val) = readl(addr); \ ++ if (cond) \ ++ break; \ ++ if (timeout_us && time_after(jiffies, timeout)) { \ ++ (val) = readl(addr); \ ++ break; \ ++ } \ ++ if (sleep_us) \ ++ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++struct qcom_pcie { ++ void __iomem *elbi_base; ++ void __iomem *parf_base; ++ void __iomem *dwc_base; ++ void __iomem *cfg_base; ++ int reset_gpio; ++ struct clk *iface_clk; ++ struct clk *bus_clk; ++ struct clk *phy_clk; ++ int irq_int[4]; ++ struct reset_control *axi_reset; ++ struct reset_control *ahb_reset; ++ struct reset_control *por_reset; ++ struct reset_control *pci_reset; ++ struct reset_control *phy_reset; ++ ++ struct resource conf; ++ struct resource io; ++ struct resource mem; ++}; ++ ++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); ++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys); ++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, ++ int size, u32 *val); ++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 val); ++ ++static struct pci_ops qcom_pcie_ops = { ++ .read = msm_pcie_rd_conf, ++ .write = msm_pcie_wr_conf, ++}; ++ ++static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = { ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 0, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 1, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++ { ++#ifdef CONFIG_PCI_DOMAINS ++ .domain = 2, ++#endif ++ .ops = &qcom_pcie_ops, ++ .nr_controllers = 1, ++ .swizzle = pci_common_swizzle, ++ .setup = qcom_pcie_setup, ++ .map_irq = qcom_pcie_map_irq, ++ }, ++}; ++ ++static int nr_controllers; ++static DEFINE_SPINLOCK(qcom_hw_pci_lock); ++ ++static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys) ++{ ++ return sys->private_data; ++} ++ ++inline int is_msm_pcie_rc(struct pci_bus *bus) ++{ ++ return (bus->number == 0); ++} ++ ++static int qcom_pcie_is_link_up(struct qcom_pcie *dev) ++{ ++ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) & BIT(29); ++} ++ ++inline int msm_pcie_get_cfgtype(struct pci_bus *bus) ++{ ++ /* ++ * http://www.tldp.org/LDP/tlk/dd/pci.html ++ * Pass it onto the secondary bus interface unchanged if the ++ * bus number specified is greater than the secondary bus ++ * number and less than or equal to the subordinate bus ++ * number. ++ * ++ * Read/Write to the RC and Device/Switch connected to the RC ++ * are CFG0 type transactions. Rest have to be forwarded ++ * down stream as CFG1 transactions. ++ * ++ */ ++ if (bus->number == 0) ++ return PCI_CFG0_RDWR; ++ ++ return PCI_CFG0_RDWR; ++} ++ ++void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn) ++{ ++ uint32_t bdf, cfgtype; ++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); ++ ++ cfgtype = msm_pcie_get_cfgtype(bus); ++ ++ if (cfgtype == PCI_CFG0_RDWR) { ++ bdf = MSM_PCIE_DEV_CFG_ADDR; ++ } else { ++ /* ++ * iATU Lower Target Address Register ++ * Bits Description ++ * *-1:0 Forms bits [*:0] of the ++ * start address of the new ++ * address of the translated ++ * region. The start address ++ * must be aligned to a ++ * CX_ATU_MIN_REGION_SIZE kB ++ * boundary, so these bits are ++ * always 0. A write to this ++ * location is ignored by the ++ * PCIe core. ++ * 31:*1 Forms bits [31:*] of the of ++ * the new address of the ++ * translated region. ++ * ++ * * is log2(CX_ATU_MIN_REGION_SIZE) ++ */ ++ bdf = (((bus->number & 0xff) << 24) & 0xff000000) | ++ (((devfn & 0xff) << 16) & 0x00ff0000); ++ } ++ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ wmb(); ++ ++ /* Program Bdf Address */ ++ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ wmb(); ++ ++ /* Write Config Request Type */ ++ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ wmb(); ++} ++ ++static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper, ++ int where, int size, u32 *val) ++{ ++ uint32_t word_offset, byte_offset, mask; ++ uint32_t rd_val, wr_val; ++ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); ++ void __iomem *config_base; ++ int rc; ++ ++ rc = is_msm_pcie_rc(bus); ++ ++ /* ++ * For downstream bus, make sure link is up ++ */ ++ if (rc && (devfn != 0)) { ++ *val = ~0; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) { ++ *val = ~0; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } ++ ++ msm_pcie_config_cfgtype(bus, devfn); ++ ++ word_offset = where & ~0x3; ++ byte_offset = where & 0x3; ++ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset); ++ ++ config_base = (rc) ? dev->dwc_base : dev->cfg_base; ++ rd_val = readl_relaxed(config_base + word_offset); ++ ++ if (oper == RD) { ++ *val = ((rd_val & mask) >> (8 * byte_offset)); ++ } else { ++ wr_val = (rd_val & ~mask) | ++ ((*val << (8 * byte_offset)) & mask); ++ writel_relaxed(wr_val, config_base + word_offset); ++ wmb(); /* ensure config data is written to hardware register */ ++ } ++ ++ return 0; ++} ++ ++static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, ++ int size, u32 *val) ++{ ++ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val); ++} ++ ++static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 val) ++{ ++ /* ++ *Attempt to reset secondary bus is causing PCIE core to reset. ++ *Disable secondary bus reset functionality. ++ */ ++ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) && ++ (val & PCI_BRIDGE_CTL_BUS_RESET)) { ++ pr_info("PCIE secondary bus reset not supported\n"); ++ val &= ~PCI_BRIDGE_CTL_BUS_RESET; ++ } ++ ++ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val); ++} ++ ++static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev); ++ ++ return pcie_dev->irq_int[pin-1]; ++} ++ ++static int qcom_pcie_setup(int nr, struct pci_sys_data *sys) ++{ ++ struct qcom_pcie *qcom_pcie = sys->private_data; ++ ++ /* ++ * specify linux PCI framework to allocate device memory (BARs) ++ * from msm_pcie_dev.dev_mem_res resource. ++ */ ++ sys->mem_offset = 0; ++ sys->io_offset = 0; ++ ++ pci_add_resource(&sys->resources, &qcom_pcie->mem); ++ pci_add_resource(&sys->resources, &qcom_pcie->io); ++ ++ return 1; ++} ++ ++static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) ++{ ++ writel_relaxed(val, pcie->elbi_base + reg); ++} ++ ++static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg) ++{ ++ return readl_relaxed(pcie->elbi_base + reg); ++} ++ ++static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) ++{ ++ writel_relaxed(val, pcie->parf_base + reg); ++} ++ ++static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg) ++{ ++ return readl_relaxed(pcie->parf_base + reg); ++} ++ ++static void msm_pcie_write_mask(void __iomem *addr, ++ uint32_t clear_mask, uint32_t set_mask) ++{ ++ uint32_t val; ++ ++ val = (readl_relaxed(addr) & ~clear_mask) | set_mask; ++ writel_relaxed(val, addr); ++ wmb(); /* ensure data is written to hardware register */ ++} ++ ++static void qcom_pcie_config_controller(struct qcom_pcie *dev) ++{ ++ /* ++ * program and enable address translation region 0 (device config ++ * address space); region type config; ++ * axi config address range to device config address range ++ */ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ /* ensure that hardware locks the region before programming it */ ++ wmb(); ++ ++ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); ++ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); ++ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); ++ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR, ++ dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++ ++ /* ++ * program and enable address translation region 2 (device resource ++ * address space); region type memory; ++ * axi device bar address range to device bar address range ++ */ ++ writel_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); ++ /* ensure that hardware locks the region before programming it */ ++ wmb(); ++ ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); ++ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); ++ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); ++ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); ++ writel_relaxed(dev->mem.start, ++ dev->dwc_base + PCIE20_PLR_IATU_LTAR); ++ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++ ++ /* 1K PCIE buffer setting */ ++ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); ++ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); ++ /* ensure that hardware registers the configuration */ ++ wmb(); ++} ++ ++static int qcom_pcie_probe(struct platform_device *pdev) ++{ ++ unsigned long flags; ++ struct qcom_pcie *qcom_pcie; ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *elbi_base, *parf_base, *dwc_base; ++ struct hw_pci *hw; ++ struct of_pci_range range; ++ struct of_pci_range_parser parser; ++ int ret, i; ++ u32 val; ++ ++ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL); ++ if (!qcom_pcie) { ++ dev_err(&pdev->dev, "no memory for qcom_pcie\n"); ++ return -ENOMEM; ++ } ++ ++ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); ++ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); ++ if (IS_ERR(qcom_pcie->elbi_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap elbi space\n"); ++ return PTR_ERR(qcom_pcie->elbi_base); ++ } ++ ++ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); ++ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base); ++ if (IS_ERR(qcom_pcie->parf_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap parf space\n"); ++ return PTR_ERR(qcom_pcie->parf_base); ++ } ++ ++ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); ++ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base); ++ if (IS_ERR(qcom_pcie->dwc_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n"); ++ return PTR_ERR(qcom_pcie->dwc_base); ++ } ++ ++ if (of_pci_range_parser_init(&parser, np)) { ++ dev_err(&pdev->dev, "missing ranges property\n"); ++ return -EINVAL; ++ } ++ ++ /* Get the I/O and memory ranges from DT */ ++ for_each_of_pci_range(&parser, &range) { ++ switch (range.pci_space & 0x3) { ++ case 0: /* cfg */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->conf); ++ qcom_pcie->conf.flags = IORESOURCE_MEM; ++ break; ++ case 1: /* io */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->io); ++ break; ++ default: /* mem */ ++ of_pci_range_to_resource(&range, np, &qcom_pcie->mem); ++ break; ++ } ++ } ++ ++ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev, &qcom_pcie->conf); ++ if (IS_ERR(qcom_pcie->cfg_base)) { ++ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n"); ++ return PTR_ERR(qcom_pcie->cfg_base); ++ } ++ ++ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); ++ if (!gpio_is_valid(qcom_pcie->reset_gpio)) { ++ dev_err(&pdev->dev, "pcie reset gpio is not valid\n"); ++ return -EINVAL; ++ } ++ ++ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio, ++ GPIOF_DIR_OUT, "pcie_reset"); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n"); ++ return ret; ++ } ++ ++ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface"); ++ if (IS_ERR(qcom_pcie->iface_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie iface clock\n"); ++ return PTR_ERR(qcom_pcie->iface_clk); ++ } ++ ++ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy"); ++ if (IS_ERR(qcom_pcie->phy_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie phy clock\n"); ++ return PTR_ERR(qcom_pcie->phy_clk); ++ } ++ ++ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(qcom_pcie->bus_clk)) { ++ dev_err(&pdev->dev, "Failed to get pcie core clock\n"); ++ return PTR_ERR(qcom_pcie->bus_clk); ++ } ++ ++ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi"); ++ if (IS_ERR(qcom_pcie->axi_reset)) { ++ dev_err(&pdev->dev, "Failed to get axi reset\n"); ++ return PTR_ERR(qcom_pcie->axi_reset); ++ } ++ ++ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb"); ++ if (IS_ERR(qcom_pcie->ahb_reset)) { ++ dev_err(&pdev->dev, "Failed to get ahb reset\n"); ++ return PTR_ERR(qcom_pcie->ahb_reset); ++ } ++ ++ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por"); ++ if (IS_ERR(qcom_pcie->por_reset)) { ++ dev_err(&pdev->dev, "Failed to get por reset\n"); ++ return PTR_ERR(qcom_pcie->por_reset); ++ } ++ ++ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci"); ++ if (IS_ERR(qcom_pcie->pci_reset)) { ++ dev_err(&pdev->dev, "Failed to get pci reset\n"); ++ return PTR_ERR(qcom_pcie->pci_reset); ++ } ++ ++ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy"); ++ if (IS_ERR(qcom_pcie->phy_reset)) { ++ dev_err(&pdev->dev, "Failed to get phy reset\n"); ++ return PTR_ERR(qcom_pcie->phy_reset); ++ } ++ ++ for (i = 0; i < 4; i++) { ++ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1); ++ if (qcom_pcie->irq_int[i] < 0) { ++ dev_err(&pdev->dev, "failed to get irq resource\n"); ++ return qcom_pcie->irq_int[i]; ++ } ++ } ++ ++ gpio_set_value(qcom_pcie->reset_gpio, 0); ++ usleep_range(10000, 15000); ++ ++ /* assert PCIe PARF reset while powering the core */ ++ reset_control_assert(qcom_pcie->ahb_reset); ++ ++ /* enable clocks */ ++ ret = clk_prepare_enable(qcom_pcie->iface_clk); ++ if (ret) ++ return ret; ++ ret = clk_prepare_enable(qcom_pcie->phy_clk); ++ if (ret) ++ return ret; ++ ret = clk_prepare_enable(qcom_pcie->bus_clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * de-assert PCIe PARF reset; ++ * wait 1us before accessing PARF registers ++ */ ++ reset_control_deassert(qcom_pcie->ahb_reset); ++ udelay(1); ++ ++ /* enable PCIe clocks and resets */ ++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* Set Tx Termination Offset */ ++ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL); ++ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7); ++ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL); ++ ++ /* PARF programming */ ++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | ++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | ++ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), ++ PCIE20_PARF_PCS_DEEMPH); ++ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) | ++ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78), ++ PCIE20_PARF_PCS_SWING); ++ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS); ++ /* ensure that hardware registers the PARF configuration */ ++ wmb(); ++ ++ /* enable reference clock */ ++ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK, BIT(12), BIT(16)); ++ ++ /* ensure that access is enabled before proceeding */ ++ wmb(); ++ ++ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */ ++ reset_control_deassert(qcom_pcie->phy_reset); ++ reset_control_deassert(qcom_pcie->pci_reset); ++ reset_control_deassert(qcom_pcie->por_reset); ++ reset_control_deassert(qcom_pcie->axi_reset); ++ ++ /* wait 150ms for clock acquisition */ ++ usleep_range(10000, 15000); ++ ++ /* de-assert PCIe reset link to bring EP out of reset */ ++ gpio_set_value(qcom_pcie->reset_gpio, 1 - 0); ++ usleep_range(10000, 15000); ++ ++ /* enable link training */ ++ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL); ++ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN; ++ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL); ++ wmb(); ++ ++ /* poll for link to come up for upto 100ms */ ++ ret = readl_poll_timeout( ++ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS), ++ val, (val & BIT(29)), 10000, 100000); ++ ++ printk("link initialized %d\n", ret); ++ ++ qcom_pcie_config_controller(qcom_pcie); ++ ++ platform_set_drvdata(pdev, qcom_pcie); ++ ++ spin_lock_irqsave(&qcom_hw_pci_lock, flags); ++ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie; ++ hw = &qcom_hw_pci[nr_controllers]; ++ nr_controllers++; ++ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags); ++ ++ pci_common_init(hw); ++ ++ return 0; ++} ++ ++static int __exit qcom_pcie_remove(struct platform_device *pdev) ++{ ++ struct qcom_pcie *qcom_pcie = platform_get_drvdata(pdev); ++ ++ return 0; ++} ++ ++static struct of_device_id qcom_pcie_match[] = { ++ { .compatible = "qcom,pcie-ipq8064", }, ++ {} ++}; ++ ++static struct platform_driver qcom_pcie_driver = { ++ .probe = qcom_pcie_probe, ++ .remove = qcom_pcie_remove, ++ .driver = { ++ .name = "qcom_pcie", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_pcie_match, ++ }, ++}; ++ ++static int qcom_pcie_init(void) ++{ ++ return platform_driver_register(&qcom_pcie_driver); ++} ++subsys_initcall(qcom_pcie_init); ++ ++/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */ ++static void msm_pcie_fixup_early(struct pci_dev *dev) ++{ ++ if (dev->hdr_type == 1) ++ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8); ++} ++DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch new file mode 100644 index 0000000000..1105f140e7 --- /dev/null +++ b/target/linux/ipq806x/patches/0139-ARM-dts-msm-Add-PCIe-related-nodes-for-IPQ8064-AP148.patch @@ -0,0 +1,179 @@ +From 7c6525a0d5cf88f9244187fbe8ee293fa4ee43c1 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Mon, 12 May 2014 19:36:23 -0500 +Subject: [PATCH 139/182] ARM: dts: msm: Add PCIe related nodes for + IPQ8064/AP148 + +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 38 ++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 93 ++++++++++++++++++++++++++++++ + 2 files changed, 131 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 158a09f..11f7a77 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -21,6 +21,22 @@ + bias-disable; + }; + ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio3"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie2_pins: pcie2_pinmux { ++ mux { ++ pins = "gpio48"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; +@@ -80,5 +96,27 @@ + }; + }; + }; ++ ++ pci@1b500000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 3 0>; ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ ++ ranges = <0x00000000 0 0x00000000 0x0ff00000 0 0x00100000 /* configuration space */ ++ 0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ ++ }; ++ ++ pci@1b700000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 48 0>; ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ ++ ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */ ++ 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 244f857..42a651f 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -2,6 +2,7 @@ + + #include "skeleton.dtsi" + #include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/reset/qcom,gcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> + + / { +@@ -246,5 +247,97 @@ + #clock-cells = <1>; + #reset-cells = <1>; + }; ++ ++ pci@1b500000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ interrupts = <0 35 0x0 ++ 0 36 0x0 ++ 0 37 0x0 ++ 0 38 0x0 ++ 0 39 0x0>; ++ resets = <&gcc PCIE_ACLK_RESET>, ++ <&gcc PCIE_HCLK_RESET>, ++ <&gcc PCIE_POR_RESET>, ++ <&gcc PCIE_PCI_RESET>, ++ <&gcc PCIE_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; ++ ++ pci@1b700000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b700000 0x1000 ++ 0x1b702000 0x80 ++ 0x1b800000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ ++ interrupts = <0 57 0x0 ++ 0 58 0x0 ++ 0 59 0x0 ++ 0 60 0x0 ++ 0 61 0x0>; ++ resets = <&gcc PCIE_1_ACLK_RESET>, ++ <&gcc PCIE_1_HCLK_RESET>, ++ <&gcc PCIE_1_POR_RESET>, ++ <&gcc PCIE_1_PCI_RESET>, ++ <&gcc PCIE_1_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ clocks = <&gcc PCIE_1_A_CLK>, ++ <&gcc PCIE_1_H_CLK>, ++ <&gcc PCIE_1_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; ++ ++ pci@1b900000 { ++ compatible = "qcom,pcie-ipq8064"; ++ reg = <0x1b900000 0x1000 ++ 0x1b902000 0x80 ++ 0x1ba00000 0x100 ++ >; ++ reg-names = "base", "elbi", "parf"; ++ ++ #address-cells = <3>; ++ #size-cells = <2>; ++ device_type = "pci"; ++ ++ interrupts = <0 71 0x0 ++ 0 72 0x0 ++ 0 73 0x0 ++ 0 74 0x0 ++ 0 75 0x0>; ++ resets = <&gcc PCIE_2_ACLK_RESET>, ++ <&gcc PCIE_2_HCLK_RESET>, ++ <&gcc PCIE_2_POR_RESET>, ++ <&gcc PCIE_2_PCI_RESET>, ++ <&gcc PCIE_2_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ clocks = <&gcc PCIE_2_A_CLK>, ++ <&gcc PCIE_2_H_CLK>, ++ <&gcc PCIE_2_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch new file mode 100644 index 0000000000..e8519e1107 --- /dev/null +++ b/target/linux/ipq806x/patches/0140-ARM-qcom-config-Enable-PCI-support-for-IPQ806x.patch @@ -0,0 +1,33 @@ +From a7e90802be401083098511e72866a0f824c10e15 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 16 May 2014 16:54:48 -0500 +Subject: [PATCH 140/182] ARM: qcom: config: Enable PCI support for IPQ806x + +--- + arch/arm/configs/qcom_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 1752a4d..4866dec 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -21,6 +21,8 @@ CONFIG_ARCH_QCOM=y + CONFIG_ARCH_MSM8X60=y + CONFIG_ARCH_MSM8960=y + CONFIG_ARCH_MSM8974=y ++CONFIG_PCI=y ++CONFIG_PCI_MSI=y + CONFIG_SMP=y + CONFIG_PREEMPT=y + CONFIG_AEABI=y +@@ -83,6 +85,7 @@ CONFIG_INPUT_MISC=y + CONFIG_INPUT_UINPUT=y + CONFIG_SERIO_LIBPS2=y + # CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y + CONFIG_SERIAL_MSM=y + CONFIG_SERIAL_MSM_CONSOLE=y + CONFIG_HW_RANDOM=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch new file mode 100644 index 0000000000..0a41b3e866 --- /dev/null +++ b/target/linux/ipq806x/patches/0141-ahci-platform-Bump-max-number-of-clocks-to-5.patch @@ -0,0 +1,28 @@ +From fd475809eefc0870515d0b04815e2bbae67be906 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 6 Jun 2014 12:09:01 -0500 +Subject: [PATCH 141/182] ahci-platform: Bump max number of clocks to 5 + +Qualcomm IPQ806x SoCs with SATA controllers need 5 clocks to be enabled. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/ata/ahci.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 51af275..6357e34 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -53,7 +53,7 @@ + + enum { + AHCI_MAX_PORTS = 32, +- AHCI_MAX_CLKS = 3, ++ AHCI_MAX_CLKS = 5, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch new file mode 100644 index 0000000000..678a33f472 --- /dev/null +++ b/target/linux/ipq806x/patches/0142-ata-Add-Qualcomm-ARM-SoC-AHCI-SATA-host-controller-d.patch @@ -0,0 +1,146 @@ +From c5ea64dcf7b5d45e402e78b9f291d521669b7b80 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Fri, 30 May 2014 15:35:40 -0500 +Subject: [PATCH 142/182] ata: Add Qualcomm ARM SoC AHCI SATA host controller + driver + +Add support for the Qualcomm AHCI SATA controller that exists on several +SoC and specifically the IPQ806x family of chips. The IPQ806x SATA support +requires the associated IPQ806x SATA PHY Driver to be enabled as well. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/ata/Kconfig | 10 ++++++ + drivers/ata/Makefile | 1 + + drivers/ata/ahci_qcom.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 97 insertions(+) + create mode 100644 drivers/ata/ahci_qcom.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index dc950f3..da05034 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -106,6 +106,16 @@ config AHCI_IMX + + If unsure, say N. + ++config AHCI_QCOM ++ tristate "Qualcomm AHCI SATA support" ++ depends on ARCH_QCOM ++ help ++ This option enables support for AHCI SATA controller ++ integrated into Qualcomm ARM SoC chipsets. For more ++ information please refer to http://www.qualcomm.com/chipsets. ++ ++ If unsure, say N. ++ + config SATA_FSL + tristate "Freescale 3.0Gbps SATA support" + depends on FSL_SOC +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 366b743..e08044f 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o + obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o ++obj-$(CONFIG_AHCI_QCOM) += ahci_qcom.o libahci.o libahci_platform.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci_qcom.c b/drivers/ata/ahci_qcom.c +new file mode 100644 +index 0000000..bfb7a77 +--- /dev/null ++++ b/drivers/ata/ahci_qcom.c +@@ -0,0 +1,86 @@ ++/* ++ * Qualcomm ARM SoC AHCI SATA platform driver ++ * ++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov ++ * ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/libata.h> ++#include <linux/ahci_platform.h> ++#include "ahci.h" ++ ++static const struct ata_port_info qcom_ahci_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, ++}; ++ ++static int qcom_ahci_probe(struct platform_device *pdev) ++{ ++ struct ahci_host_priv *hpriv; ++ struct clk *rxoob_clk; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ /* Try and set the rxoob clk to 100Mhz */ ++ rxoob_clk = of_clk_get_by_name(pdev->dev.of_node, "rxoob"); ++ if (IS_ERR(rxoob_clk)) ++ return PTR_ERR(rxoob_clk); ++ ++ rc = clk_set_rate(rxoob_clk, 100000000); ++ if (rc) ++ return rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, &qcom_ahci_port_info, 0, 0); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++ ++static const struct of_device_id qcom_ahci_of_match[] = { ++ { .compatible = "qcom,msm-ahci", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, qcom_ahci_of_match); ++ ++static struct platform_driver qcom_ahci_driver = { ++ .probe = qcom_ahci_probe, ++ .remove = ata_platform_remove_one, ++ .driver = { ++ .name = "qcom_ahci_qcom", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_ahci_of_match, ++ }, ++}; ++module_platform_driver(qcom_ahci_driver); ++ ++MODULE_DESCRIPTION("Qualcomm AHCI SATA platform driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("ahci:qcom"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..ef795bf054 --- /dev/null +++ b/target/linux/ipq806x/patches/0143-ata-qcom-Add-device-tree-bindings-information.patch @@ -0,0 +1,63 @@ +From dd280a4cf6b826144f74d3fc4285633f1c337a1d Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 12 Jun 2014 11:28:47 -0500 +Subject: [PATCH 143/182] ata: qcom: Add device tree bindings information + +Add device tree binding for Qualcomm AHCI SATA controller and specifically +the sata controller on the IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + .../devicetree/bindings/ata/qcom-sata.txt | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + create mode 100644 Documentation/devicetree/bindings/ata/qcom-sata.txt + +diff --git a/Documentation/devicetree/bindings/ata/qcom-sata.txt b/Documentation/devicetree/bindings/ata/qcom-sata.txt +new file mode 100644 +index 0000000..5e74e41 +--- /dev/null ++++ b/Documentation/devicetree/bindings/ata/qcom-sata.txt +@@ -0,0 +1,40 @@ ++* Qualcomm AHCI SATA Controller ++ ++SATA nodes are defined to describe on-chip Serial ATA controllers. ++Each SATA controller should have its own node. ++ ++Required properties: ++- compatible : compatible list, contains "qcom,msm-ahci" ++- interrupts : <interrupt mapping for SATA IRQ> ++- reg : <registers mapping> ++- phys : Must contain exactly one entry as specified ++ in phy-bindings.txt ++- phy-names : Must be "sata-phy" ++ ++Required properties for "qcom,ipq806x-ahci" compatible: ++- clocks : Must contain an entry for each entry in clock-names. ++- clock-names : Shall be: ++ "slave_iface" - Fabric port AHB clock for SATA ++ "iface" - AHB clock ++ "core" - core clock ++ "rxoob" - RX out-of-band clock ++ "pmalive" - Power Module Alive clock ++ ++Example: ++ sata@29000000 { ++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci"; ++ reg = <0x29000000 0x180>; ++ ++ interrupts = <0 209 0x0>; ++ ++ clocks = <&gcc SFAB_SATA_S_H_CLK>, ++ <&gcc SATA_H_CLK>, ++ <&gcc SATA_A_CLK>, ++ <&gcc SATA_RXOOB_CLK>, ++ <&gcc SATA_PMALIVE_CLK>; ++ clock-names = "slave_iface", "iface", "core", ++ "rxoob", "pmalive"; ++ ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch new file mode 100644 index 0000000000..862be0835e --- /dev/null +++ b/target/linux/ipq806x/patches/0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch @@ -0,0 +1,261 @@ +From 7554dfbbec255e4cd5dd4445841c28c48fd4a855 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 3 Jun 2014 11:59:09 -0500 +Subject: [PATCH 144/182] phy: qcom: Add driver for QCOM IPQ806x SATA PHY + +Add a PHY driver for uses with AHCI based SATA controller driver on the +IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + drivers/phy/Kconfig | 7 ++ + drivers/phy/Makefile | 1 + + drivers/phy/phy-qcom-ipq806x-sata.c | 211 +++++++++++++++++++++++++++++++++++ + 3 files changed, 219 insertions(+) + create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c + +diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig +index c7a551c..3642a65 100644 +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -65,4 +65,11 @@ config BCM_KONA_USB2_PHY + help + Enable this to support the Broadcom Kona USB 2.0 PHY. + ++config PHY_QCOM_IPQ806X_SATA ++ tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ + endmenu +diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile +index b57c253..74f78cc 100644 +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o + obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o + obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o + obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o ++obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o +diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c +new file mode 100644 +index 0000000..e931aee +--- /dev/null ++++ b/drivers/phy/phy-qcom-ipq806x-sata.c +@@ -0,0 +1,211 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/time.h> ++#include <linux/delay.h> ++#include <linux/clk.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/phy/phy.h> ++ ++struct qcom_ipq806x_sata_phy { ++ void __iomem *mmio; ++ struct clk *cfg_clk; ++}; ++ ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++ ++#define SATA_PHY_P0_PARAM0 0x200 ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) ++ ++#define SATA_PHY_P0_PARAM1 0x204 ++#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) ++ ++#define SATA_PHY_P0_PARAM2 0x208 ++#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) ++#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) ++ ++#define SATA_PHY_P0_PARAM3 0x20C ++#define SATA_PHY_SSC_EN 0x8 ++#define SATA_PHY_P0_PARAM4 0x210 ++#define SATA_PHY_REF_SSP_EN 0x2 ++#define SATA_PHY_RESET 0x1 ++ ++static inline void qcom_ipq806x_sata_delay_us(unsigned int delay) ++{ ++ /* sleep for max. 50us more to combine processor wakeups */ ++ usleep_range(delay, delay + 50); ++} ++ ++static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting SSC_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); ++ reg = reg | SATA_PHY_SSC_EN; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & ++ ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & ++ ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & ++ ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; ++ reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ /* Setting REF_SSP_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ mb(); ++ qcom_ipq806x_sata_delay_us(20); ++ ++ /* Clearing PHY_RESET to 0 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg & ~SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static struct phy_ops qcom_ipq806x_sata_phy_ops = { ++ .init = qcom_ipq806x_sata_phy_init, ++ .exit = qcom_ipq806x_sata_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ int ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ dev_err(dev, "%s: failed to allocate phy\n", __func__); ++ return -ENOMEM; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->mmio)) ++ return PTR_ERR(phy->mmio); ++ ++ generic_phy = devm_phy_create(dev, &qcom_ipq806x_sata_phy_ops, NULL); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "%s: failed to create phy\n", __func__); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ phy->cfg_clk = devm_clk_get(dev, "cfg"); ++ if (IS_ERR(phy->cfg_clk)) { ++ dev_err(dev, "Failed to get sata cfg clock\n"); ++ return PTR_ERR(phy->cfg_clk); ++ } ++ ++ ret = clk_prepare_enable(phy->cfg_clk); ++ if (ret) ++ return ret; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ clk_disable_unprepare(phy->cfg_clk); ++ dev_err(dev, "%s: failed to register phy\n", __func__); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(phy->cfg_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { ++ { .compatible = "qcom,ipq806x-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); ++ ++static struct platform_driver qcom_ipq806x_sata_phy_driver = { ++ .probe = qcom_ipq806x_sata_phy_probe, ++ .remove = qcom_ipq806x_sata_phy_remove, ++ .driver = { ++ .name = "qcom-ipq806x-sata-phy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_ipq806x_sata_phy_of_match, ++ } ++}; ++module_platform_driver(qcom_ipq806x_sata_phy_driver); ++ ++MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch new file mode 100644 index 0000000000..1791228113 --- /dev/null +++ b/target/linux/ipq806x/patches/0145-phy-qcom-Add-device-tree-bindings-information.patch @@ -0,0 +1,46 @@ +From 37258bc8fe832e4c681593a864686f627f6d3455 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 10 Jun 2014 13:09:01 -0500 +Subject: [PATCH 145/182] phy: qcom: Add device tree bindings information + +Add binding spec for Qualcomm SoC PHYs, starting with the SATA PHY on +the IPQ806x family of SoCs. + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + Documentation/devicetree/bindings/phy/qcom-phy.txt | 23 ++++++++++++++++++++ + 1 file changed, 23 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/qcom-phy.txt + +diff --git a/Documentation/devicetree/bindings/phy/qcom-phy.txt b/Documentation/devicetree/bindings/phy/qcom-phy.txt +new file mode 100644 +index 0000000..76bfbd0 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/qcom-phy.txt +@@ -0,0 +1,23 @@ ++Qualcomm IPQ806x SATA PHY Controller ++------------------------------------ ++ ++SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. ++Each SATA PHY controller should have its own node. ++ ++Required properties: ++- compatible: compatible list, contains "qcom,ipq806x-sata-phy" ++- reg: offset and length of the SATA PHY register set; ++- #phy-cells: must be zero ++- clocks: must be exactly one entry ++- clock-names: must be "cfg" ++ ++Example: ++ sata_phy: sata-phy@1b400000 { ++ compatible = "qcom,ipq806x-sata-phy"; ++ reg = <0x1b400000 0x200>; ++ ++ clocks = <&gcc SATA_PHY_CFG_CLK>; ++ clock-names = "cfg"; ++ ++ #phy-cells = <0>; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch new file mode 100644 index 0000000000..0d196ad212 --- /dev/null +++ b/target/linux/ipq806x/patches/0146-ARM-dts-qcom-Add-SATA-support-for-IPQ8064-and-AP148-.patch @@ -0,0 +1,72 @@ +From 6992cf3e8900d042a845eafc11e7841f32fec0a6 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Thu, 12 Jun 2014 10:56:54 -0500 +Subject: [PATCH 146/182] ARM: dts: qcom: Add SATA support for IPQ8064 and + AP148 board + +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 8 ++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 11f7a77..c752889 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -118,5 +118,13 @@ + 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ + }; ++ ++ sata-phy@1b400000 { ++ status = "ok"; ++ }; ++ ++ sata@29000000 { ++ status = "ok"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 42a651f..93c0315 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -339,5 +339,35 @@ + clock-names = "core", "iface", "phy"; + status = "disabled"; + }; ++ ++ sata_phy: sata-phy@1b400000 { ++ compatible = "qcom,ipq806x-sata-phy"; ++ reg = <0x1b400000 0x200>; ++ ++ clocks = <&gcc SATA_PHY_CFG_CLK>; ++ clock-names = "cfg"; ++ ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ sata@29000000 { ++ compatible = "qcom,ipq806x-ahci", "qcom,msm-ahci"; ++ reg = <0x29000000 0x180>; ++ ++ interrupts = <0 209 0x0>; ++ ++ clocks = <&gcc SFAB_SATA_S_H_CLK>, ++ <&gcc SATA_H_CLK>, ++ <&gcc SATA_A_CLK>, ++ <&gcc SATA_RXOOB_CLK>, ++ <&gcc SATA_PMALIVE_CLK>; ++ clock-names = "slave_face", "iface", "core", ++ "rxoob", "pmalive"; ++ ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch new file mode 100644 index 0000000000..43e9dff160 --- /dev/null +++ b/target/linux/ipq806x/patches/0147-ARM-qcom-Enable-SATA-SATA-PHY-drivers-in-defconfig.patch @@ -0,0 +1,42 @@ +From 54a945eca8381fa8cda03b20eda3b2f23a88d289 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Tue, 3 Jun 2014 10:36:41 -0500 +Subject: [PATCH 147/182] ARM: qcom: Enable SATA/SATA-PHY drivers in defconfig + +--- + arch/arm/configs/qcom_defconfig | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 4866dec..84c6639 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -57,7 +57,6 @@ CONFIG_MTD_BLOCK=y + CONFIG_MTD_M25P80=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_RAM=y +-CONFIG_SCSI=y + CONFIG_SCSI_TGT=y + CONFIG_BLK_DEV_SD=y + CONFIG_CHR_DEV_SG=y +@@ -66,6 +65,8 @@ CONFIG_SCSI_MULTI_LUN=y + CONFIG_SCSI_CONSTANTS=y + CONFIG_SCSI_LOGGING=y + CONFIG_SCSI_SCAN_ASYNC=y ++CONFIG_ATA=y ++CONFIG_AHCI_QCOM=y + CONFIG_NETDEVICES=y + CONFIG_DUMMY=y + CONFIG_MDIO_BITBANG=y +@@ -141,7 +142,7 @@ CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y + CONFIG_MSM_IOMMU=y +-CONFIG_GENERIC_PHY=y ++CONFIG_PHY_QCOM_IPQ806X_SATA=y + CONFIG_EXT2_FS=y + CONFIG_EXT2_FS_XATTR=y + CONFIG_EXT3_FS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch new file mode 100644 index 0000000000..29d712679d --- /dev/null +++ b/target/linux/ipq806x/patches/0148-ARM-qcom-enable-default-CPU_IDLE-to-get-wfi-support-.patch @@ -0,0 +1,26 @@ +From 8c9156c67c3e3e37812bb0e4d24263be0e564881 Mon Sep 17 00:00:00 2001 +From: Kumar Gala <galak@codeaurora.org> +Date: Mon, 16 Jun 2014 14:40:29 -0500 +Subject: [PATCH 148/182] ARM: qcom: enable default CPU_IDLE to get wfi + support on IPQ806x + +Signed-off-by: Kumar Gala <galak@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 84c6639..85a35af 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -31,6 +31,7 @@ CONFIG_HIGHPTE=y + CONFIG_CLEANCACHE=y + CONFIG_ARM_APPENDED_DTB=y + CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_CPU_IDLE=y + CONFIG_VFP=y + CONFIG_NEON=y + # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch new file mode 100644 index 0000000000..8e21ee8a97 --- /dev/null +++ b/target/linux/ipq806x/patches/0149-pinctrl-qcom-Add-BUS_HOLD-Keeper-bias.patch @@ -0,0 +1,55 @@ +From b1325bfad3ad0e543ce90a6b08d74500dc96f4b9 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:52:55 -0500 +Subject: [PATCH 149/182] pinctrl: qcom: Add BUS_HOLD/Keeper bias + +This patch adds the bus_hold bias option for pins. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/pinctrl/pinctrl-msm.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c +index 7d67d34..f054b25 100644 +--- a/drivers/pinctrl/pinctrl-msm.c ++++ b/drivers/pinctrl/pinctrl-msm.c +@@ -207,6 +207,7 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: ++ case PIN_CONFIG_BIAS_BUS_HOLD: + *bit = g->pull_bit; + *mask = 3; + break; +@@ -243,6 +244,7 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + + #define MSM_NO_PULL 0 + #define MSM_PULL_DOWN 1 ++#define MSM_KEEPER 2 + #define MSM_PULL_UP 3 + + static unsigned msm_regval_to_drive(u32 val) +@@ -280,6 +282,9 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = arg == MSM_PULL_DOWN; + break; ++ case PIN_CONFIG_BIAS_BUS_HOLD: ++ arg = arg == MSM_KEEPER; ++ break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = arg == MSM_PULL_UP; + break; +@@ -339,6 +344,9 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = MSM_PULL_DOWN; + break; ++ case PIN_CONFIG_BIAS_BUS_HOLD: ++ arg = MSM_KEEPER; ++ break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = MSM_PULL_UP; + break; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch new file mode 100644 index 0000000000..5a9fb51f03 --- /dev/null +++ b/target/linux/ipq806x/patches/0150-mtd-nand-Add-Qualcomm-NAND-controller.patch @@ -0,0 +1,8803 @@ +From d2981ca1343b837fc574c4e46806d041b258720d Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 17:13:22 -0500 +Subject: [PATCH 150/182] mtd: nand: Add Qualcomm NAND controller + +This patch adds the Qualcomm NAND controller and required infrastructure. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/mtd/nand/Kconfig | 18 + + drivers/mtd/nand/Makefile | 2 + + drivers/mtd/nand/qcom_adm_dma.c | 797 +++++ + drivers/mtd/nand/qcom_adm_dma.h | 268 ++ + drivers/mtd/nand/qcom_nand.c | 7455 +++++++++++++++++++++++++++++++++++++++ + drivers/mtd/nand/qcom_nand.h | 196 + + 6 files changed, 8736 insertions(+) + create mode 100644 drivers/mtd/nand/qcom_adm_dma.c + create mode 100644 drivers/mtd/nand/qcom_adm_dma.h + create mode 100644 drivers/mtd/nand/qcom_nand.c + create mode 100644 drivers/mtd/nand/qcom_nand.h + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 90ff447..6e3842f 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -510,4 +510,22 @@ config MTD_NAND_XWAY + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + ++config MTD_QCOM_DMA ++ tristate "QCMO NAND DMA Support" ++ depends on ARCH_QCOM && MTD_QCOM_NAND ++ default n ++ help ++ DMA support for QCOM NAND ++ ++config MTD_QCOM_NAND ++ tristate "QCOM NAND Device Support" ++ depends on MTD && ARCH_QCOM ++ select CRC16 ++ select BITREVERSE ++ select MTD_NAND_IDS ++ select MTD_QCOM_DMA ++ default y ++ help ++ Support for some NAND chips connected to the QCOM NAND controller. ++ + endif # MTD_NAND +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 542b568..6ef3c02 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -49,5 +49,7 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ + obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o + obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ ++obj-$(CONFIG_MTD_QCOM_NAND) += qcom_nand.o ++obj-$(CONFIG_MTD_QCOM_DMA) += qcom_adm_dma.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c +new file mode 100644 +index 0000000..46d8473 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_adm_dma.c +@@ -0,0 +1,797 @@ ++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */ ++/* linux/arch/arm/mach-msm/dma.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/pm_runtime.h> ++#include <linux/reset.h> ++#include <linux/reset-controller.h> ++#include "qcom_adm_dma.h" ++ ++#define MODULE_NAME "msm_dmov" ++ ++#define MSM_DMOV_CHANNEL_COUNT 16 ++#define MSM_DMOV_CRCI_COUNT 16 ++ ++enum { ++ CLK_DIS, ++ CLK_TO_BE_DIS, ++ CLK_EN ++}; ++ ++struct msm_dmov_ci_conf { ++ int start; ++ int end; ++ int burst; ++}; ++ ++struct msm_dmov_crci_conf { ++ int sd; ++ int blk_size; ++}; ++ ++struct msm_dmov_chan_conf { ++ int sd; ++ int block; ++ int priority; ++}; ++ ++struct msm_dmov_conf { ++ void *base; ++ struct msm_dmov_crci_conf *crci_conf; ++ struct msm_dmov_chan_conf *chan_conf; ++ int channel_active; ++ int sd; ++ size_t sd_size; ++ struct list_head staged_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; ++ struct mutex lock; ++ spinlock_t list_lock; ++ unsigned int irq; ++ struct clk *clk; ++ struct clk *pclk; ++ struct clk *ebiclk; ++ unsigned int clk_ctl; ++ struct delayed_work work; ++ struct workqueue_struct *cmd_wq; ++ ++ struct reset_control *adm_reset; ++ struct reset_control *pbus_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ ++}; ++ ++static void msm_dmov_clock_work(struct work_struct *); ++ ++#define DMOV_CRCI_DEFAULT_CONF { .sd = 0, .blk_size = 0 } ++#define DMOV_CRCI_CONF(secd, blk) { .sd = secd, .blk_size = blk } ++ ++static struct msm_dmov_crci_conf adm_crci_conf[] = { ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_CONF(0, 1), ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++ DMOV_CRCI_DEFAULT_CONF, ++}; ++ ++#define DMOV_CHANNEL_DEFAULT_CONF { .sd = 0, .block = 0, .priority = 1 } ++ ++static struct msm_dmov_chan_conf adm_chan_conf[] = { ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++ DMOV_CHANNEL_DEFAULT_CONF, ++}; ++ ++#define DMOV_IRQ_TO_ADM(irq) 0 ++ ++static struct msm_dmov_conf dmov_conf[] = { ++ { ++ .crci_conf = adm_crci_conf, ++ .chan_conf = adm_chan_conf, ++ .lock = __MUTEX_INITIALIZER(dmov_conf[0].lock), ++ .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock), ++ .clk_ctl = CLK_EN, ++ .work = __DELAYED_WORK_INITIALIZER(dmov_conf[0].work, ++ msm_dmov_clock_work,0), ++ } ++}; ++ ++#define MSM_DMOV_ID_COUNT (MSM_DMOV_CHANNEL_COUNT * ARRAY_SIZE(dmov_conf)) ++#define DMOV_REG(name, adm) ((name) + (dmov_conf[adm].base) +\ ++ (dmov_conf[adm].sd * dmov_conf[adm].sd_size)) ++#define DMOV_ID_TO_ADM(id) ((id) / MSM_DMOV_CHANNEL_COUNT) ++#define DMOV_ID_TO_CHAN(id) ((id) % MSM_DMOV_CHANNEL_COUNT) ++#define DMOV_CHAN_ADM_TO_ID(ch, adm) ((ch) + (adm) * MSM_DMOV_CHANNEL_COUNT) ++ ++enum { ++ MSM_DMOV_PRINT_ERRORS = 1, ++ MSM_DMOV_PRINT_IO = 2, ++ MSM_DMOV_PRINT_FLOW = 4 ++}; ++ ++unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS; ++ ++#define MSM_DMOV_DPRINTF(mask, format, args...) \ ++ do { \ ++ if ((mask) & msm_dmov_print_mask) \ ++ printk(KERN_ERR format, args); \ ++ } while (0) ++#define PRINT_ERROR(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_ERRORS, format, args); ++#define PRINT_IO(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_IO, format, args); ++#define PRINT_FLOW(format, args...) \ ++ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_FLOW, format, args); ++ ++static int msm_dmov_clk_on(int adm) ++{ ++ int ret; ++ ++return 0; ++ ret = clk_prepare_enable(dmov_conf[adm].clk); ++ if (ret) ++ return ret; ++ if (dmov_conf[adm].pclk) { ++ ret = clk_prepare_enable(dmov_conf[adm].pclk); ++ if (ret) { ++ clk_disable_unprepare(dmov_conf[adm].clk); ++ return ret; ++ } ++ } ++ if (dmov_conf[adm].ebiclk) { ++ ret = clk_prepare_enable(dmov_conf[adm].ebiclk); ++ if (ret) { ++ if (dmov_conf[adm].pclk) ++ clk_disable_unprepare(dmov_conf[adm].pclk); ++ clk_disable_unprepare(dmov_conf[adm].clk); ++ } ++ } ++ return ret; ++} ++ ++static void msm_dmov_clk_off(int adm) ++{ ++#if 0 ++ if (dmov_conf[adm].ebiclk) ++ clk_disable_unprepare(dmov_conf[adm].ebiclk); ++ if (dmov_conf[adm].pclk) ++ clk_disable_unprepare(dmov_conf[adm].pclk); ++ clk_disable_unprepare(dmov_conf[adm].clk); ++#endif ++} ++ ++static void msm_dmov_clock_work(struct work_struct *work) ++{ ++ struct msm_dmov_conf *conf = ++ container_of(to_delayed_work(work), struct msm_dmov_conf, work); ++ int adm = DMOV_IRQ_TO_ADM(conf->irq); ++ mutex_lock(&conf->lock); ++ if (conf->clk_ctl == CLK_TO_BE_DIS) { ++ BUG_ON(conf->channel_active); ++ msm_dmov_clk_off(adm); ++ conf->clk_ctl = CLK_DIS; ++ } ++ mutex_unlock(&conf->lock); ++} ++ ++enum { ++ NOFLUSH = 0, ++ GRACEFUL, ++ NONGRACEFUL, ++}; ++ ++/* Caller must hold the list lock */ ++static struct msm_dmov_cmd *start_ready_cmd(unsigned ch, int adm) ++{ ++ struct msm_dmov_cmd *cmd; ++ ++ if (list_empty(&dmov_conf[adm].ready_commands[ch])) { ++ return NULL; ++ } ++ ++ cmd = list_entry(dmov_conf[adm].ready_commands[ch].next, typeof(*cmd), ++ list); ++ list_del(&cmd->list); ++ if (cmd->exec_func) ++ cmd->exec_func(cmd); ++ list_add_tail(&cmd->list, &dmov_conf[adm].active_commands[ch]); ++ if (!dmov_conf[adm].channel_active) { ++ enable_irq(dmov_conf[adm].irq); ++ } ++ dmov_conf[adm].channel_active |= BIT(ch); ++ PRINT_IO("msm dmov enqueue command, %x, ch %d\n", cmd->cmdptr, ch); ++ writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm)); ++ ++ return cmd; ++} ++ ++static void msm_dmov_enqueue_cmd_ext_work(struct work_struct *work) ++{ ++ struct msm_dmov_cmd *cmd = ++ container_of(work, struct msm_dmov_cmd, work); ++ unsigned id = cmd->id; ++ unsigned status; ++ unsigned long flags; ++ int adm = DMOV_ID_TO_ADM(id); ++ int ch = DMOV_ID_TO_CHAN(id); ++ ++ mutex_lock(&dmov_conf[adm].lock); ++ if (dmov_conf[adm].clk_ctl == CLK_DIS) { ++ status = msm_dmov_clk_on(adm); ++ if (status != 0) ++ goto error; ++ } ++ dmov_conf[adm].clk_ctl = CLK_EN; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags); ++ ++ cmd = list_entry(dmov_conf[adm].staged_commands[ch].next, typeof(*cmd), ++ list); ++ list_del(&cmd->list); ++ list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]); ++ status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); ++ if (status & DMOV_STATUS_CMD_PTR_RDY) { ++ PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", ++ id, status); ++ cmd = start_ready_cmd(ch, adm); ++ /* ++ * We added something to the ready list, and still hold the ++ * list lock. Thus, no need to check for cmd == NULL ++ */ ++ if (cmd->toflush) { ++ int flush = (cmd->toflush == GRACEFUL) ? 1 << 31 : 0; ++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ } ++ } else { ++ cmd->toflush = 0; ++ if (list_empty(&dmov_conf[adm].active_commands[ch]) && ++ !list_empty(&dmov_conf[adm].ready_commands[ch])) ++ PRINT_ERROR("msm_dmov_enqueue_cmd_ext(%d), stalled, " ++ "status %x\n", id, status); ++ PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status " ++ "%x\n", id, status); ++ } ++ if (!dmov_conf[adm].channel_active) { ++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; ++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); ++ } ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); ++error: ++ mutex_unlock(&dmov_conf[adm].lock); ++} ++ ++static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ int adm = DMOV_ID_TO_ADM(id); ++ int ch = DMOV_ID_TO_CHAN(id); ++ unsigned long flags; ++ cmd->id = id; ++ cmd->toflush = 0; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, flags); ++ list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]); ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); ++ ++ queue_work(dmov_conf[adm].cmd_wq, &cmd->work); ++} ++ ++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); ++ __msm_dmov_enqueue_cmd_ext(id, cmd); ++} ++EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext); ++ ++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) ++{ ++ /* Disable callback function (for backwards compatibility) */ ++ cmd->exec_func = NULL; ++ INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); ++ __msm_dmov_enqueue_cmd_ext(id, cmd); ++} ++EXPORT_SYMBOL(msm_dmov_enqueue_cmd); ++ ++void msm_dmov_flush(unsigned int id, int graceful) ++{ ++ unsigned long irq_flags; ++ int ch = DMOV_ID_TO_CHAN(id); ++ int adm = DMOV_ID_TO_ADM(id); ++ int flush = graceful ? DMOV_FLUSH_TYPE : 0; ++ struct msm_dmov_cmd *cmd; ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); ++ /* XXX not checking if flush cmd sent already */ ++ if (!list_empty(&dmov_conf[adm].active_commands[ch])) { ++ PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id); ++ writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ } ++ list_for_each_entry(cmd, &dmov_conf[adm].staged_commands[ch], list) ++ cmd->toflush = graceful ? GRACEFUL : NONGRACEFUL; ++ /* spin_unlock_irqrestore has the necessary barrier */ ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); ++} ++EXPORT_SYMBOL(msm_dmov_flush); ++ ++struct msm_dmov_exec_cmdptr_cmd { ++ struct msm_dmov_cmd dmov_cmd; ++ struct completion complete; ++ unsigned id; ++ unsigned int result; ++ struct msm_dmov_errdata err; ++}; ++ ++static void ++dmov_exec_cmdptr_complete_func(struct msm_dmov_cmd *_cmd, ++ unsigned int result, ++ struct msm_dmov_errdata *err) ++{ ++ struct msm_dmov_exec_cmdptr_cmd *cmd = container_of(_cmd, struct msm_dmov_exec_cmdptr_cmd, dmov_cmd); ++ cmd->result = result; ++ if (result != 0x80000002 && err) ++ memcpy(&cmd->err, err, sizeof(struct msm_dmov_errdata)); ++ ++ complete(&cmd->complete); ++} ++ ++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) ++{ ++ struct msm_dmov_exec_cmdptr_cmd cmd; ++ ++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x)\n", id, cmdptr); ++ ++ cmd.dmov_cmd.cmdptr = cmdptr; ++ cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func; ++ cmd.dmov_cmd.exec_func = NULL; ++ cmd.id = id; ++ cmd.result = 0; ++ INIT_WORK_ONSTACK(&cmd.dmov_cmd.work, msm_dmov_enqueue_cmd_ext_work); ++ init_completion(&cmd.complete); ++ ++ __msm_dmov_enqueue_cmd_ext(id, &cmd.dmov_cmd); ++ wait_for_completion_timeout(&cmd.complete, msecs_to_jiffies(1000)); ++ ++ if (cmd.result != 0x80000002) { ++ PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); ++ PRINT_ERROR("dmov_exec_cmdptr(%d): flush: %x %x %x %x\n", ++ id, cmd.err.flush[0], cmd.err.flush[1], cmd.err.flush[2], cmd.err.flush[3]); ++ return -EIO; ++ } ++ PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr); ++ return 0; ++} ++EXPORT_SYMBOL(msm_dmov_exec_cmd); ++ ++static void fill_errdata(struct msm_dmov_errdata *errdata, int ch, int adm) ++{ ++ errdata->flush[0] = readl_relaxed(DMOV_REG(DMOV_FLUSH0(ch), adm)); ++ errdata->flush[1] = readl_relaxed(DMOV_REG(DMOV_FLUSH1(ch), adm)); ++ errdata->flush[2] = 0; ++ errdata->flush[3] = readl_relaxed(DMOV_REG(DMOV_FLUSH3(ch), adm)); ++ errdata->flush[4] = readl_relaxed(DMOV_REG(DMOV_FLUSH4(ch), adm)); ++ errdata->flush[5] = readl_relaxed(DMOV_REG(DMOV_FLUSH5(ch), adm)); ++} ++ ++static irqreturn_t msm_dmov_isr(int irq, void *dev_id) ++{ ++ unsigned int int_status; ++ unsigned int mask; ++ unsigned int id; ++ unsigned int ch; ++ unsigned long irq_flags; ++ unsigned int ch_status; ++ unsigned int ch_result; ++ unsigned int valid = 0; ++ struct msm_dmov_cmd *cmd; ++ int adm = DMOV_IRQ_TO_ADM(irq); ++ ++ mutex_lock(&dmov_conf[adm].lock); ++ /* read and clear isr */ ++ int_status = readl_relaxed(DMOV_REG(DMOV_ISR, adm)); ++ PRINT_FLOW("msm_datamover_irq_handler: DMOV_ISR %x\n", int_status); ++ ++ spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); ++ while (int_status) { ++ mask = int_status & -int_status; ++ ch = fls(mask) - 1; ++ id = DMOV_CHAN_ADM_TO_ID(ch, adm); ++ PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", int_status, mask, id); ++ int_status &= ~mask; ++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); ++ if (!(ch_status & DMOV_STATUS_RSLT_VALID)) { ++ PRINT_FLOW("msm_datamover_irq_handler id %d, " ++ "result not valid %x\n", id, ch_status); ++ continue; ++ } ++ do { ++ valid = 1; ++ ch_result = readl_relaxed(DMOV_REG(DMOV_RSLT(ch), adm)); ++ if (list_empty(&dmov_conf[adm].active_commands[ch])) { ++ PRINT_ERROR("msm_datamover_irq_handler id %d, got result " ++ "with no active command, status %x, result %x\n", ++ id, ch_status, ch_result); ++ cmd = NULL; ++ } else { ++ cmd = list_entry(dmov_conf[adm]. ++ active_commands[ch].next, typeof(*cmd), ++ list); ++ } ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x, result %x\n", id, ch_status, ch_result); ++ if (ch_result & DMOV_RSLT_DONE) { ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", ++ id, ch_status); ++ PRINT_IO("msm_datamover_irq_handler id %d, got result " ++ "for %p, result %x\n", id, cmd, ch_result); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, NULL); ++ } ++ } ++ if (ch_result & DMOV_RSLT_FLUSH) { ++ struct msm_dmov_errdata errdata; ++ ++ fill_errdata(&errdata, ch, adm); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, &errdata); ++ } ++ } ++ if (ch_result & DMOV_RSLT_ERROR) { ++ struct msm_dmov_errdata errdata; ++ ++ fill_errdata(&errdata, ch, adm); ++ ++ PRINT_ERROR("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); ++ if (cmd) { ++ list_del(&cmd->list); ++ cmd->complete_func(cmd, ch_result, &errdata); ++ } ++ /* this does not seem to work, once we get an error */ ++ /* the datamover will no longer accept commands */ ++ writel_relaxed(0, DMOV_REG(DMOV_FLUSH0(ch), ++ adm)); ++ } ++ rmb(); ++ ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), ++ adm)); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ if (ch_status & DMOV_STATUS_CMD_PTR_RDY) ++ start_ready_cmd(ch, adm); ++ } while (ch_status & DMOV_STATUS_RSLT_VALID); ++ if (list_empty(&dmov_conf[adm].active_commands[ch]) && ++ list_empty(&dmov_conf[adm].ready_commands[ch])) ++ dmov_conf[adm].channel_active &= ~(1U << ch); ++ PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); ++ } ++ spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); ++ ++ if (!dmov_conf[adm].channel_active && valid) { ++ disable_irq_nosync(dmov_conf[adm].irq); ++ dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; ++ schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); ++ } ++ ++ mutex_unlock(&dmov_conf[adm].lock); ++ ++ return valid ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static int msm_dmov_suspend_late(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ mutex_lock(&dmov_conf[adm].lock); ++ if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) { ++ BUG_ON(dmov_conf[adm].channel_active); ++ msm_dmov_clk_off(adm); ++ dmov_conf[adm].clk_ctl = CLK_DIS; ++ } ++ mutex_unlock(&dmov_conf[adm].lock); ++ return 0; ++} ++ ++static int msm_dmov_runtime_suspend(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: suspending...\n"); ++ return 0; ++} ++ ++static int msm_dmov_runtime_resume(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: resuming...\n"); ++ return 0; ++} ++ ++static int msm_dmov_runtime_idle(struct device *dev) ++{ ++ dev_dbg(dev, "pm_runtime: idling...\n"); ++ return 0; ++} ++ ++static struct dev_pm_ops msm_dmov_dev_pm_ops = { ++ .runtime_suspend = msm_dmov_runtime_suspend, ++ .runtime_resume = msm_dmov_runtime_resume, ++ .runtime_idle = msm_dmov_runtime_idle, ++ .suspend = msm_dmov_suspend_late, ++}; ++ ++static int msm_dmov_init_clocks(struct platform_device *pdev) ++{ ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ int ret; ++ ++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk"); ++ if (IS_ERR(dmov_conf[adm].clk)) { ++ printk(KERN_ERR "%s: Error getting adm_clk\n", __func__); ++ dmov_conf[adm].clk = NULL; ++ return -ENOENT; ++ } ++ ++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk"); ++ if (IS_ERR(dmov_conf[adm].pclk)) { ++ dmov_conf[adm].pclk = NULL; ++ /* pclk not present on all SoCs, don't bail on failure */ ++ } ++ ++ dmov_conf[adm].ebiclk = devm_clk_get(&pdev->dev, "mem_clk"); ++ if (IS_ERR(dmov_conf[adm].ebiclk)) { ++ dmov_conf[adm].ebiclk = NULL; ++ /* ebiclk not present on all SoCs, don't bail on failure */ ++ } else { ++ ret = clk_set_rate(dmov_conf[adm].ebiclk, 27000000); ++ if (ret) ++ return -ENOENT; ++ } ++ ++ return 0; ++} ++ ++static void config_datamover(int adm) ++{ ++ int i; ++ ++ /* Reset the ADM */ ++ reset_control_assert(dmov_conf[adm].adm_reset); ++ reset_control_assert(dmov_conf[adm].c0_reset); ++ reset_control_assert(dmov_conf[adm].c1_reset); ++ reset_control_assert(dmov_conf[adm].c2_reset); ++ ++ reset_control_deassert(dmov_conf[adm].c2_reset); ++ reset_control_deassert(dmov_conf[adm].c1_reset); ++ reset_control_deassert(dmov_conf[adm].c0_reset); ++ reset_control_deassert(dmov_conf[adm].adm_reset); ++ ++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { ++ struct msm_dmov_chan_conf *chan_conf = ++ dmov_conf[adm].chan_conf; ++ unsigned conf; ++ /* Only configure scorpion channels */ ++ if (chan_conf[i].sd <= 1) { ++ conf = readl_relaxed(DMOV_REG(DMOV_CONF(i), adm)); ++ conf |= DMOV_CONF_MPU_DISABLE | ++ DMOV_CONF_PERM_MPU_CONF | ++ DMOV_CONF_FLUSH_RSLT_EN | ++ DMOV_CONF_FORCE_RSLT_EN | ++ DMOV_CONF_IRQ_EN | ++ DMOV_CONF_PRIORITY(chan_conf[i].priority); ++ ++ conf &= ~DMOV_CONF_SD(7); ++ conf |= DMOV_CONF_SD(chan_conf[i].sd); ++ writel_relaxed(conf, DMOV_REG(DMOV_CONF(i), adm)); ++ } ++ } ++ ++ for (i = 0; i < MSM_DMOV_CRCI_COUNT; i++) { ++ writel_relaxed(DMOV_CRCI_CTL_RST, ++ DMOV_REG(DMOV_CRCI_CTL(i), adm)); ++ } ++ ++ /* NAND CRCI Enable */ ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_DATA), adm)); ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_NAND_CRCI_CMD), adm)); ++ ++ /* GSBI5 CRCI Enable */ ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_RX_CRCI), adm)); ++ writel_relaxed(0, DMOV_REG(DMOV_CRCI_CTL(DMOV_SPI_GSBI5_TX_CRCI), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x40) | /* EBI1 */ ++ DMOV_CI_CONF_RANGE_END(0xb0) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(0), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x2a) | /* IMEM */ ++ DMOV_CI_CONF_RANGE_END(0x2c) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(1), adm)); ++ ++ writel_relaxed(DMOV_CI_CONF_RANGE_START(0x12) | /* CPSS/SPS */ ++ DMOV_CI_CONF_RANGE_END(0x28) | ++ DMOV_CI_CONF_MAX_BURST(0x8), ++ DMOV_REG(DMOV_CI_CONF(2), adm)); ++ ++ writel_relaxed(DMOV_HI_GP_CTL_CORE_CLK_LP_EN | /* will disable LP */ ++ DMOV_HI_GP_CTL_LP_CNT(0xf), ++ DMOV_REG(DMOV_HI_GP_CTL, adm)); ++ ++} ++ ++static int msm_dmov_probe(struct platform_device *pdev) ++{ ++ ++ int adm = (pdev->id >= 0) ? pdev->id : 0; ++ int i; ++ int ret; ++ struct resource *irqres = ++ platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ struct resource *mres = ++ platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ dmov_conf[adm].sd=0; ++ dmov_conf[adm].sd_size=0x800; ++ ++ dmov_conf[adm].irq = irqres->start; ++ ++ dmov_conf[adm].base = devm_ioremap_resource(&pdev->dev, mres); ++ if (!dmov_conf[adm].base) ++ return -ENOMEM; ++ ++ dmov_conf[adm].cmd_wq = alloc_ordered_workqueue("dmov%d_wq", 0, adm); ++ if (!dmov_conf[adm].cmd_wq) { ++ PRINT_ERROR("Couldn't allocate ADM%d workqueue.\n", adm); ++ return -ENOMEM; ++ } ++ ++ /* get resets */ ++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm"); ++ if (IS_ERR(dmov_conf[adm].adm_reset)) { ++ dev_err(&pdev->dev, "failed to get adm reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].adm_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].pbus_reset = devm_reset_control_get(&pdev->dev, "pbus"); ++ if (IS_ERR(dmov_conf[adm].pbus_reset)) { ++ dev_err(&pdev->dev, "failed to get pbus reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].pbus_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(dmov_conf[adm].c0_reset)) { ++ dev_err(&pdev->dev, "failed to get c0 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c0_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(dmov_conf[adm].c1_reset)) { ++ dev_err(&pdev->dev, "failed to get c1 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c1_reset); ++ goto out_wq; ++ } ++ ++ dmov_conf[adm].c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(dmov_conf[adm].c2_reset)) { ++ dev_err(&pdev->dev, "failed to get c2 reset\n"); ++ ret = PTR_ERR(dmov_conf[adm].c2_reset); ++ goto out_wq; ++ } ++ ++ ret = devm_request_threaded_irq(&pdev->dev, dmov_conf[adm].irq, NULL, ++ msm_dmov_isr, IRQF_ONESHOT, "msmdatamover", NULL); ++ ++ if (ret) { ++ PRINT_ERROR("Requesting ADM%d irq %d failed\n", adm, ++ dmov_conf[adm].irq); ++ goto out_wq; ++ } ++ ++ disable_irq(dmov_conf[adm].irq); ++ ret = msm_dmov_init_clocks(pdev); ++ if (ret) { ++ PRINT_ERROR("Requesting ADM%d clocks failed\n", adm); ++ goto out_wq; ++ } ++ clk_prepare_enable(dmov_conf[adm].clk); ++ clk_prepare_enable(dmov_conf[adm].pclk); ++ ++// ret = msm_dmov_clk_on(adm); ++// if (ret) { ++// PRINT_ERROR("Enabling ADM%d clocks failed\n", adm); ++// goto out_wq; ++// } ++ ++ config_datamover(adm); ++ for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { ++ INIT_LIST_HEAD(&dmov_conf[adm].staged_commands[i]); ++ INIT_LIST_HEAD(&dmov_conf[adm].ready_commands[i]); ++ INIT_LIST_HEAD(&dmov_conf[adm].active_commands[i]); ++ ++ writel_relaxed(DMOV_RSLT_CONF_IRQ_EN ++ | DMOV_RSLT_CONF_FORCE_FLUSH_RSLT, ++ DMOV_REG(DMOV_RSLT_CONF(i), adm)); ++ } ++ wmb(); ++// msm_dmov_clk_off(adm); ++ return ret; ++out_wq: ++ destroy_workqueue(dmov_conf[adm].cmd_wq); ++ return ret; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++#endif ++ ++static struct platform_driver msm_dmov_driver = { ++ .probe = msm_dmov_probe, ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = adm_of_match, ++ .pm = &msm_dmov_dev_pm_ops, ++ }, ++}; ++ ++/* static int __init */ ++static int __init msm_init_datamover(void) ++{ ++ int ret; ++ ret = platform_driver_register(&msm_dmov_driver); ++ if (ret) ++ return ret; ++ return 0; ++} ++arch_initcall(msm_init_datamover); +diff --git a/drivers/mtd/nand/qcom_adm_dma.h b/drivers/mtd/nand/qcom_adm_dma.h +new file mode 100644 +index 0000000..1014d57 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_adm_dma.h +@@ -0,0 +1,268 @@ ++/* * Copyright (c) 2012 The Linux Foundation. All rights reserved.* */ ++/* linux/include/asm-arm/arch-msm/dma.h ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __ASM_ARCH_MSM_DMA_H ++#define __ASM_ARCH_MSM_DMA_H ++#include <linux/list.h> ++ ++struct msm_dmov_errdata { ++ uint32_t flush[6]; ++}; ++ ++struct msm_dmov_cmd { ++ struct list_head list; ++ unsigned int cmdptr; ++ void (*complete_func)(struct msm_dmov_cmd *cmd, ++ unsigned int result, ++ struct msm_dmov_errdata *err); ++ void (*exec_func)(struct msm_dmov_cmd *cmd); ++ struct work_struct work; ++ unsigned id; /* For internal use */ ++ void *user; /* Pointer for caller's reference */ ++ u8 toflush; ++}; ++ ++struct msm_dmov_pdata { ++ int sd; ++ size_t sd_size; ++}; ++ ++void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); ++void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd); ++void msm_dmov_flush(unsigned int id, int graceful); ++int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); ++ ++#define DMOV_CRCIS_PER_CONF 10 ++ ++#define DMOV_ADDR(off, ch) ((off) + ((ch) << 2)) ++ ++#define DMOV_CMD_PTR(ch) DMOV_ADDR(0x000, ch) ++#define DMOV_CMD_LIST (0 << 29) /* does not work */ ++#define DMOV_CMD_PTR_LIST (1 << 29) /* works */ ++#define DMOV_CMD_INPUT_CFG (2 << 29) /* untested */ ++#define DMOV_CMD_OUTPUT_CFG (3 << 29) /* untested */ ++#define DMOV_CMD_ADDR(addr) ((addr) >> 3) ++ ++#define DMOV_RSLT(ch) DMOV_ADDR(0x040, ch) ++#define DMOV_RSLT_VALID (1 << 31) /* 0 == host has empties result fifo */ ++#define DMOV_RSLT_ERROR (1 << 3) ++#define DMOV_RSLT_FLUSH (1 << 2) ++#define DMOV_RSLT_DONE (1 << 1) /* top pointer done */ ++#define DMOV_RSLT_USER (1 << 0) /* command with FR force result */ ++ ++#define DMOV_FLUSH0(ch) DMOV_ADDR(0x080, ch) ++#define DMOV_FLUSH1(ch) DMOV_ADDR(0x0C0, ch) ++#define DMOV_FLUSH2(ch) DMOV_ADDR(0x100, ch) ++#define DMOV_FLUSH3(ch) DMOV_ADDR(0x140, ch) ++#define DMOV_FLUSH4(ch) DMOV_ADDR(0x180, ch) ++#define DMOV_FLUSH5(ch) DMOV_ADDR(0x1C0, ch) ++#define DMOV_FLUSH_TYPE (1 << 31) ++ ++#define DMOV_STATUS(ch) DMOV_ADDR(0x200, ch) ++#define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29)) ++#define DMOV_STATUS_CMD_COUNT(n) (((n) >> 27) & 3) ++#define DMOV_STATUS_RSLT_VALID (1 << 1) ++#define DMOV_STATUS_CMD_PTR_RDY (1 << 0) ++ ++#define DMOV_CONF(ch) DMOV_ADDR(0x240, ch) ++#define DMOV_CONF_SD(sd) (((sd & 4) << 11) | ((sd & 3) << 4)) ++#define DMOV_CONF_OTHER_CH_BLK_MASK(m) ((m << 0x10) & 0xffff0000) ++#define DMOV_CONF_SHADOW_EN (1 << 12) ++#define DMOV_CONF_MPU_DISABLE (1 << 11) ++#define DMOV_CONF_PERM_MPU_CONF (1 << 9) ++#define DMOV_CONF_FLUSH_RSLT_EN (1 << 8) ++#define DMOV_CONF_IRQ_EN (1 << 6) ++#define DMOV_CONF_FORCE_RSLT_EN (1 << 7) ++#define DMOV_CONF_PRIORITY(n) (n << 0) ++ ++#define DMOV_DBG_ERR(ci) DMOV_ADDR(0x280, ci) ++ ++#define DMOV_RSLT_CONF(ch) DMOV_ADDR(0x300, ch) ++#define DMOV_RSLT_CONF_FORCE_TOP_PTR_RSLT (1 << 2) ++#define DMOV_RSLT_CONF_FORCE_FLUSH_RSLT (1 << 1) ++#define DMOV_RSLT_CONF_IRQ_EN (1 << 0) ++ ++#define DMOV_ISR DMOV_ADDR(0x380, 0) ++ ++#define DMOV_CI_CONF(ci) DMOV_ADDR(0x390, ci) ++#define DMOV_CI_CONF_RANGE_END(n) ((n) << 24) ++#define DMOV_CI_CONF_RANGE_START(n) ((n) << 16) ++#define DMOV_CI_CONF_MAX_BURST(n) ((n) << 0) ++ ++#define DMOV_CI_DBG_ERR(ci) DMOV_ADDR(0x3B0, ci) ++ ++#define DMOV_CRCI_CONF0 DMOV_ADDR(0x3D0, 0) ++#define DMOV_CRCI_CONF0_CRCI9_SD (2 << 0x1b) ++ ++#define DMOV_CRCI_CONF1 DMOV_ADDR(0x3D4, 0) ++#define DMOV_CRCI_CONF0_SD(crci, sd) (sd << (crci*3)) ++#define DMOV_CRCI_CONF1_SD(crci, sd) (sd << ((crci-DMOV_CRCIS_PER_CONF)*3)) ++ ++#define DMOV_HI_GP_CTL DMOV_ADDR(0x3D8, 0) ++#define DMOV_HI_GP_CTL_CORE_CLK_LP_EN (1 << 12) ++#define DMOV_HI_GP_CTL_LP_CNT(x) (((x) & 0xf) << 8) ++#define DMOV_HI_GP_CTL_CI3_CLK_LP_EN (1 << 7) ++#define DMOV_HI_GP_CTL_CI2_CLK_LP_EN (1 << 6) ++#define DMOV_HI_GP_CTL_CI1_CLK_LP_EN (1 << 5) ++#define DMOV_HI_GP_CTL_CI0_CLK_LP_EN (1 << 4) ++ ++#define DMOV_CRCI_CTL(crci) DMOV_ADDR(0x400, crci) ++#define DMOV_CRCI_CTL_BLK_SZ(n) ((n) << 0) ++#define DMOV_CRCI_CTL_RST (1 << 17) ++#define DMOV_CRCI_MUX (1 << 18) ++ ++/* channel assignments */ ++ ++/* ++ * Format of CRCI numbers: crci number + (muxsel << 4) ++ */ ++ ++#define DMOV_GP_CHAN 9 ++ ++#define DMOV_CE_IN_CHAN 0 ++#define DMOV_CE_IN_CRCI 2 ++ ++#define DMOV_CE_OUT_CHAN 1 ++#define DMOV_CE_OUT_CRCI 3 ++ ++#define DMOV_TSIF_CHAN 2 ++#define DMOV_TSIF_CRCI 11 ++ ++#define DMOV_HSUART_GSBI6_TX_CHAN 7 ++#define DMOV_HSUART_GSBI6_TX_CRCI 6 ++ ++#define DMOV_HSUART_GSBI6_RX_CHAN 8 ++#define DMOV_HSUART_GSBI6_RX_CRCI 11 ++ ++#define DMOV_HSUART_GSBI8_TX_CHAN 7 ++#define DMOV_HSUART_GSBI8_TX_CRCI 10 ++ ++#define DMOV_HSUART_GSBI8_RX_CHAN 8 ++#define DMOV_HSUART_GSBI8_RX_CRCI 9 ++ ++#define DMOV_HSUART_GSBI9_TX_CHAN 4 ++#define DMOV_HSUART_GSBI9_TX_CRCI 13 ++ ++#define DMOV_HSUART_GSBI9_RX_CHAN 3 ++#define DMOV_HSUART_GSBI9_RX_CRCI 12 ++ ++#define DMOV_NAND_CHAN 3 ++#define DMOV_NAND_CRCI_CMD 15 ++#define DMOV_NAND_CRCI_DATA 3 ++ ++#define DMOV_SPI_GSBI5_RX_CRCI 9 ++#define DMOV_SPI_GSBI5_TX_CRCI 10 ++#define DMOV_SPI_GSBI5_RX_CHAN 6 ++#define DMOV_SPI_GSBI5_TX_CHAN 5 ++ ++/* channels for APQ8064 */ ++#define DMOV8064_CE_IN_CHAN 0 ++#define DMOV8064_CE_IN_CRCI 14 ++ ++#define DMOV8064_CE_OUT_CHAN 1 ++#define DMOV8064_CE_OUT_CRCI 15 ++ ++#define DMOV8064_TSIF_CHAN 2 ++#define DMOV8064_TSIF_CRCI 1 ++ ++/* channels for APQ8064 SGLTE*/ ++#define DMOV_APQ8064_HSUART_GSBI4_TX_CHAN 11 ++#define DMOV_APQ8064_HSUART_GSBI4_TX_CRCI 8 ++ ++#define DMOV_APQ8064_HSUART_GSBI4_RX_CHAN 10 ++#define DMOV_APQ8064_HSUART_GSBI4_RX_CRCI 7 ++ ++/* channels for MPQ8064 */ ++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN 7 ++#define DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI 6 ++ ++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN 6 ++#define DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI 11 ++ ++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CHAN DMOV_MPQ8064_HSUART_GSBI6_TX_CHAN ++#define DMOV_IPQ806X_HSUART_GSBI6_TX_CRCI DMOV_MPQ8064_HSUART_GSBI6_TX_CRCI ++ ++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CHAN DMOV_MPQ8064_HSUART_GSBI6_RX_CHAN ++#define DMOV_IPQ806X_HSUART_GSBI6_RX_CRCI DMOV_MPQ8064_HSUART_GSBI6_RX_CRCI ++ ++/* no client rate control ifc (eg, ram) */ ++#define DMOV_NONE_CRCI 0 ++ ++ ++/* If the CMD_PTR register has CMD_PTR_LIST selected, the data mover ++ * is going to walk a list of 32bit pointers as described below. Each ++ * pointer points to a *array* of dmov_s, etc structs. The last pointer ++ * in the list is marked with CMD_PTR_LP. The last struct in each array ++ * is marked with CMD_LC (see below). ++ */ ++#define CMD_PTR_ADDR(addr) ((addr) >> 3) ++#define CMD_PTR_LP (1 << 31) /* last pointer */ ++#define CMD_PTR_PT (3 << 29) /* ? */ ++ ++/* Single Item Mode */ ++typedef struct { ++ unsigned cmd; ++ unsigned src; ++ unsigned dst; ++ unsigned len; ++} dmov_s; ++ ++/* Scatter/Gather Mode */ ++typedef struct { ++ unsigned cmd; ++ unsigned src_dscr; ++ unsigned dst_dscr; ++ unsigned _reserved; ++} dmov_sg; ++ ++/* Box mode */ ++typedef struct { ++ uint32_t cmd; ++ uint32_t src_row_addr; ++ uint32_t dst_row_addr; ++ uint32_t src_dst_len; ++ uint32_t num_rows; ++ uint32_t row_offset; ++} dmov_box; ++ ++/* bits for the cmd field of the above structures */ ++ ++#define CMD_LC (1 << 31) /* last command */ ++#define CMD_FR (1 << 22) /* force result -- does not work? */ ++#define CMD_OCU (1 << 21) /* other channel unblock */ ++#define CMD_OCB (1 << 20) /* other channel block */ ++#define CMD_TCB (1 << 19) /* ? */ ++#define CMD_DAH (1 << 18) /* destination address hold -- does not work?*/ ++#define CMD_SAH (1 << 17) /* source address hold -- does not work? */ ++ ++#define CMD_MODE_SINGLE (0 << 0) /* dmov_s structure used */ ++#define CMD_MODE_SG (1 << 0) /* untested */ ++#define CMD_MODE_IND_SG (2 << 0) /* untested */ ++#define CMD_MODE_BOX (3 << 0) /* untested */ ++ ++#define CMD_DST_SWAP_BYTES (1 << 14) /* exchange each byte n with byte n+1 */ ++#define CMD_DST_SWAP_SHORTS (1 << 15) /* exchange each short n with short n+1 */ ++#define CMD_DST_SWAP_WORDS (1 << 16) /* exchange each word n with word n+1 */ ++ ++#define CMD_SRC_SWAP_BYTES (1 << 11) /* exchange each byte n with byte n+1 */ ++#define CMD_SRC_SWAP_SHORTS (1 << 12) /* exchange each short n with short n+1 */ ++#define CMD_SRC_SWAP_WORDS (1 << 13) /* exchange each word n with word n+1 */ ++ ++#define CMD_DST_CRCI(n) (((n) & 15) << 7) ++#define CMD_SRC_CRCI(n) (((n) & 15) << 3) ++ ++#endif +diff --git a/drivers/mtd/nand/qcom_nand.c b/drivers/mtd/nand/qcom_nand.c +new file mode 100644 +index 0000000..9314132 +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nand.c +@@ -0,0 +1,7455 @@ ++/* ++ * Copyright (C) 2007 Google, Inc. ++ * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/slab.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/platform_device.h> ++#include <linux/sched.h> ++#include <linux/dma-mapping.h> ++#include <linux/io.h> ++#include <linux/crc16.h> ++#include <linux/bitrev.h> ++#include <linux/clk.h> ++ ++#include <asm/dma.h> ++#include <asm/mach/flash.h> ++ ++#include "qcom_adm_dma.h" ++ ++#include "qcom_nand.h" ++unsigned long msm_nand_phys = 0; ++unsigned long msm_nandc01_phys = 0; ++unsigned long msm_nandc10_phys = 0; ++unsigned long msm_nandc11_phys = 0; ++unsigned long ebi2_register_base = 0; ++static uint32_t dual_nand_ctlr_present; ++static uint32_t interleave_enable; ++static uint32_t enable_bch_ecc; ++static uint32_t boot_layout; ++ ++ ++#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K ++#define MSM_NAND_DMA_BUFFER_SLOTS \ ++ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) ++ ++#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800 ++#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000 ++#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d ++#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d ++ ++#define ONFI_IDENTIFIER_LENGTH 0x0004 ++#define ONFI_PARAM_INFO_LENGTH 0x0200 ++#define ONFI_PARAM_PAGE_LENGTH 0x0100 ++ ++#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F ++ ++#define FLASH_READ_ONFI_IDENTIFIER_COMMAND 0x90 ++#define FLASH_READ_ONFI_IDENTIFIER_ADDRESS 0x20 ++#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC ++#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00 ++ ++#define UD_SIZE_BYTES_MASK (0x3FF << 9) ++#define SPARE_SIZE_BYTES_MASK (0xF << 23) ++#define ECC_NUM_DATA_BYTES_MASK (0x3FF << 16) ++ ++#define VERBOSE 0 ++ ++struct msm_nand_chip { ++ struct device *dev; ++ wait_queue_head_t wait_queue; ++ atomic_t dma_buffer_busy; ++ unsigned dma_channel; ++ uint8_t *dma_buffer; ++ dma_addr_t dma_addr; ++ unsigned CFG0, CFG1, CFG0_RAW, CFG1_RAW; ++ uint32_t ecc_buf_cfg; ++ uint32_t ecc_bch_cfg; ++ uint32_t ecc_parity_bytes; ++ unsigned cw_size; ++ unsigned int uncorrectable_bit_mask; ++ unsigned int num_err_mask; ++}; ++ ++#define CFG1_WIDE_FLASH (1U << 1) ++ ++/* TODO: move datamover code out */ ++ ++#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) ++#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD) ++#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA) ++#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA) ++ ++#define msm_virt_to_dma(chip, vaddr) \ ++ ((chip)->dma_addr + \ ++ ((uint8_t *)(vaddr) - (chip)->dma_buffer)) ++ ++/** ++ * msm_nand_oob_64 - oob info for 2KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_64 = { ++ .eccbytes = 40, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, ++ }, ++ .oobavail = 16, ++ .oobfree = { ++ {30, 16}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_128 - oob info for 4KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_128 = { ++ .eccbytes = 80, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, ++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {70, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_224 - oob info for 4KB page 8Bit interface ++ */ ++static struct nand_ecclayout msm_nand_oob_224_x8 = { ++ .eccbytes = 104, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ++ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ++ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, ++ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, ++ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, ++ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, ++ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, ++ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {91, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_224 - oob info for 4KB page 16Bit interface ++ */ ++static struct nand_ecclayout msm_nand_oob_224_x16 = { ++ .eccbytes = 112, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ++ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ++ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, ++ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, ++ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, ++ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, ++ }, ++ .oobavail = 32, ++ .oobfree = { ++ {98, 32}, ++ } ++}; ++ ++/** ++ * msm_nand_oob_256 - oob info for 8KB page ++ */ ++static struct nand_ecclayout msm_nand_oob_256 = { ++ .eccbytes = 160, ++ .eccpos = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ++ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ++ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ++ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ++ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, ++ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ++ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, ++ 90, 91, 92, 93, 94, 96, 97, 98 , 99, 100, ++ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, ++ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, ++ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, ++ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, ++ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, ++ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, ++ }, ++ .oobavail = 64, ++ .oobfree = { ++ {151, 64}, ++ } ++}; ++ ++/** ++ * msm_onenand_oob_64 - oob info for large (2KB) page ++ */ ++static struct nand_ecclayout msm_onenand_oob_64 = { ++ .eccbytes = 20, ++ .eccpos = { ++ 8, 9, 10, 11, 12, ++ 24, 25, 26, 27, 28, ++ 40, 41, 42, 43, 44, ++ 56, 57, 58, 59, 60, ++ }, ++ .oobavail = 20, ++ .oobfree = { ++ {2, 3}, {14, 2}, {18, 3}, {30, 2}, ++ {34, 3}, {46, 2}, {50, 3}, {62, 2} ++ } ++}; ++ ++static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) ++{ ++ unsigned int bitmask, free_bitmask, old_bitmask; ++ unsigned int need_mask, current_need_mask; ++ int free_index; ++ ++ need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; ++ bitmask = atomic_read(&chip->dma_buffer_busy); ++ free_bitmask = ~bitmask; ++ while (free_bitmask) { ++ free_index = __ffs(free_bitmask); ++ current_need_mask = need_mask << free_index; ++ ++ if (size + free_index * MSM_NAND_DMA_BUFFER_SLOTS >= ++ MSM_NAND_DMA_BUFFER_SIZE) ++ return NULL; ++ ++ if ((bitmask & current_need_mask) == 0) { ++ old_bitmask = ++ atomic_cmpxchg(&chip->dma_buffer_busy, ++ bitmask, ++ bitmask | current_need_mask); ++ if (old_bitmask == bitmask) ++ return chip->dma_buffer + ++ free_index * MSM_NAND_DMA_BUFFER_SLOTS; ++ free_bitmask = 0; /* force return */ ++ } ++ /* current free range was too small, clear all free bits */ ++ /* below the top busy bit within current_need_mask */ ++ free_bitmask &= ++ ~(~0U >> (32 - fls(bitmask & current_need_mask))); ++ } ++ ++ return NULL; ++} ++ ++static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip, ++ void *buffer, size_t size) ++{ ++ int index; ++ unsigned int used_mask; ++ ++ used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; ++ index = ((uint8_t *)buffer - chip->dma_buffer) / ++ MSM_NAND_DMA_BUFFER_SLOTS; ++ atomic_sub(used_mask << index, &chip->dma_buffer_busy); ++ ++ wake_up(&chip->wait_queue); ++} ++ ++ ++unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) ++{ ++ struct { ++ dmov_s cmd; ++ unsigned cmdptr; ++ unsigned data; ++ } *dma_buffer; ++ unsigned rv; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; ++ dma_buffer->cmd.src = addr; ++ dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data); ++ dma_buffer->cmd.len = 4; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ dma_buffer->data = 0xeeeeeeee; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ rv = dma_buffer->data; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ return rv; ++} ++ ++void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) ++{ ++ struct { ++ dmov_s cmd; ++ unsigned cmdptr; ++ unsigned data; ++ } *dma_buffer; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; ++ dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data); ++ dma_buffer->cmd.dst = addr; ++ dma_buffer->cmd.len = 4; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ dma_buffer->data = val; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++} ++ ++/* ++ * Allocates a bounce buffer, and stores the buffer address in ++ * variable pointed to by bounce_buf. bounce_buf should point to a ++ * stack variable, to avoid SMP issues. ++ */ ++static int msm_nand_alloc_bounce(void *addr, size_t size, ++ enum dma_data_direction dir, ++ uint8_t **bounce_buf) ++{ ++ if (bounce_buf == NULL) { ++ printk(KERN_ERR "not allocating bounce buffer\n"); ++ return -EINVAL; ++ } ++ ++ *bounce_buf = kmalloc(size, GFP_KERNEL | GFP_NOFS | GFP_DMA); ++ if (*bounce_buf == NULL) { ++ printk(KERN_ERR "error alloc bounce buffer %zu\n", size); ++ return -ENOMEM; ++ } ++ ++ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) ++ memcpy(*bounce_buf, addr, size); ++ ++ return 0; ++} ++ ++/* ++ * Maps the user buffer for DMA. If the buffer is vmalloced and the ++ * buffer crosses a page boundary, then we kmalloc a bounce buffer and ++ * copy the data into it. The bounce buffer is stored in the variable ++ * pointed to by bounce_buf, for freeing up later on. The bounce_buf ++ * should point to a stack variable, to avoid SMP issues. ++ */ ++static dma_addr_t ++msm_nand_dma_map(struct device *dev, void *addr, size_t size, ++ enum dma_data_direction dir, uint8_t **bounce_buf) ++{ ++ int ret; ++ struct page *page; ++ unsigned long offset = (unsigned long)addr & ~PAGE_MASK; ++ ++ if (virt_addr_valid(addr)) { ++ page = virt_to_page(addr); ++ } else { ++ if (size + offset > PAGE_SIZE) { ++ ret = msm_nand_alloc_bounce(addr, size, dir, bounce_buf); ++ if (ret < 0) ++ return DMA_ERROR_CODE; ++ ++ offset = (unsigned long)*bounce_buf & ~PAGE_MASK; ++ page = virt_to_page(*bounce_buf); ++ } else { ++ page = vmalloc_to_page(addr); ++ } ++ } ++ ++ return dma_map_page(dev, page, offset, size, dir); ++} ++ ++static void msm_nand_dma_unmap(struct device *dev, dma_addr_t addr, size_t size, ++ enum dma_data_direction dir, ++ void *orig_buf, void *bounce_buf) ++{ ++ dma_unmap_page(dev, addr, size, dir); ++ ++ if (bounce_buf != NULL) { ++ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) ++ memcpy(orig_buf, bounce_buf, size); ++ ++ kfree(bounce_buf); ++ } ++} ++ ++uint32_t flash_read_id(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[9]; ++ unsigned cmdptr; ++ unsigned data[7]; ++ } *dma_buffer; ++ uint32_t rv; ++ dmov_s *cmd; ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data[0] = 0 | 4; ++ dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID; ++ dma_buffer->data[2] = 1; ++ dma_buffer->data[3] = 0xeeeeeeee; ++ dma_buffer->data[4] = 0xeeeeeeee; ++ dma_buffer->data[5] = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data[6] = 0x00000000; ++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->data) - 1); ++ ++ cmd = dma_buffer->cmd; ++ ++ cmd->cmd = 0 | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[6]); ++ cmd->dst = MSM_NAND_ADDR1; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[0]); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[1]); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[2]); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_READ_ID; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data[4]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data[5]); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3 ++ ) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ pr_info("status: %x\n", dma_buffer->data[3]); ++ pr_info("nandid: %x maker %02x device %02x\n", ++ dma_buffer->data[4], dma_buffer->data[4] & 0xff, ++ (dma_buffer->data[4] >> 8) & 0xff); ++ rv = dma_buffer->data[4]; ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ return rv; ++} ++ ++struct flash_identification { ++ uint32_t flash_id; ++ uint32_t density; ++ uint32_t widebus; ++ uint32_t pagesize; ++ uint32_t blksize; ++ uint32_t oobsize; ++ uint32_t ecc_correctability; ++} supported_flash; ++ ++uint16_t flash_onfi_crc_check(uint8_t *buffer, uint16_t count) ++{ ++ int i; ++ uint16_t result; ++ ++ for (i = 0; i < count; i++) ++ buffer[i] = bitrev8(buffer[i]); ++ ++ result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count)); ++ ++ for (i = 0; i < count; i++) ++ buffer[i] = bitrev8(buffer[i]); ++ ++ return result; ++} ++ ++static void flash_reset(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[6]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t sflash_bcfg_orig; ++ uint32_t sflash_bcfg_mod; ++ uint32_t chip_select; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ dma_addr_t dma_cmd; ++ dma_addr_t dma_cmdptr; ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data.sflash_bcfg_orig ++ = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data.sflash_bcfg_mod = 0x00000000; ++ dma_buffer->data.chip_select = 4; ++ dma_buffer->data.cmd = MSM_NAND_CMD_RESET; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ ++ cmd = dma_buffer->cmd; ++ ++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_mod); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready, & write Reset command */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the SFLASH_BURST_CONFIG register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sflash_bcfg_orig); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->cmd)); ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_cmd = msm_virt_to_dma(chip, dma_buffer->cmd); ++ dma_buffer->cmdptr = (dma_cmd >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ dma_cmdptr = msm_virt_to_dma(chip, &dma_buffer->cmdptr); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(dma_cmdptr)); ++ mb(); ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++} ++ ++uint32_t flash_onfi_probe(struct msm_nand_chip *chip) ++{ ++ ++ ++ struct onfi_param_page { ++ uint32_t parameter_page_signature; ++ uint16_t revision_number; ++ uint16_t features_supported; ++ uint16_t optional_commands_supported; ++ uint8_t reserved0[22]; ++ uint8_t device_manufacturer[12]; ++ uint8_t device_model[20]; ++ uint8_t jedec_manufacturer_id; ++ uint16_t date_code; ++ uint8_t reserved1[13]; ++ uint32_t number_of_data_bytes_per_page; ++ uint16_t number_of_spare_bytes_per_page; ++ uint32_t number_of_data_bytes_per_partial_page; ++ uint16_t number_of_spare_bytes_per_partial_page; ++ uint32_t number_of_pages_per_block; ++ uint32_t number_of_blocks_per_logical_unit; ++ uint8_t number_of_logical_units; ++ uint8_t number_of_address_cycles; ++ uint8_t number_of_bits_per_cell; ++ uint16_t maximum_bad_blocks_per_logical_unit; ++ uint16_t block_endurance; ++ uint8_t guaranteed_valid_begin_blocks; ++ uint16_t guaranteed_valid_begin_blocks_endurance; ++ uint8_t number_of_programs_per_page; ++ uint8_t partial_program_attributes; ++ uint8_t number_of_bits_ecc_correctability; ++ uint8_t number_of_interleaved_address_bits; ++ uint8_t interleaved_operation_attributes; ++ uint8_t reserved2[13]; ++ uint8_t io_pin_capacitance; ++ uint16_t timing_mode_support; ++ uint16_t program_cache_timing_mode_support; ++ uint16_t maximum_page_programming_time; ++ uint16_t maximum_block_erase_time; ++ uint16_t maximum_page_read_time; ++ uint16_t maximum_change_column_setup_time; ++ uint8_t reserved3[23]; ++ uint16_t vendor_specific_revision_number; ++ uint8_t vendor_specific[88]; ++ uint16_t integrity_crc; ++ ++ } __attribute__((__packed__)); ++ ++ struct onfi_param_page *onfi_param_page_ptr; ++ uint8_t *onfi_identifier_buf = NULL; ++ uint8_t *onfi_param_info_buf = NULL; ++ ++ struct { ++ dmov_s cmd[12]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t devcmd1_orig; ++ uint32_t devcmdvld_orig; ++ uint32_t devcmd1_mod; ++ uint32_t devcmdvld_mod; ++ uint32_t sflash_bcfg_orig; ++ uint32_t sflash_bcfg_mod; ++ uint32_t chip_select; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ unsigned page_address = 0; ++ int err = 0; ++ dma_addr_t dma_addr_param_info = 0; ++ dma_addr_t dma_addr_identifier = 0; ++ unsigned cmd_set_count = 2; ++ unsigned crc_chk_count = 0; ++ ++ /*if (msm_nand_data.nr_parts) { ++ page_address = ((msm_nand_data.parts[0]).offset << 6); ++ } else { ++ pr_err("flash_onfi_probe: " ++ "No partition info available\n"); ++ err = -EIO; ++ return err; ++ }*/ ++ ++ wait_event(chip->wait_queue, (onfi_identifier_buf = ++ msm_nand_get_dma_buffer(chip, ONFI_IDENTIFIER_LENGTH))); ++ dma_addr_identifier = msm_virt_to_dma(chip, onfi_identifier_buf); ++ ++ wait_event(chip->wait_queue, (onfi_param_info_buf = ++ msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH))); ++ dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf); ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ dma_buffer->data.sflash_bcfg_orig = flash_rd_reg ++ (chip, MSM_NAND_SFLASHC_BURST_CFG); ++ dma_buffer->data.devcmd1_orig = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); ++ dma_buffer->data.devcmdvld_orig = flash_rd_reg(chip, ++ MSM_NAND_DEV_CMD_VLD); ++ dma_buffer->data.chip_select = 4; ++ ++ while (cmd_set_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.devcmd1_mod = (dma_buffer->data.devcmd1_orig & ++ 0xFFFFFF00) | (cmd_set_count ++ ? FLASH_READ_ONFI_IDENTIFIER_COMMAND ++ : FLASH_READ_ONFI_PARAMETERS_COMMAND); ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.addr0 = (page_address << 16) | (cmd_set_count ++ ? FLASH_READ_ONFI_IDENTIFIER_ADDRESS ++ : FLASH_READ_ONFI_PARAMETERS_ADDRESS); ++ dma_buffer->data.addr1 = (page_address >> 16) & 0xFF; ++ dma_buffer->data.cfg0 = (cmd_set_count ++ ? MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER ++ : MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO); ++ dma_buffer->data.cfg1 = (cmd_set_count ++ ? MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER ++ : MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO); ++ dma_buffer->data.sflash_bcfg_mod = 0x00000000; ++ dma_buffer->data.devcmdvld_mod = (dma_buffer-> ++ data.devcmdvld_orig & 0xFFFFFFFE); ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ ++ /* Put the Nand ctlr in Async mode and disable SFlash ctlr */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sflash_bcfg_mod); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chip_select); ++ cmd->dst = MSM_NAND_FLASH_CHIP_SELECT; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready, & write CMD,ADDR0,ADDR1,CHIPSEL regs */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Configure the CFG0 and CFG1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Configure the DEV_CMD_VLD register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmdvld_mod); ++ cmd->dst = MSM_NAND_DEV_CMD_VLD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Configure the DEV_CMD1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmd1_mod); ++ cmd->dst = MSM_NAND_DEV_CMD1; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the two status registers */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read data block - valid only if status says success */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = (cmd_set_count ? dma_addr_identifier : ++ dma_addr_param_info); ++ cmd->len = (cmd_set_count ? ONFI_IDENTIFIER_LENGTH : ++ ONFI_PARAM_INFO_LENGTH); ++ cmd++; ++ ++ /* Restore the DEV_CMD1 register */ ++ cmd->cmd = 0 ; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmd1_orig); ++ cmd->dst = MSM_NAND_DEV_CMD1; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the DEV_CMD_VLD register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.devcmdvld_orig); ++ cmd->dst = MSM_NAND_DEV_CMD_VLD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Restore the SFLASH_BURST_CONFIG register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sflash_bcfg_orig); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(12 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* Check for errors, protection violations etc */ ++ if (dma_buffer->data.flash_status & 0x110) { ++ pr_info("MPU/OP error (0x%x) during " ++ "ONFI probe\n", ++ dma_buffer->data.flash_status); ++ err = -EIO; ++ break; ++ } ++ ++ if (cmd_set_count) { ++ onfi_param_page_ptr = (struct onfi_param_page *) ++ (&(onfi_identifier_buf[0])); ++ if (onfi_param_page_ptr->parameter_page_signature != ++ ONFI_PARAMETER_PAGE_SIGNATURE) { ++ pr_info("ONFI probe : Found a non" ++ "ONFI Compliant device \n"); ++ err = -EIO; ++ break; ++ } ++ } else { ++ for (crc_chk_count = 0; crc_chk_count < ++ ONFI_PARAM_INFO_LENGTH ++ / ONFI_PARAM_PAGE_LENGTH; ++ crc_chk_count++) { ++ onfi_param_page_ptr = ++ (struct onfi_param_page *) ++ (&(onfi_param_info_buf ++ [ONFI_PARAM_PAGE_LENGTH * ++ crc_chk_count])); ++ if (flash_onfi_crc_check( ++ (uint8_t *)onfi_param_page_ptr, ++ ONFI_PARAM_PAGE_LENGTH - 2) == ++ onfi_param_page_ptr->integrity_crc) { ++ break; ++ } ++ } ++ if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH ++ / ONFI_PARAM_PAGE_LENGTH) { ++ pr_info("ONFI probe : CRC Check " ++ "failed on ONFI Parameter " ++ "data \n"); ++ err = -EIO; ++ break; ++ } else { ++ supported_flash.flash_id = ++ flash_read_id(chip); ++ supported_flash.widebus = ++ onfi_param_page_ptr-> ++ features_supported & 0x01; ++ supported_flash.pagesize = ++ onfi_param_page_ptr-> ++ number_of_data_bytes_per_page; ++ supported_flash.blksize = ++ onfi_param_page_ptr-> ++ number_of_pages_per_block * ++ supported_flash.pagesize; ++ supported_flash.oobsize = ++ onfi_param_page_ptr-> ++ number_of_spare_bytes_per_page; ++ supported_flash.density = ++ onfi_param_page_ptr-> ++ number_of_blocks_per_logical_unit ++ * supported_flash.blksize; ++ supported_flash.ecc_correctability = ++ onfi_param_page_ptr-> ++ number_of_bits_ecc_correctability; ++ ++ pr_info("ONFI probe : Found an ONFI " ++ "compliant device %s\n", ++ onfi_param_page_ptr->device_model); ++ ++ /* Temporary hack for MT29F4G08ABC device. ++ * Since the device is not properly adhering ++ * to ONFi specification it is reporting ++ * as 16 bit device though it is 8 bit device!!! ++ */ ++ if (!strncmp(onfi_param_page_ptr->device_model, ++ "MT29F4G08ABC", 12)) ++ supported_flash.widebus = 0; ++ } ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ msm_nand_release_dma_buffer(chip, onfi_param_info_buf, ++ ONFI_PARAM_INFO_LENGTH); ++ msm_nand_release_dma_buffer(chip, onfi_identifier_buf, ++ ONFI_IDENTIFIER_LENGTH); ++ ++ return err; ++} ++ ++static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[8 * 5 + 2]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[8]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatasize; ++ uint32_t sectoroobsize; ++ int err, pageerr, rawerr; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *dat_bounce_buf = NULL; ++ uint8_t *oob_bounce_buf = NULL; ++ uint32_t oob_col = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ unsigned start_sector = 0; ++ uint32_t ecc_errors; ++ uint32_t total_ecc_errors = 0; ++ unsigned cwperpage; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, from, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = from >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = from >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", ++ __func__, from); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) ++ start_sector = cwperpage - 1; ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, ++ DMA_FROM_DEVICE, &dat_bounce_buf); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_read_oob: failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0xff, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_BIDIRECTIONAL, ++ &oob_bounce_buf); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_read_oob: failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ oob_col = start_sector * chip->cw_size; ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ oob_col >>= 1; ++ ++ err = 0; ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; ++ dma_buffer->data.cfg0 = ++ (chip->CFG0 & ~(7U << 6)) ++ | (((cwperpage-1) - start_sector) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = (chip->CFG0_RAW ++ & ~(7U << 6)) | ((cwperpage-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ dma_buffer->data.addr0 = (page << 16) | oob_col; ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); ++ ++ for (n = start_sector; n < cwperpage; n++) { ++ /* flash + buffer status return words */ ++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; ++ ++ /* block on cmd ready, then ++ * write CMD / ADDR0 / ADDR1 / CHIPSEL ++ * regs in a burst ++ */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ if (n == start_sector) ++ cmd->len = 16; ++ else ++ cmd->len = 4; ++ cmd++; ++ ++ if (n == start_sector) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */ ++ cmd->len = 8; ++ cmd++; ++ ++ /* read data block ++ * (only valid if status says success) ++ */ ++ if (ops->datbuf) { ++ if (ops->mode != MTD_OPS_RAW) { ++ if (!boot_layout) ++ sectordatasize = (n < (cwperpage - 1)) ++ ? 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatasize = 512; ++ } else { ++ sectordatasize = chip->cw_size; ++ } ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ ++ if (ops->oobbuf && (n == (cwperpage - 1) ++ || ops->mode != MTD_OPS_AUTO_OOB)) { ++ cmd->cmd = 0; ++ if (n == (cwperpage - 1)) { ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ (512 - ((cwperpage - 1) << 2)); ++ sectoroobsize = (cwperpage << 2); ++ if (ops->mode != MTD_OPS_AUTO_OOB) ++ sectoroobsize += ++ chip->ecc_parity_bytes; ++ } else { ++ cmd->src = MSM_NAND_FLASH_BUFFER + 516; ++ sectoroobsize = chip->ecc_parity_bytes; ++ } ++ ++ cmd->dst = oob_dma_addr_curr; ++ if (sectoroobsize < oob_len) ++ cmd->len = sectoroobsize; ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ } ++ ++ BUILD_BUG_ON(8 * 5 + 2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there ++ * was a protection violation (0x100), we lose ++ */ ++ pageerr = rawerr = 0; ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].flash_status & 0x110) { ++ rawerr = -EIO; ++ break; ++ } ++ } ++ if (rawerr) { ++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) { ++ uint8_t *datbuf = ops->datbuf + ++ pages_read * mtd->writesize; ++ ++ dma_sync_single_for_cpu(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < mtd->writesize; n++) { ++ /* empty blocks read 0x54 at ++ * these offsets ++ */ ++ if ((n % 516 == 3 || n % 516 == 175) ++ && datbuf[n] == 0x54) ++ datbuf[n] = 0xff; ++ if (datbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ } ++ if (ops->oobbuf) { ++ dma_sync_single_for_cpu(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < ops->ooblen; n++) { ++ if (ops->oobbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ } ++ } ++ if (pageerr) { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].buffer_status & ++ chip->uncorrectable_bit_mask) { ++ /* not thread safe */ ++ mtd->ecc_stats.failed++; ++ pageerr = -EBADMSG; ++ break; ++ } ++ } ++ } ++ if (!rawerr) { /* check for corretable errors */ ++ for (n = start_sector; n < cwperpage; n++) { ++ ecc_errors = ++ (dma_buffer->data.result[n].buffer_status ++ & chip->num_err_mask); ++ if (ecc_errors) { ++ total_ecc_errors += ecc_errors; ++ /* not thread safe */ ++ mtd->ecc_stats.corrected += ecc_errors; ++ if (ecc_errors > 1) ++ pageerr = -EUCLEAN; ++ } ++ } ++ } ++ if (pageerr && (pageerr != -EUCLEAN || err == 0)) ++ err = pageerr; ++ ++#if VERBOSE ++ if (rawerr && !pageerr) { ++ pr_err("msm_nand_read_oob %llx %x %x empty page\n", ++ (loff_t)page * mtd->writesize, ops->len, ++ ops->ooblen); ++ } else { ++ for (n = start_sector; n < cwperpage; n++) ++ pr_info("flash_status[%d] = %x,\ ++ buffr_status[%d] = %x\n", ++ n, dma_buffer->data.result[n].flash_status, ++ n, dma_buffer->data.result[n].buffer_status); ++ } ++#endif ++ if (err && err != -EUCLEAN && err != -EBADMSG) ++ break; ++ pages_read++; ++ page++; ++ } ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ msm_nand_dma_unmap(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_FROM_DEVICE, ++ ops->oobbuf, oob_bounce_buf); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ msm_nand_dma_unmap(chip->dev, data_dma_addr, ++ ops->len, DMA_BIDIRECTIONAL, ++ ops->datbuf, dat_bounce_buf); ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * ++ pages_read; ++ ops->oobretlen = ops->ooblen - oob_len; ++ if (err) ++ pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", ++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err, ++ total_ecc_errors); ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++static int msm_nand_read_oob_dualnandc(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[16 * 6 + 20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t nandc01_addr0; ++ uint32_t nandc10_addr0; ++ uint32_t nandc11_addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc10_flash_dev_cmd_vld; ++ uint32_t nc10_flash_dev_cmd1; ++ uint32_t nc10_flash_dev_cmd_vld_default; ++ uint32_t nc10_flash_dev_cmd1_default; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[16]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatasize; ++ uint32_t sectoroobsize; ++ int err, pageerr, rawerr; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint32_t oob_col = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ unsigned start_sector = 0; ++ uint32_t ecc_errors; ++ uint32_t total_ecc_errors = 0; ++ unsigned cwperpage; ++ unsigned cw_offset = chip->cw_size; ++#if VERBOSE ++ pr_info("=================================================" ++ "============\n"); ++ pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n\n", ++ __func__, from, ops->mode, ops->datbuf, ++ ops->len, ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = from >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = from >> 12; ++ ++ if (interleave_enable) ++ page = (from >> 1) >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", ++ __func__, from); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) ++ start_sector = cwperpage - 1; ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, ++ DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_read_oob_dualnandc: " ++ "failed to get dma addr for %p\n", ++ ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0xff, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_BIDIRECTIONAL, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_read_oob_dualnandc: " ++ "failed to get dma addr for %p\n", ++ ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ oob_col = start_sector * chip->cw_size; ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ oob_col >>= 1; ++ cw_offset >>= 1; ++ } ++ ++ err = 0; ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; ++ if (start_sector == (cwperpage - 1)) { ++ dma_buffer->data.cfg0 = (chip->CFG0 & ++ ~(7U << 6)); ++ } else { ++ dma_buffer->data.cfg0 = (chip->CFG0 & ++ ~(7U << 6)) ++ | (((cwperpage >> 1)-1) << 6); ++ } ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW & ++ ~(7U << 6)) | ((((cwperpage >> 1)-1) << 6))); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ if (!interleave_enable) { ++ if (start_sector == (cwperpage - 1)) { ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | oob_col; ++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0xD; ++ dma_buffer->data.nc10_flash_dev_cmd1 = ++ 0xF00F3000; ++ } else { ++ dma_buffer->data.nandc01_addr0 = page << 16; ++ /* NC10 ADDR0 points to the next code word */ ++ dma_buffer->data.nandc10_addr0 = (page << 16) | ++ cw_offset; ++ dma_buffer->data.nc10_flash_dev_cmd_vld = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd1 = ++ 0xF00FE005; ++ } ++ } else { ++ dma_buffer->data.nandc01_addr0 = ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | oob_col; ++ } ++ /* ADDR1 */ ++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; ++ ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.nc10_flash_dev_cmd_vld_default = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd1_default = 0xF00F3000; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ /* chipsel_1 + enable DM interface */ ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.result)); ++ ++ for (n = start_sector; n < cwperpage; n++) { ++ /* flash + buffer status return words */ ++ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; ++ ++ if (n == start_sector) { ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.nc10_flash_dev_cmd_vld); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd1); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01, NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC11(MSM_NAND_DEV0_CFG0); ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ } else { ++ /* enable CS0 & CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01, NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS0 for NC01 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs0); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS1 for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs1); ++ cmd->dst = ++ NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* config DEV0_CFG0 & CFG1 for CS0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ /* config DEV1_CFG0 & CFG1 for CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ } ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); ++ cmd->len = 4; ++ cmd++; ++ ++ /* if 'only' the last code word */ ++ if (n == cwperpage - 1) { ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR0 ( 0x0 ) */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute reg for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ } else { ++ /* NC01 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc01_addr0); ++ cmd->dst = NC01(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register for NC01*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ } ++ ++ /* read data block ++ * (only valid if status says success) ++ */ ++ if (ops->datbuf || (ops->oobbuf && ++ ops->mode != MTD_OPS_AUTO_OOB)) { ++ if (ops->mode != MTD_OPS_RAW) ++ sectordatasize = (n < (cwperpage - 1)) ++ ? 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatasize = chip->cw_size; ++ ++ if (n % 2 == 0) { ++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC01, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read only when there is data ++ * buffer ++ */ ++ if (ops->datbuf) { ++ cmd->cmd = 0; ++ cmd->src = ++ NC01(MSM_NAND_FLASH_BUFFER); ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += ++ sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ } else { ++ /* MASK DATA ACK/REQ --> ++ * NC01 (0xA3C) ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10 ++ * then read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = ++ NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.result[n]); ++ /* MSM_NAND_FLASH_STATUS + ++ * MSM_NAND_BUFFER_STATUS ++ */ ++ cmd->len = 8; ++ cmd++; ++ if (n != cwperpage - 1) { ++ /* MASK CMD ACK/REQ --> ++ * NC10 (0xF14) ++ */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* EXEC */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = ++ NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* Read only when there is data ++ * buffer ++ */ ++ if (ops->datbuf) { ++ cmd->cmd = 0; ++ cmd->src = ++ NC10(MSM_NAND_FLASH_BUFFER); ++ cmd->dst = data_dma_addr_curr; ++ data_dma_addr_curr += ++ sectordatasize; ++ cmd->len = sectordatasize; ++ cmd++; ++ } ++ } ++ } ++ ++ if (ops->oobbuf && (n == (cwperpage - 1) ++ || ops->mode != MTD_OPS_AUTO_OOB)) { ++ cmd->cmd = 0; ++ if (n == (cwperpage - 1)) { ++ /* Use NC10 for reading the ++ * last codeword!!! ++ */ ++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ++ (512 - ((cwperpage - 1) << 2)); ++ sectoroobsize = (cwperpage << 2); ++ if (ops->mode != MTD_OPS_AUTO_OOB) ++ sectoroobsize += ++ chip->ecc_parity_bytes; ++ } else { ++ if (n % 2 == 0) ++ cmd->src = ++ NC01(MSM_NAND_FLASH_BUFFER) ++ + 516; ++ else ++ cmd->src = ++ NC10(MSM_NAND_FLASH_BUFFER) ++ + 516; ++ sectoroobsize = chip->ecc_parity_bytes; ++ } ++ cmd->dst = oob_dma_addr_curr; ++ if (sectoroobsize < oob_len) ++ cmd->len = sectoroobsize; ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ } ++ /* ADM --> Default mux state (0xFC0) */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd_vld_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd1_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD1); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* disable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ BUILD_BUG_ON(16 * 6 + 20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there ++ * was a protection violation (0x100), we lose ++ */ ++ pageerr = rawerr = 0; ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].flash_status & 0x110) { ++ rawerr = -EIO; ++ break; ++ } ++ } ++ if (rawerr) { ++ if (ops->datbuf && ops->mode != MTD_OPS_RAW) { ++ uint8_t *datbuf = ops->datbuf + ++ pages_read * mtd->writesize; ++ ++ dma_sync_single_for_cpu(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < mtd->writesize; n++) { ++ /* empty blocks read 0x54 at ++ * these offsets ++ */ ++ if ((n % 516 == 3 || n % 516 == 175) ++ && datbuf[n] == 0x54) ++ datbuf[n] = 0xff; ++ if (datbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ data_dma_addr_curr-mtd->writesize, ++ mtd->writesize, DMA_BIDIRECTIONAL); ++ ++ } ++ if (ops->oobbuf) { ++ dma_sync_single_for_cpu(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ ++ for (n = 0; n < ops->ooblen; n++) { ++ if (ops->oobbuf[n] != 0xff) { ++ pageerr = rawerr; ++ break; ++ } ++ } ++ ++ dma_sync_single_for_device(chip->dev, ++ oob_dma_addr_curr - (ops->ooblen - oob_len), ++ ops->ooblen - oob_len, DMA_BIDIRECTIONAL); ++ } ++ } ++ if (pageerr) { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (dma_buffer->data.result[n].buffer_status ++ & chip->uncorrectable_bit_mask) { ++ /* not thread safe */ ++ mtd->ecc_stats.failed++; ++ pageerr = -EBADMSG; ++ break; ++ } ++ } ++ } ++ if (!rawerr) { /* check for corretable errors */ ++ for (n = start_sector; n < cwperpage; n++) { ++ ecc_errors = dma_buffer->data. ++ result[n].buffer_status ++ & chip->num_err_mask; ++ if (ecc_errors) { ++ total_ecc_errors += ecc_errors; ++ /* not thread safe */ ++ mtd->ecc_stats.corrected += ecc_errors; ++ if (ecc_errors > 1) ++ pageerr = -EUCLEAN; ++ } ++ } ++ } ++ if (pageerr && (pageerr != -EUCLEAN || err == 0)) ++ err = pageerr; ++ ++#if VERBOSE ++ if (rawerr && !pageerr) { ++ pr_err("msm_nand_read_oob_dualnandc " ++ "%llx %x %x empty page\n", ++ (loff_t)page * mtd->writesize, ops->len, ++ ops->ooblen); ++ } else { ++ for (n = start_sector; n < cwperpage; n++) { ++ if (n%2) { ++ pr_info("NC10: flash_status[%d] = %x, " ++ "buffr_status[%d] = %x\n", ++ n, dma_buffer-> ++ data.result[n].flash_status, ++ n, dma_buffer-> ++ data.result[n].buffer_status); ++ } else { ++ pr_info("NC01: flash_status[%d] = %x, " ++ "buffr_status[%d] = %x\n", ++ n, dma_buffer-> ++ data.result[n].flash_status, ++ n, dma_buffer-> ++ data.result[n].buffer_status); ++ } ++ } ++ } ++#endif ++ if (err && err != -EUCLEAN && err != -EBADMSG) ++ break; ++ pages_read++; ++ page++; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_FROM_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ++ ops->len, DMA_BIDIRECTIONAL); ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * ++ pages_read; ++ ops->oobretlen = ops->ooblen - oob_len; ++ if (err) ++ pr_err("msm_nand_read_oob_dualnandc " ++ "%llx %x %x failed %d, corrected %d\n", ++ from, ops->datbuf ? ops->len : 0, ops->ooblen, err, ++ total_ecc_errors); ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==========\n"); ++#endif ++ return err; ++} ++ ++static int ++msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ int ret; ++ struct mtd_ecc_stats stats; ++ struct mtd_oob_ops ops; ++ int (*read_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *); ++ ++ if (!dual_nand_ctlr_present) ++ read_oob = msm_nand_read_oob; ++ else ++ read_oob = msm_nand_read_oob_dualnandc; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobbuf = NULL; ++ ret = 0; ++ *retlen = 0; ++ stats = mtd->ecc_stats; ++ ++ if ((from & (mtd->writesize - 1)) == 0 && len == mtd->writesize) { ++ /* reading a page on page boundary */ ++ ops.len = len; ++ ops.datbuf = buf; ++ ret = read_oob(mtd, from, &ops); ++ *retlen = ops.retlen; ++ } else if (len > 0) { ++ /* reading any size on any offset. partial page is supported */ ++ u8 *bounce_buf; ++ loff_t aligned_from; ++ loff_t offset; ++ size_t actual_len; ++ ++ bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); ++ if (!bounce_buf) { ++ pr_err("%s: could not allocate memory\n", __func__); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ops.len = mtd->writesize; ++ offset = from & (mtd->writesize - 1); ++ aligned_from = from - offset; ++ ++ for (;;) { ++ int no_copy; ++ ++ actual_len = mtd->writesize - offset; ++ if (actual_len > len) ++ actual_len = len; ++ ++ no_copy = (offset == 0 && actual_len == mtd->writesize); ++ ops.datbuf = (no_copy) ? buf : bounce_buf; ++ ++ /* ++ * MTD API requires that all the pages are to ++ * be read even if uncorrectable or ++ * correctable ECC errors occur. ++ */ ++ ret = read_oob(mtd, aligned_from, &ops); ++ if (ret == -EBADMSG || ret == -EUCLEAN) ++ ret = 0; ++ ++ if (ret < 0) ++ break; ++ ++ if (!no_copy) ++ memcpy(buf, bounce_buf + offset, actual_len); ++ ++ len -= actual_len; ++ *retlen += actual_len; ++ if (len == 0) ++ break; ++ ++ buf += actual_len; ++ offset = 0; ++ aligned_from += mtd->writesize; ++ } ++ ++ kfree(bounce_buf); ++ } ++ ++out: ++ if (ret) ++ return ret; ++ ++ if (mtd->ecc_stats.failed - stats.failed) ++ return -EBADMSG; ++ ++ if (mtd->ecc_stats.corrected - stats.corrected) ++ return -EUCLEAN; ++ ++ return 0; ++} ++ ++static int ++msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[8 * 7 + 2]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ uint32_t flash_status[8]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatawritesize; ++ int err = 0; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *dat_bounce_buf = NULL; ++ uint8_t *oob_bounce_buf = NULL; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ unsigned cwperpage; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = to >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = to >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { ++ pr_err("%s: unsupported ops->mode,%d\n", ++ __func__, ops->mode); ++ return -EINVAL; ++ } ++ if ((ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len, " ++ "%d for MTD_OPS_RAW mode\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->datbuf == NULL) { ++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); ++ return -EINVAL; ++ } ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ++ ops->len, DMA_TO_DEVICE, ++ &dat_bounce_buf); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_write_oob: failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_TO_DEVICE, ++ &oob_bounce_buf); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_write_oob: failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ wait_event(chip->wait_queue, (dma_buffer = ++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cfg0 = chip->CFG0; ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cfg0 = (chip->CFG0_RAW & ++ ~(7U << 6)) | ((cwperpage-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; ++ dma_buffer->data.addr0 = page << 16; ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ /* chipsel_0 + enable DM interface */ ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); ++ ++ for (n = 0; n < cwperpage ; n++) { ++ /* status return words */ ++ dma_buffer->data.flash_status[n] = 0xeeeeeeee; ++ /* block on cmd ready, then ++ * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst ++ */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ if (n == 0) ++ cmd->len = 16; ++ else ++ cmd->len = 4; ++ cmd++; ++ ++ if (n == 0) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ /* write data block */ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (!boot_layout) ++ sectordatawritesize = (n < (cwperpage - 1)) ? ++ 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatawritesize = 512; ++ } else { ++ sectordatawritesize = chip->cw_size; ++ } ++ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatawritesize; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = sectordatawritesize; ++ cmd++; ++ ++ if (ops->oobbuf) { ++ if (n == (cwperpage - 1)) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER + ++ (512 - ((cwperpage - 1) << 2)); ++ if ((cwperpage << 2) < oob_len) ++ cmd->len = (cwperpage << 2); ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ if (ops->mode != MTD_OPS_AUTO_OOB) { ++ /* skip ecc bytes in oobbuf */ ++ if (oob_len < chip->ecc_parity_bytes) { ++ oob_dma_addr_curr += ++ chip->ecc_parity_bytes; ++ oob_len -= ++ chip->ecc_parity_bytes; ++ } else { ++ oob_dma_addr_curr += oob_len; ++ oob_len = 0; ++ } ++ } ++ } ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.clrfstatus); ++ cmd->dst = MSM_NAND_FLASH_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.clrrstatus); ++ cmd->dst = MSM_NAND_READ_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ } ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ BUILD_BUG_ON(8 * 7 + 2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | ++ CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( ++ msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there was a ++ * protection violation (0x100), or the program success ++ * bit (0x80) is unset, we lose ++ */ ++ err = 0; ++ for (n = 0; n < cwperpage; n++) { ++ if (dma_buffer->data.flash_status[n] & 0x110) { ++ err = -EIO; ++ break; ++ } ++ if (!(dma_buffer->data.flash_status[n] & 0x80)) { ++ err = -EIO; ++ break; ++ } ++ } ++ ++#if VERBOSE ++ for (n = 0; n < cwperpage; n++) ++ pr_info("write pg %d: flash_status[%d] = %x\n", page, ++ n, dma_buffer->data.flash_status[n]); ++ ++#endif ++ if (err) ++ break; ++ pages_written++; ++ page++; ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; ++ ++ ops->oobretlen = ops->ooblen - oob_len; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ msm_nand_dma_unmap(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_TO_DEVICE, ++ ops->oobbuf, oob_bounce_buf); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ msm_nand_dma_unmap(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE, ops->datbuf, ++ dat_bounce_buf); ++ } ++ if (err) ++ pr_err("msm_nand_write_oob %llx %x %x failed %d\n", ++ to, ops->len, ops->ooblen, err); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++static int ++msm_nand_write_oob_dualnandc(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[16 * 6 + 18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t nandc01_addr0; ++ uint32_t nandc10_addr0; ++ uint32_t nandc11_addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t cfg0_nc01; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc01_flash_dev_cmd_vld; ++ uint32_t nc10_flash_dev_cmd0; ++ uint32_t nc01_flash_dev_cmd_vld_default; ++ uint32_t nc10_flash_dev_cmd0_default; ++ uint32_t flash_status[16]; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned n; ++ unsigned page = 0; ++ uint32_t oob_len; ++ uint32_t sectordatawritesize; ++ int err = 0; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ unsigned cwperpage; ++ unsigned cw_offset = chip->cw_size; ++#if VERBOSE ++ pr_info("=================================================" ++ "============\n"); ++ pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ ++ if (mtd->writesize == 2048) ++ page = to >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = to >> 12; ++ ++ if (interleave_enable) ++ page = (to >> 1) >> 12; ++ ++ oob_len = ops->ooblen; ++ cwperpage = (mtd->writesize >> 9); ++ ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { ++ pr_err("%s: unsupported ops->mode,%d\n", ++ __func__, ops->mode); ++ return -EINVAL; ++ } ++ if ((ops->len % mtd->writesize) != 0) { ++ pr_err("%s: unsupported ops->len, %d\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len, " ++ "%d for MTD_OPS_RAW mode\n", ++ __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if (ops->datbuf == NULL) { ++ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->datbuf, ++ ops->len, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("msm_nand_write_oob_dualnandc:" ++ "failed to get dma addr " ++ "for %p\n", ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = ++ msm_nand_dma_map(chip->dev, ops->oobbuf, ++ ops->ooblen, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("msm_nand_write_oob_dualnandc:" ++ "failed to get dma addr " ++ "for %p\n", ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ wait_event(chip->wait_queue, (dma_buffer = ++ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ cw_offset >>= 1; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ dma_buffer->data.nc01_flash_dev_cmd_vld = 0x9; ++ dma_buffer->data.nc10_flash_dev_cmd0 = 0x1085D060; ++ dma_buffer->data.nc01_flash_dev_cmd_vld_default = 0x1D; ++ dma_buffer->data.nc10_flash_dev_cmd0_default = 0x1080D060; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6)) ++ & ~(1 << 4)) | ((((cwperpage >> 1)-1)) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1; ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ } else { ++ dma_buffer->data.cfg0 = ((chip->CFG0_RAW & ++ ~(7U << 6)) & ~(1 << 4)) | (((cwperpage >> 1)-1) << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ } ++ ++ /* Disables the automatic issuing of the read ++ * status command for first NAND controller. ++ */ ++ if (!interleave_enable) ++ dma_buffer->data.cfg0_nc01 = dma_buffer->data.cfg0 ++ | (1 << 4); ++ else ++ dma_buffer->data.cfg0 |= (1 << 4); ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ /* GO bit for the EXEC register */ ++ dma_buffer->data.exec = 1; ++ ++ if (!interleave_enable) { ++ dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0; ++ /* NC10 ADDR0 points to the next code word */ ++ dma_buffer->data.nandc10_addr0 = ++ (page << 16) | cw_offset; ++ } else { ++ dma_buffer->data.nandc01_addr0 = ++ dma_buffer->data.nandc10_addr0 = (page << 16) | 0x0; ++ } ++ /* ADDR1 */ ++ dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; ++ ++ BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.flash_status)); ++ ++ for (n = 0; n < cwperpage; n++) { ++ /* status return words */ ++ dma_buffer->data.flash_status[n] = 0xeeeeeeee; ++ ++ if (n == 0) { ++ if (!interleave_enable) { ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.nc01_flash_dev_cmd_vld); ++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd0); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* common settings for both NC01 & NC10 ++ * NC01, NC10 --> ADDR1 / CHIPSEL ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 8; ++ cmd++; ++ ++ /* Disables the automatic issue of the ++ * read status command after the write ++ * operation. ++ */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0_nc01); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV0_CFG0); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg1); ++ cmd->dst = NC11(MSM_NAND_DEV0_CFG1); ++ if (enable_bch_ecc) ++ cmd->len = 8; ++ else ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* enable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC11 --> ADDR1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc11_addr1); ++ cmd->dst = NC11(MSM_NAND_ADDR1); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS0 for NC01 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs0); ++ cmd->dst = ++ NC01(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Enable CS1 for NC10 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.chipsel_cs1); ++ cmd->dst = ++ NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ /* config DEV0_CFG0 & CFG1 for CS0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ /* config DEV1_CFG0 & CFG1 for CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ } ++ ++ dma_buffer->data.ecccfg = chip->ecc_buf_cfg; ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ecccfg); ++ cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC01 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc01_addr0); ++ cmd->dst = NC01(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ ++ /* NC10 --> ADDR0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nandc10_addr0); ++ cmd->dst = NC10(MSM_NAND_ADDR0); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ if (n % 2 == 0) { ++ /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* CMD */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) ++ sectordatawritesize = (n < (cwperpage - 1)) ? ++ 516 : (512 - ((cwperpage - 1) << 2)); ++ else ++ sectordatawritesize = chip->cw_size; ++ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ data_dma_addr_curr += sectordatawritesize; ++ ++ if (n % 2 == 0) ++ cmd->dst = NC01(MSM_NAND_FLASH_BUFFER); ++ else ++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER); ++ cmd->len = sectordatawritesize; ++ cmd++; ++ ++ if (ops->oobbuf) { ++ if (n == (cwperpage - 1)) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = NC10(MSM_NAND_FLASH_BUFFER) + ++ (512 - ((cwperpage - 1) << 2)); ++ if ((cwperpage << 2) < oob_len) ++ cmd->len = (cwperpage << 2); ++ else ++ cmd->len = oob_len; ++ oob_dma_addr_curr += cmd->len; ++ oob_len -= cmd->len; ++ if (cmd->len > 0) ++ cmd++; ++ } ++ if (ops->mode != MTD_OPS_AUTO_OOB) { ++ /* skip ecc bytes in oobbuf */ ++ if (oob_len < chip->ecc_parity_bytes) { ++ oob_dma_addr_curr += ++ chip->ecc_parity_bytes; ++ oob_len -= ++ chip->ecc_parity_bytes; ++ } else { ++ oob_dma_addr_curr += oob_len; ++ oob_len = 0; ++ } ++ } ++ } ++ ++ if (n % 2 == 0) { ++ if (n != 0) { ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer-> ++ data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC10, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ } ++ /* kick the NC01 execute register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* block on data ready from NC01, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* kick the execute register */ ++ cmd->cmd = 0; ++ cmd->src = ++ msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ } ++ } ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* we should process outstanding request */ ++ /* block on data ready, then ++ * read the status register ++ */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.flash_status[n-1]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = NC11(MSM_NAND_READ_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ /* MASK DATA ACK/REQ --> NC01 (0xFC0)*/ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ if (!interleave_enable) { ++ /* setting to defalut values back */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc01_flash_dev_cmd_vld_default); ++ cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.nc10_flash_dev_cmd0_default); ++ cmd->dst = NC10(MSM_NAND_DEV_CMD0); ++ cmd->len = 4; ++ cmd++; ++ } else { ++ /* disable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ } ++ ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ BUILD_BUG_ON(16 * 6 + 18 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ ((msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP); ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( ++ msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* if any of the writes failed (0x10), or there was a ++ * protection violation (0x100), or the program success ++ * bit (0x80) is unset, we lose ++ */ ++ err = 0; ++ for (n = 0; n < cwperpage; n++) { ++ if (dma_buffer->data.flash_status[n] & 0x110) { ++ err = -EIO; ++ break; ++ } ++ if (!(dma_buffer->data.flash_status[n] & 0x80)) { ++ err = -EIO; ++ break; ++ } ++ } ++ /* check for flash status busy for the last codeword */ ++ if (!interleave_enable) ++ if (!(dma_buffer->data.flash_status[cwperpage - 1] ++ & 0x20)) { ++ err = -EIO; ++ break; ++ } ++#if VERBOSE ++ for (n = 0; n < cwperpage; n++) { ++ if (n%2) { ++ pr_info("NC10: write pg %d: flash_status[%d] = %x\n", ++ page, n, dma_buffer->data.flash_status[n]); ++ } else { ++ pr_info("NC01: write pg %d: flash_status[%d] = %x\n", ++ page, n, dma_buffer->data.flash_status[n]); ++ } ++ } ++#endif ++ if (err) ++ break; ++ pages_written++; ++ page++; ++ } ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; ++ ++ ops->oobretlen = ops->ooblen - oob_len; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) ++ dma_unmap_page(chip->dev, oob_dma_addr, ++ ops->ooblen, DMA_TO_DEVICE); ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE); ++ if (err) ++ pr_err("msm_nand_write_oob_dualnandc %llx %x %x failed %d\n", ++ to, ops->len, ops->ooblen, err); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==========\n"); ++#endif ++ return err; ++} ++ ++static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ int (*write_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *); ++ ++ if (!dual_nand_ctlr_present) ++ write_oob = msm_nand_write_oob; ++ else ++ write_oob = msm_nand_write_oob_dualnandc; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobbuf = NULL; ++ ret = 0; ++ *retlen = 0; ++ ++ if (!virt_addr_valid(buf) && ++ ((to | len) & (mtd->writesize - 1)) == 0 && ++ ((unsigned long) buf & ~PAGE_MASK) + len > PAGE_SIZE) { ++ /* ++ * Handle writing of large size write buffer in vmalloc ++ * address space that does not fit in an MMU page. ++ * The destination address must be on page boundary, ++ * and the size must be multiple of NAND page size. ++ * Writing partial page is not supported. ++ */ ++ ops.len = mtd->writesize; ++ ++ for (;;) { ++ ops.datbuf = (uint8_t *) buf; ++ ++ ret = write_oob(mtd, to, &ops); ++ if (ret < 0) ++ break; ++ ++ len -= mtd->writesize; ++ *retlen += mtd->writesize; ++ if (len == 0) ++ break; ++ ++ buf += mtd->writesize; ++ to += mtd->writesize; ++ } ++ } else { ++ ops.len = len; ++ ops.datbuf = (uint8_t *) buf; ++ ret = write_oob(mtd, to, &ops); ++ *retlen = ops.retlen; ++ } ++ ++ return ret; ++} ++ ++static int ++msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ int err; ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[6]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t flash_status; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned page = 0; ++ ++ if (mtd->writesize == 2048) ++ page = instr->addr >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = instr->addr >> 12; ++ ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; ++ dma_buffer->data.addr0 = page; ++ dma_buffer->data.addr1 = 0; ++ dma_buffer->data.chipsel = 0 | 4; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status = 0xeeeeeeee; ++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ ++ dma_buffer->data.cfg1 = chip->CFG1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = MSM_NAND_FLASH_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = MSM_NAND_READ_STATUS; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* we fail if there was an operation error, a mpu error, or the ++ * erase success bit was not set. ++ */ ++ ++ if (dma_buffer->data.flash_status & 0x110 || ++ !(dma_buffer->data.flash_status & 0x80)) ++ err = -EIO; ++ else ++ err = 0; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ if (err) { ++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ return err; ++} ++ ++static int ++msm_nand_erase_dualnandc(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ int err; ++ struct msm_nand_chip *chip = mtd->priv; ++ struct { ++ dmov_s cmd[18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ uint32_t nc01_flash_dev_cmd0; ++ uint32_t nc01_flash_dev_cmd0_default; ++ uint32_t flash_status[2]; ++ uint32_t clrfstatus; ++ uint32_t clrrstatus; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ unsigned page = 0; ++ ++ if (mtd->writesize == 2048) ++ page = instr->addr >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = instr->addr >> 12; ++ ++ if (mtd->writesize == 8192) ++ page = (instr->addr >> 1) >> 12; ++ ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; ++ dma_buffer->data.addr0 = page; ++ dma_buffer->data.addr1 = 0; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.flash_status[0] = 0xeeeeeeee; ++ dma_buffer->data.flash_status[1] = 0xeeeeeeee; ++ dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ ++ dma_buffer->data.cfg1 = chip->CFG1; ++ dma_buffer->data.clrfstatus = 0x00000020; ++ dma_buffer->data.clrrstatus = 0x000000C0; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* enable CS1 */ ++ cmd->cmd = 0 | CMD_OCB; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* erase CS0 block now !!! */ ++ /* 0xF14 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF28 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* erase CS1 block now !!! */ ++ /* 0x53C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 12; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); ++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xA3C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); ++ cmd->dst = NC11(MSM_NAND_FLASH_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); ++ cmd->dst = NC11(MSM_NAND_READ_STATUS); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* disable CS1 */ ++ cmd->cmd = CMD_OCU | CMD_LC; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.default_ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ ++ dma_buffer->cmdptr = ++ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd( ++ chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* we fail if there was an operation error, a mpu error, or the ++ * erase success bit was not set. ++ */ ++ ++ if (dma_buffer->data.flash_status[0] & 0x110 || ++ !(dma_buffer->data.flash_status[0] & 0x80) || ++ dma_buffer->data.flash_status[1] & 0x110 || ++ !(dma_buffer->data.flash_status[1] & 0x80)) ++ err = -EIO; ++ else ++ err = 0; ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ if (err) { ++ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ return err; ++} ++ ++static int ++msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ int ret; ++ struct { ++ dmov_s cmd[5]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t eccbchcfg; ++ uint32_t exec; ++ uint32_t ecccfg; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ uint8_t *buf; ++ unsigned page = 0; ++ unsigned cwperpage; ++ ++ if (mtd->writesize == 2048) ++ page = ofs >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = ofs >> 12; ++ ++ cwperpage = (mtd->writesize >> 9); ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer(chip , ++ sizeof(*dma_buffer) + 4))); ++ buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer); ++ ++ /* Read 4 bytes starting from the bad block marker location ++ * in the last code word of the page ++ */ ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ if (enable_bch_ecc) ++ dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ dma_buffer->data.addr0 = (page << 16) | ++ ((chip->cw_size * (cwperpage-1)) >> 1); ++ else ++ dma_buffer->data.addr0 = (page << 16) | ++ (chip->cw_size * (cwperpage-1)); ++ ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ dma_buffer->data.chipsel = 0 | 4; ++ ++ dma_buffer->data.exec = 1; ++ ++ dma_buffer->data.result.flash_status = 0xeeeeeeee; ++ dma_buffer->data.result.buffer_status = 0xeeeeeeee; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_FLASH_CMD; ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = MSM_NAND_DEV0_CFG0; ++ if (enable_bch_ecc) ++ cmd->len = 12; ++ else ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_FLASH_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ (mtd->writesize - (chip->cw_size * (cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf); ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, ++ dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ ret = 0; ++ if (dma_buffer->data.result.flash_status & 0x110) ++ ret = -EIO; ++ ++ if (!ret) { ++ /* Check for bad block marker byte */ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ if (buf[0] != 0xFF || buf[1] != 0xFF) ++ ret = 1; ++ } else { ++ if (buf[0] != 0xFF) ++ ret = 1; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4); ++ return ret; ++} ++ ++static int ++msm_nand_block_isbad_dualnandc(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ int ret; ++ struct { ++ dmov_s cmd[18]; ++ unsigned cmdptr; ++ struct { ++ uint32_t cmd; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t chipsel_cs0; ++ uint32_t chipsel_cs1; ++ uint32_t cfg0; ++ uint32_t cfg1; ++ uint32_t exec; ++ uint32_t ecccfg; ++ uint32_t ebi2_chip_select_cfg0; ++ uint32_t adm_mux_data_ack_req_nc01; ++ uint32_t adm_mux_cmd_ack_req_nc01; ++ uint32_t adm_mux_data_ack_req_nc10; ++ uint32_t adm_mux_cmd_ack_req_nc10; ++ uint32_t adm_default_mux; ++ uint32_t default_ebi2_chip_select_cfg0; ++ struct { ++ uint32_t flash_status; ++ uint32_t buffer_status; ++ } result[2]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ uint8_t *buf01; ++ uint8_t *buf10; ++ unsigned page = 0; ++ unsigned cwperpage; ++ ++ if (mtd->writesize == 2048) ++ page = ofs >> 11; ++ ++ if (mtd->writesize == 4096) ++ page = ofs >> 12; ++ ++ if (mtd->writesize == 8192) ++ page = (ofs >> 1) >> 12; ++ ++ cwperpage = ((mtd->writesize >> 1) >> 9); ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer(chip , ++ sizeof(*dma_buffer) + 8))); ++ buf01 = (uint8_t *)dma_buffer + sizeof(*dma_buffer); ++ buf10 = buf01 + 4; ++ ++ /* Read 4 bytes starting from the bad block marker location ++ * in the last code word of the page ++ */ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; ++ dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); ++ dma_buffer->data.cfg1 = chip->CFG1_RAW | ++ (chip->CFG1 & CFG1_WIDE_FLASH); ++ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) ++ dma_buffer->data.addr0 = (page << 16) | ++ ((528*(cwperpage-1)) >> 1); ++ else ++ dma_buffer->data.addr0 = (page << 16) | ++ (528*(cwperpage-1)); ++ ++ dma_buffer->data.addr1 = (page >> 16) & 0xff; ++ dma_buffer->data.chipsel_cs0 = (1<<4) | 4; ++ dma_buffer->data.chipsel_cs1 = (1<<4) | 5; ++ ++ dma_buffer->data.exec = 1; ++ ++ dma_buffer->data.result[0].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[0].buffer_status = 0xeeeeeeee; ++ dma_buffer->data.result[1].flash_status = 0xeeeeeeee; ++ dma_buffer->data.result[1].buffer_status = 0xeeeeeeee; ++ ++ dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; ++ dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; ++ dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; ++ dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; ++ dma_buffer->data.adm_default_mux = 0x00000FC0; ++ dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; ++ ++ /* Reading last code word from NC01 */ ++ /* enable CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF14 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC01(MSM_NAND_FLASH_CMD); ++ cmd->len = 16; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC01(MSM_NAND_DEV0_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC01(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* 0xF28 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc10); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC01(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[0]); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = NC01(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - ++ (528*(cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf01); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Reading last code word from NC10 */ ++ /* 0x53C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_cmd_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = NC10(MSM_NAND_FLASH_CMD); ++ cmd->len = 12; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); ++ cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); ++ cmd->dst = NC10(MSM_NAND_DEV1_CFG0); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = NC10(MSM_NAND_EXEC_CMD); ++ cmd->len = 4; ++ cmd++; ++ ++ /* A3C */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_mux_data_ack_req_nc01); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = NC10(MSM_NAND_FLASH_STATUS); ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[1]); ++ cmd->len = 8; ++ cmd++; ++ ++ cmd->cmd = 0; ++ cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - ++ (528*(cwperpage-1))); ++ cmd->dst = msm_virt_to_dma(chip, buf10); ++ cmd->len = 4; ++ cmd++; ++ ++ /* FC0 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.adm_default_mux); ++ cmd->dst = EBI2_NAND_ADM_MUX; ++ cmd->len = 4; ++ cmd++; ++ ++ /* disble CS1 */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.ebi2_chip_select_cfg0); ++ cmd->dst = EBI2_CHIP_SELECT_CFG0; ++ cmd->len = 4; ++ cmd++; ++ ++ BUILD_BUG_ON(18 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, ++ dma_buffer->cmd) >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | ++ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); ++ mb(); ++ ++ ret = 0; ++ if ((dma_buffer->data.result[0].flash_status & 0x110) || ++ (dma_buffer->data.result[1].flash_status & 0x110)) ++ ret = -EIO; ++ ++ if (!ret) { ++ /* Check for bad block marker byte for NC01 & NC10 */ ++ if (chip->CFG1 & CFG1_WIDE_FLASH) { ++ if ((buf01[0] != 0xFF || buf01[1] != 0xFF) || ++ (buf10[0] != 0xFF || buf10[1] != 0xFF)) ++ ret = 1; ++ } else { ++ if (buf01[0] != 0xFF || buf10[0] != 0xFF) ++ ret = 1; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 8); ++ return ret; ++} ++ ++static int ++msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int ret; ++ uint8_t *buf; ++ ++ /* Check for invalid offset */ ++ if (ofs > mtd->size) ++ return -EINVAL; ++ if (ofs & (mtd->erasesize - 1)) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ /* ++ Write all 0s to the first page ++ This will set the BB marker to 0 ++ */ ++ buf = page_address(ZERO_PAGE()); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = mtd->writesize + mtd->oobsize; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.datbuf = buf; ++ ops.oobbuf = NULL; ++ if (!interleave_enable) ++ ret = msm_nand_write_oob(mtd, ofs, &ops); ++ else ++ ret = msm_nand_write_oob_dualnandc(mtd, ofs, &ops); ++ ++ return ret; ++} ++ ++/** ++ * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash ++ * @param mtd MTD device structure ++ */ ++static int msm_nand_suspend(struct mtd_info *mtd) ++{ ++ return 0; ++} ++ ++/** ++ * msm_nand_resume - [MTD Interface] Resume the msm_nand flash ++ * @param mtd MTD device structure ++ */ ++static void msm_nand_resume(struct mtd_info *mtd) ++{ ++} ++ ++struct onenand_information { ++ uint16_t manufacturer_id; ++ uint16_t device_id; ++ uint16_t version_id; ++ uint16_t data_buf_size; ++ uint16_t boot_buf_size; ++ uint16_t num_of_buffers; ++ uint16_t technology; ++}; ++ ++static struct onenand_information onenand_info; ++static uint32_t nand_sfcmd_mode; ++ ++uint32_t flash_onenand_probe(struct msm_nand_chip *chip) ++{ ++ struct { ++ dmov_s cmd[7]; ++ unsigned cmdptr; ++ struct { ++ uint32_t bcfg; ++ uint32_t cmd; ++ uint32_t exec; ++ uint32_t status; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ uint32_t initialsflashcmd = 0; ++ ++ initialsflashcmd = flash_rd_reg(chip, MSM_NAND_SFLASHC_CMD); ++ ++ if ((initialsflashcmd & 0x10) == 0x10) ++ nand_sfcmd_mode = MSM_NAND_SFCMD_ASYNC; ++ else ++ nand_sfcmd_mode = MSM_NAND_SFCMD_BURST; ++ ++ printk(KERN_INFO "SFLASHC Async Mode bit: %x \n", nand_sfcmd_mode); ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ dma_buffer->data.bcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.cmd = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.exec = 1; ++ dma_buffer->data.status = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_DEVICE_ID << 16) | ++ (ONENAND_MANUFACTURER_ID); ++ dma_buffer->data.addr1 = (ONENAND_DATA_BUFFER_SIZE << 16) | ++ (ONENAND_VERSION_ID); ++ dma_buffer->data.addr2 = (ONENAND_AMOUNT_OF_BUFFERS << 16) | ++ (ONENAND_BOOT_BUFFER_SIZE); ++ dma_buffer->data.addr3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_TECHNOLOGY << 0); ++ dma_buffer->data.data0 = CLEAN_DATA_32; ++ dma_buffer->data.data1 = CLEAN_DATA_32; ++ dma_buffer->data.data2 = CLEAN_DATA_32; ++ dma_buffer->data.data3 = CLEAN_DATA_32; ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.bcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Configure the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Configure the ADDR2 and ADDR3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the two status registers */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.status); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read data registers - valid only if status says success */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG0; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->len = 16; ++ cmd++; ++ ++ BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ /* Check for errors, protection violations etc */ ++ if (dma_buffer->data.status & 0x110) { ++ pr_info("%s: MPU/OP error" ++ "(0x%x) during Onenand probe\n", ++ __func__, dma_buffer->data.status); ++ err = -EIO; ++ } else { ++ ++ onenand_info.manufacturer_id = ++ (dma_buffer->data.data0 >> 0) & 0x0000FFFF; ++ onenand_info.device_id = ++ (dma_buffer->data.data0 >> 16) & 0x0000FFFF; ++ onenand_info.version_id = ++ (dma_buffer->data.data1 >> 0) & 0x0000FFFF; ++ onenand_info.data_buf_size = ++ (dma_buffer->data.data1 >> 16) & 0x0000FFFF; ++ onenand_info.boot_buf_size = ++ (dma_buffer->data.data2 >> 0) & 0x0000FFFF; ++ onenand_info.num_of_buffers = ++ (dma_buffer->data.data2 >> 16) & 0x0000FFFF; ++ onenand_info.technology = ++ (dma_buffer->data.data3 >> 0) & 0x0000FFFF; ++ ++ ++ pr_info("=======================================" ++ "==========================\n"); ++ ++ pr_info("%s: manufacturer_id = 0x%x\n" ++ , __func__, onenand_info.manufacturer_id); ++ pr_info("%s: device_id = 0x%x\n" ++ , __func__, onenand_info.device_id); ++ pr_info("%s: version_id = 0x%x\n" ++ , __func__, onenand_info.version_id); ++ pr_info("%s: data_buf_size = 0x%x\n" ++ , __func__, onenand_info.data_buf_size); ++ pr_info("%s: boot_buf_size = 0x%x\n" ++ , __func__, onenand_info.boot_buf_size); ++ pr_info("%s: num_of_buffers = 0x%x\n" ++ , __func__, onenand_info.num_of_buffers); ++ pr_info("%s: technology = 0x%x\n" ++ , __func__, onenand_info.technology); ++ ++ pr_info("=======================================" ++ "==========================\n"); ++ ++ if ((onenand_info.manufacturer_id != 0x00EC) ++ || ((onenand_info.device_id & 0x0040) != 0x0040) ++ || (onenand_info.data_buf_size != 0x0800) ++ || (onenand_info.boot_buf_size != 0x0200) ++ || (onenand_info.num_of_buffers != 0x0201) ++ || (onenand_info.technology != 0)) { ++ ++ pr_info("%s: Detected an unsupported device\n" ++ , __func__); ++ err = -EIO; ++ } ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ return err; ++} ++ ++int msm_onenand_read_oob(struct mtd_info *mtd, ++ loff_t from, struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[53]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[9]; ++ uint32_t sfexec; ++ uint32_t sfstat[9]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ uint32_t macro[5]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ int i; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ ++ loff_t from_curr = 0; ++ unsigned page_count; ++ unsigned pages_read = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ uint16_t onenand_sysconfig1; ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: from 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, from, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ if (!mtd) { ++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, ++ (uint32_t)mtd); ++ return -EINVAL; ++ } ++ if (from & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported from, 0x%llx\n", __func__, ++ from); ++ return -EINVAL; ++ } ++ ++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && ++ (ops->mode != MTD_OPS_RAW)) { ++ pr_err("%s: unsupported ops->mode, %d\n", __func__, ++ ops->mode); ++ return -EINVAL; ++ } ++ ++ if (((ops->datbuf == NULL) || (ops->len == 0)) && ++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { ++ pr_err("%s: incorrect ops fields - nothing to do\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->datbuf != NULL) && (ops->len == 0)) { ++ pr_err("%s: data buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { ++ pr_err("%s: oob buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", __func__, ++ ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { ++ pr_err("%s: unsupported operation, oobbuf pointer " ++ "passed in for RAW mode, %x\n", __func__, ++ (uint32_t)ops->oobbuf); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count * mtd->oobsize > ops->ooblen) { ++ pr_err("%s: unsupported ops->ooblen for " ++ "PLACE, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && ++ (ops->ooboffs != 0)) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ++ ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ if (ops->datbuf) { ++ memset(ops->datbuf, 0x55, ops->len); ++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->datbuf, ops->len, DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ memset(ops->oobbuf, 0x55, ops->ooblen); ++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->oobbuf, ops->ooblen, DMA_FROM_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ from_curr = from; ++ ++ while (page_count-- > 0) { ++ ++ cmd = dma_buffer->cmd; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (from_curr >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(from_curr-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)from_curr / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = (((uint32_t)from_curr & ++ (mtd->erasesize - 1)) / mtd->writesize) << 2; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? ++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : ++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(32, 0, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATRD); ++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (onenand_sysconfig1); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDLOADSPARE); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ dma_buffer->data.macro[0] = 0x0200; ++ dma_buffer->data.macro[1] = 0x0300; ++ dma_buffer->data.macro[2] = 0x0400; ++ dma_buffer->data.macro[3] = 0x0500; ++ dma_buffer->data.macro[4] = 0x8010; ++ ++ /*************************************************************/ ++ /* Write necessary address registers in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read necessary status registers from the onenand device */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read the data ram area from the onenand buffer ram */ ++ /*************************************************************/ ++ ++ if (ops->datbuf) { ++ ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDLOAD); ++ ++ for (i = 0; i < 4; i++) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[3+i]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[i]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data rdy, & read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[3+i]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Transfer nand ctlr buf contents to usr buf */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ cmd->len = 512; ++ data_dma_addr_curr += 512; ++ cmd++; ++ } ++ } ++ ++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[7]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[4]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[7]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Transfer nand ctlr buffer contents into usr buf */ ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER + ++ mtd->ecclayout->oobfree[i].offset; ++ cmd->dst = oob_dma_addr_curr; ++ cmd->len = ++ mtd->ecclayout->oobfree[i].length; ++ oob_dma_addr_curr += ++ mtd->ecclayout->oobfree[i].length; ++ cmd++; ++ } ++ } ++ if (ops->mode == MTD_OPS_PLACE_OOB) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = oob_dma_addr_curr; ++ cmd->len = mtd->oobsize; ++ oob_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_RAW) { ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_FLASH_BUFFER; ++ cmd->dst = data_dma_addr_curr; ++ cmd->len = mtd->oobsize; ++ data_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ } ++ ++ /*************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & ++ 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & ++ 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & ++ 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x" ++ "%x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3], ++ dma_buffer->data.sfstat[4], ++ dma_buffer->data.sfstat[5], ++ dma_buffer->data.sfstat[6], ++ dma_buffer->data.sfstat[7], ++ dma_buffer->data.sfstat[8]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[8] & 0x110) ++ || ((dma_buffer->data.sfstat[3] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[4] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[5] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[6] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[7] & 0x110) && ++ ((ops->oobbuf) ++ || (ops->mode == MTD_OPS_RAW)))) { ++ pr_info("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ pages_read++; ++ from_curr += mtd->writesize; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, ++ DMA_FROM_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ if (err) { ++ pr_err("%s: %llx %x %x failed\n", __func__, from_curr, ++ ops->datbuf ? ops->len : 0, ops->ooblen); ++ } else { ++ ops->retlen = ops->oobretlen = 0; ++ if (ops->datbuf != NULL) { ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_read; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) ++ * pages_read; ++ } ++ if (ops->oobbuf != NULL) { ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ops->oobretlen = mtd->oobavail * pages_read; ++ else ++ ops->oobretlen = mtd->oobsize * pages_read; ++ } ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("===================================================" ++ "==============\n"); ++#endif ++ return err; ++} ++ ++int msm_onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.datbuf = buf; ++ ops.len = len; ++ ops.retlen = 0; ++ ops.oobbuf = NULL; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ret = msm_onenand_read_oob(mtd, from, &ops); ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int msm_onenand_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[53]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[10]; ++ uint32_t sfexec; ++ uint32_t sfstat[10]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ uint32_t macro[5]; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ int i, j, k; ++ dma_addr_t data_dma_addr = 0; ++ dma_addr_t oob_dma_addr = 0; ++ dma_addr_t init_dma_addr = 0; ++ dma_addr_t data_dma_addr_curr = 0; ++ dma_addr_t oob_dma_addr_curr = 0; ++ uint8_t *init_spare_bytes; ++ ++ loff_t to_curr = 0; ++ unsigned page_count; ++ unsigned pages_written = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ uint16_t onenand_sysconfig1; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++ ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: to 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" ++ "\noobbuf 0x%p ooblen 0x%x\n", ++ __func__, to, ops->mode, ops->datbuf, ops->len, ++ ops->oobbuf, ops->ooblen); ++#endif ++ if (!mtd) { ++ pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, ++ (uint32_t)mtd); ++ return -EINVAL; ++ } ++ if (to & (mtd->writesize - 1)) { ++ pr_err("%s: unsupported to, 0x%llx\n", __func__, to); ++ return -EINVAL; ++ } ++ ++ if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && ++ (ops->mode != MTD_OPS_RAW)) { ++ pr_err("%s: unsupported ops->mode, %d\n", __func__, ++ ops->mode); ++ return -EINVAL; ++ } ++ ++ if (((ops->datbuf == NULL) || (ops->len == 0)) && ++ ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { ++ pr_err("%s: incorrect ops fields - nothing to do\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->datbuf != NULL) && (ops->len == 0)) { ++ pr_err("%s: data buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { ++ pr_err("%s: oob buffer passed but length 0\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_RAW) { ++ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { ++ /* when ops->datbuf is NULL, ops->len can be ooblen */ ++ pr_err("%s: unsupported ops->len, %d\n", __func__, ++ ops->len); ++ return -EINVAL; ++ } ++ } else { ++ if (ops->datbuf != NULL && ++ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { ++ pr_err("%s: unsupported ops->len," ++ " %d for MTD_OPS_RAW\n", __func__, ops->len); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { ++ pr_err("%s: unsupported operation, oobbuf pointer " ++ "passed in for RAW mode, %x\n", __func__, ++ (uint32_t)ops->oobbuf); ++ return -EINVAL; ++ } ++ ++ if (ops->oobbuf && !ops->datbuf) { ++ page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? ++ mtd->oobavail : mtd->oobsize); ++ if ((page_count == 0) && (ops->ooblen)) ++ page_count = 1; ++ } else if (ops->mode != MTD_OPS_RAW) ++ page_count = ops->len / mtd->writesize; ++ else ++ page_count = ops->len / (mtd->writesize + mtd->oobsize); ++ ++ if ((ops->mode == MTD_OPS_AUTO_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count > 1) { ++ pr_err("%s: unsupported ops->ooblen for" ++ "AUTO, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { ++ if (page_count * mtd->oobsize > ops->ooblen) { ++ pr_err("%s: unsupported ops->ooblen for" ++ "PLACE, %d\n", __func__, ops->ooblen); ++ return -EINVAL; ++ } ++ } ++ ++ if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && ++ (ops->ooboffs != 0)) { ++ pr_err("%s: unsupported ops->ooboffs, %d\n", ++ __func__, ops->ooboffs); ++ return -EINVAL; ++ } ++ ++ init_spare_bytes = kmalloc(64, GFP_KERNEL); ++ if (!init_spare_bytes) { ++ pr_err("%s: failed to alloc init_spare_bytes buffer\n", ++ __func__); ++ return -ENOMEM; ++ } ++ for (i = 0; i < 64; i++) ++ init_spare_bytes[i] = 0xFF; ++ ++ if ((ops->oobbuf) && (ops->mode == MTD_OPS_AUTO_OOB)) { ++ for (i = 0, k = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) ++ for (j = 0; j < mtd->ecclayout->oobfree[i].length; ++ j++) { ++ init_spare_bytes[j + ++ mtd->ecclayout->oobfree[i].offset] ++ = (ops->oobbuf)[k]; ++ k++; ++ } ++ } ++ ++ if (ops->datbuf) { ++ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->datbuf, ops->len, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, data_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->datbuf); ++ return -EIO; ++ } ++ } ++ if (ops->oobbuf) { ++ oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ++ ops->oobbuf, ops->ooblen, DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, oob_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, ops->oobbuf); ++ err = -EIO; ++ goto err_dma_map_oobbuf_failed; ++ } ++ } ++ ++ init_dma_addr = msm_nand_dma_map(chip->dev, init_spare_bytes, 64, ++ DMA_TO_DEVICE, NULL); ++ if (dma_mapping_error(chip->dev, init_dma_addr)) { ++ pr_err("%s: failed to get dma addr for %p\n", ++ __func__, init_spare_bytes); ++ err = -EIO; ++ goto err_dma_map_initbuf_failed; ++ } ++ ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ to_curr = to; ++ ++ while (page_count-- > 0) { ++ cmd = dma_buffer->cmd; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (to_curr >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(to_curr-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)to_curr / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = (((uint32_t)to_curr & ++ (mtd->erasesize - 1)) / mtd->writesize) << 2; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? ++ ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : ++ ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(6, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(32, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_DATWR); ++ dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(1, 6, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[4] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[5] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[6] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[7] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[8] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[9] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (onenand_sysconfig1); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDPROGSPARE); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ dma_buffer->data.macro[0] = 0x0200; ++ dma_buffer->data.macro[1] = 0x0300; ++ dma_buffer->data.macro[2] = 0x0400; ++ dma_buffer->data.macro[3] = 0x0500; ++ dma_buffer->data.macro[4] = 0x8010; ++ ++ ++ /*************************************************************/ ++ /* Write necessary address registers in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Write the data ram area in the onenand buffer ram */ ++ /*************************************************************/ ++ ++ if (ops->datbuf) { ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDPROG); ++ ++ for (i = 0; i < 4; i++) { ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfcmd[1+i]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Trnsfr usr buf contents to nand ctlr buf */ ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = 512; ++ data_dma_addr_curr += 512; ++ cmd++; ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.macro[i]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data rdy, & read status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, ++ &dma_buffer->data.sfstat[1+i]); ++ cmd->len = 4; ++ cmd++; ++ ++ } ++ } ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[5]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { ++ ++ /* Transfer user buf contents into nand ctlr buffer */ ++ if (ops->mode == MTD_OPS_AUTO_OOB) { ++ cmd->cmd = 0; ++ cmd->src = init_dma_addr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_PLACE_OOB) { ++ cmd->cmd = 0; ++ cmd->src = oob_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ oob_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ if (ops->mode == MTD_OPS_RAW) { ++ cmd->cmd = 0; ++ cmd->src = data_dma_addr_curr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ data_dma_addr_curr += mtd->oobsize; ++ cmd++; ++ } ++ } else { ++ cmd->cmd = 0; ++ cmd->src = init_dma_addr; ++ cmd->dst = MSM_NAND_FLASH_BUFFER; ++ cmd->len = mtd->oobsize; ++ cmd++; ++ } ++ ++ /* Write the MACRO1 register */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[4]); ++ cmd->dst = MSM_NAND_MACRO1_REG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[5]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Issuing write command */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[6]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[6]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[7]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[7]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Read necessary status registers from the onenand device */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[9]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[9]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0)&0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16)&0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x %x %x %x" ++ " %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3], ++ dma_buffer->data.sfstat[4], ++ dma_buffer->data.sfstat[5], ++ dma_buffer->data.sfstat[6], ++ dma_buffer->data.sfstat[7], ++ dma_buffer->data.sfstat[8], ++ dma_buffer->data.sfstat[9]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[6] & 0x110) ++ || (dma_buffer->data.sfstat[7] & 0x110) ++ || (dma_buffer->data.sfstat[8] & 0x110) ++ || (dma_buffer->data.sfstat[9] & 0x110) ++ || ((dma_buffer->data.sfstat[1] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[2] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[3] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[4] & 0x110) && ++ (ops->datbuf)) ++ || ((dma_buffer->data.sfstat[5] & 0x110) && ++ ((ops->oobbuf) ++ || (ops->mode == MTD_OPS_RAW)))) { ++ pr_info("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ pages_written++; ++ to_curr += mtd->writesize; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ dma_unmap_page(chip->dev, init_dma_addr, 64, DMA_TO_DEVICE); ++ ++err_dma_map_initbuf_failed: ++ if (ops->oobbuf) { ++ dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, ++ DMA_TO_DEVICE); ++ } ++err_dma_map_oobbuf_failed: ++ if (ops->datbuf) { ++ dma_unmap_page(chip->dev, data_dma_addr, ops->len, ++ DMA_TO_DEVICE); ++ } ++ ++ if (err) { ++ pr_err("%s: %llx %x %x failed\n", __func__, to_curr, ++ ops->datbuf ? ops->len : 0, ops->ooblen); ++ } else { ++ ops->retlen = ops->oobretlen = 0; ++ if (ops->datbuf != NULL) { ++ if (ops->mode != MTD_OPS_RAW) ++ ops->retlen = mtd->writesize * pages_written; ++ else ++ ops->retlen = (mtd->writesize + mtd->oobsize) ++ * pages_written; ++ } ++ if (ops->oobbuf != NULL) { ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ops->oobretlen = mtd->oobavail * pages_written; ++ else ++ ops->oobretlen = mtd->oobsize * pages_written; ++ } ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", ++ __func__, err, ops->retlen, ops->oobretlen); ++ ++ pr_info("=================================================" ++ "================\n"); ++#endif ++ kfree(init_spare_bytes); ++ return err; ++} ++ ++static int msm_onenand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ int ret; ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.datbuf = (uint8_t *)buf; ++ ops.len = len; ++ ops.retlen = 0; ++ ops.oobbuf = NULL; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ret = msm_onenand_write_oob(mtd, to, &ops); ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int msm_onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startbuffer; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t ecc_status; ++ ++ uint64_t temp; ++ ++#if VERBOSE ++ pr_info("=================================================" ++ "================\n"); ++ pr_info("%s: addr 0x%llx len 0x%llx\n", ++ __func__, instr->addr, instr->len); ++#endif ++ if (instr->addr & (mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported erase address, 0x%llx\n", ++ __func__, instr->addr); ++ return -EINVAL; ++ } ++ if (instr->len != mtd->erasesize) { ++ pr_err("%s: Unsupported erase len, %lld\n", ++ __func__, instr->len); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ temp = instr->addr; ++ ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (temp >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(temp-(mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)temp / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ onenand_startbuffer = DATARAM0_0 << 8; ++ ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startbuffer << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMDERAS); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /***************************************************************/ ++ /* Write the necessary address registers in the onenand device */ ++ /***************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Read the necessary status registers from the onenand device */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /***************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /***************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: ecc_status = %x\n", __func__, ++ ecc_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++ if (err) { ++ pr_err("%s: Erase failed, 0x%llx\n", __func__, ++ instr->addr); ++ instr->fail_addr = instr->addr; ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ instr->fail_addr = 0xffffffff; ++ mtd_erase_callback(instr); ++ } ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int rval, i; ++ int ret = 0; ++ uint8_t *buffer; ++ uint8_t *oobptr; ++ ++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ buffer = kmalloc(2112, GFP_KERNEL|GFP_DMA); ++ if (buffer == 0) { ++ pr_err("%s: Could not kmalloc for buffer\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ memset(buffer, 0x00, 2112); ++ oobptr = &(buffer[2048]); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = 2112; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ops.ooboffs = 0; ++ ops.datbuf = buffer; ++ ops.oobbuf = NULL; ++ ++ for (i = 0; i < 2; i++) { ++ ofs = ofs + i*mtd->writesize; ++ rval = msm_onenand_read_oob(mtd, ofs, &ops); ++ if (rval) { ++ pr_err("%s: Error in reading bad blk info\n", ++ __func__); ++ ret = rval; ++ break; ++ } ++ if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) || ++ (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) || ++ (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) || ++ (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF) ++ ) { ++ ret = 1; ++ break; ++ } ++ } ++ ++ kfree(buffer); ++ ++#if VERBOSE ++ if (ret == 1) ++ pr_info("%s : Block containing 0x%x is bad\n", ++ __func__, (unsigned int)ofs); ++#endif ++ return ret; ++} ++ ++static int msm_onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ int rval, i; ++ int ret = 0; ++ uint8_t *buffer; ++ ++ if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { ++ pr_err("%s: unsupported block address, 0x%x\n", ++ __func__, (uint32_t)ofs); ++ return -EINVAL; ++ } ++ ++ buffer = page_address(ZERO_PAGE()); ++ ++ ops.mode = MTD_OPS_RAW; ++ ops.len = 2112; ++ ops.retlen = 0; ++ ops.ooblen = 0; ++ ops.oobretlen = 0; ++ ops.ooboffs = 0; ++ ops.datbuf = buffer; ++ ops.oobbuf = NULL; ++ ++ for (i = 0; i < 2; i++) { ++ ofs = ofs + i*mtd->writesize; ++ rval = msm_onenand_write_oob(mtd, ofs, &ops); ++ if (rval) { ++ pr_err("%s: Error in writing bad blk info\n", ++ __func__); ++ ret = rval; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int msm_onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startblock; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t write_prot_status; ++ ++ uint64_t start_ofs; ++ ++#if VERBOSE ++ pr_info("====================================================" ++ "=============\n"); ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ /* 'ofs' & 'len' should align to block size */ ++ if (ofs&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported ofs address, 0x%llx\n", ++ __func__, ofs); ++ return -EINVAL; ++ } ++ ++ if (len&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported len, %lld\n", ++ __func__, len); ++ return -EINVAL; ++ } ++ ++ if (ofs+len > mtd->size) { ++ pr_err("%s: Maximum chip size exceeded\n", __func__); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { ++#if VERBOSE ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ ++ cmd = dma_buffer->cmd; ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (ofs >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize); ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)ofs / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ onenand_startblock = ((uint32_t)ofs ++ / mtd->erasesize); ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startblock << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMD_UNLOCK); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /*************************************************************/ ++ /* Write the necessary address reg in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Read the necessary status reg from the onenand device */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: write_prot_status = %x\n", __func__, ++ write_prot_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (!(write_prot_status & ONENAND_WP_US)) { ++ pr_err("%s: Unexpected status ofs = 0x%llx," ++ "wp_status = %x\n", ++ __func__, ofs, write_prot_status); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[20]; ++ unsigned cmdptr; ++ struct { ++ uint32_t sfbcfg; ++ uint32_t sfcmd[4]; ++ uint32_t sfexec; ++ uint32_t sfstat[4]; ++ uint32_t addr0; ++ uint32_t addr1; ++ uint32_t addr2; ++ uint32_t addr3; ++ uint32_t addr4; ++ uint32_t addr5; ++ uint32_t addr6; ++ uint32_t data0; ++ uint32_t data1; ++ uint32_t data2; ++ uint32_t data3; ++ uint32_t data4; ++ uint32_t data5; ++ uint32_t data6; ++ } data; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ int err = 0; ++ ++ uint16_t onenand_startaddr1; ++ uint16_t onenand_startaddr8; ++ uint16_t onenand_startaddr2; ++ uint16_t onenand_startblock; ++ ++ uint16_t controller_status; ++ uint16_t interrupt_status; ++ uint16_t write_prot_status; ++ ++ uint64_t start_ofs; ++ ++#if VERBOSE ++ pr_info("====================================================" ++ "=============\n"); ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ /* 'ofs' & 'len' should align to block size */ ++ if (ofs&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported ofs address, 0x%llx\n", ++ __func__, ofs); ++ return -EINVAL; ++ } ++ ++ if (len&(mtd->erasesize - 1)) { ++ pr_err("%s: Unsupported len, %lld\n", ++ __func__, len); ++ return -EINVAL; ++ } ++ ++ if (ofs+len > mtd->size) { ++ pr_err("%s: Maximum chip size exceeded\n", __func__); ++ return -EINVAL; ++ } ++ ++ wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer ++ (chip, sizeof(*dma_buffer)))); ++ ++ for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { ++#if VERBOSE ++ pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); ++#endif ++ ++ cmd = dma_buffer->cmd; ++ if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) ++ && (ofs >= (mtd->size>>1))) { /* DDP Device */ ++ onenand_startaddr1 = DEVICE_FLASHCORE_1 | ++ (((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize)); ++ onenand_startaddr2 = DEVICE_BUFFERRAM_1; ++ onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) ++ / mtd->erasesize); ++ } else { ++ onenand_startaddr1 = DEVICE_FLASHCORE_0 | ++ ((uint32_t)ofs / mtd->erasesize) ; ++ onenand_startaddr2 = DEVICE_BUFFERRAM_0; ++ onenand_startblock = ((uint32_t)ofs ++ / mtd->erasesize); ++ } ++ ++ onenand_startaddr8 = 0x0000; ++ dma_buffer->data.sfbcfg = SFLASH_BCFG | ++ (nand_sfcmd_mode ? 0 : (1 << 24)); ++ dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_INTHI); ++ dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, ++ MSM_NAND_SFCMD_DATXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGRD); ++ dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, ++ MSM_NAND_SFCMD_CMDXS, ++ nand_sfcmd_mode, ++ MSM_NAND_SFCMD_REGWR); ++ dma_buffer->data.sfexec = 1; ++ dma_buffer->data.sfstat[0] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[1] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[2] = CLEAN_DATA_32; ++ dma_buffer->data.sfstat[3] = CLEAN_DATA_32; ++ dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | ++ (ONENAND_START_ADDRESS_2); ++ dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | ++ (ONENAND_COMMAND); ++ dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | ++ (ONENAND_INTERRUPT_STATUS); ++ dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | ++ (ONENAND_SYSTEM_CONFIG_1); ++ dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | ++ (ONENAND_START_ADDRESS_1); ++ dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data1 = (onenand_startaddr8 << 16) | ++ (onenand_startaddr1); ++ dma_buffer->data.data2 = (onenand_startblock << 16) | ++ (onenand_startaddr2); ++ dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | ++ (ONENAND_CMD_LOCK); ++ dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | ++ (CLEAN_DATA_16); ++ dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | ++ (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); ++ dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | ++ (ONENAND_STARTADDR1_RES); ++ ++ /*************************************************************/ ++ /* Write the necessary address reg in the onenand device */ ++ /*************************************************************/ ++ ++ /* Enable and configure the SFlash controller */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); ++ cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the ADDR0 and ADDR1 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); ++ cmd->dst = MSM_NAND_ADDR0; ++ cmd->len = 8; ++ cmd++; ++ ++ /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); ++ cmd->dst = MSM_NAND_ADDR2; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the ADDR6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); ++ cmd->dst = MSM_NAND_ADDR6; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); ++ cmd->dst = MSM_NAND_GENP_REG0; ++ cmd->len = 16; ++ cmd++; ++ ++ /* Write the FLASH_DEV_CMD4,5,6 registers */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->dst = MSM_NAND_DEV_CMD4; ++ cmd->len = 12; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*************************************************************/ ++ /* Wait for the interrupt from the Onenand device controller */ ++ /*************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); ++ cmd->len = 4; ++ cmd++; ++ ++ /*********************************************************/ ++ /* Read the necessary status reg from the onenand device */ ++ /*********************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the GENP3 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_GENP_REG3; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); ++ cmd->len = 4; ++ cmd++; ++ ++ /* Read the DEVCMD4 register */ ++ cmd->cmd = 0; ++ cmd->src = MSM_NAND_DEV_CMD4; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); ++ cmd->len = 4; ++ cmd++; ++ ++ /************************************************************/ ++ /* Restore the necessary registers to proper values */ ++ /************************************************************/ ++ ++ /* Block on cmd ready and write CMD register */ ++ cmd->cmd = DST_CRCI_NAND_CMD; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); ++ cmd->dst = MSM_NAND_SFLASHC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Kick the execute command */ ++ cmd->cmd = 0; ++ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); ++ cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; ++ cmd->len = 4; ++ cmd++; ++ ++ /* Block on data ready, and read the status register */ ++ cmd->cmd = SRC_CRCI_NAND_DATA; ++ cmd->src = MSM_NAND_SFLASHC_STATUS; ++ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); ++ cmd->len = 4; ++ cmd++; ++ ++ ++ BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) ++ >> 3) | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, ++ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ ++ write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; ++ interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; ++ controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; ++ ++#if VERBOSE ++ pr_info("\n%s: sflash status %x %x %x %x\n", __func__, ++ dma_buffer->data.sfstat[0], ++ dma_buffer->data.sfstat[1], ++ dma_buffer->data.sfstat[2], ++ dma_buffer->data.sfstat[3]); ++ ++ pr_info("%s: controller_status = %x\n", __func__, ++ controller_status); ++ pr_info("%s: interrupt_status = %x\n", __func__, ++ interrupt_status); ++ pr_info("%s: write_prot_status = %x\n", __func__, ++ write_prot_status); ++#endif ++ /* Check for errors, protection violations etc */ ++ if ((controller_status != 0) ++ || (dma_buffer->data.sfstat[0] & 0x110) ++ || (dma_buffer->data.sfstat[1] & 0x110) ++ || (dma_buffer->data.sfstat[2] & 0x110) ++ || (dma_buffer->data.sfstat[3] & 0x110)) { ++ pr_err("%s: ECC/MPU/OP error\n", __func__); ++ err = -EIO; ++ } ++ ++ if (!(write_prot_status & ONENAND_WP_LS)) { ++ pr_err("%s: Unexpected status ofs = 0x%llx," ++ "wp_status = %x\n", ++ __func__, ofs, write_prot_status); ++ err = -EIO; ++ } ++ ++ if (err) ++ break; ++ } ++ ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ ++#if VERBOSE ++ pr_info("\n%s: ret %d\n", __func__, err); ++ pr_info("====================================================" ++ "=============\n"); ++#endif ++ return err; ++} ++ ++static int msm_onenand_suspend(struct mtd_info *mtd) ++{ ++ return 0; ++} ++ ++static void msm_onenand_resume(struct mtd_info *mtd) ++{ ++} ++ ++int msm_onenand_scan(struct mtd_info *mtd, int maxchips) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ /* Probe and check whether onenand device is present */ ++ if (flash_onenand_probe(chip)) ++ return -ENODEV; ++ ++ mtd->size = 0x1000000 << ((onenand_info.device_id & 0xF0) >> 4); ++ mtd->writesize = onenand_info.data_buf_size; ++ mtd->oobsize = mtd->writesize >> 5; ++ mtd->erasesize = mtd->writesize << 6; ++ mtd->oobavail = msm_onenand_oob_64.oobavail; ++ mtd->ecclayout = &msm_onenand_oob_64; ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->_erase = msm_onenand_erase; ++ mtd->_point = NULL; ++ mtd->_unpoint = NULL; ++ mtd->_read = msm_onenand_read; ++ mtd->_write = msm_onenand_write; ++ mtd->_read_oob = msm_onenand_read_oob; ++ mtd->_write_oob = msm_onenand_write_oob; ++ mtd->_lock = msm_onenand_lock; ++ mtd->_unlock = msm_onenand_unlock; ++ mtd->_suspend = msm_onenand_suspend; ++ mtd->_resume = msm_onenand_resume; ++ mtd->_block_isbad = msm_onenand_block_isbad; ++ mtd->_block_markbad = msm_onenand_block_markbad; ++ mtd->owner = THIS_MODULE; ++ ++ pr_info("Found a supported onenand device\n"); ++ ++ return 0; ++} ++ ++static const unsigned int bch_sup_cntrl[] = { ++ 0x307, /* MSM7x2xA */ ++ 0x4030, /* MDM 9x15 */ ++}; ++ ++static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) { ++ if (hw_id == bch_sup_cntrl[i]) ++ return true; ++ } ++ ++ return false; ++} ++ ++/** ++ * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device ++ * @param mtd MTD device structure ++ * @param maxchips Number of chips to scan for ++ * ++ * This fills out all the not initialized function pointers ++ * with the defaults. ++ * The flash ID is read and the mtd/chip structures are ++ * filled with the appropriate values. ++ */ ++int msm_nand_scan(struct mtd_info *mtd, int maxchips) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ uint32_t flash_id = 0, i, mtd_writesize; ++ uint8_t dev_found = 0; ++ uint8_t wide_bus; ++ uint32_t manid; ++ uint32_t devid; ++ uint32_t devcfg; ++ struct nand_flash_dev *flashdev = NULL; ++ struct nand_manufacturers *flashman = NULL; ++ unsigned int hw_id; ++ ++ /* ++ * Some Spansion parts, like the S34MS04G2, requires that the ++ * NAND Flash be reset before issuing an ONFI probe. ++ */ ++ flash_reset(chip); ++ ++ /* Probe the Flash device for ONFI compliance */ ++ if (!flash_onfi_probe(chip)) { ++ dev_found = 1; ++ } else { ++ /* Read the Flash ID from the Nand Flash Device */ ++ flash_id = flash_read_id(chip); ++ manid = flash_id & 0xFF; ++ devid = (flash_id >> 8) & 0xFF; ++ devcfg = (flash_id >> 24) & 0xFF; ++ ++ for (i = 0; !flashman && nand_manuf_ids[i].id; ++i) ++ if (nand_manuf_ids[i].id == manid) ++ flashman = &nand_manuf_ids[i]; ++ for (i = 0; !flashdev && nand_flash_ids[i].id; ++i) ++ if (nand_flash_ids[i].id == devid) ++ flashdev = &nand_flash_ids[i]; ++ if (!flashdev || !flashman) { ++ pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", ++ manid, devid); ++ return -ENOENT; ++ } else ++ dev_found = 1; ++ ++ if (!flashdev->pagesize) { ++ supported_flash.flash_id = flash_id; ++ supported_flash.density = flashdev->chipsize << 20; ++ supported_flash.widebus = devcfg & (1 << 6) ? 1 : 0; ++ supported_flash.pagesize = 1024 << (devcfg & 0x3); ++ supported_flash.blksize = (64 * 1024) << ++ ((devcfg >> 4) & 0x3); ++ supported_flash.oobsize = (8 << ((devcfg >> 2) & 0x3)) * ++ (supported_flash.pagesize >> 9); ++ ++ if ((supported_flash.oobsize > 64) && ++ (supported_flash.pagesize == 2048)) { ++ pr_info("msm_nand: Found a 2K page device with" ++ " %d oobsize - changing oobsize to 64 " ++ "bytes.\n", supported_flash.oobsize); ++ supported_flash.oobsize = 64; ++ } ++ } else { ++ supported_flash.flash_id = flash_id; ++ supported_flash.density = flashdev->chipsize << 20; ++ supported_flash.widebus = flashdev->options & ++ NAND_BUSWIDTH_16 ? 1 : 0; ++ supported_flash.pagesize = flashdev->pagesize; ++ supported_flash.blksize = flashdev->erasesize; ++ supported_flash.oobsize = flashdev->pagesize >> 5; ++ } ++ } ++ ++ if (dev_found) { ++ (!interleave_enable) ? (i = 1) : (i = 2); ++ wide_bus = supported_flash.widebus; ++ mtd->size = supported_flash.density * i; ++ mtd->writesize = supported_flash.pagesize * i; ++ mtd->oobsize = supported_flash.oobsize * i; ++ mtd->erasesize = supported_flash.blksize * i; ++ mtd->writebufsize = mtd->writesize; ++ ++ if (!interleave_enable) ++ mtd_writesize = mtd->writesize; ++ else ++ mtd_writesize = mtd->writesize >> 1; ++ ++ /* Check whether controller and NAND device support 8bit ECC*/ ++ hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO); ++ if (msm_nand_has_bch_ecc_engine(hw_id) ++ && (supported_flash.ecc_correctability >= 8)) { ++ pr_info("Found supported NAND device for %dbit ECC\n", ++ supported_flash.ecc_correctability); ++ enable_bch_ecc = 1; ++ } else { ++ pr_info("Found a supported NAND device\n"); ++ } ++ pr_info("NAND Controller ID : 0x%x\n", hw_id); ++ pr_info("NAND Device ID : 0x%x\n", supported_flash.flash_id); ++ pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8); ++ pr_info("Density : %lld MByte\n", (mtd->size>>20)); ++ pr_info("Pagesize : %d Bytes\n", mtd->writesize); ++ pr_info("Erasesize: %d Bytes\n", mtd->erasesize); ++ pr_info("Oobsize : %d Bytes\n", mtd->oobsize); ++ } else { ++ pr_err("Unsupported Nand,Id: 0x%x \n", flash_id); ++ return -ENODEV; ++ } ++ ++ /* Size of each codeword is 532Bytes incase of 8bit BCH ECC*/ ++ chip->cw_size = enable_bch_ecc ? 532 : 528; ++ chip->CFG0 = (((mtd_writesize >> 9)-1) << 6) /* 4/8 cw/pg for 2/4k */ ++ | (516 << 9) /* 516 user data bytes */ ++ | (10 << 19) /* 10 parity bytes */ ++ | (5 << 27) /* 5 address cycles */ ++ | (0 << 30) /* Do not read status before data */ ++ | (1 << 31) /* Send read cmd */ ++ /* 0 spare bytes for 16 bit nand or 1/2 spare bytes for 8 bit */ ++ | (wide_bus ? 0 << 23 : (enable_bch_ecc ? 2 << 23 : 1 << 23)); ++ ++ chip->CFG1 = (0 << 0) /* Enable ecc */ ++ | (7 << 2) /* 8 recovery cycles */ ++ | (0 << 5) /* Allow CS deassertion */ ++ /* Bad block marker location */ ++ | ((mtd_writesize - (chip->cw_size * ( ++ (mtd_writesize >> 9) - 1)) + 1) << 6) ++ | (0 << 16) /* Bad block in user data area */ ++ | (2 << 17) /* 6 cycle tWB/tRB */ ++ | ((wide_bus) ? CFG1_WIDE_FLASH : 0); /* Wide flash bit */ ++ ++ chip->ecc_buf_cfg = 0x203; ++ chip->CFG0_RAW = 0xA80420C0; ++ chip->CFG1_RAW = 0x5045D; ++ ++ if (enable_bch_ecc) { ++ chip->CFG1 |= (1 << 27); /* Enable BCH engine */ ++ chip->ecc_bch_cfg = (0 << 0) /* Enable ECC*/ ++ | (0 << 1) /* Enable/Disable SW reset of ECC engine */ ++ | (1 << 4) /* 8bit ecc*/ ++ | ((wide_bus) ? (14 << 8) : (13 << 8))/*parity bytes*/ ++ | (516 << 16) /* 516 user data bytes */ ++ | (1 << 30); /* Turn on ECC engine clocks always */ ++ chip->CFG0_RAW = 0xA80428C0; /* CW size is increased to 532B */ ++ } ++ ++ /* ++ * For 4bit RS ECC (default ECC), parity bytes = 10 (for x8 and x16 I/O) ++ * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O). ++ */ ++ chip->ecc_parity_bytes = enable_bch_ecc ? (wide_bus ? 14 : 13) : 10; ++ ++ pr_info("CFG0 Init : 0x%08x\n", chip->CFG0); ++ pr_info("CFG1 Init : 0x%08x\n", chip->CFG1); ++ pr_info("ECCBUFCFG : 0x%08x\n", chip->ecc_buf_cfg); ++ ++ if (mtd->oobsize == 64) { ++ mtd->oobavail = msm_nand_oob_64.oobavail; ++ mtd->ecclayout = &msm_nand_oob_64; ++ } else if (mtd->oobsize == 128) { ++ mtd->oobavail = msm_nand_oob_128.oobavail; ++ mtd->ecclayout = &msm_nand_oob_128; ++ } else if (mtd->oobsize == 224) { ++ mtd->oobavail = wide_bus ? msm_nand_oob_224_x16.oobavail : ++ msm_nand_oob_224_x8.oobavail; ++ mtd->ecclayout = wide_bus ? &msm_nand_oob_224_x16 : ++ &msm_nand_oob_224_x8; ++ } else if (mtd->oobsize == 256) { ++ mtd->oobavail = msm_nand_oob_256.oobavail; ++ mtd->ecclayout = &msm_nand_oob_256; ++ } else { ++ pr_err("Unsupported Nand, oobsize: 0x%x \n", ++ mtd->oobsize); ++ return -ENODEV; ++ } ++ ++ /* Fill in remaining MTD driver data */ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ /* mtd->ecctype = MTD_ECC_SW; */ ++ mtd->_erase = msm_nand_erase; ++ mtd->_block_isbad = msm_nand_block_isbad; ++ mtd->_block_markbad = msm_nand_block_markbad; ++ mtd->_point = NULL; ++ mtd->_unpoint = NULL; ++ mtd->_read = msm_nand_read; ++ mtd->_write = msm_nand_write; ++ mtd->_read_oob = msm_nand_read_oob; ++ mtd->_write_oob = msm_nand_write_oob; ++ if (dual_nand_ctlr_present) { ++ mtd->_read_oob = msm_nand_read_oob_dualnandc; ++ mtd->_write_oob = msm_nand_write_oob_dualnandc; ++ if (interleave_enable) { ++ mtd->_erase = msm_nand_erase_dualnandc; ++ mtd->_block_isbad = msm_nand_block_isbad_dualnandc; ++ } ++ } ++ ++ /* mtd->sync = msm_nand_sync; */ ++ mtd->_lock = NULL; ++ /* mtd->_unlock = msm_nand_unlock; */ ++ mtd->_suspend = msm_nand_suspend; ++ mtd->_resume = msm_nand_resume; ++ mtd->owner = THIS_MODULE; ++ ++ /* Unlock whole block */ ++ /* msm_nand_unlock_all(mtd); */ ++ ++ /* return this->scan_bbt(mtd); */ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(msm_nand_scan); ++ ++/** ++ * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device ++ * @param mtd MTD device structure ++ */ ++void msm_nand_release(struct mtd_info *mtd) ++{ ++ /* struct msm_nand_chip *this = mtd->priv; */ ++ ++ /* Deregister the device */ ++ mtd_device_unregister(mtd); ++} ++EXPORT_SYMBOL_GPL(msm_nand_release); ++ ++struct msm_nand_info { ++ struct mtd_info mtd; ++ struct mtd_partition *parts; ++ struct msm_nand_chip msm_nand; ++}; ++ ++/* duplicating the NC01 XFR contents to NC10 */ ++static int msm_nand_nc10_xfr_settings(struct mtd_info *mtd) ++{ ++ struct msm_nand_chip *chip = mtd->priv; ++ ++ struct { ++ dmov_s cmd[2]; ++ unsigned cmdptr; ++ } *dma_buffer; ++ dmov_s *cmd; ++ ++ wait_event(chip->wait_queue, ++ (dma_buffer = msm_nand_get_dma_buffer( ++ chip, sizeof(*dma_buffer)))); ++ ++ cmd = dma_buffer->cmd; ++ ++ /* Copying XFR register contents from NC01 --> NC10 */ ++ cmd->cmd = 0; ++ cmd->src = NC01(MSM_NAND_XFR_STEP1); ++ cmd->dst = NC10(MSM_NAND_XFR_STEP1); ++ cmd->len = 28; ++ cmd++; ++ ++ BUILD_BUG_ON(2 != ARRAY_SIZE(dma_buffer->cmd)); ++ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); ++ dma_buffer->cmd[0].cmd |= CMD_OCB; ++ cmd[-1].cmd |= CMD_OCU | CMD_LC; ++ dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) ++ | CMD_PTR_LP; ++ ++ mb(); ++ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST ++ | DMOV_CMD_ADDR(msm_virt_to_dma(chip, ++ &dma_buffer->cmdptr))); ++ mb(); ++ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); ++ return 0; ++} ++ ++static ssize_t boot_layout_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", boot_layout); ++} ++ ++static ssize_t boot_layout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t n) ++{ ++ struct msm_nand_info *info = dev_get_drvdata(dev); ++ struct msm_nand_chip *chip = info->mtd.priv; ++ unsigned int ud_size; ++ unsigned int spare_size; ++ unsigned int ecc_num_data_bytes; ++ ++ sscanf(buf, "%d", &boot_layout); ++ ++ ud_size = boot_layout? 512: 516; ++ spare_size = boot_layout? (chip->cw_size - ++ (chip->ecc_parity_bytes+ 1+ ud_size)): ++ (enable_bch_ecc ? 2 : 1); ++ ecc_num_data_bytes = boot_layout? 512: 516; ++ ++ chip->CFG0 = (chip->CFG0 & ~SPARE_SIZE_BYTES_MASK); ++ chip->CFG0 |= (spare_size << 23); ++ ++ chip->CFG0 = (chip->CFG0 & ~UD_SIZE_BYTES_MASK); ++ chip->CFG0 |= (ud_size << 9); ++ ++ chip->ecc_buf_cfg = (chip->ecc_buf_cfg & ~ECC_NUM_DATA_BYTES_MASK) ++ | (ecc_num_data_bytes << 16); ++ ++ return n; ++} ++ ++static const DEVICE_ATTR(boot_layout, 0644, boot_layout_show, boot_layout_store); ++ ++static int msm_nand_probe(struct platform_device *pdev) ++ ++{ ++ struct msm_nand_info *info; ++ struct resource *res; ++ int err; ++ struct mtd_part_parser_data ppdata = {}; ++ ++ ++ res = platform_get_resource(pdev, ++ IORESOURCE_MEM, 0); ++ if (!res || !res->start) { ++ pr_err("%s: msm_nand_phys resource invalid/absent\n", ++ __func__); ++ return -ENODEV; ++ } ++ msm_nand_phys = res->start; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(struct msm_nand_info), GFP_KERNEL); ++ if (!info) { ++ pr_err("%s: No memory for msm_nand_info\n", __func__); ++ return -ENOMEM; ++ } ++ ++ info->msm_nand.dev = &pdev->dev; ++ ++ init_waitqueue_head(&info->msm_nand.wait_queue); ++ ++ info->msm_nand.dma_channel = 3; ++ pr_info("%s: dmac 0x%x\n", __func__, info->msm_nand.dma_channel); ++ ++ /* this currently fails if dev is passed in */ ++ info->msm_nand.dma_buffer = ++ dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ &info->msm_nand.dma_addr, GFP_KERNEL); ++ if (info->msm_nand.dma_buffer == NULL) { ++ pr_err("%s: No memory for msm_nand.dma_buffer\n", __func__); ++ err = -ENOMEM; ++ goto out_free_info; ++ } ++ ++ pr_info("%s: allocated dma buffer at %p, dma_addr %x\n", ++ __func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr); ++ ++ /* Let default be VERSION_1 for backward compatibility */ ++ info->msm_nand.uncorrectable_bit_mask = BIT(8); ++ info->msm_nand.num_err_mask = 0x1F; ++ ++ info->mtd.name = dev_name(&pdev->dev); ++ info->mtd.priv = &info->msm_nand; ++ info->mtd.owner = THIS_MODULE; ++ ++ /* config ebi2_cfg register only for ping pong mode!!! */ ++ if (!interleave_enable && dual_nand_ctlr_present) ++ flash_wr_reg(&info->msm_nand, EBI2_CFG_REG, 0x4010080); ++ ++ if (dual_nand_ctlr_present) ++ msm_nand_nc10_xfr_settings(&info->mtd); ++ ++ if (msm_nand_scan(&info->mtd, 1)) ++ if (msm_onenand_scan(&info->mtd, 1)) { ++ pr_err("%s: No nand device found\n", __func__); ++ err = -ENXIO; ++ goto out_free_dma_buffer; ++ } ++ ++ flash_wr_reg(&info->msm_nand, MSM_NAND_DEV_CMD_VLD, ++ DEV_CMD_VLD_SEQ_READ_START_VLD | ++ DEV_CMD_VLD_ERASE_START_VLD | ++ DEV_CMD_VLD_WRITE_START_VLD | ++ DEV_CMD_VLD_READ_START_VLD); ++ ++ ppdata.of_node = pdev->dev.of_node; ++ err = mtd_device_parse_register(&info->mtd, NULL, &ppdata, NULL, 0); ++ ++ if (err < 0) { ++ pr_err("%s: mtd_device_parse_register failed with err=%d\n", ++ __func__, err); ++ goto out_free_dma_buffer; ++ } ++ ++ err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr); ++ if (err) ++ goto out_free_dma_buffer; ++ ++ dev_set_drvdata(&pdev->dev, info); ++ ++ return 0; ++ ++out_free_dma_buffer: ++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ info->msm_nand.dma_buffer, ++ info->msm_nand.dma_addr); ++out_free_info: ++ return err; ++} ++ ++static int msm_nand_remove(struct platform_device *pdev) ++{ ++ struct msm_nand_info *info = dev_get_drvdata(&pdev->dev); ++ ++ dev_set_drvdata(&pdev->dev, NULL); ++ ++ if (info) { ++ msm_nand_release(&info->mtd); ++ dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, ++ info->msm_nand.dma_buffer, ++ info->msm_nand.dma_addr); ++ } ++ ++ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_boot_layout.attr); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_OF ++static const struct of_device_id msm_nand_of_match[] = { ++ { .compatible = "qcom,qcom_nand", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, msm_nand_of_match); ++#endif ++ ++ ++static struct platform_driver msm_nand_driver = { ++ .probe = msm_nand_probe, ++ .remove = msm_nand_remove, ++ .driver = { ++ .name = "qcom_nand", ++ .owner = THIS_MODULE, ++ .of_match_table = msm_nand_of_match, ++ } ++}; ++ ++ ++module_platform_driver(msm_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("msm_nand flash driver code"); +diff --git a/drivers/mtd/nand/qcom_nand.h b/drivers/mtd/nand/qcom_nand.h +new file mode 100644 +index 0000000..468186c +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nand.h +@@ -0,0 +1,196 @@ ++/* drivers/mtd/devices/msm_nand.h ++ * ++ * Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H ++#define __DRIVERS_MTD_DEVICES_MSM_NAND_H ++ ++extern unsigned long msm_nand_phys; ++extern unsigned long msm_nandc01_phys; ++extern unsigned long msm_nandc10_phys; ++extern unsigned long msm_nandc11_phys; ++extern unsigned long ebi2_register_base; ++ ++#define NC01(X) ((X) + msm_nandc01_phys - msm_nand_phys) ++#define NC10(X) ((X) + msm_nandc10_phys - msm_nand_phys) ++#define NC11(X) ((X) + msm_nandc11_phys - msm_nand_phys) ++ ++#define MSM_NAND_REG(off) (msm_nand_phys + (off)) ++ ++#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000) ++#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004) ++#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008) ++#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C) ++#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010) ++#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014) ++#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018) ++#define MSM_NAND_SFLASHC_STATUS MSM_NAND_REG(0x001C) ++#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020) ++#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024) ++#define MSM_NAND_DEV0_ECC_CFG MSM_NAND_REG(0x0028) ++#define MSM_NAND_DEV1_ECC_CFG MSM_NAND_REG(0x002C) ++#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030) ++#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034) ++#define MSM_NAND_SFLASHC_CMD MSM_NAND_REG(0x0038) ++#define MSM_NAND_SFLASHC_EXEC_CMD MSM_NAND_REG(0x003C) ++#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040) ++#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044) ++#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050) ++#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054) ++#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058) ++#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060) ++#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064) ++#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070) ++#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074) ++#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078) ++#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C) ++#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080) ++#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084) ++#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088) ++#define MSM_NAND_GENP_REG0 MSM_NAND_REG(0x0090) ++#define MSM_NAND_GENP_REG1 MSM_NAND_REG(0x0094) ++#define MSM_NAND_GENP_REG2 MSM_NAND_REG(0x0098) ++#define MSM_NAND_GENP_REG3 MSM_NAND_REG(0x009C) ++#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0) ++#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4) ++#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8) ++#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC) ++#define DEV_CMD_VLD_SEQ_READ_START_VLD 0x10 ++#define DEV_CMD_VLD_ERASE_START_VLD 0x8 ++#define DEV_CMD_VLD_WRITE_START_VLD 0x4 ++#define DEV_CMD_VLD_READ_STOP_VLD 0x2 ++#define DEV_CMD_VLD_READ_START_VLD 0x1 ++ ++#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0) ++#define MSM_NAND_ADDR2 MSM_NAND_REG(0x00C0) ++#define MSM_NAND_ADDR3 MSM_NAND_REG(0x00C4) ++#define MSM_NAND_ADDR4 MSM_NAND_REG(0x00C8) ++#define MSM_NAND_ADDR5 MSM_NAND_REG(0x00CC) ++#define MSM_NAND_DEV_CMD3 MSM_NAND_REG(0x00D0) ++#define MSM_NAND_DEV_CMD4 MSM_NAND_REG(0x00D4) ++#define MSM_NAND_DEV_CMD5 MSM_NAND_REG(0x00D8) ++#define MSM_NAND_DEV_CMD6 MSM_NAND_REG(0x00DC) ++#define MSM_NAND_SFLASHC_BURST_CFG MSM_NAND_REG(0x00E0) ++#define MSM_NAND_ADDR6 MSM_NAND_REG(0x00E4) ++#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0) ++#define MSM_NAND_HW_INFO MSM_NAND_REG(0x00FC) ++#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100) ++ ++/* device commands */ ++ ++#define MSM_NAND_CMD_SOFT_RESET 0x01 ++#define MSM_NAND_CMD_PAGE_READ 0x32 ++#define MSM_NAND_CMD_PAGE_READ_ECC 0x33 ++#define MSM_NAND_CMD_PAGE_READ_ALL 0x34 ++#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15 ++#define MSM_NAND_CMD_PRG_PAGE 0x36 ++#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37 ++#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39 ++#define MSM_NAND_CMD_BLOCK_ERASE 0x3A ++#define MSM_NAND_CMD_FETCH_ID 0x0B ++#define MSM_NAND_CMD_STATUS 0x0C ++#define MSM_NAND_CMD_RESET 0x0D ++ ++/* Sflash Commands */ ++ ++#define MSM_NAND_SFCMD_DATXS 0x0 ++#define MSM_NAND_SFCMD_CMDXS 0x1 ++#define MSM_NAND_SFCMD_BURST 0x0 ++#define MSM_NAND_SFCMD_ASYNC 0x1 ++#define MSM_NAND_SFCMD_ABORT 0x1 ++#define MSM_NAND_SFCMD_REGRD 0x2 ++#define MSM_NAND_SFCMD_REGWR 0x3 ++#define MSM_NAND_SFCMD_INTLO 0x4 ++#define MSM_NAND_SFCMD_INTHI 0x5 ++#define MSM_NAND_SFCMD_DATRD 0x6 ++#define MSM_NAND_SFCMD_DATWR 0x7 ++ ++#define SFLASH_PREPCMD(numxfr, offval, delval, trnstp, mode, opcode) \ ++ ((numxfr<<20)|(offval<<12)|(delval<<6)|(trnstp<<5)|(mode<<4)|opcode) ++ ++#define SFLASH_BCFG 0x20100327 ++ ++/* Onenand addresses */ ++ ++#define ONENAND_MANUFACTURER_ID 0xF000 ++#define ONENAND_DEVICE_ID 0xF001 ++#define ONENAND_VERSION_ID 0xF002 ++#define ONENAND_DATA_BUFFER_SIZE 0xF003 ++#define ONENAND_BOOT_BUFFER_SIZE 0xF004 ++#define ONENAND_AMOUNT_OF_BUFFERS 0xF005 ++#define ONENAND_TECHNOLOGY 0xF006 ++#define ONENAND_START_ADDRESS_1 0xF100 ++#define ONENAND_START_ADDRESS_2 0xF101 ++#define ONENAND_START_ADDRESS_3 0xF102 ++#define ONENAND_START_ADDRESS_4 0xF103 ++#define ONENAND_START_ADDRESS_5 0xF104 ++#define ONENAND_START_ADDRESS_6 0xF105 ++#define ONENAND_START_ADDRESS_7 0xF106 ++#define ONENAND_START_ADDRESS_8 0xF107 ++#define ONENAND_START_BUFFER 0xF200 ++#define ONENAND_COMMAND 0xF220 ++#define ONENAND_SYSTEM_CONFIG_1 0xF221 ++#define ONENAND_SYSTEM_CONFIG_2 0xF222 ++#define ONENAND_CONTROLLER_STATUS 0xF240 ++#define ONENAND_INTERRUPT_STATUS 0xF241 ++#define ONENAND_START_BLOCK_ADDRESS 0xF24C ++#define ONENAND_WRITE_PROT_STATUS 0xF24E ++#define ONENAND_ECC_STATUS 0xFF00 ++#define ONENAND_ECC_ERRPOS_MAIN0 0xFF01 ++#define ONENAND_ECC_ERRPOS_SPARE0 0xFF02 ++#define ONENAND_ECC_ERRPOS_MAIN1 0xFF03 ++#define ONENAND_ECC_ERRPOS_SPARE1 0xFF04 ++#define ONENAND_ECC_ERRPOS_MAIN2 0xFF05 ++#define ONENAND_ECC_ERRPOS_SPARE2 0xFF06 ++#define ONENAND_ECC_ERRPOS_MAIN3 0xFF07 ++#define ONENAND_ECC_ERRPOS_SPARE3 0xFF08 ++ ++/* Onenand commands */ ++#define ONENAND_WP_US (1 << 2) ++#define ONENAND_WP_LS (1 << 1) ++ ++#define ONENAND_CMDLOAD 0x0000 ++#define ONENAND_CMDLOADSPARE 0x0013 ++#define ONENAND_CMDPROG 0x0080 ++#define ONENAND_CMDPROGSPARE 0x001A ++#define ONENAND_CMDERAS 0x0094 ++#define ONENAND_CMD_UNLOCK 0x0023 ++#define ONENAND_CMD_LOCK 0x002A ++ ++#define ONENAND_SYSCFG1_ECCENA(mode) (0x40E0 | (mode ? 0 : 0x8002)) ++#define ONENAND_SYSCFG1_ECCDIS(mode) (0x41E0 | (mode ? 0 : 0x8002)) ++ ++#define ONENAND_CLRINTR 0x0000 ++#define ONENAND_STARTADDR1_RES 0x07FF ++#define ONENAND_STARTADDR3_RES 0x07FF ++ ++#define DATARAM0_0 0x8 ++#define DEVICE_FLASHCORE_0 (0 << 15) ++#define DEVICE_FLASHCORE_1 (1 << 15) ++#define DEVICE_BUFFERRAM_0 (0 << 15) ++#define DEVICE_BUFFERRAM_1 (1 << 15) ++#define ONENAND_DEVICE_IS_DDP (1 << 3) ++ ++#define CLEAN_DATA_16 0xFFFF ++#define CLEAN_DATA_32 0xFFFFFFFF ++ ++#define EBI2_REG(off) (ebi2_register_base + (off)) ++#define EBI2_CHIP_SELECT_CFG0 EBI2_REG(0x0000) ++#define EBI2_CFG_REG EBI2_REG(0x0004) ++#define EBI2_NAND_ADM_MUX EBI2_REG(0x005C) ++ ++extern struct flash_platform_data msm_nand_data; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch new file mode 100644 index 0000000000..b79c16f34e --- /dev/null +++ b/target/linux/ipq806x/patches/0151-ARM-ipq8064-Add-nand-device-info.patch @@ -0,0 +1,115 @@ +From 4490cfa66379909cdddc3518c8e75b7cd26d8f69 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:53:49 -0500 +Subject: [PATCH 151/182] ARM: ipq8064: Add nand device info + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 34 ++++++++++++++++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++ + 2 files changed, 67 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index c752889..4062eb6 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -45,6 +45,29 @@ + bias-none; + }; + }; ++ nand_pins: nand_pins { ++ mux { ++ pins = "gpio34", "gpio35", "gpio36", ++ "gpio37", "gpio38", "gpio39", ++ "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-disable; ++ }; ++ pullups { ++ pins = "gpio39"; ++ bias-pull-up; ++ }; ++ hold { ++ pins = "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ bias-bus-hold; ++ }; ++ }; ++ + }; + + gsbi@16300000 { +@@ -126,5 +149,16 @@ + sata@29000000 { + status = "ok"; + }; ++ ++ dma@18300000 { ++ status = "ok"; ++ }; ++ ++ nand@0x1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 93c0315..d9fce15 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -76,6 +76,7 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 32 0x4>; ++ + }; + + intc: interrupt-controller@2000000 { +@@ -369,5 +370,37 @@ + phy-names = "sata-phy"; + status = "disabled"; + }; ++ ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core_clk", "iface_clk"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_PBUS_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ ++ reset-names = "adm", "pbus", "c0", "c1", "c2"; ++ ++ status = "disabled"; ++ }; ++ ++ nand@0x1ac00000 { ++ compatible = "qcom,qcom_nand"; ++ reg = <0x1ac00000 0x800>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ clocks = <&gcc EBI2_CLK>; ++ clock-names = "core_clk"; ++ ++ ++ status = "disabled"; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch new file mode 100644 index 0000000000..97f012dfb6 --- /dev/null +++ b/target/linux/ipq806x/patches/0152-ARM-qcom-config-Add-NAND-config-options.patch @@ -0,0 +1,27 @@ +From d04f697bf7cfe756de6187be1d0e5669d4d5bca0 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 17:30:04 -0500 +Subject: [PATCH 152/182] ARM: qcom: config: Add NAND config options + +Add NAND config options to enable QCOM NAND controller + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 85a35af..e3f9013 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -56,6 +56,7 @@ CONFIG_DEVTMPFS_MOUNT=y + CONFIG_MTD=y + CONFIG_MTD_BLOCK=y + CONFIG_MTD_M25P80=y ++CONFIG_MTD_NAND=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_RAM=y + CONFIG_SCSI_TGT=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch new file mode 100644 index 0000000000..03b65e6872 --- /dev/null +++ b/target/linux/ipq806x/patches/0153-soc-qcom-tcsr-Add-TCSR-driver.patch @@ -0,0 +1,169 @@ +From 0b469f3f4c0e6a0e0b1b60a059600e325a03b6f5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 00:52:06 -0500 +Subject: [PATCH 153/182] soc: qcom: tcsr: Add TCSR driver + +This patch adds support for the TCSR IP block present in Qualcomm processors. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + .../devicetree/bindings/soc/qcom/qcom,tcsr.txt | 25 ++++++++ + drivers/soc/qcom/Kconfig | 6 ++ + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/qcom_tcsr.c | 64 ++++++++++++++++++++ + include/dt-bindings/soc/qcom,tcsr.h | 19 ++++++ + 5 files changed, 115 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt + create mode 100644 drivers/soc/qcom/qcom_tcsr.c + create mode 100644 include/dt-bindings/soc/qcom,tcsr.h + +diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt +new file mode 100644 +index 0000000..6ea74c1 +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,tcsr.txt +@@ -0,0 +1,25 @@ ++QCOM TCSR (Top Control and Status Register) Driver ++ ++The TCSR provides miscellaneous control functions and status registers for ++Qualcomm processors. ++ ++Required properties: ++- compatible: must contain "qcom,tcsr" for IPQ806x ++- reg: Address range for TCSR registers ++ ++Optional properties: ++- qcom,usb-ctrl-select : indicates USB port type selection. Please reference ++ dt-bindings/soc/qcom,tcsr.h for valid USB port selection values. ++ ++Example for IPQ8064: ++ ++#include <dt-bindings/soc/qcom,tcsr.h> ++ ++ tcsr: tcsr@1a400000 { ++ compatible = "qcom,tcsr"; ++ reg = <0x1a400000 0x100>; ++ ++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; ++ }; ++ ++ +diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig +index 7bd2c94..3e4486a 100644 +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -9,3 +9,9 @@ config QCOM_GSBI + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. + ++config QCOM_TCSR ++ tristate "QCOM Top Control and Status Registers" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable TCSR support. The TCSR provides control ++ functions for various peripherals. +diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile +index 4389012..d299492 100644 +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o ++obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o +diff --git a/drivers/soc/qcom/qcom_tcsr.c b/drivers/soc/qcom/qcom_tcsr.c +new file mode 100644 +index 0000000..dd33153 +--- /dev/null ++++ b/drivers/soc/qcom/qcom_tcsr.c +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++ ++#define TCSR_USB_PORT_SEL 0xb0 ++ ++static int tcsr_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ const struct device_node *node = pdev->dev.of_node; ++ void __iomem *base; ++ u32 val; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) { ++ dev_err(&pdev->dev, "setting usb port select = %d\n", val); ++ writel(val, base + TCSR_USB_PORT_SEL); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id tcsr_dt_match[] = { ++ { .compatible = "qcom,tcsr", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, tcsr_dt_match); ++ ++static struct platform_driver tcsr_driver = { ++ .driver = { ++ .name = "tcsr", ++ .owner = THIS_MODULE, ++ .of_match_table = tcsr_dt_match, ++ }, ++ .probe = tcsr_probe, ++}; ++ ++module_platform_driver(tcsr_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM TCSR driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/soc/qcom,tcsr.h b/include/dt-bindings/soc/qcom,tcsr.h +new file mode 100644 +index 0000000..c9d497a +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,tcsr.h +@@ -0,0 +1,19 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DT_BINDINGS_QCOM_TCSR_H ++#define __DT_BINDINGS_QCOM_TCSR_H ++ ++#define TCSR_USB_SELECT_USB3_P0 0x1 ++#define TCSR_USB_SELECT_USB3_P1 0x2 ++#define TCSR_USB_SELECT_USB3_DUAL 0x3 ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch new file mode 100644 index 0000000000..28951ab18b --- /dev/null +++ b/target/linux/ipq806x/patches/0154-clk-qcom-Correct-UTMI-clock-frequency-table.patch @@ -0,0 +1,29 @@ +From 2561c49dcf5cb35ad72df34bf225a51fe2fa87e5 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 12 Jun 2014 00:53:19 -0500 +Subject: [PATCH 154/182] clk: qcom: Correct UTMI clock frequency table + +This patch changes the UTMI clock frequency from 20MHz to the correct value of +60MHz. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index f7916be..d80dc69 100644 +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -1992,7 +1992,7 @@ static struct clk_branch usb30_1_branch_clk = { + }; + + static const struct freq_tbl clk_tbl_usb30_utmi[] = { +- { 60000000, P_PLL0, 1, 1, 40 }, ++ { 60000000, P_PLL8, 1, 5, 32 }, + { } + }; + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch new file mode 100644 index 0000000000..7bfbbcb8f1 --- /dev/null +++ b/target/linux/ipq806x/patches/0155-clk-qcom-Fix-incorrect-UTMI-DT-include-values.patch @@ -0,0 +1,63 @@ +From 9ab5cb48696dca02bf43170b50d1034a96fb9e85 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sun, 15 Jun 2014 00:39:57 -0500 +Subject: [PATCH 155/182] clk: qcom: Fix incorrect UTMI DT include values + +Corrected values for UTMI clock definitions. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 38 +++++++++++++------------- + 1 file changed, 19 insertions(+), 19 deletions(-) + +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +index 0fd3e8a..163ba85 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -273,24 +273,24 @@ + #define USB30_SLEEP_CLK 262 + #define USB30_UTMI_SRC 263 + #define USB30_0_UTMI_CLK 264 +-#define USB30_1_UTMI_CLK 264 +-#define USB30_MASTER_SRC 265 +-#define USB30_0_MASTER_CLK 266 +-#define USB30_1_MASTER_CLK 267 +-#define GMAC_CORE1_CLK_SRC 268 +-#define GMAC_CORE2_CLK_SRC 269 +-#define GMAC_CORE3_CLK_SRC 270 +-#define GMAC_CORE4_CLK_SRC 271 +-#define GMAC_CORE1_CLK 272 +-#define GMAC_CORE2_CLK 273 +-#define GMAC_CORE3_CLK 274 +-#define GMAC_CORE4_CLK 275 +-#define UBI32_CORE1_CLK_SRC 276 +-#define UBI32_CORE2_CLK_SRC 277 +-#define UBI32_CORE1_CLK 278 +-#define UBI32_CORE2_CLK 279 +-#define NSSTCM_CLK_SRC 280 +-#define NSSTCM_CLK 281 +-#define NSS_CORE_CLK 282 /* Virtual */ ++#define USB30_1_UTMI_CLK 265 ++#define USB30_MASTER_SRC 266 ++#define USB30_0_MASTER_CLK 267 ++#define USB30_1_MASTER_CLK 268 ++#define GMAC_CORE1_CLK_SRC 269 ++#define GMAC_CORE2_CLK_SRC 270 ++#define GMAC_CORE3_CLK_SRC 271 ++#define GMAC_CORE4_CLK_SRC 272 ++#define GMAC_CORE1_CLK 273 ++#define GMAC_CORE2_CLK 274 ++#define GMAC_CORE3_CLK 275 ++#define GMAC_CORE4_CLK 276 ++#define UBI32_CORE1_CLK_SRC 277 ++#define UBI32_CORE2_CLK_SRC 278 ++#define UBI32_CORE1_CLK 279 ++#define UBI32_CORE2_CLK 280 ++#define NSSTCM_CLK_SRC 281 ++#define NSSTCM_CLK 282 ++#define NSS_CORE_CLK 283 /* Virtual */ + + #endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch new file mode 100644 index 0000000000..f8ad3cdbcb --- /dev/null +++ b/target/linux/ipq806x/patches/0156-usb-dwc3-Add-Qualcomm-DWC3-glue-layer-driver.patch @@ -0,0 +1,212 @@ +From 364532aeb9024d0ff7b88121f9a953f559b1c136 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:57 +0300 +Subject: [PATCH 156/182] usb: dwc3: Add Qualcomm DWC3 glue layer driver + +DWC3 glue layer is hardware layer around Synopsys DesignWare +USB3 core. Its purpose is to supply Synopsys IP with required +clocks, voltages and interface it with the rest of the SoC. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/usb/dwc3/Kconfig | 8 +++ + drivers/usb/dwc3/Makefile | 1 + + drivers/usb/dwc3/dwc3-qcom.c | 156 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 165 insertions(+) + create mode 100644 drivers/usb/dwc3/dwc3-qcom.c + +diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig +index e2c730f..2d01983 100644 +--- a/drivers/usb/dwc3/Kconfig ++++ b/drivers/usb/dwc3/Kconfig +@@ -59,6 +59,14 @@ config USB_DWC3_EXYNOS + Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside, + say 'Y' or 'M' if you have one such device. + ++config USB_DWC3_QCOM ++ tristate "Qualcomm Platforms" ++ default USB_DWC3 ++ select USB_QCOM_DWC3_PHYS ++ help ++ Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, ++ say 'Y' or 'M' if you have one such device. ++ + config USB_DWC3_PCI + tristate "PCIe-based Platforms" + depends on PCI +diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile +index 10ac3e7..4066e4e 100644 +--- a/drivers/usb/dwc3/Makefile ++++ b/drivers/usb/dwc3/Makefile +@@ -31,5 +31,6 @@ endif + + obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o + obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o ++obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o + obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o + obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c +new file mode 100644 +index 0000000..8d17360 +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3-qcom.c +@@ -0,0 +1,156 @@ ++/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++#include "core.h" ++ ++ ++struct dwc3_qcom { ++ struct device *dev; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ struct clk *sleep_clk; ++ ++ struct regulator *gdsc; ++}; ++ ++static int dwc3_qcom_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct dwc3_qcom *mdwc; ++ int ret = 0; ++ ++ mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL); ++ if (!mdwc) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, mdwc); ++ ++ mdwc->dev = &pdev->dev; ++ ++ mdwc->gdsc = devm_regulator_get(mdwc->dev, "gdsc"); ++ ++ mdwc->core_clk = devm_clk_get(mdwc->dev, "core"); ++ if (IS_ERR(mdwc->core_clk)) { ++ dev_dbg(mdwc->dev, "failed to get core clock\n"); ++ return PTR_ERR(mdwc->core_clk); ++ } ++ ++ mdwc->iface_clk = devm_clk_get(mdwc->dev, "iface"); ++ if (IS_ERR(mdwc->iface_clk)) { ++ dev_dbg(mdwc->dev, "failed to get iface clock, skipping\n"); ++ mdwc->iface_clk = NULL; ++ } ++ ++ mdwc->sleep_clk = devm_clk_get(mdwc->dev, "sleep"); ++ if (IS_ERR(mdwc->sleep_clk)) { ++ dev_dbg(mdwc->dev, "failed to get sleep clock, skipping\n"); ++ mdwc->sleep_clk = NULL; ++ } ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_enable(mdwc->gdsc); ++ if (ret) ++ dev_err(mdwc->dev, "cannot enable gdsc\n"); ++ } ++ ++ clk_prepare_enable(mdwc->core_clk); ++ ++ if (mdwc->iface_clk) ++ clk_prepare_enable(mdwc->iface_clk); ++ ++ if (mdwc->sleep_clk) ++ clk_prepare_enable(mdwc->sleep_clk); ++ ++ ret = of_platform_populate(node, NULL, NULL, mdwc->dev); ++ if (ret) { ++ dev_err(mdwc->dev, "failed to register core - %d\n", ret); ++ dev_dbg(mdwc->dev, "failed to add create dwc3 core\n"); ++ goto dis_clks; ++ } ++ ++ return 0; ++ ++dis_clks: ++ ++ dev_err(mdwc->dev, "disabling clocks\n"); ++ ++ if (mdwc->sleep_clk) ++ clk_disable_unprepare(mdwc->sleep_clk); ++ ++ if (mdwc->iface_clk) ++ clk_disable_unprepare(mdwc->iface_clk); ++ ++ clk_disable_unprepare(mdwc->core_clk); ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_disable(mdwc->gdsc); ++ if (ret) ++ dev_dbg(mdwc->dev, "cannot disable gdsc\n"); ++ } ++ ++ return ret; ++} ++ ++static int dwc3_qcom_remove(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ struct dwc3_qcom *mdwc = platform_get_drvdata(pdev); ++ ++ if (mdwc->sleep_clk) ++ clk_disable_unprepare(mdwc->sleep_clk); ++ ++ if (mdwc->iface_clk) ++ clk_disable_unprepare(mdwc->iface_clk); ++ ++ clk_disable_unprepare(mdwc->core_clk); ++ ++ if (!IS_ERR(mdwc->gdsc)) { ++ ret = regulator_disable(mdwc->gdsc); ++ if (ret) ++ dev_dbg(mdwc->dev, "cannot disable gdsc\n"); ++ } ++ return ret; ++} ++ ++static const struct of_device_id of_dwc3_match[] = { ++ { .compatible = "qcom,dwc3" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, of_dwc3_match); ++ ++static struct platform_driver dwc3_qcom_driver = { ++ .probe = dwc3_qcom_probe, ++ .remove = dwc3_qcom_remove, ++ .driver = { ++ .name = "qcom-dwc3", ++ .owner = THIS_MODULE, ++ .of_match_table = of_dwc3_match, ++ }, ++}; ++ ++module_platform_driver(dwc3_qcom_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch new file mode 100644 index 0000000000..332a32c910 --- /dev/null +++ b/target/linux/ipq806x/patches/0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch @@ -0,0 +1,874 @@ +From d968f8c82db73141c6bc145148642391cb698442 Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:56 +0300 +Subject: [PATCH 157/182] usb: phy: Add Qualcomm DWC3 HS/SS PHY drivers + +These drivers handles control and configuration of the HS +and SS USB PHY transceivers. They are part of the driver +which manage Synopsys DesignWare USB3 controller stack +inside Qualcomm SoC's. + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +--- + drivers/usb/phy/Kconfig | 13 +- + drivers/usb/phy/Makefile | 2 + + drivers/usb/phy/phy-qcom-hsusb.c | 340 ++++++++++++++++++++++++++++ + drivers/usb/phy/phy-qcom-ssusb.c | 455 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 809 insertions(+), 1 deletion(-) + create mode 100644 drivers/usb/phy/phy-qcom-hsusb.c + create mode 100644 drivers/usb/phy/phy-qcom-ssusb.c + +diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig +index 7d1451d..ddb65be 100644 +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -193,7 +193,7 @@ config USB_ISP1301 + + config USB_MSM_OTG + tristate "OTG support for Qualcomm on-chip USB controller" +- depends on (USB || USB_GADGET) && ARCH_MSM ++ depends on (USB || USB_GADGET) && ARCH_QCOM + select USB_PHY + help + Enable this to support the USB OTG transceiver on MSM chips. It +@@ -251,6 +251,17 @@ config USB_RCAR_GEN2_PHY + To compile this driver as a module, choose M here: the + module will be called phy-rcar-gen2-usb. + ++config USB_QCOM_DWC3_PHY ++ tristate "Qualcomm USB controller DWC3 PHY wrappers support" ++ depends on (USB || USB_GADGET) && ARCH_QCOM ++ select USB_PHY ++ help ++ Enable this to support the DWC3 USB PHY transceivers on QCOM chips ++ with DWC3 USB core. It handles PHY initialization, clock ++ management required after resetting the hardware and power ++ management. This driver is required even for peripheral only or ++ host only mode configurations. ++ + config USB_ULPI + bool "Generic ULPI Transceiver Driver" + depends on ARM +diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile +index be58ada..857f04e 100644 +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -26,6 +26,8 @@ obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o + obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o + obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o + obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o ++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-hsusb.o ++obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-ssusb.o + obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o + obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o + obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o +diff --git a/drivers/usb/phy/phy-qcom-hsusb.c b/drivers/usb/phy/phy-qcom-hsusb.c +new file mode 100644 +index 0000000..f96b2b9 +--- /dev/null ++++ b/drivers/usb/phy/phy-qcom-hsusb.c +@@ -0,0 +1,340 @@ ++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define QSCRATCH_CTRL_REG (0x04) ++#define QSCRATCH_GENERAL_CFG (0x08) ++#define PHY_CTRL_REG (0x10) ++#define PARAMETER_OVERRIDE_X_REG (0x14) ++#define CHARGING_DET_CTRL_REG (0x18) ++#define CHARGING_DET_OUTPUT_REG (0x1c) ++#define ALT_INTERRUPT_EN_REG (0x20) ++#define PHY_IRQ_STAT_REG (0x24) ++#define CGCTL_REG (0x28) ++ ++#define PHY_3P3_VOL_MIN 3050000 /* uV */ ++#define PHY_3P3_VOL_MAX 3300000 /* uV */ ++#define PHY_3P3_HPM_LOAD 16000 /* uA */ ++ ++#define PHY_1P8_VOL_MIN 1800000 /* uV */ ++#define PHY_1P8_VOL_MAX 1800000 /* uV */ ++#define PHY_1P8_HPM_LOAD 19000 /* uA */ ++ ++/* TODO: these are suspicious */ ++#define USB_VDDCX_NO 1 /* index */ ++#define USB_VDDCX_MIN 5 /* index */ ++#define USB_VDDCX_MAX 7 /* index */ ++ ++struct qcom_dwc3_hs_phy { ++ struct usb_phy phy; ++ void __iomem *base; ++ struct device *dev; ++ ++ struct clk *xo_clk; ++ struct clk *utmi_clk; ++ ++ struct regulator *v3p3; ++ struct regulator *v1p8; ++ struct regulator *vddcx; ++ struct regulator *vbus; ++}; ++ ++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_hs_phy, phy) ++ ++ ++/** ++ * Write register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_hs_write(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++/** ++ * Write register and read back masked value to confirm it is written ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @mask - register bitmask specifying what should be updated ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_hs_write_readback(void __iomem *base, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset); ++} ++ ++static int qcom_dwc3_hs_notify_connect(struct usb_phy *x, ++ enum usb_device_speed speed) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ ++ dev_err(phy->dev, "notify connect\n"); ++ return 0; ++} ++ ++static int qcom_dwc3_hs_notify_disconnect(struct usb_phy *x, ++ enum usb_device_speed speed) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ ++ dev_err(phy->dev, "notify disconnect\n"); ++ return 0; ++} ++ ++ ++static void qcom_dwc3_hs_phy_shutdown(struct usb_phy *x) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ ++ ret = regulator_set_voltage(phy->v3p3, 0, PHY_3P3_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v1p8\n"); ++ ++ ret = regulator_disable(phy->v1p8); ++ if (ret) ++ dev_err(phy->dev, "cannot disable v1p8\n"); ++ ++ ret = regulator_disable(phy->v3p3); ++ if (ret) ++ dev_err(phy->dev, "cannot disable v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ ret = regulator_disable(phy->vddcx); ++ if (ret) ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ ++ clk_disable_unprepare(phy->utmi_clk); ++} ++ ++static int qcom_dwc3_hs_phy_init(struct usb_phy *x) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ u32 val; ++ ++ clk_prepare_enable(phy->utmi_clk); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ ret = regulator_enable(phy->vddcx); ++ if (ret) ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ ++ ret = regulator_set_voltage(phy->v3p3, PHY_3P3_VOL_MIN, ++ PHY_3P3_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v3p3\n"); ++ ++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN, ++ PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for v1p8\n"); ++ ++ ret = regulator_set_optimum_mode(phy->v1p8, PHY_1P8_HPM_LOAD); ++ if (ret < 0) ++ dev_err(phy->dev, "cannot set HPM of regulator v1p8\n"); ++ ++ ret = regulator_enable(phy->v1p8); ++ if (ret) ++ dev_err(phy->dev, "cannot enable v1p8\n"); ++ ++ ret = regulator_set_optimum_mode(phy->v3p3, PHY_3P3_HPM_LOAD); ++ if (ret < 0) ++ dev_err(phy->dev, "cannot set HPM of regulator v3p3\n"); ++ ++ ret = regulator_enable(phy->v3p3); ++ if (ret) ++ dev_err(phy->dev, "cannot enable v3p3\n"); ++ ++ /* ++ * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs, ++ * and disable RETENTION (power-on default is ENABLED) ++ */ ++ val = readl(phy->base + PHY_CTRL_REG); ++ val |= BIT(18) | BIT(20) | BIT(11) | 0x70; ++ qcom_dwc3_hs_write(phy->base, PHY_CTRL_REG, val); ++ usleep_range(2000, 2200); ++ ++ /* ++ * write HSPHY init value to QSCRATCH reg to set HSPHY parameters like ++ * VBUS valid threshold, disconnect valid threshold, DC voltage level, ++ * preempasis and rise/fall time. ++ */ ++ qcom_dwc3_hs_write_readback(phy->base, PARAMETER_OVERRIDE_X_REG, ++ 0x03ffffff, 0x00d191a4); ++ ++ /* Disable (bypass) VBUS and ID filters */ ++ qcom_dwc3_hs_write(phy->base, QSCRATCH_GENERAL_CFG, BIT(2)); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_hs_phy_set_vbus(struct usb_phy *x, int on) ++{ ++ struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x); ++ int ret = 0; ++ ++ if (IS_ERR(phy->vbus)) ++ return on ? PTR_ERR(phy->vbus) : 0; ++ ++ if (on) ++ ret = regulator_enable(phy->vbus); ++ else ++ ret = regulator_disable(phy->vbus); ++ ++ if (ret) ++ dev_err(x->dev, "Cannot %s Vbus\n", on ? "set" : "off"); ++ return ret; ++} ++ ++static int qcom_dwc3_hs_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_hs_phy *phy; ++ struct resource *res; ++ void __iomem *base; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ ++ phy->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(phy->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx"); ++ if (IS_ERR(phy->vddcx)) { ++ dev_dbg(phy->dev, "cannot get vddcx\n"); ++ return PTR_ERR(phy->vddcx); ++ } ++ ++ phy->v3p3 = devm_regulator_get(phy->dev, "v3p3"); ++ if (IS_ERR(phy->v3p3)) { ++ dev_dbg(phy->dev, "cannot get v3p3\n"); ++ return PTR_ERR(phy->v3p3); ++ } ++ ++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8"); ++ if (IS_ERR(phy->v1p8)) { ++ dev_dbg(phy->dev, "cannot get v1p8\n"); ++ return PTR_ERR(phy->v1p8); ++ } ++ ++ phy->vbus = devm_regulator_get(phy->dev, "vbus"); ++ if (IS_ERR(phy->vbus)) ++ dev_dbg(phy->dev, "Failed to get vbus\n"); ++ ++ phy->utmi_clk = devm_clk_get(phy->dev, "utmi"); ++ if (IS_ERR(phy->utmi_clk)) { ++ dev_dbg(phy->dev, "cannot get UTMI handle\n"); ++ return PTR_ERR(phy->utmi_clk); ++ } ++ ++ phy->xo_clk = devm_clk_get(phy->dev, "xo"); ++ if (IS_ERR(phy->xo_clk)) { ++ dev_dbg(phy->dev, "cannot get TCXO buffer handle\n"); ++ phy->xo_clk = NULL; ++ } ++ ++ clk_set_rate(phy->utmi_clk, 60000000); ++ ++ if (phy->xo_clk) ++ clk_prepare_enable(phy->xo_clk); ++ ++ phy->base = base; ++ phy->phy.dev = phy->dev; ++ phy->phy.label = "qcom-dwc3-hsphy"; ++ phy->phy.init = qcom_dwc3_hs_phy_init; ++ phy->phy.notify_connect = qcom_dwc3_hs_notify_connect; ++ phy->phy.notify_disconnect = qcom_dwc3_hs_notify_disconnect; ++ phy->phy.shutdown = qcom_dwc3_hs_phy_shutdown; ++ phy->phy.set_vbus = qcom_dwc3_hs_phy_set_vbus; ++ phy->phy.type = USB_PHY_TYPE_USB2; ++ phy->phy.state = OTG_STATE_UNDEFINED; ++ ++ usb_add_phy_dev(&phy->phy); ++ return 0; ++} ++ ++static int qcom_dwc3_hs_remove(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_hs_phy *phy = platform_get_drvdata(pdev); ++ ++ if (phy->xo_clk) ++ clk_disable_unprepare(phy->xo_clk); ++ usb_remove_phy(&phy->phy); ++ return 0; ++} ++ ++static const struct of_device_id qcom_dwc3_hs_id_table[] = { ++ { .compatible = "qcom,dwc3-hsphy" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_hs_id_table); ++ ++static struct platform_driver qcom_dwc3_hs_driver = { ++ .probe = qcom_dwc3_hs_probe, ++ .remove = qcom_dwc3_hs_remove, ++ .driver = { ++ .name = "qcom-dwc3-hsphy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_hs_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_hs_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3-hsphy"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM HSPHY driver"); +diff --git a/drivers/usb/phy/phy-qcom-ssusb.c b/drivers/usb/phy/phy-qcom-ssusb.c +new file mode 100644 +index 0000000..3da778f +--- /dev/null ++++ b/drivers/usb/phy/phy-qcom-ssusb.c +@@ -0,0 +1,455 @@ ++/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/usb/phy.h> ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define PHY_CTRL_REG (0x00) ++#define PHY_PARAM_CTRL_1 (0x04) ++#define PHY_PARAM_CTRL_2 (0x08) ++#define CR_PROTOCOL_DATA_IN_REG (0x0c) ++#define CR_PROTOCOL_DATA_OUT_REG (0x10) ++#define CR_PROTOCOL_CAP_ADDR_REG (0x14) ++#define CR_PROTOCOL_CAP_DATA_REG (0x18) ++#define CR_PROTOCOL_READ_REG (0x1c) ++#define CR_PROTOCOL_WRITE_REG (0x20) ++ ++#define PHY_1P8_VOL_MIN 1800000 /* uV */ ++#define PHY_1P8_VOL_MAX 1800000 /* uV */ ++#define PHY_1P8_HPM_LOAD 23000 /* uA */ ++ ++/* TODO: these are suspicious */ ++#define USB_VDDCX_NO 1 /* index */ ++#define USB_VDDCX_MIN 5 /* index */ ++#define USB_VDDCX_MAX 7 /* index */ ++ ++struct qcom_dwc3_ss_phy { ++ struct usb_phy phy; ++ void __iomem *base; ++ struct device *dev; ++ ++ struct regulator *v1p8; ++ struct regulator *vddcx; ++ ++ struct clk *xo_clk; ++ struct clk *ref_clk; ++}; ++ ++#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_ss_phy, phy) ++ ++ ++/** ++ * Write register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_ss_write(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++/** ++ * Write register and read back masked value to confirm it is written ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @mask - register bitmask specifying what should be updated ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_ss_write_readback(void __iomem *base, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset); ++} ++ ++/** ++ * Write SSPHY register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to write. ++ * @val - value to write. ++ */ ++static void qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val) ++{ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG)) ++ cpu_relax(); ++ ++ writel(val, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG); ++ while (readl(base + CR_PROTOCOL_CAP_DATA_REG)) ++ cpu_relax(); ++ ++ writel(0x1, base + CR_PROTOCOL_WRITE_REG); ++ while (readl(base + CR_PROTOCOL_WRITE_REG)) ++ cpu_relax(); ++} ++ ++/** ++ * Read SSPHY register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to read. ++ */ ++static u32 qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr) ++{ ++ bool first_read = true; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ while (readl(base + CR_PROTOCOL_CAP_ADDR_REG)) ++ cpu_relax(); ++ ++ /* ++ * Due to hardware bug, first read of SSPHY register might be ++ * incorrect. Hence as workaround, SW should perform SSPHY register ++ * read twice, but use only second read and ignore first read. ++ */ ++retry: ++ writel(0x1, base + CR_PROTOCOL_READ_REG); ++ while (readl(base + CR_PROTOCOL_READ_REG)) ++ cpu_relax(); ++ ++ if (first_read) { ++ readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ first_read = false; ++ goto retry; ++ } ++ ++ return readl(base + CR_PROTOCOL_DATA_OUT_REG); ++} ++ ++static void qcom_dwc3_ss_phy_shutdown(struct usb_phy *x) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ int ret; ++ ++ /* Sequence to put SSPHY in low power state: ++ * 1. Clear REF_PHY_EN in PHY_CTRL_REG ++ * 2. Clear REF_USE_PAD in PHY_CTRL_REG ++ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention ++ * 4. Disable SSPHY ref clk ++ */ ++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 8), 0x0); ++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 28), 0x0); ++ qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, ++ (1 << 26), (1 << 26)); ++ ++ usleep_range(1000, 1200); ++ clk_disable_unprepare(phy->ref_clk); ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ ++ regulator_disable(phy->vddcx); ++ ++ ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX); ++ if (ret) ++ dev_err(phy->dev, "cannot set v1p8\n"); ++ ++ regulator_disable(phy->v1p8); ++} ++ ++static int qcom_dwc3_ss_phy_init(struct usb_phy *x) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ u32 data = 0; ++ int ret; ++ ++ ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX); ++ if (ret) { ++ dev_err(phy->dev, "cannot set voltage for vddcx\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(phy->vddcx); ++ if (ret) { ++ dev_err(phy->dev, "cannot enable vddcx\n"); ++ return ret; ++ } ++ ++ ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN, ++ PHY_1P8_VOL_MAX); ++ if (ret) { ++ regulator_disable(phy->vddcx); ++ dev_err(phy->dev, "cannot set v1p8\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(phy->v1p8); ++ if (ret) { ++ regulator_disable(phy->vddcx); ++ dev_err(phy->dev, "cannot enable v1p8\n"); ++ return ret; ++ } ++ ++ clk_prepare_enable(phy->ref_clk); ++ usleep_range(1000, 1200); ++ ++ /* reset phy */ ++ data = readl_relaxed(phy->base + PHY_CTRL_REG); ++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* clear REF_PAD, we don't have XO clk */ ++ data &= ~BIT(28); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ msleep(30); ++ ++ data |= BIT(8) | BIT(24); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* ++ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates ++ * in HS mode instead of SS mode. Workaround it by asserting ++ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d); ++ data |= (1 << 7); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data); ++ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010); ++ data &= ~0xff0; ++ data |= 0x20; ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data); ++ ++ /* ++ * Fix RX Equalization setting as follows ++ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006); ++ data &= ~(1 << 6); ++ data |= (1 << 7); ++ data &= ~(0x7 << 8); ++ data |= (0x3 << 8); ++ data |= (0x1 << 11); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data); ++ ++ /* ++ * Set EQ and TX launch amplitudes as follows ++ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22 ++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 ++ * LANE0.TX_OVRD_DRV_LO.EN set to 1. ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002); ++ data &= ~0x3f80; ++ data |= (0x16 << 7); ++ data &= ~0x7f; ++ data |= (0x7f | (1 << 14)); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data); ++ ++ /* ++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows ++ * TX_FULL_SWING [26:20] amplitude to 127 ++ * TX_DEEMPH_3_5DB [13:8] to 22 ++ * LOS_BIAS [2:0] to 0x5 ++ */ ++ qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ return 0; ++} ++ ++static int qcom_dwc3_ss_set_suspend(struct usb_phy *x, int suspend) ++{ ++ struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x); ++ u32 data; ++ ++ if (!suspend) { ++ /* reset phy */ ++ data = readl_relaxed(phy->base + PHY_CTRL_REG); ++ writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* clear REF_PAD, we don't have XO clk */ ++ data &= ~BIT(28); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ msleep(30); ++ ++ data |= BIT(8) | BIT(24); ++ writel_relaxed(data, phy->base + PHY_CTRL_REG); ++ ++ /* ++ * WORKAROUND: There is SSPHY suspend bug due to which USB ++ * enumerates in HS mode instead of SS mode. Workaround it by ++ * asserting LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use ++ * alt bus mode ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d); ++ data |= (1 << 7); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data); ++ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010); ++ data &= ~0xff0; ++ data |= 0x20; ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data); ++ ++ /* ++ * Fix RX Equalization setting as follows ++ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006); ++ data &= ~(1 << 6); ++ data |= (1 << 7); ++ data &= ~(0x7 << 8); ++ data |= (0x3 << 8); ++ data |= (0x1 << 11); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data); ++ ++ /* ++ * Set EQ and TX launch amplitudes as follows ++ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22 ++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 ++ * LANE0.TX_OVRD_DRV_LO.EN set to 1. ++ */ ++ data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002); ++ data &= ~0x3f80; ++ data |= (0x16 << 7); ++ data &= ~0x7f; ++ data |= (0x7f | (1 << 14)); ++ qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data); ++ ++ /* ++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows ++ * TX_FULL_SWING [26:20] amplitude to 127 ++ * TX_DEEMPH_3_5DB [13:8] to 22 ++ * LOS_BIAS [2:0] to 0x5 ++ */ ++ qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ } ++ return 0; ++} ++ ++static int qcom_dwc3_ss_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_ss_phy *phy; ++ struct resource *res; ++ void __iomem *base; ++ int ret; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ ++ phy->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(phy->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ phy->vddcx = devm_regulator_get(phy->dev, "vddcx"); ++ if (IS_ERR(phy->vddcx)) { ++ dev_dbg(phy->dev, "cannot get vddcx\n"); ++ return PTR_ERR(phy->vddcx); ++ } ++ ++ phy->v1p8 = devm_regulator_get(phy->dev, "v1p8"); ++ if (IS_ERR(phy->v1p8)) { ++ dev_dbg(phy->dev, "cannot get v1p8\n"); ++ return PTR_ERR(phy->v1p8); ++ } ++ ++ phy->xo_clk = devm_clk_get(phy->dev, "xo"); ++ if (IS_ERR(phy->xo_clk)) { ++ dev_dbg(phy->dev, "cannot get XO clk, assuming not present\n"); ++ phy->xo_clk = NULL; ++ } ++ ++ phy->ref_clk = devm_clk_get(phy->dev, "ref"); ++ if (IS_ERR(phy->ref_clk)) { ++ dev_dbg(phy->dev, "cannot get ref clock handle\n"); ++ return PTR_ERR(phy->ref_clk); ++ } ++ ++ clk_set_rate(phy->ref_clk, 125000000); ++ if (phy->xo_clk) ++ clk_prepare_enable(phy->xo_clk); ++ ++ phy->base = base; ++ phy->phy.dev = phy->dev; ++ phy->phy.label = "qcom-dwc3-ssphy"; ++ phy->phy.init = qcom_dwc3_ss_phy_init; ++ phy->phy.shutdown = qcom_dwc3_ss_phy_shutdown; ++ phy->phy.set_suspend = qcom_dwc3_ss_set_suspend; ++ phy->phy.type = USB_PHY_TYPE_USB3; ++ ++ ret = usb_add_phy_dev(&phy->phy); ++ return ret; ++} ++ ++static int qcom_dwc3_ss_remove(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_ss_phy *phy = platform_get_drvdata(pdev); ++ ++ if (phy->xo_clk) ++ clk_disable_unprepare(phy->xo_clk); ++ usb_remove_phy(&phy->phy); ++ return 0; ++} ++ ++static const struct of_device_id qcom_dwc3_ss_id_table[] = { ++ { .compatible = "qcom,dwc3-ssphy" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_ss_id_table); ++ ++static struct platform_driver qcom_dwc3_ss_driver = { ++ .probe = qcom_dwc3_ss_probe, ++ .remove = qcom_dwc3_ss_remove, ++ .driver = { ++ .name = "qcom-dwc3-ssphy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_ss_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_ss_driver); ++ ++MODULE_ALIAS("platform:qcom-dwc3-ssphy"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM SSPHY driver"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch new file mode 100644 index 0000000000..cf1b300b50 --- /dev/null +++ b/target/linux/ipq806x/patches/0158-usb-dwc3-qcom-Add-device-tree-binding.patch @@ -0,0 +1,131 @@ +From c7045330c5976eb31bd79bc57c5db684588d595e Mon Sep 17 00:00:00 2001 +From: "Ivan T. Ivanov" <iivanov@mm-sol.com> +Date: Mon, 7 Oct 2013 10:44:55 +0300 +Subject: [PATCH 158/182] usb: dwc3: qcom: Add device tree binding + +QCOM USB3.0 core wrapper consist of USB3.0 IP from Synopsys +(SNPS) and HS, SS PHY's control and configuration registers. + +It could operate in device mode (SS, HS, FS) and host +mode (SS, HS, FS, LS). + +Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com> +Acked-by: Stephen Warren <swarren@nvidia.com> +--- + .../devicetree/bindings/usb/qcom,dwc3.txt | 104 ++++++++++++++++++++ + 1 file changed, 104 insertions(+) + create mode 100644 Documentation/devicetree/bindings/usb/qcom,dwc3.txt + +diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +new file mode 100644 +index 0000000..105b6b7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +@@ -0,0 +1,104 @@ ++Qualcomm SuperSpeed DWC3 USB SoC controller ++ ++ ++QCOM DWC3 Highspeed USB PHY ++======================== ++Required properities: ++- compatible: should contain "qcom,dwc3-hsphy"; ++- reg: offset and length of the register set in the memory map ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "utmi" UTMI clock ++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY. ++- v3p3-supply: phandle to the regulator for the 3.3v supply to HSPHY. ++- vbus-supply: phandle to the regulator for the vbus supply for host ++ mode. ++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY ++ digital circuit operation. ++ ++Optional clocks: ++ "xo" External reference clock ++ ++ ++QCOM DWC3 Superspeed USB PHY ++========================= ++Required properities: ++- compatible: should contain "qcom,dwc3-ssphy"; ++- reg: offset and length of the register set in the memory map ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "ref" Reference clock used in host mode. ++- v1p8-supply: phandle to the regulator for the 1.8v supply to HSPHY. ++- vddcx-supply: phandle to the regulator for the vdd supply for HSPHY ++ digital circuit operation. ++ ++Optional clocks: ++ "xo" External reference clock ++ ++QCOM DWC3 controller wrapper ++=========================== ++Required properties: ++- compatible: should contain "qcom,dwc3" ++- clocks: A list of phandle + clock-specifier pairs for the ++ clocks listed in clock-names ++- clock-names: Should contain the following: ++ "core" Master/Core clock, have to be >= 125 MHz for SS ++ operation and >= 60MHz for HS operation ++ ++Optional clocks: ++ "iface" System bus AXI clock. Not present on all platforms ++ "sleep" Sleep clock, used when USB3 core goes into low ++ power mode (U3). ++ ++Optional regulator: ++- gdsc-supply: phandle to the regulator from globally distributed ++ switch controller ++ ++Required child node: ++A child node must exist to represent the core DWC3 IP block. The name of ++the node is not important. The content of the node is defined in dwc3.txt. ++ ++Example device nodes: ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x110f8830 0x30>; ++ ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ status = "disabled"; ++ }; ++ ++ usb3_0: usb30@0 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@11000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x11000000 0xcd00>; ++ interrupts = <0 110 0x4>; ++ usb-phy = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch new file mode 100644 index 0000000000..5a0d099af8 --- /dev/null +++ b/target/linux/ipq806x/patches/0159-arm-ipq8064-Add-USB3-DT-information.patch @@ -0,0 +1,162 @@ +From 269a71c81438604d27f01ec703daa7f5e3f39e8b Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Sun, 15 Jun 2014 00:48:18 -0500 +Subject: [PATCH 159/182] arm: ipq8064: Add USB3 DT information + +This patch fleshes out the USB3 specific information for the IPQ8064 platform. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 29 ++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 90 ++++++++++++++++++++++++++++++ + 2 files changed, 119 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 4062eb6..2b2d63c 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -160,5 +160,34 @@ + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + }; ++ ++ tcsr@1a400000 { ++ status = "ok"; ++ qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; ++ }; ++ ++ phy@100f8800 { /* USB3 port 1 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@100f8830 { /* USB3 port 1 SS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8800 { /* USB3 port 0 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8830 { /* USB3 port 0 SS phy */ ++ status = "ok"; ++ }; ++ ++ usb30@0 { ++ status = "ok"; ++ }; ++ ++ usb30@1 { ++ status = "ok"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index d9fce15..6be6ac9 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -4,6 +4,7 @@ + #include <dt-bindings/clock/qcom,gcc-ipq806x.h> + #include <dt-bindings/reset/qcom,gcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> ++#include <dt-bindings/soc/qcom,tcsr.h> + + / { + model = "Qualcomm IPQ8064"; +@@ -402,5 +403,94 @@ + + status = "disabled"; + }; ++ ++ tcsr: tcsr@1a400000 { ++ compatible = "qcom,tcsr"; ++ reg = <0x1a400000 0x100>; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_1: phy@100f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x100f8800 0x30>; ++ clocks = <&gcc USB30_1_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_1: phy@100f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x100f8830 0x30>; ++ ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hsphy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "utmi"; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ssphy"; ++ reg = <0x110f8830 0x30>; ++ ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ ++ status = "disabled"; ++ }; ++ ++ usb3_0: usb30@0 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@11000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x11000000 0xcd00>; ++ interrupts = <0 110 0x4>; ++ usb-phy = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; ++ ++ usb3_1: usb30@1 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@10000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x10000000 0xcd00>; ++ interrupts = <0 205 0x4>; ++ usb-phy = <&hs_phy_1>, <&ss_phy_1>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; + }; + }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch new file mode 100644 index 0000000000..b74db3edbc --- /dev/null +++ b/target/linux/ipq806x/patches/0160-ARM-qcom-config-Add-TCSR-and-USB3-options.patch @@ -0,0 +1,42 @@ +From 8e69ee9a592f363476f18e91f57d871662e9a393 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 16 Jun 2014 16:37:22 -0500 +Subject: [PATCH 160/182] ARM: qcom: config: Add TCSR and USB3 options + +Enable TCSR and USB3 options. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index e3f9013..3d55d79 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -122,9 +122,14 @@ CONFIG_HID_BATTERY_STRENGTH=y + CONFIG_USB=y + CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + CONFIG_USB_MON=y ++CONFIG_USB_XHCI_HCD=y + CONFIG_USB_EHCI_HCD=y + CONFIG_USB_ACM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_DWC3=y ++CONFIG_USB_DWC3_HOST=y + CONFIG_USB_SERIAL=y ++CONFIG_USB_QCOM_DWC3_PHY=y + CONFIG_USB_GADGET=y + CONFIG_USB_GADGET_DEBUG_FILES=y + CONFIG_USB_GADGET_VBUS_DRAW=500 +@@ -138,6 +143,7 @@ CONFIG_DMADEVICES=y + CONFIG_QCOM_BAM_DMA=y + CONFIG_STAGING=y + CONFIG_QCOM_GSBI=y ++CONFIG_QCOM_TCSR=y + CONFIG_COMMON_CLK_QCOM=y + CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch new file mode 100644 index 0000000000..41dae6e81b --- /dev/null +++ b/target/linux/ipq806x/patches/0161-ARM-Remove-ARCH_HAS_CPUFREQ-config-option.patch @@ -0,0 +1,259 @@ +From 5d102a45ee224fa32c775deb75bf7eb9d2ee2cf0 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 3 Jun 2014 11:24:23 -0700 +Subject: [PATCH 161/182] ARM: Remove ARCH_HAS_CPUFREQ config option + +This config exists entirely to hide the cpufreq menu from the +kernel configuration unless a platform has selected it. Nothing +is actually built if this config is 'Y' and it just leads to more +patches that add a select under a platform Kconfig so that some +other CPUfreq option can be chosen. Let's remove the option so +that we can always enable CPUfreq drivers on ARM platforms. + +Acked-by: Viresh Kumar <viresh.kumar@linaro.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/Kconfig | 17 ----------------- + arch/arm/mach-davinci/Kconfig | 1 - + arch/arm/mach-highbank/Kconfig | 1 - + arch/arm/mach-imx/Kconfig | 2 -- + arch/arm/mach-omap2/Kconfig | 1 - + arch/arm/mach-shmobile/Kconfig | 2 -- + arch/arm/mach-spear/Kconfig | 1 - + arch/arm/mach-tegra/Kconfig | 1 - + arch/arm/mach-ux500/Kconfig | 1 - + arch/arm/mach-vexpress/Kconfig | 1 - + arch/arm/mach-vt8500/Kconfig | 1 - + 11 files changed, 29 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4332e8d..1a61d4a 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -180,13 +180,6 @@ config ARCH_HAS_ILOG2_U32 + config ARCH_HAS_ILOG2_U64 + bool + +-config ARCH_HAS_CPUFREQ +- bool +- help +- Internal node to signify that the ARCH has CPUFREQ support +- and that the relevant menu configurations are displayed for +- it. +- + config ARCH_HAS_BANDGAP + bool + +@@ -315,7 +308,6 @@ config ARCH_MULTIPLATFORM + + config ARCH_INTEGRATOR + bool "ARM Ltd. Integrator family" +- select ARCH_HAS_CPUFREQ + select ARM_AMBA + select ARM_PATCH_PHYS_VIRT + select AUTO_ZRELADDR +@@ -540,7 +532,6 @@ config ARCH_DOVE + + config ARCH_KIRKWOOD + bool "Marvell Kirkwood" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CPU_FEROCEON + select GENERIC_CLOCKEVENTS +@@ -641,7 +632,6 @@ config ARCH_LPC32XX + config ARCH_PXA + bool "PXA2xx/PXA3xx-based" + depends on MMU +- select ARCH_HAS_CPUFREQ + select ARCH_MTD_XIP + select ARCH_REQUIRE_GPIOLIB + select ARM_CPU_SUSPEND if PM +@@ -710,7 +700,6 @@ config ARCH_RPC + + config ARCH_SA1100 + bool "SA1100-based" +- select ARCH_HAS_CPUFREQ + select ARCH_MTD_XIP + select ARCH_REQUIRE_GPIOLIB + select ARCH_SPARSEMEM_ENABLE +@@ -728,7 +717,6 @@ config ARCH_SA1100 + + config ARCH_S3C24XX + bool "Samsung S3C24XX SoCs" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CLKDEV_LOOKUP + select CLKSRC_SAMSUNG_PWM +@@ -748,7 +736,6 @@ config ARCH_S3C24XX + + config ARCH_S3C64XX + bool "Samsung S3C64XX" +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA + select ARM_VIC +@@ -809,7 +796,6 @@ config ARCH_S5PC100 + + config ARCH_S5PV210 + bool "Samsung S5PV210/S5PC110" +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_SPARSEMEM_ENABLE + select CLKDEV_LOOKUP +@@ -863,7 +849,6 @@ config ARCH_DAVINCI + config ARCH_OMAP1 + bool "TI OMAP1" + depends on MMU +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_OMAP + select ARCH_REQUIRE_GPIOLIB +@@ -2170,9 +2155,7 @@ endmenu + + menu "CPU Power Management" + +-if ARCH_HAS_CPUFREQ + source "drivers/cpufreq/Kconfig" +-endif + + source "drivers/cpuidle/Kconfig" + +diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig +index a075b3e..34cfecc 100644 +--- a/arch/arm/mach-davinci/Kconfig ++++ b/arch/arm/mach-davinci/Kconfig +@@ -39,7 +39,6 @@ config ARCH_DAVINCI_DA830 + config ARCH_DAVINCI_DA850 + bool "DA850/OMAP-L138/AM18x based system" + select ARCH_DAVINCI_DA8XX +- select ARCH_HAS_CPUFREQ + select CP_INTC + + config ARCH_DAVINCI_DA8XX +diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig +index 0aded64..9f74755 100644 +--- a/arch/arm/mach-highbank/Kconfig ++++ b/arch/arm/mach-highbank/Kconfig +@@ -1,7 +1,6 @@ + config ARCH_HIGHBANK + bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_HAS_OPP + select ARCH_SUPPORTS_BIG_ENDIAN +diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig +index 33567aa..e7ae0ee 100644 +--- a/arch/arm/mach-imx/Kconfig ++++ b/arch/arm/mach-imx/Kconfig +@@ -103,7 +103,6 @@ config SOC_IMX25 + + config SOC_IMX27 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select CPU_ARM926T + select IMX_HAVE_IOMUX_V1 +@@ -129,7 +128,6 @@ config SOC_IMX35 + + config SOC_IMX5 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select ARCH_MXC_IOMUX_V3 + select CPU_V7 +diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig +index 0af7ca0..0674cb7 100644 +--- a/arch/arm/mach-omap2/Kconfig ++++ b/arch/arm/mach-omap2/Kconfig +@@ -93,7 +93,6 @@ config SOC_DRA7XX + config ARCH_OMAP2PLUS + bool + select ARCH_HAS_BANDGAP +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_OMAP + select ARCH_REQUIRE_GPIOLIB +diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig +index 05fa505..61d4d31 100644 +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -85,7 +85,6 @@ config ARCH_R8A73A4 + select CPU_V7 + select SH_CLK_CPG + select RENESAS_IRQC +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + + config ARCH_R8A7740 +@@ -271,7 +270,6 @@ config MACH_KOELSCH + config MACH_KZM9G + bool "KZM-A9-GT board" + depends on ARCH_SH73A0 +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR +diff --git a/arch/arm/mach-spear/Kconfig b/arch/arm/mach-spear/Kconfig +index ac1710e6..811ba13 100644 +--- a/arch/arm/mach-spear/Kconfig ++++ b/arch/arm/mach-spear/Kconfig +@@ -16,7 +16,6 @@ if PLAT_SPEAR + config ARCH_SPEAR13XX + bool "ST SPEAr13xx" + depends on ARCH_MULTI_V7 || PLAT_SPEAR_SINGLE +- select ARCH_HAS_CPUFREQ + select ARM_GIC + select CPU_V7 + select GPIO_SPEAR_SPICS +diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig +index b1232d8..52bfc9e 100644 +--- a/arch/arm/mach-tegra/Kconfig ++++ b/arch/arm/mach-tegra/Kconfig +@@ -1,6 +1,5 @@ + config ARCH_TEGRA + bool "NVIDIA Tegra" if ARCH_MULTI_V7 +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS + select ARM_GIC +diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig +index 0034d2c..cb1176e 100644 +--- a/arch/arm/mach-ux500/Kconfig ++++ b/arch/arm/mach-ux500/Kconfig +@@ -3,7 +3,6 @@ config ARCH_U8500 + depends on MMU + select AB8500_CORE + select ABX500_CORE +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA + select ARM_ERRATA_754322 +diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig +index 4a70be4..ca5b7e5 100644 +--- a/arch/arm/mach-vexpress/Kconfig ++++ b/arch/arm/mach-vexpress/Kconfig +@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB + + config ARCH_VEXPRESS_SPC + bool "Versatile Express Serial Power Controller (SPC)" +- select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP + select PM_OPP + help +diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig +index 927be93..788d0b4 100644 +--- a/arch/arm/mach-vt8500/Kconfig ++++ b/arch/arm/mach-vt8500/Kconfig +@@ -1,6 +1,5 @@ + config ARCH_VT8500 + bool +- select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CLKDEV_LOOKUP + select CLKSRC_OF +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch new file mode 100644 index 0000000000..979212fe8f --- /dev/null +++ b/target/linux/ipq806x/patches/0162-PM-OPP-Remove-ARCH_HAS_OPP.patch @@ -0,0 +1,134 @@ +From 18e4542f7b02f586d46594977818cd6b24d9cbcb Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@linaro.org> +Date: Fri, 6 Jun 2014 11:36:56 +0100 +Subject: [PATCH 162/182] PM / OPP: Remove ARCH_HAS_OPP + +Since the OPP layer is a kernel library which has been converted to be +directly selectable by its callers rather than user selectable and +requiring architectures to enable it explicitly the ARCH_HAS_OPP symbol +has become redundant and can be removed. Do so. + +Signed-off-by: Mark Brown <broonie@linaro.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + Documentation/power/opp.txt | 3 --- + arch/arm/mach-exynos/Kconfig | 1 - + arch/arm/mach-highbank/Kconfig | 1 - + arch/arm/mach-omap2/Kconfig | 1 - + arch/arm/mach-shmobile/Kconfig | 2 -- + arch/arm/mach-vexpress/Kconfig | 1 - + drivers/devfreq/Kconfig | 1 - + kernel/power/Kconfig | 3 --- + 8 files changed, 13 deletions(-) + +diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt +index b8a907d..7b6a021 100644 +--- a/Documentation/power/opp.txt ++++ b/Documentation/power/opp.txt +@@ -52,9 +52,6 @@ Typical usage of the OPP library is as follows: + SoC framework -> modifies on required cases certain OPPs -> OPP layer + -> queries to search/retrieve information -> + +-Architectures that provide a SoC framework for OPP should select ARCH_HAS_OPP +-to make the OPP layer available. +- + OPP layer expects each domain to be represented by a unique device pointer. SoC + framework registers a set of initial OPPs per device with the OPP layer. This + list is expected to be an optimally small number typically around 5 per device. +diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig +index 4c414af..67e69a8 100644 +--- a/arch/arm/mach-exynos/Kconfig ++++ b/arch/arm/mach-exynos/Kconfig +@@ -107,7 +107,6 @@ config SOC_EXYNOS5440 + depends on ARCH_EXYNOS5 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select ARCH_HAS_BANDGAP +- select ARCH_HAS_OPP + select HAVE_ARM_ARCH_TIMER + select AUTO_ZRELADDR + select MIGHT_HAVE_PCI +diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig +index 9f74755..43bd782 100644 +--- a/arch/arm/mach-highbank/Kconfig ++++ b/arch/arm/mach-highbank/Kconfig +@@ -2,7 +2,6 @@ config ARCH_HIGHBANK + bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select ARCH_HAS_HOLES_MEMORYMODEL +- select ARCH_HAS_OPP + select ARCH_SUPPORTS_BIG_ENDIAN + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_AMBA +diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig +index 0674cb7..3e6596c 100644 +--- a/arch/arm/mach-omap2/Kconfig ++++ b/arch/arm/mach-omap2/Kconfig +@@ -13,7 +13,6 @@ config ARCH_OMAP3 + bool "TI OMAP3" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS +- select ARCH_HAS_OPP + select ARM_CPU_SUSPEND if PM + select CPU_V7 + select MULTI_IRQ_HANDLER +diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig +index 61d4d31..9fc2dd2 100644 +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -85,7 +85,6 @@ config ARCH_R8A73A4 + select CPU_V7 + select SH_CLK_CPG + select RENESAS_IRQC +- select ARCH_HAS_OPP + + config ARCH_R8A7740 + bool "R-Mobile A1 (R8A77400)" +@@ -270,7 +269,6 @@ config MACH_KOELSCH + config MACH_KZM9G + bool "KZM-A9-GT board" + depends on ARCH_SH73A0 +- select ARCH_HAS_OPP + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select SND_SOC_AK4642 if SND_SIMPLE_CARD +diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig +index ca5b7e5..4ed6e97 100644 +--- a/arch/arm/mach-vexpress/Kconfig ++++ b/arch/arm/mach-vexpress/Kconfig +@@ -67,7 +67,6 @@ config ARCH_VEXPRESS_DCSCB + + config ARCH_VEXPRESS_SPC + bool "Versatile Express Serial Power Controller (SPC)" +- select ARCH_HAS_OPP + select PM_OPP + help + The TC2 (A15x2 A7x3) versatile express core tile integrates a logic +diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig +index 7d2f435..d416754 100644 +--- a/drivers/devfreq/Kconfig ++++ b/drivers/devfreq/Kconfig +@@ -68,7 +68,6 @@ comment "DEVFREQ Drivers" + config ARM_EXYNOS4_BUS_DEVFREQ + bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" + depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM +- select ARCH_HAS_OPP + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +index 2fac9cc..caa040c 100644 +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -253,9 +253,6 @@ config APM_EMULATION + anything, try disabling/enabling this option (or disabling/enabling + APM in your BIOS). + +-config ARCH_HAS_OPP +- bool +- + config PM_OPP + bool "Operating Performance Point (OPP) Layer library" + depends on ARCH_HAS_OPP +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch new file mode 100644 index 0000000000..7ea69f331b --- /dev/null +++ b/target/linux/ipq806x/patches/0163-clk-return-probe-defer-when-DT-clock-not-yet-ready.patch @@ -0,0 +1,49 @@ +From 1ce8667062bc7b8813adf6103ad2374d9dd52fb0 Mon Sep 17 00:00:00 2001 +From: Jean-Francois Moine <moinejf@free.fr> +Date: Mon, 25 Nov 2013 19:47:04 +0100 +Subject: [PATCH 163/182] clk: return probe defer when DT clock not yet ready + +At probe time, a clock device may not be ready when some other device +wants to use it. + +This patch lets the functions clk_get/devm_clk_get return a probe defer +when the clock is defined in the DT but not yet available. + +Signed-off-by: Jean-Francois Moine <moinejf@free.fr> +Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> +Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com> +Signed-off-by: Mike Turquette <mturquette@linaro.org> +--- + drivers/clk/clk.c | 2 +- + drivers/clk/clkdev.c | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index 0582068..a3facad 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -2502,7 +2502,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); + struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) + { + struct of_clk_provider *provider; +- struct clk *clk = ERR_PTR(-ENOENT); ++ struct clk *clk = ERR_PTR(-EPROBE_DEFER); + + /* Check if we have such a provider in our array */ + list_for_each_entry(provider, &of_clk_providers, link) { +diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c +index 48f6721..a360b2e 100644 +--- a/drivers/clk/clkdev.c ++++ b/drivers/clk/clkdev.c +@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id) + clk = of_clk_get_by_name(dev->of_node, con_id); + if (!IS_ERR(clk)) + return clk; ++ if (PTR_ERR(clk) == -EPROBE_DEFER) ++ return clk; + } + + return clk_get_sys(dev_id, con_id); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 0000000000..652590e984 --- /dev/null +++ b/target/linux/ipq806x/patches/0164-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,146 @@ +From d8bf5e13683e027c52476b89b874d50e5281c130 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 18 Oct 2013 16:45:05 -0700 +Subject: [PATCH 164/182] ARM: Add Krait L2 register accessor functions + +Krait CPUs have a handful of L2 cache controller registers that +live behind a cp15 based indirection register. First you program +the indirection register (l2cpselr) to point the L2 'window' +register (l2cpdr) at what you want to read/write. Then you +read/write the 'window' register to do what you want. The +l2cpselr register is not banked per-cpu so we must lock around +accesses to it to prevent other CPUs from re-pointing l2cpdr +underneath us. + +Cc: Mark Rutland <mark.rutland@arm.com> +Cc: Russell King <linux@arm.linux.org.uk> +Cc: Courtney Cavin <courtney.cavin@sonymobile.com> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/common/Kconfig | 3 ++ + arch/arm/common/Makefile | 1 + + arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++ + arch/arm/include/asm/krait-l2-accessors.h | 20 ++++++++++ + 4 files changed, 82 insertions(+) + create mode 100644 arch/arm/common/krait-l2-accessors.c + create mode 100644 arch/arm/include/asm/krait-l2-accessors.h + +diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig +index c3a4e9c..9da52dc 100644 +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -9,6 +9,9 @@ config DMABOUNCE + bool + select ZONE_DMA + ++config KRAIT_L2_ACCESSORS ++ bool ++ + config SHARP_LOCOMO + bool + +diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile +index 4bdc4162..2836f99 100644 +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -7,6 +7,7 @@ obj-y += firmware.o + obj-$(CONFIG_ICST) += icst.o + obj-$(CONFIG_SA1111) += sa1111.o + obj-$(CONFIG_DMABOUNCE) += dmabounce.o ++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o + obj-$(CONFIG_SHARP_LOCOMO) += locomo.o + obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o +diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c +new file mode 100644 +index 0000000..5d514bb +--- /dev/null ++++ b/arch/arm/common/krait-l2-accessors.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/spinlock.h> ++#include <linux/export.h> ++ ++#include <asm/barrier.h> ++#include <asm/krait-l2-accessors.h> ++ ++static DEFINE_RAW_SPINLOCK(krait_l2_lock); ++ ++void krait_set_l2_indirect_reg(u32 addr, u32 val) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then write to the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); ++ isb(); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++} ++EXPORT_SYMBOL(krait_set_l2_indirect_reg); ++ ++u32 krait_get_l2_indirect_reg(u32 addr) ++{ ++ u32 val; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then read from the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++ ++ return val; ++} ++EXPORT_SYMBOL(krait_get_l2_indirect_reg); +diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h +new file mode 100644 +index 0000000..48fe552 +--- /dev/null ++++ b/arch/arm/include/asm/krait-l2-accessors.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H ++#define __ASMARM_KRAIT_L2_ACCESSORS_H ++ ++extern void krait_set_l2_indirect_reg(u32 addr, u32 val); ++extern u32 krait_get_l2_indirect_reg(u32 addr); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch new file mode 100644 index 0000000000..0964106770 --- /dev/null +++ b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch @@ -0,0 +1,663 @@ +From 151d7e91baaa4016ba687b80e8f7ccead62d6c72 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 25 Mar 2014 13:37:55 -0700 +Subject: [PATCH 165/182] clk: qcom: Add support for muxes, dividers, and mux + dividers + +The Krait CPU clocks are made up of muxes and dividers with a +handful of sources. Add a set of clk_ops that allow us to +configure these clocks so we can support CPU frequency scaling on +Krait CPUs. + +Based on code originally written by Saravana Kannan. + +Cc: Saravana Kannan <skannan@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-generic.c | 405 +++++++++++++++++++++++++++++++++++ + include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++ + 3 files changed, 614 insertions(+) + create mode 100644 drivers/clk/qcom/clk-generic.c + create mode 100644 include/linux/clk/msm-clk-generic.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index df2a1b3..2cc6039 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o + clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o ++clk-qcom-y += clk-generic.o + clk-qcom-y += reset.o + + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c +new file mode 100644 +index 0000000..a0d778b +--- /dev/null ++++ b/drivers/clk/qcom/clk-generic.c +@@ -0,0 +1,405 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/export.h> ++#include <linux/bug.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/clk/msm-clk-generic.h> ++ ++ ++/* ==================== Mux clock ==================== */ ++ ++static int mux_set_parent(struct clk_hw *hw, u8 sel) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ ++ if (mux->parent_map) ++ sel = mux->parent_map[sel]; ++ ++ return mux->ops->set_mux_sel(mux, sel); ++} ++ ++static u8 mux_get_parent(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ int i; ++ u8 sel; ++ ++ sel = mux->ops->get_mux_sel(mux); ++ if (mux->parent_map) { ++ for (i = 0; i < num_parents; i++) ++ if (sel == mux->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++ } ++ ++ return sel; ++} ++ ++static int mux_enable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->enable) ++ return mux->ops->enable(mux); ++ return 0; ++} ++ ++static void mux_disable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->disable) ++ return mux->ops->disable(mux); ++} ++ ++static struct clk *mux_get_safe_parent(struct clk_hw *hw) ++{ ++ int i; ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ ++ if (!mux->has_safe_parent) ++ return NULL; ++ ++ i = mux->safe_sel; ++ if (mux->parent_map) ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return clk_get_parent_by_index(hw->clk, i); ++} ++ ++const struct clk_ops clk_ops_gen_mux = { ++ .enable = mux_enable, ++ .disable = mux_disable, ++ .set_parent = mux_set_parent, ++ .get_parent = mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate, ++ .get_safe_parent = mux_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_gen_mux); ++ ++/* ==================== Divider clock ==================== */ ++ ++static long __div_round_rate(struct div_data *data, unsigned long rate, ++ struct clk *parent, unsigned int *best_div, unsigned long *best_prate, ++ bool set_parent) ++{ ++ unsigned int div, min_div, max_div, _best_div = 1; ++ unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate; ++ unsigned int numer; ++ ++ rate = max(rate, 1UL); ++ ++ min_div = max(data->min_div, 1U); ++ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate)); ++ ++ /* ++ * div values are doubled for half dividers. ++ * Adjust for that by picking a numer of 2. ++ */ ++ numer = data->is_half_divider ? 2 : 1; ++ ++ if (!set_parent) { ++ prate = *best_prate * numer; ++ div = DIV_ROUND_UP(prate, rate); ++ div = clamp(1U, div, max_div); ++ if (best_div) ++ *best_div = div; ++ return mult_frac(*best_prate, numer, div); ++ } ++ ++ for (div = min_div; div <= max_div; div++) { ++ req_prate = mult_frac(rate, div, numer); ++ prate = __clk_round_rate(parent, req_prate); ++ if (IS_ERR_VALUE(prate)) ++ break; ++ ++ actual_rate = mult_frac(prate, numer, div); ++ if (is_better_rate(rate, rrate, actual_rate)) { ++ rrate = actual_rate; ++ _best_div = div; ++ _best_prate = prate; ++ } ++ ++ /* ++ * Trying higher dividers is only going to ask the parent for ++ * a higher rate. If it can't even output a rate higher than ++ * the one we request for this divider, the parent is not ++ * going to be able to output an even higher rate required ++ * for a higher divider. So, stop trying higher dividers. ++ */ ++ if (actual_rate < rate) ++ break; ++ ++ if (rrate <= rate) ++ break; ++ } ++ ++ if (!rrate) ++ return -EINVAL; ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ ++ return rrate; ++} ++ ++static long div_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk), ++ NULL, parent_rate, set_parent); ++} ++ ++static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long ++ parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ int div, rc = 0; ++ struct div_data *data = &d->data; ++ ++ div = parent_rate / rate; ++ if (div != data->div) ++ rc = d->ops->set_div(d, div); ++ data->div = div; ++ ++ return rc; ++} ++ ++static int div_enable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->enable) ++ return d->ops->enable(d); ++ return 0; ++} ++ ++static void div_disable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->disable) ++ return d->ops->disable(d); ++} ++ ++static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ unsigned int div = d->data.div; ++ ++ if (d->ops && d->ops->get_div) ++ div = max(d->ops->get_div(d), 1); ++ div = max(div, 1U); ++ ++ if (!d->ops || !d->ops->set_div) ++ d->data.min_div = d->data.max_div = div; ++ d->data.div = div; ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_div = { ++ .enable = div_enable, ++ .disable = div_disable, ++ .round_rate = div_round_rate, ++ .set_rate = div_set_rate, ++ .recalc_rate = div_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_div); ++ ++/* ==================== Mux_div clock ==================== */ ++ ++static int mux_div_clk_enable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->enable) ++ return md->ops->enable(md); ++ return 0; ++} ++ ++static void mux_div_clk_disable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->disable) ++ return md->ops->disable(md); ++} ++ ++static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate, ++ struct clk **best_parent, int *best_div, unsigned long *best_prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ unsigned int i; ++ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0; ++ struct clk *_best_parent = 0; ++ int num_parents = __clk_get_num_parents(hw->clk); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ for (i = 0; i < num_parents; i++) { ++ int div; ++ unsigned long prate; ++ struct clk *p = clk_get_parent_by_index(hw->clk, i); ++ ++ rrate = __div_round_rate(&md->data, rate, p, &div, &prate, ++ set_parent); ++ ++ if (is_better_rate(rate, best, rrate)) { ++ best = rrate; ++ _best_div = div; ++ _best_prate = prate; ++ _best_parent = p; ++ } ++ ++ if (rate <= rrate) ++ break; ++ } ++ ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ if (best_parent) ++ *best_parent = _best_parent; ++ ++ if (best) ++ return best; ++ return -EINVAL; ++} ++ ++static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate); ++} ++ ++/* requires enable lock to be held */ ++static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div) ++{ ++ int rc; ++ ++ rc = md->ops->set_src_div(md, src_sel, div); ++ if (!rc) { ++ md->data.div = div; ++ md->src_sel = src_sel; ++ } ++ ++ return rc; ++} ++ ++/* Must be called after handoff to ensure parent clock rates are initialized */ ++static int safe_parent_init_once(struct clk_hw *hw) ++{ ++ unsigned long rrate; ++ u32 best_div; ++ struct clk *best_parent; ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (IS_ERR(md->safe_parent)) ++ return -EINVAL; ++ if (!md->safe_freq || md->safe_parent) ++ return 0; ++ ++ rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent, ++ &best_div, NULL); ++ ++ if (rrate == md->safe_freq) { ++ md->safe_div = best_div; ++ md->safe_parent = best_parent; ++ } else { ++ md->safe_parent = ERR_PTR(-EINVAL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int ++__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int rc; ++ ++ rc = safe_parent_init_once(hw); ++ if (rc) ++ return rc; ++ ++ return __set_src_div(md, index, div); ++} ++ ++static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate); ++} ++ ++static int mux_div_clk_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->src_sel, ++ parent_rate / rate); ++} ++ ++static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index], ++ md->data.div); ++} ++ ++static u8 mux_div_clk_get_parent(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ u32 i, div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ md->src_sel = sel; ++ ++ for (i = 0; i < num_parents; i++) ++ if (sel == md->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++} ++ ++static unsigned long ++mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ u32 div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_mux_div_clk = { ++ .enable = mux_div_clk_enable, ++ .disable = mux_div_clk_disable, ++ .set_rate_and_parent = mux_div_clk_set_rate_and_parent, ++ .set_rate = mux_div_clk_set_rate, ++ .set_parent = mux_div_clk_set_parent, ++ .round_rate = mux_div_clk_round_rate, ++ .get_parent = mux_div_clk_get_parent, ++ .recalc_rate = mux_div_clk_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk); +diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h +new file mode 100644 +index 0000000..cee3863 +--- /dev/null ++++ b/include/linux/clk/msm-clk-generic.h +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __QCOM_CLK_GENERIC_H__ ++#define __QCOM_CLK_GENERIC_H__ ++ ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++ ++static inline bool is_better_rate(unsigned long req, unsigned long best, ++ unsigned long new) ++{ ++ if (IS_ERR_VALUE(new)) ++ return false; ++ ++ return (req <= new && new < best) || (best < req && best < new); ++} ++ ++/* ==================== Mux clock ==================== */ ++ ++struct mux_clk; ++ ++struct clk_mux_ops { ++ int (*set_mux_sel)(struct mux_clk *clk, int sel); ++ int (*get_mux_sel)(struct mux_clk *clk); ++ ++ /* Optional */ ++ bool (*is_enabled)(struct mux_clk *clk); ++ int (*enable)(struct mux_clk *clk); ++ void (*disable)(struct mux_clk *clk); ++}; ++ ++struct mux_clk { ++ /* Parents in decreasing order of preference for obtaining rates. */ ++ u8 *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ const struct clk_mux_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 en_offset; ++ int en_reg; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ ++ struct clk_hw hw; ++}; ++ ++static inline struct mux_clk *to_mux_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_gen_mux; ++ ++/* ==================== Divider clock ==================== */ ++ ++struct div_clk; ++ ++struct clk_div_ops { ++ int (*set_div)(struct div_clk *clk, int div); ++ int (*get_div)(struct div_clk *clk); ++ bool (*is_enabled)(struct div_clk *clk); ++ int (*enable)(struct div_clk *clk); ++ void (*disable)(struct div_clk *clk); ++}; ++ ++struct div_data { ++ unsigned int div; ++ unsigned int min_div; ++ unsigned int max_div; ++ /* ++ * Indicate whether this divider clock supports half-interger divider. ++ * If it is, all the min_div and max_div have been doubled. It means ++ * they are 2*N. ++ */ ++ bool is_half_divider; ++}; ++ ++struct div_clk { ++ struct div_data data; ++ ++ /* Optional */ ++ const struct clk_div_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ struct clk_hw hw; ++}; ++ ++static inline struct div_clk *to_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_div; ++ ++#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \ ++static struct div_clk clk_name = { \ ++ .data = { \ ++ .max_div = _div, \ ++ .min_div = _div, \ ++ .div = _div, \ ++ }, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .parent_names = (const char *[]){ _parent }, \ ++ .num_parents = 1, \ ++ .name = #clk_name, \ ++ .ops = &clk_ops_div, \ ++ .flags = CLK_SET_RATE_PARENT, \ ++ } \ ++} ++ ++/* ==================== Mux Div clock ==================== */ ++ ++struct mux_div_clk; ++ ++/* ++ * struct mux_div_ops ++ * the enable and disable ops are optional. ++ */ ++ ++struct mux_div_ops { ++ int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div); ++ void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div); ++ int (*enable)(struct mux_div_clk *); ++ void (*disable)(struct mux_div_clk *); ++ bool (*is_enabled)(struct mux_div_clk *); ++}; ++ ++/* ++ * struct mux_div_clk - combined mux/divider clock ++ * @priv ++ parameters needed by ops ++ * @safe_freq ++ when switching rates from A to B, the mux div clock will ++ instead switch from A -> safe_freq -> B. This allows the ++ mux_div clock to change rates while enabled, even if this ++ behavior is not supported by the parent clocks. ++ ++ If changing the rate of parent A also causes the rate of ++ parent B to change, then safe_freq must be defined. ++ ++ safe_freq is expected to have a source clock which is always ++ on and runs at only one rate. ++ * @parents ++ list of parents and mux indicies ++ * @ops ++ function pointers for hw specific operations ++ * @src_sel ++ the mux index which will be used if the clock is enabled. ++ */ ++ ++struct mux_div_clk { ++ /* Required parameters */ ++ const struct mux_div_ops *ops; ++ struct div_data data; ++ u8 *parent_map; ++ ++ struct clk_hw hw; ++ ++ /* Internal */ ++ u32 src_sel; ++ ++ /* Optional parameters */ ++ void *priv; ++ void __iomem *base; ++ u32 div_mask; ++ u32 div_offset; ++ u32 div_shift; ++ u32 src_mask; ++ u32 src_offset; ++ u32 src_shift; ++ u32 en_mask; ++ u32 en_offset; ++ ++ u32 safe_div; ++ struct clk *safe_parent; ++ unsigned long safe_freq; ++}; ++ ++static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_mux_div_clk; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 0000000000..3ff1a95def --- /dev/null +++ b/target/linux/ipq806x/patches/0166-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,359 @@ +From 8a51ac2a4e36505e1ff82b0e49fd8b2edd0b7695 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:15:58 -0700 +Subject: [PATCH 166/182] clk: qcom: Add support for High-Frequency PLLs + (HFPLLs) + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-hfpll.c | 260 ++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ + 3 files changed, 315 insertions(+) + create mode 100644 drivers/clk/qcom/clk-hfpll.c + create mode 100644 drivers/clk/qcom/clk-hfpll.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 2cc6039..93fd03f 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-generic.o ++clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c +new file mode 100644 +index 0000000..f8a40a7 +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.c +@@ -0,0 +1,260 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/regmap.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++#define PLL_OUTCTRL BIT(0) ++#define PLL_BYPASSNL BIT(1) ++#define PLL_RESET_N BIT(2) ++ ++/* Initialize a HFPLL at a given rate and enable it. */ ++static void __clk_hfpll_init_once(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ if (likely(h->init_done)) ++ return; ++ ++ /* Configure PLL parameters for integer mode. */ ++ if (hd->config_val) ++ regmap_write(regmap, hd->config_reg, hd->config_val); ++ regmap_write(regmap, hd->m_reg, 0); ++ regmap_write(regmap, hd->n_reg, 1); ++ ++ if (hd->user_reg) { ++ u32 regval = hd->user_val; ++ unsigned long rate; ++ ++ rate = __clk_get_rate(hw->clk); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate) ++ regval |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, regval); ++ } ++ ++ if (hd->droop_reg) ++ regmap_write(regmap, hd->droop_reg, hd->droop_val); ++ ++ h->init_done = true; ++} ++ ++static void __clk_hfpll_enable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 val; ++ ++ __clk_hfpll_init_once(hw); ++ ++ /* Disable PLL bypass mode. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); ++ ++ /* ++ * H/W requires a 5us delay between disabling the bypass and ++ * de-asserting the reset. Delay 10us just to be safe. ++ */ ++ mb(); ++ udelay(10); ++ ++ /* De-assert active-low PLL reset. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); ++ ++ /* Wait for PLL to lock. */ ++ if (hd->status_reg) { ++ do { ++ regmap_read(regmap, hd->status_reg, &val); ++ } while (!(val & BIT(hd->lock_bit))); ++ } else { ++ mb(); ++ udelay(60); ++ } ++ ++ /* Enable PLL output. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); ++ ++ /* Make sure the enable is done before returning. */ ++ mb(); ++} ++ ++/* Enable an already-configured HFPLL. */ ++static int clk_hfpll_enable(struct clk_hw *hw) ++{ ++ unsigned long flags; ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) ++ __clk_hfpll_enable(hw); ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static void __clk_hfpll_disable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ /* ++ * Disable the PLL output, disable test mode, enable the bypass mode, ++ * and assert the reset. ++ */ ++ regmap_update_bits(regmap, hd->mode_reg, ++ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); ++} ++ ++static void clk_hfpll_disable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ __clk_hfpll_disable(hw); ++ spin_unlock_irqrestore(&h->lock, flags); ++} ++ ++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ unsigned long rrate; ++ ++ rate = clamp(rate, hd->min_rate, hd->max_rate); ++ ++ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; ++ if (rrate > hd->max_rate) ++ rrate -= *parent_rate; ++ ++ return rrate; ++} ++ ++/* ++ * For optimization reasons, assumes no downstream clocks are actively using ++ * it. ++ */ ++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ unsigned long flags; ++ u32 l_val, val; ++ bool enabled; ++ ++ l_val = rate / parent_rate; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ ++ enabled = __clk_is_enabled(hw->clk); ++ if (enabled) ++ __clk_hfpll_disable(hw); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_reg && hd->user_vco_mask) { ++ regmap_read(regmap, hd->user_reg, &val); ++ if (rate <= hd->low_vco_max_rate) ++ val &= ~hd->user_vco_mask; ++ else ++ val |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, val); ++ } ++ ++ regmap_write(regmap, hd->l_reg, l_val); ++ ++ if (enabled) ++ __clk_hfpll_enable(hw); ++ ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 l_val; ++ ++ regmap_read(regmap, hd->l_reg, &l_val); ++ ++ return l_val * parent_rate; ++} ++ ++static void clk_hfpll_init(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode, status; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { ++ __clk_hfpll_init_once(hw); ++ return; ++ } ++ ++ if (hd->status_reg) { ++ regmap_read(regmap, hd->status_reg, &status); ++ if (!(status & BIT(hd->lock_bit))) { ++ WARN(1, "HFPLL %s is ON, but not locked!\n", ++ __clk_get_name(hw->clk)); ++ clk_hfpll_disable(hw); ++ __clk_hfpll_init_once(hw); ++ } ++ } ++} ++ ++static int hfpll_is_enabled(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ mode &= 0x7; ++ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); ++} ++ ++const struct clk_ops clk_ops_hfpll = { ++ .enable = clk_hfpll_enable, ++ .disable = clk_hfpll_disable, ++ .is_enabled = hfpll_is_enabled, ++ .round_rate = clk_hfpll_round_rate, ++ .set_rate = clk_hfpll_set_rate, ++ .recalc_rate = clk_hfpll_recalc_rate, ++ .init = clk_hfpll_init, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_hfpll); +diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h +new file mode 100644 +index 0000000..48c18d6 +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __QCOM_CLK_HFPLL_H__ ++#define __QCOM_CLK_HFPLL_H__ ++ ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++#include "clk-regmap.h" ++ ++struct hfpll_data { ++ u32 mode_reg; ++ u32 l_reg; ++ u32 m_reg; ++ u32 n_reg; ++ u32 user_reg; ++ u32 droop_reg; ++ u32 config_reg; ++ u32 status_reg; ++ u8 lock_bit; ++ ++ u32 droop_val; ++ u32 config_val; ++ u32 user_val; ++ u32 user_vco_mask; ++ unsigned long low_vco_max_rate; ++ ++ unsigned long min_rate; ++ unsigned long max_rate; ++}; ++ ++struct clk_hfpll { ++ struct hfpll_data const *d; ++ int init_done; ++ ++ struct clk_regmap clkr; ++ spinlock_t lock; ++}; ++ ++#define to_clk_hfpll(_hw) \ ++ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) ++ ++extern const struct clk_ops clk_ops_hfpll; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 0000000000..c2c1680ea2 --- /dev/null +++ b/target/linux/ipq806x/patches/0167-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,162 @@ +From 49134da893bc11e833e3d87139c57e3b84e65219 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Thu, 19 Jun 2014 18:46:31 -0700 +Subject: [PATCH 167/182] clk: qcom: Add HFPLL driver + +On some devices (MSM8974 for example), the HFPLLs are +instantiated within the Krait processor subsystem as separate +register regions. Add a driver for these PLLs so that we can +provide HFPLL clocks for use by the system. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 ++++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 110 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 119 insertions(+) + create mode 100644 drivers/clk/qcom/hfpll.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index cfaa54c..de8ba31 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -53,3 +53,11 @@ config MSM_MMCC_8974 + Support for the multimedia clock controller on msm8974 devices. + Say Y if you want to support multimedia devices such as display, + graphics, video encode/decode, camera, etc. ++ ++config QCOM_HFPLL ++ tristate "High-Frequency PLL (HFPLL) Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the high-frequency PLLs present on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling on devices ++ such as MSM8974, APQ8084, etc. +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 93fd03f..d0d8e3d 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -16,3 +16,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o ++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c +new file mode 100644 +index 0000000..701a377 +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++#include <linux/regmap.h> ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++static const struct hfpll_data hdata = { ++ .mode_reg = 0x00, ++ .l_reg = 0x04, ++ .m_reg = 0x08, ++ .n_reg = 0x0c, ++ .user_reg = 0x10, ++ .config_reg = 0x14, ++ .config_val = 0x430405d, ++ .status_reg = 0x1c, ++ .lock_bit = 16, ++ ++ .user_val = 0x8, ++ .user_vco_mask = 0x100000, ++ .low_vco_max_rate = 1248000000, ++ .min_rate = 537600000UL, ++ .max_rate = 2900000000UL, ++}; ++ ++static const struct of_device_id qcom_hfpll_match_table[] = { ++ { .compatible = "qcom,hfpll" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); ++ ++static struct regmap_config hfpll_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x30, ++ .fast_io = true, ++}; ++ ++static int qcom_hfpll_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ void __iomem *base; ++ struct regmap *regmap; ++ struct clk_hfpll *h; ++ struct clk_init_data init = { ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_ops_hfpll, ++ }; ++ ++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); ++ if (!h) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_string_index(dev->of_node, "clock-output-names", ++ 0, &init.name)) ++ return -ENODEV; ++ ++ h->d = &hdata; ++ h->clkr.hw.init = &init; ++ spin_lock_init(&h->lock); ++ ++ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct platform_driver qcom_hfpll_driver = { ++ .probe = qcom_hfpll_probe, ++ .driver = { ++ .name = "qcom-hfpll", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_hfpll_match_table, ++ }, ++}; ++module_platform_driver(qcom_hfpll_driver); ++ ++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-hfpll"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch new file mode 100644 index 0000000000..68fda612eb --- /dev/null +++ b/target/linux/ipq806x/patches/0168-clk-qcom-Add-MSM8960-s-HFPLLs.patch @@ -0,0 +1,122 @@ +From 0a38d7a21ef0e851d025e4e16f096d5579226299 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 16 Jun 2014 17:44:08 -0700 +Subject: [PATCH 168/182] clk: qcom: Add MSM8960's HFPLLs + +Describe the HFPLLs present on MSM8960 devices. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 82 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c +index f4ffd91..d04fc99 100644 +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -30,6 +30,7 @@ + #include "clk-pll.h" + #include "clk-rcg.h" + #include "clk-branch.h" ++#include "clk-hfpll.h" + #include "reset.h" + + static struct clk_pll pll3 = { +@@ -75,6 +76,84 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 0x3300, ++ .l_reg = 0x3308, ++ .m_reg = 0x330c, ++ .n_reg = 0x3310, ++ .config_reg = 0x3304, ++ .status_reg = 0x331c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll1 = { ++ .d = &hfpll1_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll1", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), ++}; ++ ++static struct hfpll_data hfpll_l2_data = { ++ .mode_reg = 0x3400, ++ .l_reg = 0x3408, ++ .m_reg = 0x340c, ++ .n_reg = 0x3410, ++ .config_reg = 0x3404, ++ .status_reg = 0x341c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3414, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll_l2 = { ++ .d = &hfpll_l2_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll_l2", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), ++}; ++ + static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, +@@ -2763,6 +2842,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = { + [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, + [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, + [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_msm8960_resets[] = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 0000000000..9b76f59508 --- /dev/null +++ b/target/linux/ipq806x/patches/0169-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,203 @@ +From 63ecfef8560631a15ee13129b2778cd4dffbcfe2 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:18:31 -0700 +Subject: [PATCH 169/182] clk: qcom: Add support for Krait clocks + +The Krait clocks are made up of a series of muxes and a divider +that choose between a fixed rate clock and dedicated HFPLLs for +each CPU. Instead of using mmio accesses to remux parents, the +Krait implementation exposes the remux control via cp15 +registers. Support these clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 4 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-krait.c | 121 ++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 22 ++++++++ + 4 files changed, 148 insertions(+) + create mode 100644 drivers/clk/qcom/clk-krait.c + create mode 100644 drivers/clk/qcom/clk-krait.h + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index de8ba31..70b6a7c 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -61,3 +61,7 @@ config QCOM_HFPLL + Support for the high-frequency PLLs present on Qualcomm devices. + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. ++ ++config KRAIT_CLOCKS ++ bool ++ select KRAIT_L2_ACCESSORS +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index d0d8e3d..6482165 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-generic.o ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + +diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c +new file mode 100644 +index 0000000..4283426 +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.c +@@ -0,0 +1,121 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++ ++#include <asm/krait-l2-accessors.h> ++ ++#include "clk-krait.h" ++ ++/* Secondary and primary muxes share the same cp15 register */ ++static DEFINE_SPINLOCK(kpss_clock_reg_lock); ++ ++#define LPL_SHIFT 8 ++static void __kpss_mux_set_sel(struct mux_clk *mux, int sel) ++{ ++ unsigned long flags; ++ u32 regval; ++ ++ spin_lock_irqsave(&kpss_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(mux->offset); ++ regval &= ~(mux->mask << mux->shift); ++ regval |= (sel & mux->mask) << mux->shift; ++ if (mux->priv) { ++ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); ++ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); ++ } ++ krait_set_l2_indirect_reg(mux->offset, regval); ++ spin_unlock_irqrestore(&kpss_clock_reg_lock, flags); ++ ++ /* Wait for switch to complete. */ ++ mb(); ++ udelay(1); ++} ++ ++static int kpss_mux_set_sel(struct mux_clk *mux, int sel) ++{ ++ mux->en_mask = sel; ++ /* Don't touch mux if CPU is off as it won't work */ ++ if (__clk_is_enabled(mux->hw.clk)) ++ __kpss_mux_set_sel(mux, sel); ++ return 0; ++} ++ ++static int kpss_mux_get_sel(struct mux_clk *mux) ++{ ++ u32 sel; ++ ++ sel = krait_get_l2_indirect_reg(mux->offset); ++ sel >>= mux->shift; ++ sel &= mux->mask; ++ mux->en_mask = sel; ++ ++ return sel; ++} ++ ++static int kpss_mux_enable(struct mux_clk *mux) ++{ ++ __kpss_mux_set_sel(mux, mux->en_mask); ++ return 0; ++} ++ ++static void kpss_mux_disable(struct mux_clk *mux) ++{ ++ __kpss_mux_set_sel(mux, mux->safe_sel); ++} ++ ++const struct clk_mux_ops clk_mux_ops_kpss = { ++ .enable = kpss_mux_enable, ++ .disable = kpss_mux_disable, ++ .set_mux_sel = kpss_mux_set_sel, ++ .get_mux_sel = kpss_mux_get_sel, ++}; ++EXPORT_SYMBOL_GPL(clk_mux_ops_kpss); ++ ++/* ++ * The divider can divide by 2, 4, 6 and 8. But we only really need div-2. So ++ * force it to div-2 during handoff and treat it like a fixed div-2 clock. ++ */ ++static int kpss_div2_get_div(struct div_clk *div) ++{ ++ unsigned long flags; ++ u32 regval; ++ int val; ++ ++ spin_lock_irqsave(&kpss_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(div->offset); ++ val = (regval >> div->shift) & div->mask; ++ regval &= ~(div->mask << div->shift); ++ if (div->priv) ++ regval &= ~(div->mask << (div->shift + LPL_SHIFT)); ++ krait_set_l2_indirect_reg(div->offset, regval); ++ spin_unlock_irqrestore(&kpss_clock_reg_lock, flags); ++ ++ val = (val + 1) * 2; ++ WARN(val != 2, "Divider %s was configured to div-%d instead of 2!\n", ++ __clk_get_name(div->hw.clk), val); ++ ++ return 2; ++} ++ ++const struct clk_div_ops clk_div_ops_kpss_div2 = { ++ .get_div = kpss_div2_get_div, ++}; ++EXPORT_SYMBOL_GPL(clk_div_ops_kpss_div2); +diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h +new file mode 100644 +index 0000000..9c3eb38 +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.h +@@ -0,0 +1,22 @@ ++/* ++ * Copyright (c) 2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __SOC_QCOM_CLOCK_KRAIT_H ++#define __SOC_QCOM_CLOCK_KRAIT_H ++ ++#include <linux/clk/msm-clk-generic.h> ++ ++extern const struct clk_mux_ops clk_mux_ops_kpss; ++extern const struct clk_div_ops clk_div_ops_kpss_div2; ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 0000000000..8817bbbf49 --- /dev/null +++ b/target/linux/ipq806x/patches/0170-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,171 @@ +From 67a9d5a02b178644da624ef9c32b4e6abb2c4f6e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:25:41 -0700 +Subject: [PATCH 170/182] clk: qcom: Add KPSS ACC/GCC driver + +The ACC and GCC regions present in KPSSv1 contain registers to +control clocks and power to each Krait CPU and L2. For CPUfreq +purposes probe these devices and expose a mux clock that chooses +between PXO and PLL8. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 +++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/kpss-xcc.c | 115 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 124 insertions(+) + create mode 100644 drivers/clk/qcom/kpss-xcc.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 70b6a7c..e9e5360 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -62,6 +62,14 @@ config QCOM_HFPLL + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. + ++config KPSS_XCC ++ tristate "KPSS Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the Krait ACC and GCC clock controllers. Say Y ++ if you want to support CPU frequency scaling on devices such ++ as MSM8960, APQ8064, etc. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 6482165..29b2a45 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -17,4 +17,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o ++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c +new file mode 100644 +index 0000000..1061668 +--- /dev/null ++++ b/drivers/clk/qcom/kpss-xcc.c +@@ -0,0 +1,115 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++#include <linux/clk/msm-clk-generic.h> ++ ++static int kpss_xcc_set_mux_sel(struct mux_clk *clk, int sel) ++{ ++ writel_relaxed(sel, clk->base + clk->offset); ++ return 0; ++} ++ ++static int kpss_xcc_get_mux_sel(struct mux_clk *clk) ++{ ++ return readl_relaxed(clk->base + clk->offset); ++} ++ ++static const struct clk_mux_ops kpss_xcc_ops = { ++ .set_mux_sel = kpss_xcc_set_mux_sel, ++ .get_mux_sel = kpss_xcc_get_mux_sel, ++}; ++ ++static const char *aux_parents[] = { ++ "pll8_vote", ++ "pxo", ++}; ++ ++static u8 aux_parent_map[] = { ++ 3, ++ 0, ++}; ++ ++static const struct of_device_id kpss_xcc_match_table[] = { ++ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, ++ { .compatible = "qcom,kpss-gcc" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); ++ ++static int kpss_xcc_driver_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct clk *clk; ++ struct resource *res; ++ void __iomem *base; ++ struct mux_clk *mux_clk; ++ struct clk_init_data init = { ++ .parent_names = aux_parents, ++ .num_parents = 2, ++ .ops = &clk_ops_gen_mux, ++ }; ++ ++ id = of_match_device(kpss_xcc_match_table, &pdev->dev); ++ if (!id) ++ return -ENODEV; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ mux_clk = devm_kzalloc(&pdev->dev, sizeof(*mux_clk), GFP_KERNEL); ++ if (!mux_clk) ++ return -ENOMEM; ++ ++ mux_clk->mask = 0x3; ++ mux_clk->parent_map = aux_parent_map; ++ mux_clk->ops = &kpss_xcc_ops; ++ mux_clk->base = base; ++ mux_clk->hw.init = &init; ++ ++ if (id->data) { ++ if (of_property_read_string_index(pdev->dev.of_node, ++ "clock-output-names", 0, &init.name)) ++ return -ENODEV; ++ mux_clk->offset = 0x14; ++ } else { ++ init.name = "acpu_l2_aux"; ++ mux_clk->offset = 0x28; ++ } ++ ++ clk = devm_clk_register(&pdev->dev, &mux_clk->hw); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct platform_driver kpss_xcc_driver = { ++ .probe = kpss_xcc_driver_probe, ++ .driver = { ++ .name = "kpss-xcc", ++ .of_match_table = kpss_xcc_match_table, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(kpss_xcc_driver); ++ ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 0000000000..8fa83dfdc2 --- /dev/null +++ b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,420 @@ +From 6912e27d97ba5671e8c2434bed0ebd23fde5e13d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 14:29:29 -0700 +Subject: [PATCH 171/182] clk: qcom: Add Krait clock controller driver + +The Krait CPU clocks are made up of a primary mux and secondary +mux for each CPU and the L2, controlled via cp15 accessors. For +Kraits within KPSSv1 each secondary mux accepts a different aux +source, but on KPSSv2 each secondary mux accepts the same aux +source. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 364 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 373 insertions(+) + create mode 100644 drivers/clk/qcom/krait-cc.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index e9e5360..7418108 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -70,6 +70,14 @@ config KPSS_XCC + if you want to support CPU frequency scaling on devices such + as MSM8960, APQ8064, etc. + ++config KRAITCC ++ tristate "Krait Clock Controller" ++ depends on COMMON_CLK_QCOM && ARM ++ select KRAIT_CLOCKS ++ help ++ Support for the Krait CPU clocks on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 29b2a45..1b88abe 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -19,3 +19,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ++obj-$(CONFIG_KRAITCC) += krait-cc.o +diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c +new file mode 100644 +index 0000000..90985ea +--- /dev/null ++++ b/drivers/clk/qcom/krait-cc.c +@@ -0,0 +1,364 @@ ++/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++#include <linux/slab.h> ++ ++#include <asm/smp_plat.h> ++ ++#include "clk-krait.h" ++ ++DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote"); ++ ++static u8 sec_mux_map[] = { ++ 2, ++ 0, ++}; ++ ++static u8 pri_mux_map[] = { ++ 1, ++ 2, ++ 0, ++}; ++ ++static int ++krait_add_div(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct div_clk *div; ++ struct clk_init_data init = { ++ .num_parents = 1, ++ .ops = &clk_ops_div, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ const char *p_names[1]; ++ struct clk *clk; ++ ++ div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL); ++ if (!div) ++ return -ENOMEM; ++ ++ div->data.div = 2; ++ div->data.min_div = 2; ++ div->data.max_div = 2; ++ div->ops = &clk_div_ops_kpss_div2; ++ div->mask = 0x3; ++ div->shift = 6; ++ div->priv = (void *)(id >= 0); ++ div->offset = offset; ++ div->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ init.parent_names = p_names; ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ kfree(init.name); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_register(dev, &div->hw); ++ kfree(p_names[0]); ++ kfree(init.name); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int ++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset, ++ bool unique_aux) ++{ ++ struct mux_clk *mux; ++ static const char *sec_mux_list[] = { ++ "acpu_aux", ++ "qsb", ++ }; ++ struct clk_init_data init = { ++ .parent_names = sec_mux_list, ++ .num_parents = ARRAY_SIZE(sec_mux_list), ++ .ops = &clk_ops_gen_mux, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return -ENOMEM; ++ ++ mux->offset = offset; ++ mux->priv = (void *)(id >= 0); ++ mux->has_safe_parent = true; ++ mux->safe_sel = 2; ++ mux->ops = &clk_mux_ops_kpss; ++ mux->mask = 0x3; ++ mux->shift = 2; ++ mux->parent_map = sec_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ if (unique_aux) { ++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); ++ if (!sec_mux_list[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_aux; ++ } ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ if (unique_aux) ++ kfree(sec_mux_list[0]); ++err_aux: ++ kfree(init.name); ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct clk * ++krait_add_pri_mux(struct device *dev, int id, const char * s, unsigned offset) ++{ ++ struct mux_clk *mux; ++ const char *p_names[3]; ++ struct clk_init_data init = { ++ .parent_names = p_names, ++ .num_parents = ARRAY_SIZE(p_names), ++ .ops = &clk_ops_gen_mux, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return ERR_PTR(-ENOMEM); ++ ++ mux->has_safe_parent = true; ++ mux->safe_sel = 0; ++ mux->ops = &clk_mux_ops_kpss; ++ mux->mask = 0x3; ++ mux->shift = 0; ++ mux->offset = offset; ++ mux->priv = (void *)(id >= 0); ++ mux->parent_map = pri_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); ++ if (!init.name) ++ return ERR_PTR(-ENOMEM); ++ ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p0; ++ } ++ ++ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!p_names[1]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p1; ++ } ++ ++ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!p_names[2]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p2; ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ kfree(p_names[2]); ++err_p2: ++ kfree(p_names[1]); ++err_p1: ++ kfree(p_names[0]); ++err_p0: ++ kfree(init.name); ++ return clk; ++} ++ ++/* id < 0 for L2, otherwise id == physical CPU number */ ++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) ++{ ++ int ret; ++ unsigned offset; ++ void *p = NULL; ++ const char *s; ++ struct clk *clk; ++ ++ if (id >= 0) { ++ offset = 0x4501 + (0x1000 * id); ++ s = p = kasprintf(GFP_KERNEL, "%d", id); ++ if (!s) ++ return ERR_PTR(-ENOMEM); ++ } else { ++ offset = 0x500; ++ s = "_l2"; ++ } ++ ++ ret = krait_add_div(dev, id, s, offset); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ clk = krait_add_pri_mux(dev, id, s, offset); ++err: ++ kfree(p); ++ return clk; ++} ++ ++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) ++{ ++ unsigned int idx = clkspec->args[0]; ++ struct clk **clks = data; ++ ++ if (idx >= 5) { ++ pr_err("%s: invalid clock index %d\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return clks[idx] ? : ERR_PTR(-ENODEV); ++} ++ ++static const struct of_device_id krait_cc_match_table[] = { ++ { .compatible = "qcom,krait-cc-v1", (void *)1UL }, ++ { .compatible = "qcom,krait-cc-v2" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, krait_cc_match_table); ++ ++static int krait_cc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *id; ++ unsigned long cur_rate, aux_rate; ++ int i, cpu; ++ struct clk *clk; ++ struct clk **clks; ++ struct clk *l2_pri_mux_clk; ++ ++ id = of_match_device(krait_cc_match_table, &pdev->dev); ++ if (!id) ++ return -ENODEV; ++ ++ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ ++ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ if (!id->data) { ++ clk = devm_clk_register(dev, &acpu_aux.hw); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ ++ /* Krait configurations have at most 4 CPUs and one L2 */ ++ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); ++ if (!clks) ++ return -ENOMEM; ++ ++ for_each_possible_cpu(i) { ++ cpu = cpu_logical_map(i); ++ clk = krait_add_clks(dev, cpu, id->data); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[cpu] = clk; ++ } ++ ++ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); ++ if (IS_ERR(l2_pri_mux_clk)) ++ return PTR_ERR(l2_pri_mux_clk); ++ clks[4] = l2_pri_mux_clk; ++ ++ /* ++ * We don't want the CPU or L2 clocks to be turned off at late init ++ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the ++ * refcount of these clocks. Any cpufreq/hotplug manager can assume ++ * that the clocks have already been prepared and enabled by the time ++ * they take over. ++ */ ++ for_each_online_cpu(i) { ++ cpu = cpu_logical_map(i); ++ clk_prepare_enable(l2_pri_mux_clk); ++ WARN(clk_prepare_enable(clks[cpu]), ++ "Unable to turn on CPU%d clock", cpu); ++ } ++ ++ /* ++ * Force reinit of HFPLLs and muxes to overwrite any potential ++ * incorrect configuration of HFPLLs and muxes by the bootloader. ++ * While at it, also make sure the cores are running at known rates ++ * and print the current rate. ++ * ++ * The clocks are set to aux clock rate first to make sure the ++ * secondary mux is not sourcing off of QSB. The rate is then set to ++ * two different rates to force a HFPLL reinit under all ++ * circumstances. ++ */ ++ cur_rate = clk_get_rate(l2_pri_mux_clk); ++ aux_rate = 384000000; ++ if (cur_rate == 1) { ++ pr_info("L2 @ QSB rate. Forcing new rate.\n"); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(l2_pri_mux_clk, aux_rate); ++ clk_set_rate(l2_pri_mux_clk, 2); ++ clk_set_rate(l2_pri_mux_clk, cur_rate); ++ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); ++ for_each_possible_cpu(i) { ++ cpu = cpu_logical_map(i); ++ clk = clks[cpu]; ++ cur_rate = clk_get_rate(clk); ++ if (cur_rate == 1) { ++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", i); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(clk, aux_rate); ++ clk_set_rate(clk, 2); ++ clk_set_rate(clk, cur_rate); ++ pr_info("CPU%d @ %lu KHz\n", i, clk_get_rate(clk) / 1000); ++ } ++ ++ of_clk_add_provider(dev->of_node, krait_of_get, clks); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cc_driver = { ++ .probe = krait_cc_probe, ++ .driver = { ++ .name = "clock-krait", ++ .of_match_table = krait_cc_match_table, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(krait_cc_driver); ++ ++MODULE_DESCRIPTION("Krait CPU Clock Driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch new file mode 100644 index 0000000000..3150a4dfb8 --- /dev/null +++ b/target/linux/ipq806x/patches/0172-cpufreq-Add-a-cpufreq-krait-based-on-cpufreq-cpu0.patch @@ -0,0 +1,255 @@ +From 5cf343c60d7557aefb468603065cac5062f11b8d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 30 May 2014 16:36:11 -0700 +Subject: [PATCH 172/182] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 + +Krait processors have individual clocks for each CPU that can +scale independently from one another. cpufreq-cpu0 is fairly +close to this, but assumes that there is only one clock for all +CPUs. Add a driver to support the Krait configuration. + +TODO: Merge into cpufreq-cpu0? Or make generic? + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/cpufreq/Kconfig | 13 +++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/cpufreq-krait.c | 190 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 204 insertions(+) + create mode 100644 drivers/cpufreq/cpufreq-krait.c + +diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig +index 4b029c0..4051528 100644 +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -194,6 +194,19 @@ config GENERIC_CPUFREQ_CPU0 + + If in doubt, say N. + ++config GENERIC_CPUFREQ_KRAIT ++ tristate "Krait cpufreq driver" ++ depends on HAVE_CLK && OF ++ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: ++ depends on !CPU_THERMAL || THERMAL ++ select PM_OPP ++ help ++ This adds a generic cpufreq driver for CPU0 frequency management. ++ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) ++ systems which share clock and voltage across all CPUs. ++ ++ If in doubt, say N. ++ + menu "x86 CPU frequency scaling drivers" + depends on X86 + source "drivers/cpufreq/Kconfig.x86" +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 7494565..f6f4485 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + + obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o ++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o + + ################################################################################## + # x86 drivers. +diff --git a/drivers/cpufreq/cpufreq-krait.c b/drivers/cpufreq/cpufreq-krait.c +new file mode 100644 +index 0000000..7b38b9c +--- /dev/null ++++ b/drivers/cpufreq/cpufreq-krait.c +@@ -0,0 +1,190 @@ ++/* ++ * Copyright (C) 2012 Freescale Semiconductor, Inc. ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * The OPP code in function krait_set_target() is reused from ++ * drivers/cpufreq/omap-cpufreq.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/cpu.h> ++#include <linux/cpu_cooling.h> ++#include <linux/cpufreq.h> ++#include <linux/cpumask.h> ++#include <linux/err.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/pm_opp.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++ ++static unsigned int transition_latency; ++ ++static struct device *cpu_dev; ++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); ++static struct cpufreq_frequency_table *freq_table; ++static struct thermal_cooling_device *cdev; ++ ++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ unsigned long volt = 0, volt_old = 0; ++ unsigned int old_freq, new_freq; ++ long freq_Hz, freq_exact; ++ int ret; ++ struct clk *cpu_clk; ++ ++ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); ++ if (freq_Hz <= 0) ++ freq_Hz = freq_table[index].frequency * 1000; ++ ++ freq_exact = freq_Hz; ++ new_freq = freq_Hz / 1000; ++ old_freq = clk_get_rate(cpu_clk) / 1000; ++ ++ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", ++ old_freq / 1000, volt_old ? volt_old / 1000 : -1, ++ new_freq / 1000, volt ? volt / 1000 : -1); ++ ++ ret = clk_set_rate(cpu_clk, freq_exact); ++ if (ret) ++ pr_err("failed to set clock rate: %d\n", ret); ++ ++ return ret; ++} ++ ++static int krait_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ int ret; ++ ++ policy->clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ ret = cpufreq_table_validate_and_show(policy, freq_table); ++ if (ret) { ++ pr_err("%s: invalid frequency table: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ policy->cpuinfo.transition_latency = transition_latency; ++ ++ return 0; ++} ++ ++static struct cpufreq_driver krait_cpufreq_driver = { ++ .flags = CPUFREQ_STICKY, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = krait_set_target, ++ .get = cpufreq_generic_get, ++ .init = krait_cpufreq_init, ++ .name = "generic_krait", ++ .attr = cpufreq_generic_attr, ++}; ++ ++static int krait_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct device_node *np; ++ int ret; ++ unsigned int cpu; ++ struct device *dev; ++ struct clk *clk; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get krait device\n"); ++ return -ENODEV; ++ } ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) { ++ pr_err("failed to find krait node\n"); ++ return -ENOENT; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ if (!dev) { ++ pr_err("failed to get krait device\n"); ++ ret = -ENOENT; ++ goto out_put_node; ++ } ++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ goto out_put_node; ++ } ++ } ++ ++ ret = of_init_opp_table(cpu_dev); ++ if (ret) { ++ pr_err("failed to init OPP table: %d\n", ret); ++ goto out_put_node; ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ pr_err("failed to init cpufreq table: %d\n", ret); ++ goto out_put_node; ++ } ++ ++ if (of_property_read_u32(np, "clock-latency", &transition_latency)) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ ret = cpufreq_register_driver(&krait_cpufreq_driver); ++ if (ret) { ++ pr_err("failed register driver: %d\n", ret); ++ goto out_free_table; ++ } ++ of_node_put(np); ++ ++ /* ++ * For now, just loading the cooling device; ++ * thermal DT code takes care of matching them. ++ */ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ np = of_node_get(dev->of_node); ++ if (of_find_property(np, "#cooling-cells", NULL)) { ++ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu)); ++ if (IS_ERR(cdev)) ++ pr_err("running cpufreq without cooling device: %ld\n", ++ PTR_ERR(cdev)); ++ } ++ of_node_put(np); ++ } ++ ++ return 0; ++ ++out_free_table: ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++out_put_node: ++ of_node_put(np); ++ return ret; ++} ++ ++static int krait_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_cooling_unregister(cdev); ++ cpufreq_unregister_driver(&krait_cpufreq_driver); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cpufreq_platdrv = { ++ .driver = { ++ .name = "cpufreq-krait", ++ .owner = THIS_MODULE, ++ }, ++ .probe = krait_cpufreq_probe, ++ .remove = krait_cpufreq_remove, ++}; ++module_platform_driver(krait_cpufreq_platdrv); ++ ++MODULE_DESCRIPTION("Krait CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch new file mode 100644 index 0000000000..c43221070c --- /dev/null +++ b/target/linux/ipq806x/patches/0173-cpufreq-Add-module-to-register-cpufreq-krait-device.patch @@ -0,0 +1,104 @@ +From f1db56284b01b1212a211023dcaa7846fd07d0ec Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 30 May 2014 17:16:53 -0700 +Subject: [PATCH 173/182] cpufreq: Add module to register cpufreq-krait device + +Register a cpufreq-krait device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/cpufreq/Kconfig.arm | 8 +++++++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/qcom-cpufreq.c | 48 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 57 insertions(+) + create mode 100644 drivers/cpufreq/qcom-cpufreq.c + +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index 3129749..6ae884d 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -123,6 +123,14 @@ config ARM_OMAP2PLUS_CPUFREQ + depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS + ++config ARM_QCOM_CPUFREQ ++ tristate "Qualcomm based" ++ depends on ARCH_QCOM ++ help ++ This adds the CPUFreq driver for Qualcomm SoC based boards. ++ ++ If in doubt, say N. ++ + config ARM_S3C_CPUFREQ + bool + help +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index f6f4485..f5d18a3 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -60,6 +60,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o + obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o + obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o + obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o +diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c +new file mode 100644 +index 0000000..71f4387 +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -0,0 +1,48 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/cpu.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++ ++static int qcom_cpufreq_driver_init(void) ++{ ++ struct platform_device_info devinfo = { .name = "cpufreq-krait", }; ++ struct device *cpu_dev; ++ struct device_node *np; ++ struct platform_device *pdev; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) ++ return -ENODEV; ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) ++ return -ENOENT; ++ ++ if (!of_device_is_compatible(np, "qcom,krait")) { ++ of_node_put(np); ++ return -ENODEV; ++ } ++ of_node_put(np); ++ ++ pdev = platform_device_register_full(&devinfo); ++ ++ return PTR_ERR_OR_ZERO(pdev); ++} ++module_init(qcom_cpufreq_driver_init); ++ ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch new file mode 100644 index 0000000000..38dbd6526e --- /dev/null +++ b/target/linux/ipq806x/patches/0174-clk-qcom-Add-HFPLLs-to-IPQ806X-driver.patch @@ -0,0 +1,121 @@ +From b9b3815f2a71af88ca68d3524ee4d9b6b4739257 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 15:57:06 -0700 +Subject: [PATCH 174/182] clk: qcom: Add HFPLLs to IPQ806X driver + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c +index d80dc69..83a73cb 100644 +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -30,6 +30,7 @@ + #include "clk-pll.h" + #include "clk-rcg.h" + #include "clk-branch.h" ++#include "clk-hfpll.h" + #include "reset.h" + + static struct clk_pll pll0 = { +@@ -102,6 +103,85 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 0x3240, ++ .l_reg = 0x3248, ++ .m_reg = 0x324c, ++ .n_reg = 0x3250, ++ .config_reg = 0x3244, ++ .status_reg = 0x325c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll1 = { ++ .d = &hfpll1_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll1", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), ++}; ++ ++static struct hfpll_data hfpll_l2_data = { ++ .mode_reg = 0x3300, ++ .l_reg = 0x3308, ++ .m_reg = 0x330c, ++ .n_reg = 0x3310, ++ .config_reg = 0x3304, ++ .status_reg = 0x331c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll_l2 = { ++ .d = &hfpll_l2_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll_l2", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), ++}; ++ ++ + static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, +@@ -2878,6 +2958,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { + [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, + [NSSTCM_CLK] = &nss_tcm_clk.clkr, + [NSS_CORE_CLK] = &nss_core_clk, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch new file mode 100644 index 0000000000..6fea560286 --- /dev/null +++ b/target/linux/ipq806x/patches/0175-ARM-dts-ipq8064-Add-necessary-DT-data-for-Krait-cpuf.patch @@ -0,0 +1,96 @@ +From 258a3ea23ec048603debe1621c681b0cc733f236 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Wed, 18 Jun 2014 16:04:37 -0700 +Subject: [PATCH 175/182] ARM: dts: ipq8064: Add necessary DT data for Krait + cpufreq + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 45 +++++++++++++++++++++++++++++++++++ + 1 file changed, 45 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 6be6ac9..97e4c3d 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -23,6 +23,22 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>; ++ clock-names = "cpu"; ++ operating-points = < ++ /* kHz ignored */ ++ 1400000 1000000 ++ 1200000 1000000 ++ 1000000 1000000 ++ 800000 1000000 ++ 600000 1000000 ++ 384000 1000000 ++ >; ++ clock-latency = <100000>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + }; + + cpu@1 { +@@ -33,6 +49,22 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>; ++ clock-names = "cpu"; ++ operating-points = < ++ /* kHz ignored */ ++ 1400000 1000000 ++ 1200000 1000000 ++ 1000000 1000000 ++ 800000 1000000 ++ 600000 1000000 ++ 384000 1000000 ++ >; ++ clock-latency = <100000>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + }; + + L2: l2-cache { +@@ -46,6 +78,11 @@ + interrupts = <1 10 0x304>; + }; + ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; +@@ -102,11 +139,19 @@ + acc0: clock-controller@2088000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; + + acc1: clock-controller@2098000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu1_aux"; ++ }; ++ ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; + }; + + saw0: regulator@2089000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch new file mode 100644 index 0000000000..e5656020b7 --- /dev/null +++ b/target/linux/ipq806x/patches/0176-ARM-qcom_defconfig-Enable-CPUfreq-options.patch @@ -0,0 +1,44 @@ +From e8f4d4bf1e4974bf8d17c6702fa4c410637216f3 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 30 Jun 2014 10:50:14 -0700 +Subject: [PATCH 176/182] ARM: qcom_defconfig: Enable CPUfreq options + +Enable required options to allow the CPUfreq driver to probe on +Krait based platforms. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + arch/arm/configs/qcom_defconfig | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig +index 3d55d79..3bc72eb 100644 +--- a/arch/arm/configs/qcom_defconfig ++++ b/arch/arm/configs/qcom_defconfig +@@ -31,6 +31,13 @@ CONFIG_HIGHPTE=y + CONFIG_CLEANCACHE=y + CONFIG_ARM_APPENDED_DTB=y + CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_GENERIC_CPUFREQ_KRAIT=y ++CONFIG_ARM_QCOM_CPUFREQ=y + CONFIG_CPU_IDLE=y + CONFIG_VFP=y + CONFIG_NEON=y +@@ -149,6 +156,9 @@ CONFIG_IPQ_GCC_806X=y + CONFIG_MSM_GCC_8660=y + CONFIG_MSM_MMCC_8960=y + CONFIG_MSM_MMCC_8974=y ++CONFIG_QCOM_HFPLL=y ++CONFIG_KPSS_XCC=y ++CONFIG_KRAITCC=y + CONFIG_MSM_IOMMU=y + CONFIG_PHY_QCOM_IPQ806X_SATA=y + CONFIG_EXT2_FS=y +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch new file mode 100644 index 0000000000..d7d301dede --- /dev/null +++ b/target/linux/ipq806x/patches/0177-dmaengine-Add-QCOM-ADM-DMA-driver.patch @@ -0,0 +1,931 @@ +From 8984e3fc6db029479d6aa78882b39235379aebff Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Wed, 14 May 2014 13:45:07 -0500 +Subject: [PATCH 177/182] dmaengine: Add QCOM ADM DMA driver + +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA +controller found in the MSM8960 and IPQ/APQ8064 platforms. + +The ADM supports both memory to memory transactions and memory +to/from peripheral device transactions. The controller also provides flow +control capabilities for transactions to/from peripheral devices. + +The initial release of this driver supports slave transfers to/from peripherals +and also incorporates CRCI (client rate control interface) flow control. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/dma/Kconfig | 10 + + drivers/dma/Makefile | 1 + + drivers/dma/qcom_adm.c | 871 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 882 insertions(+) + create mode 100644 drivers/dma/qcom_adm.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index f87cef9..79155fa 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -410,4 +410,14 @@ config QCOM_BAM_DMA + Enable support for the QCOM BAM DMA controller. This controller + provides DMA capabilities for a variety of on-chip devices. + ++config QCOM_ADM ++ tristate "Qualcomm ADM support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the Qualcomm ADM DMA controller. This controller ++ provides DMA capabilities for both general purpose and on-chip ++ peripheral devices. ++ + endif +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index 5150c82..4a4f521 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -46,3 +46,4 @@ obj-$(CONFIG_K3_DMA) += k3dma.o + obj-$(CONFIG_MOXART_DMA) += moxart-dma.o + obj-$(CONFIG_FSL_EDMA) += fsl-edma.o + obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o +diff --git a/drivers/dma/qcom_adm.c b/drivers/dma/qcom_adm.c +new file mode 100644 +index 0000000..035f606 +--- /dev/null ++++ b/drivers/dma/qcom_adm.c +@@ -0,0 +1,871 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/io.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/scatterlist.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/of_dma.h> ++#include <linux/reset.h> ++#include <linux/clk.h> ++#include <linux/dmaengine.h> ++ ++#include "dmaengine.h" ++#include "virt-dma.h" ++ ++/* ADM registers - calculated from channel number and security domain */ ++#define HI_CH_CMD_PTR(chan, ee) (4*chan + 0x20800*ee) ++#define HI_CH_RSLT(chan, ee) (0x40 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE0(chan, ee) (0x80 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE1(chan, ee) (0xc0 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE2(chan, ee) (0x100 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE3(chan, ee) (0x140 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE4(chan, ee) (0x180 + 4*chan + 0x20800*ee) ++#define HI_CH_FLUSH_STATE5(chan, ee) (0x1c0 + 4*chan + 0x20800*ee) ++#define HI_CH_STATUS_SD(chan, ee) (0x200 + 4*chan + 0x20800*ee) ++#define HI_CH_CONF(chan) (0x240 + 4*chan) ++#define HI_CH_RSLT_CONF(chan, ee) (0x300 + 4*chan + 0x20800*ee) ++#define HI_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + 0x20800*ee) ++#define HI_CI_CONF(ci) (0x390 + 4*ci) ++#define HI_CRCI_CONF0 0x3d0 ++#define HI_CRCI_CONF1 0x3d4 ++#define HI_GP_CTL 0x3d8 ++#define HI_CRCI_CTL(chan, ee) (0x400 + 0x4*chan + 0x20800*ee) ++ ++/* channel status */ ++#define CH_STATUS_VALID BIT(1) ++ ++/* channel result */ ++#define CH_RSLT_VALID BIT(31) ++#define CH_RSLT_ERR BIT(3) ++#define CH_RSLT_FLUSH BIT(2) ++#define CH_RSLT_TPD BIT(1) ++ ++/* channel conf */ ++#define CH_CONF_MPU_DISABLE BIT(11) ++#define CH_CONF_PERM_MPU_CONF BIT(9) ++#define CH_CONF_FLUSH_RSLT_EN BIT(8) ++#define CH_CONF_FORCE_RSLT_EN BIT(7) ++#define CH_CONF_IRQ_EN BIT(6) ++ ++/* channel result conf */ ++#define CH_RSLT_CONF_FLUSH_EN BIT(1) ++#define CH_RSLT_CONF_IRQ_EN BIT(0) ++ ++/* CRCI CTL */ ++#define CRCI_CTL_RST BIT(17) ++ ++/* CI configuration */ ++#define CI_RANGE_END(x) (x << 24) ++#define CI_RANGE_START(x) (x << 16) ++#define CI_BURST_4_WORDS 0x4 ++#define CI_BURST_8_WORDS 0x8 ++ ++/* GP CTL */ ++#define GP_CTL_LP_EN BIT(12) ++#define GP_CTL_LP_CNT(x) (x << 8) ++ ++/* Command pointer list entry */ ++#define CPLE_LP BIT(31) ++ ++/* Command list entry */ ++#define CMD_LC BIT(31) ++#define CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++ ++#define CMD_TYPE_SINGLE 0x0 ++#define CMD_TYPE_BOX 0x3 ++ ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K-1) ++#define ADM_MAX_ROWS (SZ_64K-1) ++ ++/* Command Pointer List Entry */ ++#define CMD_LP BIT(31) ++#define CMD_PT_MASK (0x3 << 29) ++#define CMD_ADDR_MASK 0x3fffffff ++ ++struct adm_desc_hw { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 row_len; ++ u32 num_rows; ++ u32 row_offset; ++}; ++ ++struct adm_cmd_ptr_list { ++ u32 cple; /* command ptr list entry */ ++ struct adm_desc_hw desc[0]; ++}; ++ ++struct adm_async_desc { ++ struct virt_dma_desc vd; ++ struct adm_device *adev; ++ ++ size_t length; ++ enum dma_transfer_direction dir; ++ dma_addr_t dma_addr; ++ size_t dma_len; ++ ++ struct adm_cmd_ptr_list *cpl; ++ u32 num_desc; ++}; ++ ++struct adm_chan { ++ struct virt_dma_chan vc; ++ struct adm_device *adev; ++ ++ /* parsed from DT */ ++ u32 id; /* channel id */ ++ u32 crci; /* CRCI to be used for transfers */ ++ u32 blk_size; /* block size for CRCI, default 16 byte */ ++ ++ struct adm_async_desc *curr_txd; ++ struct dma_slave_config slave; ++ struct list_head node; ++ ++ int error; ++ int initialized; ++}; ++ ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct adm_chan, vc.chan); ++} ++ ++struct adm_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct adm_chan *channels; ++ u32 num_channels; ++ ++ u32 ee; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ ++ struct reset_control *clk_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ int irq; ++}; ++ ++/** ++ * adm_alloc_chan - Allocates channel resources for DMA channel ++ * ++ * This function is effectively a stub, as we don't need to setup any resources ++ */ ++static int adm_alloc_chan(struct dma_chan *chan) ++{ ++ return 0; ++} ++ ++/** ++ * adm_free_chan - Frees dma resources associated with the specific channel ++ * ++ * Free all allocated descriptors associated with this channel ++ * ++ */ ++static void adm_free_chan(struct dma_chan *chan) ++{ ++ /* free all queued descriptors */ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++} ++ ++/** ++ * adm_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct scatterlist *sg; ++ u32 i, rows, num_desc = 0, idx = 0, desc_offset; ++ struct adm_desc_hw *desc; ++ struct adm_cmd_ptr_list *cpl; ++ u32 burst = ADM_MAX_XFER; ++ ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(adev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* if using CRCI flow control, validate burst settings */ ++ if (achan->slave.device_fc) { ++ burst = (direction == DMA_MEM_TO_DEV) ? ++ achan->slave.dst_maxburst : ++ achan->slave.src_maxburst; ++ ++ if (!burst) { ++ dev_err(adev->dev, "invalid burst value w/ crci: %d\n", ++ burst); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ /* iterate through sgs and compute allocation size of structures */ ++ for_each_sg(sgl, sg, sg_len, i) { ++ ++ /* calculate boxes using burst */ ++ rows = DIV_ROUND_UP(sg_dma_len(sg), burst); ++ num_desc += DIV_ROUND_UP(rows, ADM_MAX_ROWS); ++ ++ /* flow control requires length as a multiple of burst */ ++ if (achan->slave.device_fc && (sg_dma_len(sg) % burst)) { ++ dev_err(adev->dev, "length is not multiple of burst\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); ++ if (!async_desc) ++ return ERR_PTR(-ENOMEM); ++ ++ async_desc->dma_len = num_desc * sizeof(*desc) + sizeof(*cpl) + ++ ADM_DESC_ALIGN; ++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len, ++ &async_desc->dma_addr, GFP_NOWAIT); ++ ++ if (!async_desc->cpl) { ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ async_desc->num_desc = num_desc; ++ async_desc->adev = adev; ++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN); ++ desc_offset = (u32)desc - (u32)async_desc->cpl; ++ ++ /* init cmd list */ ++ cpl->cple |= CPLE_LP; ++ cpl->cple |= (async_desc->dma_addr + desc_offset) >> 3; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ unsigned int remainder = sg_dma_len(sg); ++ unsigned int curr_offset = 0; ++ unsigned int row_len; ++ ++ do { ++ desc[idx].cmd = CMD_TYPE_BOX; ++ desc[idx].row_offset = 0; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ desc[idx].dst_addr = sg_dma_address(sg) + ++ curr_offset; ++ desc[idx].src_addr = achan->slave.src_addr; ++ desc[idx].cmd |= CMD_SRC_CRCI(achan->crci); ++ desc[idx].row_offset = burst; ++ } else { ++ desc[idx].src_addr = sg_dma_address(sg) + ++ curr_offset; ++ desc[idx].dst_addr = achan->slave.dst_addr; ++ desc[idx].cmd |= CMD_DST_CRCI(achan->crci); ++ desc[idx].row_offset = burst << 16; ++ } ++ ++ if (remainder < burst) { ++ rows = 1; ++ row_len = remainder; ++ } else { ++ rows = remainder / burst; ++ rows = min_t(u32, rows, ADM_MAX_ROWS); ++ row_len = burst; ++ } ++ ++ desc[idx].num_rows = rows << 16 | rows; ++ desc[idx].row_len = row_len << 16 | row_len; ++ ++ remainder -= row_len * rows; ++ async_desc->length += row_len * rows; ++ curr_offset += row_len * rows; ++ ++ idx++; ++ } while (remainder > 0); ++ } ++ ++ /* set last command flag */ ++ desc[idx - 1].cmd |= CMD_LC; ++ ++ /* reset channel error */ ++ achan->error = 0; ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++} ++ ++/** ++ * adm_slave_config - set slave configuration for channel ++ * @chan: dma channel ++ * @cfg: slave configuration ++ * ++ * Sets slave configuration for channel ++ * ++ */ ++static int adm_slave_config(struct adm_chan *achan, ++ struct dma_slave_config *cfg) ++{ ++ int ret = 0; ++ u32 burst; ++ struct adm_device *adev = achan->adev; ++ ++ memcpy(&achan->slave, cfg, sizeof(*cfg)); ++ ++ /* set channel CRCI burst, if applicable */ ++ if (achan->crci) { ++ burst = max_t(u32, cfg->src_maxburst, cfg->dst_maxburst); ++ ++ switch (burst) { ++ case 16: ++ achan->blk_size = 0; ++ break; ++ case 32: ++ achan->blk_size = 1; ++ break; ++ case 64: ++ achan->blk_size = 2; ++ break; ++ case 128: ++ achan->blk_size = 3; ++ break; ++ case 192: ++ achan->blk_size = 4; ++ break; ++ case 256: ++ achan->blk_size = 5; ++ break; ++ default: ++ achan->slave.src_maxburst = 0; ++ achan->slave.dst_maxburst = 0; ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (!ret) ++ writel(achan->blk_size, ++ adev->regs + HI_CRCI_CTL(achan->id, adev->ee)); ++ } ++ ++ return ret; ++} ++ ++/** ++ * adm_terminate_all - terminate all transactions on a channel ++ * @achan: adm dma channel ++ * ++ * Dequeues and frees all transactions, aborts current transaction ++ * No callbacks are done ++ * ++ */ ++static void adm_terminate_all(struct adm_chan *achan) ++{ ++ struct adm_device *adev = achan->adev; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ /* send flush command to terminate current transaction */ ++ writel_relaxed(0x0, ++ adev->regs + HI_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ vchan_get_all_descriptors(&achan->vc, &head); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ vchan_dma_desc_free_list(&achan->vc, &head); ++} ++ ++/** ++ * adm_control - DMA device control ++ * @chan: dma channel ++ * @cmd: control cmd ++ * @arg: cmd argument ++ * ++ * Perform DMA control command ++ * ++ */ ++static int adm_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flag; ++ int ret = 0; ++ ++ switch (cmd) { ++ case DMA_SLAVE_CONFIG: ++ spin_lock_irqsave(&achan->vc.lock, flag); ++ ret = adm_slave_config(achan, (struct dma_slave_config *)arg); ++ spin_unlock_irqrestore(&achan->vc.lock, flag); ++ break; ++ ++ case DMA_TERMINATE_ALL: ++ adm_terminate_all(achan); ++ break; ++ ++ default: ++ ret = -ENXIO; ++ break; ++ }; ++ ++ return ret; ++} ++ ++/** ++ * adm_start_dma - start next transaction ++ * @achan - ADM dma channel ++ */ ++static void adm_start_dma(struct adm_chan *achan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct adm_desc_hw *desc; ++ struct adm_cmd_ptr_list *cpl; ++ ++ lockdep_assert_held(&achan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ /* write next command list out to the CMD FIFO */ ++ async_desc = container_of(vd, struct adm_async_desc, vd); ++ achan->curr_txd = async_desc; ++ ++ cpl = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(&cpl->desc[0], ADM_DESC_ALIGN); ++ ++ if (!achan->initialized) { ++ /* enable interrupts */ ++ writel(CH_CONF_IRQ_EN | CH_CONF_FLUSH_RSLT_EN | ++ CH_CONF_FORCE_RSLT_EN | CH_CONF_PERM_MPU_CONF | ++ CH_CONF_MPU_DISABLE, ++ adev->regs + HI_CH_CONF(achan->id)); ++ ++ writel(CH_RSLT_CONF_IRQ_EN | CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ if (achan->crci) ++ writel(achan->blk_size, adev->regs + ++ HI_CRCI_CTL(achan->crci, adev->ee)); ++ ++ achan->initialized = 1; ++ } ++ ++ /* make sure IRQ enable doesn't get reordered */ ++ wmb(); ++ ++ /* write next command list out to the CMD FIFO */ ++ writel(round_up(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, ++ adev->regs + HI_CH_CMD_PTR(achan->id, adev->ee)); ++} ++ ++/** ++ * adm_dma_irq - irq handler for ADM controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t adm_dma_irq(int irq, void *data) ++{ ++ struct adm_device *adev = data; ++ u32 srcs, i; ++ struct adm_async_desc *async_desc; ++ unsigned long flags; ++ ++ srcs = readl_relaxed(adev->regs + ++ HI_SEC_DOMAIN_IRQ_STATUS(adev->ee)); ++ ++ for (i = 0; i < 16; i++) { ++ struct adm_chan *achan = &adev->channels[i]; ++ u32 status, result; ++ if (srcs & BIT(i)) { ++ status = readl_relaxed(adev->regs + ++ HI_CH_STATUS_SD(i, adev->ee)); ++ ++ /* if no result present, skip */ ++ if (!(status & CH_STATUS_VALID)) ++ continue; ++ ++ result = readl_relaxed(adev->regs + ++ HI_CH_RSLT(i, adev->ee)); ++ ++ /* no valid results, skip */ ++ if (!(result & CH_RSLT_VALID)) ++ continue; ++ ++ /* flag error if transaction was flushed or failed */ ++ if (result & (CH_RSLT_ERR | CH_RSLT_FLUSH)) ++ achan->error = 1; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ async_desc = achan->curr_txd; ++ ++ achan->curr_txd = NULL; ++ ++ if (async_desc) { ++ vchan_cookie_complete(&async_desc->vd); ++ ++ /* kick off next DMA */ ++ adm_start_dma(achan); ++ } ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * adm_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ size_t residue = 0; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ vd = vchan_find_desc(&achan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct adm_async_desc, vd)->length; ++ else if (achan->curr_txd && achan->curr_txd->vd.tx.cookie == cookie) ++ residue = achan->curr_txd->length; ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ dma_set_residue(txstate, residue); ++ ++ if (achan->error) ++ return DMA_ERROR; ++ ++ return ret; ++} ++ ++static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *of) ++{ ++ struct adm_device *adev = container_of(of->of_dma_data, ++ struct adm_device, common); ++ struct adm_chan *achan; ++ struct dma_chan *chan; ++ unsigned int request; ++ unsigned int crci; ++ ++ if (dma_spec->args_count != 2) { ++ dev_err(adev->dev, "incorrect number of dma arguments\n"); ++ return NULL; ++ } ++ ++ request = dma_spec->args[0]; ++ if (request >= adev->num_channels) ++ return NULL; ++ ++ crci = dma_spec->args[1]; ++ ++ chan = dma_get_slave_channel(&(adev->channels[request].vc.chan)); ++ ++ if (!chan) ++ return NULL; ++ ++ achan = to_adm_chan(chan); ++ achan->crci = crci; ++ ++ return chan; ++} ++ ++/** ++ * adm_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Issues all pending transactions and starts DMA ++ */ ++static void adm_issue_pending(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) ++ adm_start_dma(achan); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++} ++ ++/** ++ * adm_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void adm_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct adm_async_desc *async_desc = container_of(vd, ++ struct adm_async_desc, vd); ++ ++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len, ++ async_desc->cpl, async_desc->dma_addr); ++ kfree(async_desc); ++} ++ ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, ++ u32 index) ++{ ++ achan->id = index; ++ achan->adev = adev; ++ ++ vchan_init(&achan->vc, &adev->common); ++ achan->vc.desc_free = adm_dma_free_desc; ++} ++ ++static int adm_dma_probe(struct platform_device *pdev) ++{ ++ struct adm_device *adev; ++ struct resource *iores; ++ int ret; ++ u32 i; ++ ++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); ++ if (!adev) ++ return -ENOMEM; ++ ++ adev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ adev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(adev->regs)) ++ return PTR_ERR(adev->regs); ++ ++ adev->irq = platform_get_irq(pdev, 0); ++ if (adev->irq < 0) ++ return adev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); ++ if (ret) { ++ dev_err(adev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ adev->core_clk = devm_clk_get(adev->dev, "core"); ++ if (IS_ERR(adev->core_clk)) ++ return PTR_ERR(adev->core_clk); ++ ++ ret = clk_prepare_enable(adev->core_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable core clock\n"); ++ return ret; ++ } ++ ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) ++ return PTR_ERR(adev->iface_clk); ++ ++ ret = clk_prepare_enable(adev->iface_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); ++ return ret; ++ } ++ ++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ return PTR_ERR(adev->clk_reset); ++ } ++ ++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ return PTR_ERR(adev->c0_reset); ++ } ++ ++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ return PTR_ERR(adev->c1_reset); ++ } ++ ++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ return PTR_ERR(adev->c2_reset); ++ } ++ ++ reset_control_assert(adev->clk_reset); ++ reset_control_assert(adev->c0_reset); ++ reset_control_assert(adev->c1_reset); ++ reset_control_assert(adev->c2_reset); ++ ++ reset_control_deassert(adev->clk_reset); ++ reset_control_deassert(adev->c0_reset); ++ reset_control_deassert(adev->c1_reset); ++ reset_control_deassert(adev->c2_reset); ++ ++ adev->num_channels = 16; ++ ++ adev->channels = devm_kcalloc(adev->dev, adev->num_channels, ++ sizeof(*adev->channels), GFP_KERNEL); ++ ++ if (!adev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clk; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&adev->common.channels); ++ ++ for (i = 0; i < adev->num_channels; i++) ++ adm_channel_init(adev, &adev->channels[i], i); ++ ++ /* reset CRCIs */ ++ for (i = 0; i < 16; i++) ++ writel(CRCI_CTL_RST, adev->regs + HI_CRCI_CTL(i, adev->ee)); ++ ++ /* configure client interfaces */ ++ writel(CI_RANGE_START(0x40) | CI_RANGE_END(0xb0) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(0)); ++ writel(CI_RANGE_START(0x2a) | CI_RANGE_END(0x2c) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(1)); ++ writel(CI_RANGE_START(0x12) | CI_RANGE_END(0x28) | CI_BURST_8_WORDS, ++ adev->regs + HI_CI_CONF(2)); ++ writel(GP_CTL_LP_EN | GP_CTL_LP_CNT(0xf), adev->regs + HI_GP_CTL); ++ ++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, ++ 0, "adm_dma", adev); ++ if (ret) ++ goto err_disable_clk; ++ ++ platform_set_drvdata(pdev, adev); ++ ++ adev->common.dev = adev->dev; ++ adev->common.dev->dma_parms = &adev->dma_parms; ++ ++ /* set capabilities */ ++ dma_cap_zero(adev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ adev->common.device_alloc_chan_resources = adm_alloc_chan; ++ adev->common.device_free_chan_resources = adm_free_chan; ++ adev->common.device_prep_slave_sg = adm_prep_slave_sg; ++ adev->common.device_control = adm_control; ++ adev->common.device_issue_pending = adm_issue_pending; ++ adev->common.device_tx_status = adm_tx_status; ++ ++ ret = dma_async_device_register(&adev->common); ++ if (ret) { ++ dev_err(adev->dev, "failed to register dma async device\n"); ++ goto err_disable_clk; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate, ++ &adev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&adev->common); ++err_disable_clk: ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return ret; ++} ++ ++static int adm_dma_remove(struct platform_device *pdev) ++{ ++ struct adm_device *adev = platform_get_drvdata(pdev); ++ struct adm_chan *achan; ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&adev->common); ++ ++ devm_free_irq(adev->dev, adev->irq, adev); ++ ++ for (i = 0; i < adev->num_channels; i++) { ++ achan = &adev->channels[i]; ++ writel(CH_CONF_FLUSH_RSLT_EN, ++ adev->regs + HI_CH_CONF(achan->id)); ++ writel(CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + HI_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ adm_terminate_all(&adev->channels[i]); ++ } ++ ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++ ++static struct platform_driver adm_dma_driver = { ++ .probe = adm_dma_probe, ++ .remove = adm_dma_remove, ++ .driver = { ++ .name = "adm-dma-engine", ++ .owner = THIS_MODULE, ++ .of_match_table = adm_of_match, ++ }, ++}; ++ ++module_platform_driver(adm_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch new file mode 100644 index 0000000000..a80fcfa7f5 --- /dev/null +++ b/target/linux/ipq806x/patches/0178-dmaengine-qcom_adm-Add-device-tree-binding.patch @@ -0,0 +1,82 @@ +From 331294fa5c703536e27b79e9c112d162393f725a Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 13:55:10 -0500 +Subject: [PATCH 178/182] dmaengine: qcom_adm: Add device tree binding + +Add device tree binding support for the QCOM ADM DMA driver. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + Documentation/devicetree/bindings/dma/qcom_adm.txt | 60 ++++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 Documentation/devicetree/bindings/dma/qcom_adm.txt + +diff --git a/Documentation/devicetree/bindings/dma/qcom_adm.txt b/Documentation/devicetree/bindings/dma/qcom_adm.txt +new file mode 100644 +index 0000000..7f05cb5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt +@@ -0,0 +1,60 @@ ++QCOM ADM DMA Controller ++ ++Required properties: ++- compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 ++- reg: Address range for DMA registers ++- interrupts: Should contain one interrupt shared by all channels ++- #dma-cells: must be <2>. First cell denotes the channel number. Second cell ++ denotes CRCI (client rate control interface) flow control assignment. ++- clocks: Should contain the core clock and interface clock. ++- clock-names: Must contain "core" for the core clock and "iface" for the ++ interface clock. ++- resets: Must contain an entry for each entry in reset names. ++- reset-names: Must include the following entries: ++ - clk ++ - c0 ++ - c1 ++ - c2 ++- qcom,ee: indicates the security domain identifier used in the secure world. ++ ++Example: ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ #dma-cells = <2>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core", "iface"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ reset-names = "clk", "c0", "c1", "c2"; ++ qcom,ee = <0>; ++ }; ++ ++DMA clients must use the format descripted in the dma.txt file, using a three ++cell specifier for each channel. ++ ++Each dmas request consists of 3 cells: ++ 1. phandle pointing to the DMA controller ++ 2. channel number ++ 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. ++ ++Example: ++ ++ spi4: spi@1a280000 { ++ status = "ok"; ++ spi-max-frequency = <50000000>; ++ ++ pinctrl-0 = <&spi_pins>; ++ pinctrl-names = "default"; ++ ++ cs-gpios = <&qcom_pinmux 20 0>; ++ ++ dmas = <&adm_dma 6 9>, ++ <&adm_dma 5 10>; ++ dma-names = "rx", "tx"; ++ }; +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch new file mode 100644 index 0000000000..c35aeb17b3 --- /dev/null +++ b/target/linux/ipq806x/patches/0179-spi-qup-Add-DMA-capabilities.patch @@ -0,0 +1,495 @@ +From 8322bafdcee1d7eaf15540ff013415bff1eacb28 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 10:50:24 -0500 +Subject: [PATCH 179/182] spi: qup: Add DMA capabilities + +This patch adds DMA capabilities to the spi-qup driver. If DMA channels are +present, the QUP will use DMA instead of block mode for transfers to/from SPI +peripherals for transactions larger greater than the length of a block. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/spi/spi-qup.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 340 insertions(+), 21 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index c137226..28754ae 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -22,6 +22,8 @@ + #include <linux/platform_device.h> + #include <linux/pm_runtime.h> + #include <linux/spi/spi.h> ++#include <linux/dmaengine.h> ++#include <linux/dma-mapping.h> + + #define QUP_CONFIG 0x0000 + #define QUP_STATE 0x0004 +@@ -116,6 +118,8 @@ + + #define SPI_NUM_CHIPSELECTS 4 + ++#define SPI_MAX_XFER (SZ_64K - 64) ++ + /* high speed mode is when bus rate is greater then 26MHz */ + #define SPI_HS_MIN_RATE 26000000 + #define SPI_MAX_RATE 50000000 +@@ -143,6 +147,14 @@ struct spi_qup { + int tx_bytes; + int rx_bytes; + int qup_v1; ++ int use_dma; ++ ++ struct dma_chan *rx_chan; ++ struct dma_slave_config rx_conf; ++ struct dma_chan *tx_chan; ++ struct dma_slave_config tx_conf; ++ void *dummy; ++ atomic_t dma_outstanding; + }; + + +@@ -266,6 +278,221 @@ static void spi_qup_fifo_write(struct spi_qup *controller, + } + } + ++static void qup_dma_callback(void *data) ++{ ++ struct spi_qup *controller = data; ++ ++ if (atomic_dec_and_test(&controller->dma_outstanding)) ++ complete(&controller->done); ++} ++ ++static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer) ++{ ++ struct dma_async_tx_descriptor *rxd, *txd; ++ dma_cookie_t rx_cookie, tx_cookie; ++ u32 xfer_len, rx_align = 0, tx_align = 0, n_words; ++ struct scatterlist tx_sg[2], rx_sg[2]; ++ int ret = 0; ++ u32 bytes_to_xfer = xfer->len; ++ u32 offset = 0; ++ u32 rx_nents = 0, tx_nents = 0; ++ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0; ++ ++ ++ if (xfer->rx_buf) { ++ rx_dma = dma_map_single(controller->dev, xfer->rx_buf, ++ xfer->len, DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, rx_dma)) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ /* check to see if we need dummy buffer for leftover bytes */ ++ rx_align = xfer->len % controller->in_blk_sz; ++ if (rx_align) { ++ rx_dummy_dma = dma_map_single(controller->dev, ++ controller->dummy, controller->in_fifo_sz, ++ DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, rx_dummy_dma)) { ++ ret = -ENOMEM; ++ goto err_map_rx_dummy; ++ } ++ } ++ } ++ ++ if (xfer->tx_buf) { ++ tx_dma = dma_map_single(controller->dev, ++ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, tx_dma)) { ++ ret = -ENOMEM; ++ goto err_map_tx; ++ } ++ ++ /* check to see if we need dummy buffer for leftover bytes */ ++ tx_align = xfer->len % controller->out_blk_sz; ++ if (tx_align) { ++ memcpy(controller->dummy + SZ_1K, ++ xfer->tx_buf + xfer->len - tx_align, ++ tx_align); ++ memset(controller->dummy + SZ_1K + tx_align, 0, ++ controller->out_blk_sz - tx_align); ++ ++ tx_dummy_dma = dma_map_single(controller->dev, ++ controller->dummy + SZ_1K, ++ controller->out_blk_sz, DMA_TO_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, tx_dummy_dma)) { ++ ret = -ENOMEM; ++ goto err_map_tx_dummy; ++ } ++ } ++ } ++ ++ atomic_set(&controller->dma_outstanding, 0); ++ ++ while (bytes_to_xfer > 0) { ++ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER); ++ n_words = DIV_ROUND_UP(xfer_len, controller->w_size); ++ ++ /* write out current word count to controller */ ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ ++ reinit_completion(&controller->done); ++ ++ if (xfer->tx_buf) { ++ /* recalc align for each transaction */ ++ tx_align = xfer_len % controller->out_blk_sz; ++ ++ if (tx_align) ++ tx_nents = 2; ++ else ++ tx_nents = 1; ++ ++ /* initialize scatterlists */ ++ sg_init_table(tx_sg, tx_nents); ++ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align; ++ sg_dma_address(&tx_sg[0]) = tx_dma + offset; ++ ++ /* account for non block size transfer */ ++ if (tx_align) { ++ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz; ++ sg_dma_address(&tx_sg[1]) = tx_dummy_dma; ++ } ++ ++ txd = dmaengine_prep_slave_sg(controller->tx_chan, ++ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0); ++ if (!txd) { ++ ret = -ENOMEM; ++ goto err_unmap; ++ } ++ ++ atomic_inc(&controller->dma_outstanding); ++ ++ txd->callback = qup_dma_callback; ++ txd->callback_param = controller; ++ ++ tx_cookie = dmaengine_submit(txd); ++ ++ dma_async_issue_pending(controller->tx_chan); ++ } ++ ++ if (xfer->rx_buf) { ++ /* recalc align for each transaction */ ++ rx_align = xfer_len % controller->in_blk_sz; ++ ++ if (rx_align) ++ rx_nents = 2; ++ else ++ rx_nents = 1; ++ ++ /* initialize scatterlists */ ++ sg_init_table(rx_sg, rx_nents); ++ sg_dma_address(&rx_sg[0]) = rx_dma + offset; ++ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align; ++ ++ /* account for non block size transfer */ ++ if (rx_align) { ++ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz; ++ sg_dma_address(&rx_sg[1]) = rx_dummy_dma; ++ } ++ ++ rxd = dmaengine_prep_slave_sg(controller->rx_chan, ++ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0); ++ if (!rxd) { ++ ret = -ENOMEM; ++ goto err_unmap; ++ } ++ ++ atomic_inc(&controller->dma_outstanding); ++ ++ rxd->callback = qup_dma_callback; ++ rxd->callback_param = controller; ++ ++ rx_cookie = dmaengine_submit(rxd); ++ ++ dma_async_issue_pending(controller->rx_chan); ++ } ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set EXECUTE state\n"); ++ goto err_unmap; ++ } ++ ++ if (!wait_for_completion_timeout(&controller->done, ++ msecs_to_jiffies(1000))) { ++ ret = -ETIMEDOUT; ++ ++ /* clear out all the DMA transactions */ ++ if (xfer->tx_buf) ++ dmaengine_terminate_all(controller->tx_chan); ++ if (xfer->rx_buf) ++ dmaengine_terminate_all(controller->rx_chan); ++ ++ goto err_unmap; ++ } ++ ++ if (rx_align) ++ memcpy(xfer->rx_buf + offset + xfer->len - rx_align, ++ controller->dummy, rx_align); ++ ++ /* adjust remaining bytes to transfer */ ++ bytes_to_xfer -= xfer_len; ++ offset += xfer_len; ++ ++ ++ /* reset mini-core state so we can program next transaction */ ++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { ++ dev_err(controller->dev, "cannot set RESET state\n"); ++ goto err_unmap; ++ } ++ } ++ ++ ret = 0; ++ ++err_unmap: ++ if (tx_align) ++ dma_unmap_single(controller->dev, tx_dummy_dma, ++ controller->out_fifo_sz, DMA_TO_DEVICE); ++err_map_tx_dummy: ++ if (xfer->tx_buf) ++ dma_unmap_single(controller->dev, tx_dma, xfer->len, ++ DMA_TO_DEVICE); ++err_map_tx: ++ if (rx_align) ++ dma_unmap_single(controller->dev, rx_dummy_dma, ++ controller->in_fifo_sz, DMA_FROM_DEVICE); ++err_map_rx_dummy: ++ if (xfer->rx_buf) ++ dma_unmap_single(controller->dev, rx_dma, xfer->len, ++ DMA_FROM_DEVICE); ++ ++ return ret; ++} ++ + static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + { + struct spi_qup *controller = dev_id; +@@ -315,11 +542,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + error = -EIO; + } + +- if (opflags & QUP_OP_IN_SERVICE_FLAG) +- spi_qup_fifo_read(controller, xfer); ++ if (!controller->use_dma) { ++ if (opflags & QUP_OP_IN_SERVICE_FLAG) ++ spi_qup_fifo_read(controller, xfer); + +- if (opflags & QUP_OP_OUT_SERVICE_FLAG) +- spi_qup_fifo_write(controller, xfer); ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) ++ spi_qup_fifo_write(controller, xfer); ++ } + + spin_lock_irqsave(&controller->lock, flags); + controller->error = error; +@@ -339,6 +568,8 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + struct spi_qup *controller = spi_master_get_devdata(spi->master); + u32 config, iomode, mode; + int ret, n_words, w_size; ++ size_t dma_align = dma_get_cache_alignment(); ++ u32 dma_available = 0; + + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", +@@ -367,6 +598,13 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + n_words = xfer->len / w_size; + controller->w_size = w_size; + ++ if (controller->rx_chan && ++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && ++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && ++ !is_vmalloc_addr(xfer->tx_buf) && ++ !is_vmalloc_addr(xfer->rx_buf)) ++ dma_available = 1; ++ + if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { + mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); +@@ -374,19 +612,30 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); +- } else { ++ controller->use_dma = 0; ++ } else if (!dma_available) { + mode = QUP_IO_M_MODE_BLOCK; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ controller->use_dma = 0; ++ } else { ++ mode = QUP_IO_M_MODE_DMOV; ++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ controller->use_dma = 1; + } + + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); + /* Set input and output transfer mode */ + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); +- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); ++ if (!controller->use_dma) ++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); ++ else ++ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; ++ + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); + iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + +@@ -419,11 +668,20 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); + config |= xfer->bits_per_word - 1; + config |= QUP_CONFIG_SPI_MODE; ++ ++ if (controller->use_dma) { ++ if (!xfer->tx_buf) ++ config |= QUP_CONFIG_NO_OUTPUT; ++ if (!xfer->rx_buf) ++ config |= QUP_CONFIG_NO_INPUT; ++ } ++ + writel_relaxed(config, controller->base + QUP_CONFIG); + + /* only write to OPERATIONAL_MASK when register is present */ + if (!controller->qup_v1) + writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); ++ + return 0; + } + +@@ -452,26 +710,32 @@ static int spi_qup_transfer_one(struct spi_master *master, + controller->tx_bytes = 0; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { +- dev_warn(controller->dev, "cannot set RUN state\n"); +- goto exit; +- } ++ if (controller->use_dma) { ++ ret = spi_qup_do_dma(controller, xfer); ++ } else { ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set RUN state\n"); ++ goto exit; ++ } + +- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { +- dev_warn(controller->dev, "cannot set PAUSE state\n"); +- goto exit; +- } ++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { ++ dev_warn(controller->dev, "cannot set PAUSE state\n"); ++ goto exit; ++ } + +- spi_qup_fifo_write(controller, xfer); ++ spi_qup_fifo_write(controller, xfer); + +- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { +- dev_warn(controller->dev, "cannot set EXECUTE state\n"); +- goto exit; +- } ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set EXECUTE state\n"); ++ goto exit; ++ } + +- if (!wait_for_completion_timeout(&controller->done, timeout)) +- ret = -ETIMEDOUT; ++ if (!ret && !wait_for_completion_timeout(&controller->done, ++ timeout)) ++ ret = -ETIMEDOUT; ++ } + exit: ++ + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = NULL; +@@ -553,6 +817,7 @@ static int spi_qup_probe(struct platform_device *pdev) + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; ++ master->dma_alignment = dma_get_cache_alignment(); + + platform_set_drvdata(pdev, master); + +@@ -612,6 +877,55 @@ static int spi_qup_probe(struct platform_device *pdev) + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_ERROR_FLAGS_EN); + ++ /* allocate dma resources, if available */ ++ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx"); ++ if (controller->rx_chan) { ++ controller->tx_chan = ++ dma_request_slave_channel(&pdev->dev, "tx"); ++ ++ if (!controller->tx_chan) { ++ dev_err(&pdev->dev, "Failed to allocate dma tx chan"); ++ dma_release_channel(controller->rx_chan); ++ } ++ ++ /* set DMA parameters */ ++ controller->rx_conf.device_fc = 1; ++ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO; ++ controller->rx_conf.src_maxburst = controller->in_blk_sz; ++ ++ controller->tx_conf.device_fc = 1; ++ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO; ++ controller->tx_conf.dst_maxburst = controller->out_blk_sz; ++ ++ if (dmaengine_slave_config(controller->rx_chan, ++ &controller->rx_conf)) { ++ dev_err(&pdev->dev, "failed to configure RX channel\n"); ++ ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } else if (dmaengine_slave_config(controller->tx_chan, ++ &controller->tx_conf)) { ++ dev_err(&pdev->dev, "failed to configure TX channel\n"); ++ ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } ++ ++ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE, ++ GFP_KERNEL); ++ ++ if (!controller->dummy) { ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } ++ } ++ + /* if earlier version of the QUP, disable INPUT_OVERRUN */ + if (controller->qup_v1) + writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN | +@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platform_device *pdev) + if (ret) + return ret; + ++ if (controller->rx_chan) ++ dma_release_channel(controller->rx_chan); ++ if (controller->tx_chan) ++ dma_release_channel(controller->tx_chan); ++ + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch new file mode 100644 index 0000000000..5fed04123e --- /dev/null +++ b/target/linux/ipq806x/patches/0180-ARM-dts-Add-ADM-DMA-nodes-and-SPI-linkage.patch @@ -0,0 +1,90 @@ +From c78ae23b6c174c9f2e0973a247942b6b4adb7e82 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Thu, 26 Jun 2014 13:02:59 -0500 +Subject: [PATCH 180/182] ARM: dts: Add ADM DMA nodes and SPI linkage + +This patch adds the ADM DMA controller DT nodes and also enables the use of dma +in SPI. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 11 +++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++--- + 2 files changed, 16 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +index 2b2d63c..c54a3ee 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -44,6 +44,10 @@ + drive-strength = <10>; + bias-none; + }; ++ cs { ++ pins = "gpio20"; ++ drive-strength = <12>; ++ }; + }; + nand_pins: nand_pins { + mux { +@@ -100,12 +104,17 @@ + + cs-gpios = <&qcom_pinmux 20 0>; + ++ dmas = <&adm_dma 6 9>, ++ <&adm_dma 5 10>; ++ dma-names = "rx", "tx"; ++ + flash: m25p80@0 { + compatible = "s25fl256s1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; ++ m25p,fast-read; + + partition@0 { + label = "rootfs"; +@@ -140,8 +149,10 @@ + ranges = <0x00000000 0 0x00000000 0x31f00000 0 0x00100000 /* configuration space */ + 0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ + }; + ++ + sata-phy@1b400000 { + status = "ok"; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index 97e4c3d..f74e923 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -421,19 +421,21 @@ + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; ++ #dma-cells = <2>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; +- clock-names = "core_clk", "iface_clk"; ++ clock-names = "core", "iface"; + + resets = <&gcc ADM0_RESET>, + <&gcc ADM0_PBUS_RESET>, + <&gcc ADM0_C0_RESET>, + <&gcc ADM0_C1_RESET>, + <&gcc ADM0_C2_RESET>; +- +- reset-names = "adm", "pbus", "c0", "c1", "c2"; ++ reset-names = "clk", "pbus", "c0", "c1", "c2"; ++ qcom,ee = <0>; + + status = "disabled"; ++ + }; + + nand@0x1ac00000 { +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch new file mode 100644 index 0000000000..763a2455c5 --- /dev/null +++ b/target/linux/ipq806x/patches/0181-mtd-nand-qcom-Align-clk-and-reset-names.patch @@ -0,0 +1,46 @@ +From ae0e9ee3bcfafc5bd5932b544e0a5c107948fc97 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 30 Jun 2014 21:17:26 -0500 +Subject: [PATCH 181/182] mtd: nand: qcom: Align clk and reset names + +This patch aligns the clk and reset names to the same ones that the dmaengine +driver uses. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/mtd/nand/qcom_adm_dma.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/nand/qcom_adm_dma.c b/drivers/mtd/nand/qcom_adm_dma.c +index 46d8473..542f901 100644 +--- a/drivers/mtd/nand/qcom_adm_dma.c ++++ b/drivers/mtd/nand/qcom_adm_dma.c +@@ -568,14 +568,14 @@ static int msm_dmov_init_clocks(struct platform_device *pdev) + int adm = (pdev->id >= 0) ? pdev->id : 0; + int ret; + +- dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core_clk"); ++ dmov_conf[adm].clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(dmov_conf[adm].clk)) { + printk(KERN_ERR "%s: Error getting adm_clk\n", __func__); + dmov_conf[adm].clk = NULL; + return -ENOENT; + } + +- dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface_clk"); ++ dmov_conf[adm].pclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(dmov_conf[adm].pclk)) { + dmov_conf[adm].pclk = NULL; + /* pclk not present on all SoCs, don't bail on failure */ +@@ -690,7 +690,7 @@ static int msm_dmov_probe(struct platform_device *pdev) + } + + /* get resets */ +- dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "adm"); ++ dmov_conf[adm].adm_reset = devm_reset_control_get(&pdev->dev, "clk"); + if (IS_ERR(dmov_conf[adm].adm_reset)) { + dev_err(&pdev->dev, "failed to get adm reset\n"); + ret = PTR_ERR(dmov_conf[adm].adm_reset); +-- +1.7.10.4 + diff --git a/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch new file mode 100644 index 0000000000..87e2fad612 --- /dev/null +++ b/target/linux/ipq806x/patches/0182-qcom-Kconfig-Make-drivers-mutually-exclusive.patch @@ -0,0 +1,54 @@ +From 0771849495b4128cac2faf7d49c85c729fc48b20 Mon Sep 17 00:00:00 2001 +From: Andy Gross <agross@codeaurora.org> +Date: Mon, 30 Jun 2014 21:18:39 -0500 +Subject: [PATCH 182/182] qcom: Kconfig: Make drivers mutually exclusive + +This patch makes sure QCOM ADM dmaengine and QCOM Nand cannot be enabled at the +same time. This is an issue because the dma drivers will conflict with one +another. + +Signed-off-by: Andy Gross <agross@codeaurora.org> +--- + drivers/dma/Kconfig | 2 +- + drivers/mtd/nand/Kconfig | 6 +++--- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index 79155fa..ed7a5f6 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -412,7 +412,7 @@ config QCOM_BAM_DMA + + config QCOM_ADM + tristate "Qualcomm ADM support" +- depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ depends on !MTD_QCOM_ADM && (ARCH_QCOM || (COMPILE_TEST && OF && ARM)) + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + ---help--- +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 6e3842f..4a84264 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -511,15 +511,15 @@ config MTD_NAND_XWAY + to the External Bus Unit (EBU). + + config MTD_QCOM_DMA +- tristate "QCMO NAND DMA Support" +- depends on ARCH_QCOM && MTD_QCOM_NAND ++ tristate "QCOM NAND DMA Support" ++ depends on !QCOM_ADM && ARCH_QCOM && MTD_QCOM_NAND + default n + help + DMA support for QCOM NAND + + config MTD_QCOM_NAND + tristate "QCOM NAND Device Support" +- depends on MTD && ARCH_QCOM ++ depends on MTD && ARCH_QCOM && !QCOM_ADM + select CRC16 + select BITREVERSE + select MTD_NAND_IDS +-- +1.7.10.4 + |