summaryrefslogtreecommitdiff
path: root/target/arm/cubox-i/patches/3.15-rc7/rmk.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/arm/cubox-i/patches/3.15-rc7/rmk.patch')
-rw-r--r--target/arm/cubox-i/patches/3.15-rc7/rmk.patch12452
1 files changed, 12452 insertions, 0 deletions
diff --git a/target/arm/cubox-i/patches/3.15-rc7/rmk.patch b/target/arm/cubox-i/patches/3.15-rc7/rmk.patch
new file mode 100644
index 000000000..83afb1a78
--- /dev/null
+++ b/target/arm/cubox-i/patches/3.15-rc7/rmk.patch
@@ -0,0 +1,12452 @@
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6dl-hummingboard.dts linux-3.15-rc6/arch/arm/boot/dts/imx6dl-hummingboard.dts
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6dl-hummingboard.dts 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6dl-hummingboard.dts 2014-05-23 11:26:48.244939835 +0200
+@@ -67,6 +67,14 @@
+ status = "okay";
+ };
+
++&hdmi {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_hummingboard_hdmi>;
++ ddc-i2c-bus = <&i2c2>;
++ status = "okay";
++ crtcs = <&ipu1 0>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hummingboard_i2c1>;
+@@ -82,6 +90,13 @@
+ */
+ };
+
++&i2c2 {
++ clock-frequency = <100000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_hummingboard_i2c2>;
++ status = "okay";
++};
++
+ &iomuxc {
+ hummingboard {
+ pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 {
+@@ -97,6 +112,12 @@
+ >;
+ };
+
++ pinctrl_hummingboard_hdmi: hummingboard-hdmi {
++ fsl,pins = <
++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0
++ >;
++ };
++
+ pinctrl_hummingboard_i2c1: hummingboard-i2c1 {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+@@ -104,6 +125,13 @@
+ >;
+ };
+
++ pinctrl_hummingboard_i2c2: hummingboard-i2c2 {
++ fsl,pins = <
++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
++ >;
++ };
++
+ pinctrl_hummingboard_spdif: hummingboard-spdif {
+ fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
+ };
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6q-cubox-i.dts linux-3.15-rc6/arch/arm/boot/dts/imx6q-cubox-i.dts
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6q-cubox-i.dts 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6q-cubox-i.dts 2014-05-23 11:26:48.244939835 +0200
+@@ -13,4 +13,8 @@
+
+ &sata {
+ status = "okay";
++ fsl,transmit-level-mV = <1104>;
++ fsl,transmit-boost-mdB = <0>;
++ fsl,transmit-atten-16ths = <9>;
++ fsl,no-spread-spectrum;
+ };
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi linux-3.15-rc6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -12,6 +12,19 @@
+ pinctrl-0 = <&pinctrl_cubox_i_ir>;
+ };
+
++ pwmleds {
++ compatible = "pwm-leds";
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_cubox_i_pwm1>;
++
++ front {
++ active-low;
++ label = "imx6:red:front";
++ max-brightness = <248>;
++ pwms = <&pwm1 0 50000>;
++ };
++ };
++
+ regulators {
+ compatible = "simple-bus";
+
+@@ -55,6 +68,21 @@
+ };
+ };
+
++&hdmi {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_cubox_i_hdmi>;
++ ddc-i2c-bus = <&i2c2>;
++ status = "okay";
++ crtcs = <&ipu1 0>;
++};
++
++&i2c2 {
++ clock-frequency = <100000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_cubox_i_i2c2>;
++ status = "okay";
++};
++
+ &i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_i2c3>;
+@@ -69,6 +97,19 @@
+
+ &iomuxc {
+ cubox_i {
++ pinctrl_cubox_i_hdmi: cubox-i-hdmi {
++ fsl,pins = <
++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0
++ >;
++ };
++
++ pinctrl_cubox_i_i2c2: cubox-i-i2c2 {
++ fsl,pins = <
++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
++ >;
++ };
++
+ pinctrl_cubox_i_i2c3: cubox-i-i2c3 {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1
+@@ -82,6 +123,10 @@
+ >;
+ };
+
++ pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led {
++ fsl,pins = <MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x1b0b0>;
++ };
++
+ pinctrl_cubox_i_spdif: cubox-i-spdif {
+ fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
+ };
+@@ -111,6 +156,28 @@
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059
+ >;
+ };
++
++ pinctrl_cubox_i_usdhc2_100mhz: cubox-i-usdhc2-100mhz {
++ fsl,pins = <
++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9
++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9
++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9
++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9
++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9
++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9
++ >;
++ };
++
++ pinctrl_cubox_i_usdhc2_200mhz: cubox-i-usdhc2-200mhz {
++ fsl,pins = <
++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9
++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9
++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9
++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9
++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9
++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9
++ >;
++ };
+ };
+ };
+
+@@ -130,9 +197,19 @@
+ status = "okay";
+ };
+
++&uart4 {
++ status = "okay";
++};
++
++&usdhc1 {
++ status = "okay";
++};
++
+ &usdhc2 {
+- pinctrl-names = "default";
++ pinctrl-names = "default", "state_100mhz", "state_200mhz";
+ pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>;
++ pinctrl-1 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_100mhz>;
++ pinctrl-2 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_200mhz>;
+ vmmc-supply = <&reg_3p3v>;
+ cd-gpios = <&gpio1 4 0>;
+ status = "okay";
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl.dtsi linux-3.15-rc6/arch/arm/boot/dts/imx6qdl.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6qdl.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -128,6 +128,8 @@
+ cache-level = <2>;
+ arm,tag-latency = <4 2 3>;
+ arm,data-latency = <4 2 3>;
++ arm,dynamic-clk-gating;
++ arm,standby-mode;
+ };
+
+ pcie: pcie@0x01000000 {
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi linux-3.15-rc6/arch/arm/boot/dts/imx6qdl-microsom.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -1,9 +1,69 @@
+ /*
+ * Copyright (C) 2013,2014 Russell King
+ */
++#include <dt-bindings/gpio/gpio.h>
++/ {
++ regulators {
++ compatible = "simple-bus";
++
++ reg_brcm_osc: brcm-osc-reg {
++ compatible = "regulator-fixed";
++ enable-active-high;
++ gpio = <&gpio5 5 0>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_microsom_brcm_osc_reg>;
++ regulator-name = "brcm_osc_reg";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-always-on;
++ regulator-boot-on;
++ };
++
++ reg_brcm: brcm-reg {
++ compatible = "regulator-fixed";
++ enable-active-high;
++ gpio = <&gpio3 19 0>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_microsom_brcm_reg>;
++ regulator-name = "brcm_reg";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ startup-delay-us = <200000>;
++ };
++ };
++};
+
+ &iomuxc {
+ microsom {
++ pinctrl_microsom_brcm_osc_reg: microsom-brcm-osc-reg {
++ fsl,pins = <
++ MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070
++ >;
++ };
++
++ pinctrl_microsom_brcm_reg: microsom-brcm-reg {
++ fsl,pins = <
++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070
++ >;
++ };
++
++ pinctrl_microsom_brcm_wifi: microsom-brcm-wifi {
++ fsl,pins = <
++ MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0
++ MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070
++ MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070
++ MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070
++ >;
++ };
++
++ pinctrl_microsom_brcm_bt: microsom-brcm-bt {
++ fsl,pins = <
++ MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070
++ MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070
++ MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070
++ >;
++ };
++
+ pinctrl_microsom_uart1: microsom-uart1 {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
+@@ -11,6 +71,15 @@
+ >;
+ };
+
++ pinctrl_microsom_uart4_1: microsom-uart4 {
++ fsl,pins = <
++ MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1
++ MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1
++ MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1
++ MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1
++ >;
++ };
++
+ pinctrl_microsom_usbotg: microsom-usbotg {
+ /*
+ * Similar to pinctrl_usbotg_2, but we want it
+@@ -18,6 +87,17 @@
+ */
+ fsl,pins = <MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059>;
+ };
++
++ pinctrl_microsom_usdhc1: microsom-usdhc1 {
++ fsl,pins = <
++ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059
++ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059
++ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059
++ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059
++ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059
++ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059
++ >;
++ };
+ };
+ };
+
+@@ -27,7 +107,25 @@
+ status = "okay";
+ };
+
++/* UART4 - Connected to optional BRCM Wifi/BT/FM */
++&uart4 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_microsom_brcm_bt &pinctrl_microsom_uart4_1>;
++ fsl,uart-has-rtscts;
++};
++
+ &usbotg {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_microsom_usbotg>;
+ };
++
++/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */
++&usdhc1 {
++ card-external-vcc-supply = <&reg_brcm>;
++ card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>;
++ keep-power-in-suspend;
++ non-removable;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>;
++ vmmc-supply = <&reg_brcm>;
++};
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/imx6sl.dtsi linux-3.15-rc6/arch/arm/boot/dts/imx6sl.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/imx6sl.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/imx6sl.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -111,6 +111,8 @@
+ cache-level = <2>;
+ arm,tag-latency = <4 2 3>;
+ arm,data-latency = <4 2 3>;
++ arm,dynamic-clk-gating;
++ arm,standby-mode;
+ };
+
+ pmu {
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/marco.dtsi linux-3.15-rc6/arch/arm/boot/dts/marco.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/marco.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/marco.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -36,7 +36,7 @@
+ ranges = <0x40000000 0x40000000 0xa0000000>;
+
+ l2-cache-controller@c0030000 {
+- compatible = "sirf,marco-pl310-cache", "arm,pl310-cache";
++ compatible = "arm,pl310-cache";
+ reg = <0xc0030000 0x1000>;
+ interrupts = <0 59 0>;
+ arm,tag-latency = <1 1 1>;
+diff -Nur linux-3.15-rc6.orig/arch/arm/boot/dts/prima2.dtsi linux-3.15-rc6/arch/arm/boot/dts/prima2.dtsi
+--- linux-3.15-rc6.orig/arch/arm/boot/dts/prima2.dtsi 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/boot/dts/prima2.dtsi 2014-05-23 11:26:48.244939835 +0200
+@@ -48,7 +48,7 @@
+ ranges = <0x40000000 0x40000000 0x80000000>;
+
+ l2-cache-controller@80040000 {
+- compatible = "arm,pl310-cache", "sirf,prima2-pl310-cache";
++ compatible = "arm,pl310-cache";
+ reg = <0x80040000 0x1000>;
+ interrupts = <59>;
+ arm,tag-latency = <1 1 1>;
+diff -Nur linux-3.15-rc6.orig/arch/arm/configs/imx_v6_v7_defconfig linux-3.15-rc6/arch/arm/configs/imx_v6_v7_defconfig
+--- linux-3.15-rc6.orig/arch/arm/configs/imx_v6_v7_defconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/configs/imx_v6_v7_defconfig 2014-05-23 11:26:48.248939848 +0200
+@@ -245,6 +245,7 @@
+ CONFIG_DRM_IMX_LDB=y
+ CONFIG_DRM_IMX_IPUV3_CORE=y
+ CONFIG_DRM_IMX_IPUV3=y
++CONFIG_DRM_IMX_HDMI=y
+ CONFIG_COMMON_CLK_DEBUG=y
+ # CONFIG_IOMMU_SUPPORT is not set
+ CONFIG_PWM=y
+diff -Nur linux-3.15-rc6.orig/arch/arm/include/asm/hardware/cache-l2x0.h linux-3.15-rc6/arch/arm/include/asm/hardware/cache-l2x0.h
+--- linux-3.15-rc6.orig/arch/arm/include/asm/hardware/cache-l2x0.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/include/asm/hardware/cache-l2x0.h 2014-05-23 11:26:48.248939848 +0200
+@@ -26,8 +26,8 @@
+ #define L2X0_CACHE_TYPE 0x004
+ #define L2X0_CTRL 0x100
+ #define L2X0_AUX_CTRL 0x104
+-#define L2X0_TAG_LATENCY_CTRL 0x108
+-#define L2X0_DATA_LATENCY_CTRL 0x10C
++#define L310_TAG_LATENCY_CTRL 0x108
++#define L310_DATA_LATENCY_CTRL 0x10C
+ #define L2X0_EVENT_CNT_CTRL 0x200
+ #define L2X0_EVENT_CNT1_CFG 0x204
+ #define L2X0_EVENT_CNT0_CFG 0x208
+@@ -54,53 +54,93 @@
+ #define L2X0_LOCKDOWN_WAY_D_BASE 0x900
+ #define L2X0_LOCKDOWN_WAY_I_BASE 0x904
+ #define L2X0_LOCKDOWN_STRIDE 0x08
+-#define L2X0_ADDR_FILTER_START 0xC00
+-#define L2X0_ADDR_FILTER_END 0xC04
++#define L310_ADDR_FILTER_START 0xC00
++#define L310_ADDR_FILTER_END 0xC04
+ #define L2X0_TEST_OPERATION 0xF00
+ #define L2X0_LINE_DATA 0xF10
+ #define L2X0_LINE_TAG 0xF30
+ #define L2X0_DEBUG_CTRL 0xF40
+-#define L2X0_PREFETCH_CTRL 0xF60
+-#define L2X0_POWER_CTRL 0xF80
+-#define L2X0_DYNAMIC_CLK_GATING_EN (1 << 1)
+-#define L2X0_STNDBY_MODE_EN (1 << 0)
++#define L310_PREFETCH_CTRL 0xF60
++#define L310_POWER_CTRL 0xF80
++#define L310_DYNAMIC_CLK_GATING_EN (1 << 1)
++#define L310_STNDBY_MODE_EN (1 << 0)
+
+ /* Registers shifts and masks */
+ #define L2X0_CACHE_ID_PART_MASK (0xf << 6)
+ #define L2X0_CACHE_ID_PART_L210 (1 << 6)
++#define L2X0_CACHE_ID_PART_L220 (2 << 6)
+ #define L2X0_CACHE_ID_PART_L310 (3 << 6)
+ #define L2X0_CACHE_ID_RTL_MASK 0x3f
+-#define L2X0_CACHE_ID_RTL_R0P0 0x0
+-#define L2X0_CACHE_ID_RTL_R1P0 0x2
+-#define L2X0_CACHE_ID_RTL_R2P0 0x4
+-#define L2X0_CACHE_ID_RTL_R3P0 0x5
+-#define L2X0_CACHE_ID_RTL_R3P1 0x6
+-#define L2X0_CACHE_ID_RTL_R3P2 0x8
+-
+-#define L2X0_AUX_CTRL_MASK 0xc0000fff
++#define L210_CACHE_ID_RTL_R0P2_02 0x00
++#define L210_CACHE_ID_RTL_R0P1 0x01
++#define L210_CACHE_ID_RTL_R0P2_01 0x02
++#define L210_CACHE_ID_RTL_R0P3 0x03
++#define L210_CACHE_ID_RTL_R0P4 0x0b
++#define L210_CACHE_ID_RTL_R0P5 0x0f
++#define L220_CACHE_ID_RTL_R1P7_01REL0 0x06
++#define L310_CACHE_ID_RTL_R0P0 0x00
++#define L310_CACHE_ID_RTL_R1P0 0x02
++#define L310_CACHE_ID_RTL_R2P0 0x04
++#define L310_CACHE_ID_RTL_R3P0 0x05
++#define L310_CACHE_ID_RTL_R3P1 0x06
++#define L310_CACHE_ID_RTL_R3P1_50REL0 0x07
++#define L310_CACHE_ID_RTL_R3P2 0x08
++#define L310_CACHE_ID_RTL_R3P3 0x09
++
++/* L2C auxiliary control register - bits common to L2C-210/220/310 */
++#define L2C_AUX_CTRL_WAY_SIZE_SHIFT 17
++#define L2C_AUX_CTRL_WAY_SIZE_MASK (7 << 17)
++#define L2C_AUX_CTRL_WAY_SIZE(n) ((n) << 17)
++#define L2C_AUX_CTRL_EVTMON_ENABLE BIT(20)
++#define L2C_AUX_CTRL_PARITY_ENABLE BIT(21)
++#define L2C_AUX_CTRL_SHARED_OVERRIDE BIT(22)
++/* L2C-210/220 common bits */
+ #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT 0
+-#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK 0x7
++#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK (7 << 0)
+ #define L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT 3
+-#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (0x7 << 3)
++#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (7 << 3)
+ #define L2X0_AUX_CTRL_TAG_LATENCY_SHIFT 6
+-#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (0x7 << 6)
++#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (7 << 6)
+ #define L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT 9
+-#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (0x7 << 9)
+-#define L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT 16
+-#define L2X0_AUX_CTRL_WAY_SIZE_SHIFT 17
+-#define L2X0_AUX_CTRL_WAY_SIZE_MASK (0x7 << 17)
+-#define L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22
+-#define L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT 26
+-#define L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT 27
+-#define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT 28
+-#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29
+-#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30
+-
+-#define L2X0_LATENCY_CTRL_SETUP_SHIFT 0
+-#define L2X0_LATENCY_CTRL_RD_SHIFT 4
+-#define L2X0_LATENCY_CTRL_WR_SHIFT 8
+-
+-#define L2X0_ADDR_FILTER_EN 1
++#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (7 << 9)
++#define L2X0_AUX_CTRL_ASSOC_SHIFT 13
++#define L2X0_AUX_CTRL_ASSOC_MASK (15 << 13)
++/* L2C-210 specific bits */
++#define L210_AUX_CTRL_WRAP_DISABLE BIT(12)
++#define L210_AUX_CTRL_WA_OVERRIDE BIT(23)
++#define L210_AUX_CTRL_EXCLUSIVE_ABORT BIT(24)
++/* L2C-220 specific bits */
++#define L220_AUX_CTRL_EXCLUSIVE_CACHE BIT(12)
++#define L220_AUX_CTRL_FWA_SHIFT 23
++#define L220_AUX_CTRL_FWA_MASK (3 << 23)
++#define L220_AUX_CTRL_NS_LOCKDOWN BIT(26)
++#define L220_AUX_CTRL_NS_INT_CTRL BIT(27)
++/* L2C-310 specific bits */
++#define L310_AUX_CTRL_FULL_LINE_ZERO BIT(0) /* R2P0+ */
++#define L310_AUX_CTRL_HIGHPRIO_SO_DEV BIT(10) /* R2P0+ */
++#define L310_AUX_CTRL_STORE_LIMITATION BIT(11) /* R2P0+ */
++#define L310_AUX_CTRL_EXCLUSIVE_CACHE BIT(12)
++#define L310_AUX_CTRL_ASSOCIATIVITY_16 BIT(16)
++#define L310_AUX_CTRL_CACHE_REPLACE_RR BIT(25) /* R2P0+ */
++#define L310_AUX_CTRL_NS_LOCKDOWN BIT(26)
++#define L310_AUX_CTRL_NS_INT_CTRL BIT(27)
++#define L310_AUX_CTRL_DATA_PREFETCH BIT(28)
++#define L310_AUX_CTRL_INSTR_PREFETCH BIT(29)
++#define L310_AUX_CTRL_EARLY_BRESP BIT(30) /* R2P0+ */
++
++#define L310_LATENCY_CTRL_SETUP(n) ((n) << 0)
++#define L310_LATENCY_CTRL_RD(n) ((n) << 4)
++#define L310_LATENCY_CTRL_WR(n) ((n) << 8)
++
++#define L310_ADDR_FILTER_EN 1
++
++#define L310_PREFETCH_CTRL_OFFSET_MASK 0x1f
++#define L310_PREFETCH_CTRL_DBL_LINEFILL_INCR BIT(23)
++#define L310_PREFETCH_CTRL_PREFETCH_DROP BIT(24)
++#define L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP BIT(27)
++#define L310_PREFETCH_CTRL_DATA_PREFETCH BIT(28)
++#define L310_PREFETCH_CTRL_INSTR_PREFETCH BIT(29)
++#define L310_PREFETCH_CTRL_DBL_LINEFILL BIT(30)
+
+ #define L2X0_CTRL_EN 1
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/include/asm/outercache.h linux-3.15-rc6/arch/arm/include/asm/outercache.h
+--- linux-3.15-rc6.orig/arch/arm/include/asm/outercache.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/include/asm/outercache.h 2014-05-23 11:26:48.248939848 +0200
+@@ -21,6 +21,7 @@
+ #ifndef __ASM_OUTERCACHE_H
+ #define __ASM_OUTERCACHE_H
+
++#include <linux/bug.h>
+ #include <linux/types.h>
+
+ struct outer_cache_fns {
+@@ -28,53 +29,84 @@
+ void (*clean_range)(unsigned long, unsigned long);
+ void (*flush_range)(unsigned long, unsigned long);
+ void (*flush_all)(void);
+- void (*inv_all)(void);
+ void (*disable)(void);
+ #ifdef CONFIG_OUTER_CACHE_SYNC
+ void (*sync)(void);
+ #endif
+- void (*set_debug)(unsigned long);
+ void (*resume)(void);
++
++ /* This is an ARM L2C thing */
++ void (*write_sec)(unsigned long, unsigned);
+ };
+
+ extern struct outer_cache_fns outer_cache;
+
+ #ifdef CONFIG_OUTER_CACHE
+-
++/**
++ * outer_inv_range - invalidate range of outer cache lines
++ * @start: starting physical address, inclusive
++ * @end: end physical address, exclusive
++ */
+ static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
+ {
+ if (outer_cache.inv_range)
+ outer_cache.inv_range(start, end);
+ }
++
++/**
++ * outer_clean_range - clean dirty outer cache lines
++ * @start: starting physical address, inclusive
++ * @end: end physical address, exclusive
++ */
+ static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
+ {
+ if (outer_cache.clean_range)
+ outer_cache.clean_range(start, end);
+ }
++
++/**
++ * outer_flush_range - clean and invalidate outer cache lines
++ * @start: starting physical address, inclusive
++ * @end: end physical address, exclusive
++ */
+ static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
+ {
+ if (outer_cache.flush_range)
+ outer_cache.flush_range(start, end);
+ }
+
++/**
++ * outer_flush_all - clean and invalidate all cache lines in the outer cache
++ *
++ * Note: depending on implementation, this may not be atomic - it must
++ * only be called with interrupts disabled and no other active outer
++ * cache masters.
++ *
++ * It is intended that this function is only used by implementations
++ * needing to override the outer_cache.disable() method due to security.
++ * (Some implementations perform this as a clean followed by an invalidate.)
++ */
+ static inline void outer_flush_all(void)
+ {
+ if (outer_cache.flush_all)
+ outer_cache.flush_all();
+ }
+
+-static inline void outer_inv_all(void)
+-{
+- if (outer_cache.inv_all)
+- outer_cache.inv_all();
+-}
+-
+-static inline void outer_disable(void)
+-{
+- if (outer_cache.disable)
+- outer_cache.disable();
+-}
+-
++/**
++ * outer_disable - clean, invalidate and disable the outer cache
++ *
++ * Disable the outer cache, ensuring that any data contained in the outer
++ * cache is pushed out to lower levels of system memory. The note and
++ * conditions above concerning outer_flush_all() applies here.
++ */
++extern void outer_disable(void);
++
++/**
++ * outer_resume - restore the cache configuration and re-enable outer cache
++ *
++ * Restore any configuration that the cache had when previously enabled,
++ * and re-enable the outer cache.
++ */
+ static inline void outer_resume(void)
+ {
+ if (outer_cache.resume)
+@@ -90,13 +122,18 @@
+ static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
+ { }
+ static inline void outer_flush_all(void) { }
+-static inline void outer_inv_all(void) { }
+ static inline void outer_disable(void) { }
+ static inline void outer_resume(void) { }
+
+ #endif
+
+ #ifdef CONFIG_OUTER_CACHE_SYNC
++/**
++ * outer_sync - perform a sync point for outer cache
++ *
++ * Ensure that all outer cache operations are complete and any store
++ * buffers are drained.
++ */
+ static inline void outer_sync(void)
+ {
+ if (outer_cache.sync)
+diff -Nur linux-3.15-rc6.orig/arch/arm/Kconfig linux-3.15-rc6/arch/arm/Kconfig
+--- linux-3.15-rc6.orig/arch/arm/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/Kconfig 2014-05-23 11:26:48.248939848 +0200
+@@ -1230,19 +1230,6 @@
+ register of the Cortex-A9 which reduces the linefill issuing
+ capabilities of the processor.
+
+-config PL310_ERRATA_588369
+- bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines"
+- depends on CACHE_L2X0
+- help
+- The PL310 L2 cache controller implements three types of Clean &
+- Invalidate maintenance operations: by Physical Address
+- (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC).
+- They are architecturally defined to behave as the execution of a
+- clean operation followed immediately by an invalidate operation,
+- both performing to the same memory location. This functionality
+- is not correctly implemented in PL310 as clean lines are not
+- invalidated as a result of these operations.
+-
+ config ARM_ERRATA_643719
+ bool "ARM errata: LoUIS bit field in CLIDR register is incorrect"
+ depends on CPU_V7 && SMP
+@@ -1265,17 +1252,6 @@
+ tables. The workaround changes the TLB flushing routines to invalidate
+ entries regardless of the ASID.
+
+-config PL310_ERRATA_727915
+- bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption"
+- depends on CACHE_L2X0
+- help
+- PL310 implements the Clean & Invalidate by Way L2 cache maintenance
+- operation (offset 0x7FC). This operation runs in background so that
+- PL310 can handle normal accesses while it is in progress. Under very
+- rare circumstances, due to this erratum, write data can be lost when
+- PL310 treats a cacheable write transaction during a Clean &
+- Invalidate by Way operation.
+-
+ config ARM_ERRATA_743622
+ bool "ARM errata: Faulty hazard checking in the Store Buffer may lead to data corruption"
+ depends on CPU_V7
+@@ -1301,21 +1277,6 @@
+ operation is received by a CPU before the ICIALLUIS has completed,
+ potentially leading to corrupted entries in the cache or TLB.
+
+-config PL310_ERRATA_753970
+- bool "PL310 errata: cache sync operation may be faulty"
+- depends on CACHE_PL310
+- help
+- This option enables the workaround for the 753970 PL310 (r3p0) erratum.
+-
+- Under some condition the effect of cache sync operation on
+- the store buffer still remains when the operation completes.
+- This means that the store buffer is always asked to drain and
+- this prevents it from merging any further writes. The workaround
+- is to replace the normal offset of cache sync operation (0x730)
+- by another offset targeting an unmapped PL310 register 0x740.
+- This has the same effect as the cache sync operation: store buffer
+- drain and waiting for all buffers empty.
+-
+ config ARM_ERRATA_754322
+ bool "ARM errata: possible faulty MMU translations following an ASID switch"
+ depends on CPU_V7
+@@ -1364,18 +1325,6 @@
+ relevant cache maintenance functions and sets a specific bit
+ in the diagnostic control register of the SCU.
+
+-config PL310_ERRATA_769419
+- bool "PL310 errata: no automatic Store Buffer drain"
+- depends on CACHE_L2X0
+- help
+- On revisions of the PL310 prior to r3p2, the Store Buffer does
+- not automatically drain. This can cause normal, non-cacheable
+- writes to be retained when the memory system is idle, leading
+- to suboptimal I/O performance for drivers using coherent DMA.
+- This option adds a write barrier to the cpu_idle loop so that,
+- on systems with an outer cache, the store buffer is drained
+- explicitly.
+-
+ config ARM_ERRATA_775420
+ bool "ARM errata: A data cache maintenance operation which aborts, might lead to deadlock"
+ depends on CPU_V7
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-berlin/berlin.c linux-3.15-rc6/arch/arm/mach-berlin/berlin.c
+--- linux-3.15-rc6.orig/arch/arm/mach-berlin/berlin.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-berlin/berlin.c 2014-05-23 11:26:48.256939874 +0200
+@@ -24,7 +24,7 @@
+ * with DT probing for L2CCs, berlin_init_machine can be removed.
+ * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc
+ */
+- l2x0_of_init(0x70c00000, 0xfeffffff);
++ l2x0_of_init(0x30c00000, 0xfeffffff);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-cns3xxx/core.c linux-3.15-rc6/arch/arm/mach-cns3xxx/core.c
+--- linux-3.15-rc6.orig/arch/arm/mach-cns3xxx/core.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-cns3xxx/core.c 2014-05-23 11:26:48.256939874 +0200
+@@ -272,9 +272,9 @@
+ *
+ * 1 cycle of latency for setup, read and write accesses
+ */
+- val = readl(base + L2X0_TAG_LATENCY_CTRL);
++ val = readl(base + L310_TAG_LATENCY_CTRL);
+ val &= 0xfffff888;
+- writel(val, base + L2X0_TAG_LATENCY_CTRL);
++ writel(val, base + L310_TAG_LATENCY_CTRL);
+
+ /*
+ * Data RAM Control register
+@@ -285,12 +285,12 @@
+ *
+ * 1 cycle of latency for setup, read and write accesses
+ */
+- val = readl(base + L2X0_DATA_LATENCY_CTRL);
++ val = readl(base + L310_DATA_LATENCY_CTRL);
+ val &= 0xfffff888;
+- writel(val, base + L2X0_DATA_LATENCY_CTRL);
++ writel(val, base + L310_DATA_LATENCY_CTRL);
+
+ /* 32 KiB, 8-way, parity disable */
+- l2x0_init(base, 0x00540000, 0xfe000fff);
++ l2x0_init(base, 0x00500000, 0xfe0f0fff);
+ }
+
+ #endif /* CONFIG_CACHE_L2X0 */
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-exynos/common.h linux-3.15-rc6/arch/arm/mach-exynos/common.h
+--- linux-3.15-rc6.orig/arch/arm/mach-exynos/common.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-exynos/common.h 2014-05-23 11:26:48.256939874 +0200
+@@ -55,7 +55,6 @@
+ NUM_SYS_POWERDOWN,
+ };
+
+-extern unsigned long l2x0_regs_phys;
+ struct exynos_pmu_conf {
+ void __iomem *reg;
+ unsigned int val[NUM_SYS_POWERDOWN];
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-exynos/exynos.c linux-3.15-rc6/arch/arm/mach-exynos/exynos.c
+--- linux-3.15-rc6.orig/arch/arm/mach-exynos/exynos.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-exynos/exynos.c 2014-05-23 11:26:48.256939874 +0200
+@@ -32,9 +32,6 @@
+ #include "mfc.h"
+ #include "regs-pmu.h"
+
+-#define L2_AUX_VAL 0x7C470001
+-#define L2_AUX_MASK 0xC200ffff
+-
+ static struct map_desc exynos4_iodesc[] __initdata = {
+ {
+ .virtual = (unsigned long)S3C_VA_SYS,
+@@ -321,17 +318,7 @@
+
+ static int __init exynos4_l2x0_cache_init(void)
+ {
+- int ret;
+-
+- ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK);
+- if (ret)
+- return ret;
+-
+- if (IS_ENABLED(CONFIG_S5P_SLEEP)) {
+- l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs);
+- clean_dcache_area(&l2x0_regs_phys, sizeof(unsigned long));
+- }
+- return 0;
++ return l2x0_of_init(0x3c400001, 0xc20fffff);
+ }
+ early_initcall(exynos4_l2x0_cache_init);
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-exynos/sleep.S linux-3.15-rc6/arch/arm/mach-exynos/sleep.S
+--- linux-3.15-rc6.orig/arch/arm/mach-exynos/sleep.S 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-exynos/sleep.S 2014-05-23 11:26:48.256939874 +0200
+@@ -16,8 +16,6 @@
+ */
+
+ #include <linux/linkage.h>
+-#include <asm/asm-offsets.h>
+-#include <asm/hardware/cache-l2x0.h>
+
+ #define CPU_MASK 0xff0ffff0
+ #define CPU_CORTEX_A9 0x410fc090
+@@ -53,33 +51,7 @@
+ and r0, r0, r1
+ ldr r1, =CPU_CORTEX_A9
+ cmp r0, r1
+- bne skip_l2_resume
+- adr r0, l2x0_regs_phys
+- ldr r0, [r0]
+- cmp r0, #0
+- beq skip_l2_resume
+- ldr r1, [r0, #L2X0_R_PHY_BASE]
+- ldr r2, [r1, #L2X0_CTRL]
+- tst r2, #0x1
+- bne skip_l2_resume
+- ldr r2, [r0, #L2X0_R_AUX_CTRL]
+- str r2, [r1, #L2X0_AUX_CTRL]
+- ldr r2, [r0, #L2X0_R_TAG_LATENCY]
+- str r2, [r1, #L2X0_TAG_LATENCY_CTRL]
+- ldr r2, [r0, #L2X0_R_DATA_LATENCY]
+- str r2, [r1, #L2X0_DATA_LATENCY_CTRL]
+- ldr r2, [r0, #L2X0_R_PREFETCH_CTRL]
+- str r2, [r1, #L2X0_PREFETCH_CTRL]
+- ldr r2, [r0, #L2X0_R_PWR_CTRL]
+- str r2, [r1, #L2X0_POWER_CTRL]
+- mov r2, #1
+- str r2, [r1, #L2X0_CTRL]
+-skip_l2_resume:
++ bleq l2c310_early_resume
+ #endif
+ b cpu_resume
+ ENDPROC(exynos_cpu_resume)
+-#ifdef CONFIG_CACHE_L2X0
+- .globl l2x0_regs_phys
+-l2x0_regs_phys:
+- .long 0
+-#endif
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-highbank/highbank.c linux-3.15-rc6/arch/arm/mach-highbank/highbank.c
+--- linux-3.15-rc6.orig/arch/arm/mach-highbank/highbank.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-highbank/highbank.c 2014-05-23 11:26:48.256939874 +0200
+@@ -51,11 +51,13 @@
+ }
+
+
+-static void highbank_l2x0_disable(void)
++static void highbank_l2c310_write_sec(unsigned long val, unsigned reg)
+ {
+- outer_flush_all();
+- /* Disable PL310 L2 Cache controller */
+- highbank_smc1(0x102, 0x0);
++ if (reg == L2X0_CTRL)
++ highbank_smc1(0x102, val);
++ else
++ WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n",
++ reg);
+ }
+
+ static void __init highbank_init_irq(void)
+@@ -66,11 +68,9 @@
+ highbank_scu_map_io();
+
+ /* Enable PL310 L2 Cache controller */
+- if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
+- of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) {
+- highbank_smc1(0x102, 0x1);
+- l2x0_of_init(0, ~0UL);
+- outer_cache.disable = highbank_l2x0_disable;
++ if (IS_ENABLED(CONFIG_CACHE_L2X0)) {
++ outer_cache.write_sec = highbank_l2c310_write_sec;
++ l2x0_of_init(0, ~0);
+ }
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-imx/clk-pllv3.c linux-3.15-rc6/arch/arm/mach-imx/clk-pllv3.c
+--- linux-3.15-rc6.orig/arch/arm/mach-imx/clk-pllv3.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-imx/clk-pllv3.c 2014-05-23 11:26:48.256939874 +0200
+@@ -273,9 +273,10 @@
+ struct clk_pllv3 *pll = to_clk_pllv3(hw);
+ unsigned long min_rate = parent_rate * 27;
+ unsigned long max_rate = parent_rate * 54;
+- u32 val, div;
++ u32 val, newval, div;
+ u32 mfn, mfd = 1000000;
+ s64 temp64;
++ int ret;
+
+ if (rate < min_rate || rate > max_rate)
+ return -EINVAL;
+@@ -287,13 +288,27 @@
+ mfn = temp64;
+
+ val = readl_relaxed(pll->base);
+- val &= ~pll->div_mask;
+- val |= div;
+- writel_relaxed(val, pll->base);
++
++ /* set the PLL into bypass mode */
++ newval = val | BM_PLL_BYPASS;
++ writel_relaxed(newval, pll->base);
++
++ /* configure the new frequency */
++ newval &= ~pll->div_mask;
++ newval |= div;
++ writel_relaxed(newval, pll->base);
+ writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
+- writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
++ writel(mfd, pll->base + PLL_DENOM_OFFSET);
++
++ ret = clk_pllv3_wait_lock(pll);
++ if (ret == 0 && val & BM_PLL_POWER) {
++ /* only if it locked can we switch back to the PLL */
++ newval &= ~BM_PLL_BYPASS;
++ newval |= val & BM_PLL_BYPASS;
++ writel(newval, pll->base);
++ }
+
+- return clk_pllv3_wait_lock(pll);
++ return ret;
+ }
+
+ static const struct clk_ops clk_pllv3_av_ops = {
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-imx/mach-vf610.c linux-3.15-rc6/arch/arm/mach-imx/mach-vf610.c
+--- linux-3.15-rc6.orig/arch/arm/mach-imx/mach-vf610.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-imx/mach-vf610.c 2014-05-23 11:26:48.256939874 +0200
+@@ -22,7 +22,7 @@
+
+ static void __init vf610_init_irq(void)
+ {
+- l2x0_of_init(0, ~0UL);
++ l2x0_of_init(0, ~0);
+ irqchip_init();
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-imx/suspend-imx6.S linux-3.15-rc6/arch/arm/mach-imx/suspend-imx6.S
+--- linux-3.15-rc6.orig/arch/arm/mach-imx/suspend-imx6.S 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-imx/suspend-imx6.S 2014-05-23 11:26:48.256939874 +0200
+@@ -334,28 +334,10 @@
+ * turned into relative ones.
+ */
+
+-#ifdef CONFIG_CACHE_L2X0
+- .macro pl310_resume
+- adr r0, l2x0_saved_regs_offset
+- ldr r2, [r0]
+- add r2, r2, r0
+- ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0
+- ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value
+- str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl
+- mov r1, #0x1
+- str r1, [r0, #L2X0_CTRL] @ re-enable L2
+- .endm
+-
+-l2x0_saved_regs_offset:
+- .word l2x0_saved_regs - .
+-
+-#else
+- .macro pl310_resume
+- .endm
+-#endif
+-
+ ENTRY(v7_cpu_resume)
+ bl v7_invalidate_l1
+- pl310_resume
++#ifdef CONFIG_CACHE_L2X0
++ bl l2c310_early_resume
++#endif
+ b cpu_resume
+ ENDPROC(v7_cpu_resume)
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-imx/system.c linux-3.15-rc6/arch/arm/mach-imx/system.c
+--- linux-3.15-rc6.orig/arch/arm/mach-imx/system.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-imx/system.c 2014-05-23 11:26:48.260939887 +0200
+@@ -124,7 +124,7 @@
+ }
+
+ /* Configure the L2 PREFETCH and POWER registers */
+- val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
++ val = readl_relaxed(l2x0_base + L310_PREFETCH_CTRL);
+ val |= 0x70800000;
+ /*
+ * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0
+@@ -137,14 +137,12 @@
+ */
+ if (cpu_is_imx6q())
+ val &= ~(1 << 30 | 1 << 23);
+- writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL);
+- val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN;
+- writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL);
++ writel_relaxed(val, l2x0_base + L310_PREFETCH_CTRL);
+
+ iounmap(l2x0_base);
+ of_node_put(np);
+
+ out:
+- l2x0_of_init(0, ~0UL);
++ l2x0_of_init(0, ~0);
+ }
+ #endif
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-mvebu/board-v7.c linux-3.15-rc6/arch/arm/mach-mvebu/board-v7.c
+--- linux-3.15-rc6.orig/arch/arm/mach-mvebu/board-v7.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-mvebu/board-v7.c 2014-05-23 11:26:48.260939887 +0200
+@@ -60,7 +60,7 @@
+ coherency_init();
+ BUG_ON(mvebu_mbus_dt_init());
+ #ifdef CONFIG_CACHE_L2X0
+- l2x0_of_init(0, ~0UL);
++ l2x0_of_init(0, ~0);
+ #endif
+
+ if (of_machine_is_compatible("marvell,armada375"))
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-nomadik/cpu-8815.c linux-3.15-rc6/arch/arm/mach-nomadik/cpu-8815.c
+--- linux-3.15-rc6.orig/arch/arm/mach-nomadik/cpu-8815.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-nomadik/cpu-8815.c 2014-05-23 11:26:48.260939887 +0200
+@@ -147,7 +147,7 @@
+ {
+ #ifdef CONFIG_CACHE_L2X0
+ /* At full speed latency must be >=2, so 0x249 in low bits */
+- l2x0_of_init(0x00730249, 0xfe000fff);
++ l2x0_of_init(0x00700249, 0xfe0fefff);
+ #endif
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ }
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-omap2/common.h linux-3.15-rc6/arch/arm/mach-omap2/common.h
+--- linux-3.15-rc6.orig/arch/arm/mach-omap2/common.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-omap2/common.h 2014-05-23 11:26:48.260939887 +0200
+@@ -91,6 +91,7 @@
+ extern void omap3_secure_sync32k_timer_init(void);
+ extern void omap3_gptimer_timer_init(void);
+ extern void omap4_local_timer_init(void);
++int omap_l2_cache_init(void);
+ extern void omap5_realtime_timer_init(void);
+
+ void omap2420_init_early(void);
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-omap2/io.c linux-3.15-rc6/arch/arm/mach-omap2/io.c
+--- linux-3.15-rc6.orig/arch/arm/mach-omap2/io.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-omap2/io.c 2014-05-23 11:26:48.260939887 +0200
+@@ -609,6 +609,7 @@
+ am43xx_clockdomains_init();
+ am43xx_hwmod_init();
+ omap_hwmod_init_postsetup();
++ omap_l2_cache_init();
+ omap_clk_soc_init = am43xx_dt_clk_init;
+ }
+
+@@ -640,6 +641,7 @@
+ omap44xx_clockdomains_init();
+ omap44xx_hwmod_init();
+ omap_hwmod_init_postsetup();
++ omap_l2_cache_init();
+ omap_clk_soc_init = omap4xxx_dt_clk_init;
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-omap2/Kconfig linux-3.15-rc6/arch/arm/mach-omap2/Kconfig
+--- linux-3.15-rc6.orig/arch/arm/mach-omap2/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-omap2/Kconfig 2014-05-23 11:26:48.260939887 +0200
+@@ -65,6 +65,7 @@
+ select ARCH_HAS_OPP
+ select ARM_GIC
+ select MACH_OMAP_GENERIC
++ select MIGHT_HAVE_CACHE_L2X0
+
+ config SOC_DRA7XX
+ bool "TI DRA7XX"
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-omap2/omap4-common.c linux-3.15-rc6/arch/arm/mach-omap2/omap4-common.c
+--- linux-3.15-rc6.orig/arch/arm/mach-omap2/omap4-common.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-omap2/omap4-common.c 2014-05-23 11:26:48.260939887 +0200
+@@ -167,75 +167,57 @@
+ return l2cache_base;
+ }
+
+-static void omap4_l2x0_disable(void)
++static void omap4_l2c310_write_sec(unsigned long val, unsigned reg)
+ {
+- outer_flush_all();
+- /* Disable PL310 L2 Cache controller */
+- omap_smc1(0x102, 0x0);
+-}
++ unsigned smc_op;
+
+-static void omap4_l2x0_set_debug(unsigned long val)
+-{
+- /* Program PL310 L2 Cache controller debug register */
+- omap_smc1(0x100, val);
++ switch (reg) {
++ case L2X0_CTRL:
++ smc_op = OMAP4_MON_L2X0_CTRL_INDEX;
++ break;
++
++ case L2X0_AUX_CTRL:
++ smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX;
++ break;
++
++ case L2X0_DEBUG_CTRL:
++ smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX;
++ break;
++
++ case L310_PREFETCH_CTRL:
++ smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX;
++ break;
++
++ default:
++ WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg);
++ return;
++ }
++
++ omap_smc1(smc_op, val);
+ }
+
+-static int __init omap_l2_cache_init(void)
++int __init omap_l2_cache_init(void)
+ {
+- u32 aux_ctrl = 0;
+-
+- /*
+- * To avoid code running on other OMAPs in
+- * multi-omap builds
+- */
+- if (!cpu_is_omap44xx())
+- return -ENODEV;
++ u32 aux_ctrl;
+
+ /* Static mapping, never released */
+ l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K);
+ if (WARN_ON(!l2cache_base))
+ return -ENOMEM;
+
+- /*
+- * 16-way associativity, parity disabled
+- * Way size - 32KB (es1.0)
+- * Way size - 64KB (es2.0 +)
+- */
+- aux_ctrl = ((1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) |
+- (0x1 << 25) |
+- (0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) |
+- (0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT));
+-
+- if (omap_rev() == OMAP4430_REV_ES1_0) {
+- aux_ctrl |= 0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT;
+- } else {
+- aux_ctrl |= ((0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
+- (1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
+- (1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
+- (1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
+- (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT));
+- }
+- if (omap_rev() != OMAP4430_REV_ES1_0)
+- omap_smc1(0x109, aux_ctrl);
+-
+- /* Enable PL310 L2 Cache controller */
+- omap_smc1(0x102, 0x1);
++ /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */
++ aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE |
++ L310_AUX_CTRL_DATA_PREFETCH |
++ L310_AUX_CTRL_INSTR_PREFETCH;
+
++ outer_cache.write_sec = omap4_l2c310_write_sec;
+ if (of_have_populated_dt())
+- l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
++ l2x0_of_init(aux_ctrl, 0xcf9fffff);
+ else
+- l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK);
+-
+- /*
+- * Override default outer_cache.disable with a OMAP4
+- * specific one
+- */
+- outer_cache.disable = omap4_l2x0_disable;
+- outer_cache.set_debug = omap4_l2x0_set_debug;
++ l2x0_init(l2cache_base, aux_ctrl, 0xcf9fffff);
+
+ return 0;
+ }
+-omap_early_initcall(omap_l2_cache_init);
+ #endif
+
+ void __iomem *omap4_get_sar_ram_base(void)
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-omap2/omap-mpuss-lowpower.c linux-3.15-rc6/arch/arm/mach-omap2/omap-mpuss-lowpower.c
+--- linux-3.15-rc6.orig/arch/arm/mach-omap2/omap-mpuss-lowpower.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-omap2/omap-mpuss-lowpower.c 2014-05-23 11:26:48.268939913 +0200
+@@ -187,19 +187,15 @@
+ * in every restore MPUSS OFF path.
+ */
+ #ifdef CONFIG_CACHE_L2X0
+-static void save_l2x0_context(void)
++static void __init save_l2x0_context(void)
+ {
+- u32 val;
+- void __iomem *l2x0_base = omap4_get_l2cache_base();
+- if (l2x0_base) {
+- val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
+- __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
+- val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
+- __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
+- }
++ __raw_writel(l2x0_saved_regs.aux_ctrl,
++ sar_base + L2X0_AUXCTRL_OFFSET);
++ __raw_writel(l2x0_saved_regs.prefetch_ctrl,
++ sar_base + L2X0_PREFETCH_CTRL_OFFSET);
+ }
+ #else
+-static void save_l2x0_context(void)
++static void __init save_l2x0_context(void)
+ {}
+ #endif
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-prima2/l2x0.c linux-3.15-rc6/arch/arm/mach-prima2/l2x0.c
+--- linux-3.15-rc6.orig/arch/arm/mach-prima2/l2x0.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-prima2/l2x0.c 2014-05-23 11:26:48.268939913 +0200
+@@ -8,42 +8,10 @@
+
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+-#include <linux/of.h>
+ #include <asm/hardware/cache-l2x0.h>
+
+-struct l2x0_aux {
+- u32 val;
+- u32 mask;
+-};
+-
+-static const struct l2x0_aux prima2_l2x0_aux __initconst = {
+- .val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT,
+- .mask = 0,
+-};
+-
+-static const struct l2x0_aux marco_l2x0_aux __initconst = {
+- .val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
+- (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT),
+- .mask = L2X0_AUX_CTRL_MASK,
+-};
+-
+-static const struct of_device_id sirf_l2x0_ids[] __initconst = {
+- { .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, },
+- { .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, },
+- {},
+-};
+-
+ static int __init sirfsoc_l2x0_init(void)
+ {
+- struct device_node *np;
+- const struct l2x0_aux *aux;
+-
+- np = of_find_matching_node(NULL, sirf_l2x0_ids);
+- if (np) {
+- aux = of_match_node(sirf_l2x0_ids, np)->data;
+- return l2x0_of_init(aux->val, aux->mask);
+- }
+-
+- return 0;
++ return l2x0_of_init(0, ~0);
+ }
+ early_initcall(sirfsoc_l2x0_init);
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-prima2/pm.c linux-3.15-rc6/arch/arm/mach-prima2/pm.c
+--- linux-3.15-rc6.orig/arch/arm/mach-prima2/pm.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-prima2/pm.c 2014-05-23 11:26:48.268939913 +0200
+@@ -71,7 +71,6 @@
+ case PM_SUSPEND_MEM:
+ sirfsoc_pre_suspend_power_off();
+
+- outer_flush_all();
+ outer_disable();
+ /* go zzz */
+ cpu_suspend(0, sirfsoc_finish_suspend);
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-realview/realview_eb.c linux-3.15-rc6/arch/arm/mach-realview/realview_eb.c
+--- linux-3.15-rc6.orig/arch/arm/mach-realview/realview_eb.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-realview/realview_eb.c 2014-05-23 11:26:48.268939913 +0200
+@@ -442,8 +442,13 @@
+ realview_eb11mp_fixup();
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled
+- * Bits: .... ...0 0111 1001 0000 .... .... .... */
++ /*
++ * The PL220 needs to be manually configured as the hardware
++ * doesn't report the correct sizes.
++ * 1MB (128KB/way), 8-way associativity, event monitor and
++ * parity enabled, ignore share bit, no force write allocate
++ * Bits: .... ...0 0111 1001 0000 .... .... ....
++ */
+ l2x0_init(__io_address(REALVIEW_EB11MP_L220_BASE), 0x00790000, 0xfe000fff);
+ #endif
+ platform_device_register(&pmu_device);
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pb1176.c linux-3.15-rc6/arch/arm/mach-realview/realview_pb1176.c
+--- linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pb1176.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-realview/realview_pb1176.c 2014-05-23 11:26:48.268939913 +0200
+@@ -355,7 +355,13 @@
+ int i;
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* 128Kb (16Kb/way) 8-way associativity. evmon/parity/share enabled. */
++ /*
++ * The PL220 needs to be manually configured as the hardware
++ * doesn't report the correct sizes.
++ * 128kB (16kB/way), 8-way associativity, event monitor and
++ * parity enabled, ignore share bit, no force write allocate
++ * Bits: .... ...0 0111 0011 0000 .... .... ....
++ */
+ l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff);
+ #endif
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pb11mp.c linux-3.15-rc6/arch/arm/mach-realview/realview_pb11mp.c
+--- linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pb11mp.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-realview/realview_pb11mp.c 2014-05-23 11:26:48.268939913 +0200
+@@ -337,8 +337,13 @@
+ int i;
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled
+- * Bits: .... ...0 0111 1001 0000 .... .... .... */
++ /*
++ * The PL220 needs to be manually configured as the hardware
++ * doesn't report the correct sizes.
++ * 1MB (128KB/way), 8-way associativity, event monitor and
++ * parity enabled, ignore share bit, no force write allocate
++ * Bits: .... ...0 0111 1001 0000 .... .... ....
++ */
+ l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff);
+ #endif
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pbx.c linux-3.15-rc6/arch/arm/mach-realview/realview_pbx.c
+--- linux-3.15-rc6.orig/arch/arm/mach-realview/realview_pbx.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-realview/realview_pbx.c 2014-05-23 11:26:48.268939913 +0200
+@@ -370,8 +370,8 @@
+ __io_address(REALVIEW_PBX_TILE_L220_BASE);
+
+ /* set RAM latencies to 1 cycle for eASIC */
+- writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL);
+- writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL);
++ writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
++ writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
+
+ /* 16KB way size, 8-way associativity, parity disabled
+ * Bits: .. 0 0 0 0 1 00 1 0 1 001 0 000 0 .... .... .... */
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-rockchip/rockchip.c linux-3.15-rc6/arch/arm/mach-rockchip/rockchip.c
+--- linux-3.15-rc6.orig/arch/arm/mach-rockchip/rockchip.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-rockchip/rockchip.c 2014-05-23 11:26:48.268939913 +0200
+@@ -26,7 +26,7 @@
+
+ static void __init rockchip_dt_init(void)
+ {
+- l2x0_of_init(0, ~0UL);
++ l2x0_of_init(0, ~0);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-armadillo800eva.c linux-3.15-rc6/arch/arm/mach-shmobile/board-armadillo800eva.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-armadillo800eva.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/board-armadillo800eva.c 2014-05-23 11:26:48.272939927 +0200
+@@ -1271,8 +1271,8 @@
+
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* Early BRESP enable, Shared attribute override enable, 32K*8way */
+- l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff);
++ /* Shared attribute override enable, 32K*8way */
++ l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff);
+ #endif
+
+ i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-armadillo800eva-reference.c linux-3.15-rc6/arch/arm/mach-shmobile/board-armadillo800eva-reference.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-armadillo800eva-reference.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/board-armadillo800eva-reference.c 2014-05-23 11:26:48.272939927 +0200
+@@ -164,8 +164,8 @@
+ r8a7740_meram_workaround();
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* Early BRESP enable, Shared attribute override enable, 32K*8way */
+- l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff);
++ /* Shared attribute override enable, 32K*8way */
++ l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff);
+ #endif
+
+ r8a7740_add_standard_devices_dt();
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-kzm9g.c linux-3.15-rc6/arch/arm/mach-shmobile/board-kzm9g.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-kzm9g.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/board-kzm9g.c 2014-05-23 11:26:48.272939927 +0200
+@@ -876,8 +876,8 @@
+ gpio_request_one(223, GPIOF_IN, NULL); /* IRQ8 */
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* Early BRESP enable, Shared attribute override enable, 64K*8way */
+- l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
++ /* Shared attribute override enable, 64K*8way */
++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
+ #endif
+
+ i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-kzm9g-reference.c linux-3.15-rc6/arch/arm/mach-shmobile/board-kzm9g-reference.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/board-kzm9g-reference.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/board-kzm9g-reference.c 2014-05-23 11:26:48.272939927 +0200
+@@ -36,8 +36,8 @@
+ sh73a0_add_standard_devices_dt();
+
+ #ifdef CONFIG_CACHE_L2X0
+- /* Early BRESP enable, Shared attribute override enable, 64K*8way */
+- l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
++ /* Shared attribute override enable, 64K*8way */
++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
+ #endif
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/setup-r8a7778.c linux-3.15-rc6/arch/arm/mach-shmobile/setup-r8a7778.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/setup-r8a7778.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/setup-r8a7778.c 2014-05-23 11:26:48.272939927 +0200
+@@ -298,10 +298,10 @@
+ void __iomem *base = ioremap_nocache(0xf0100000, 0x1000);
+ if (base) {
+ /*
+- * Early BRESP enable, Shared attribute override enable, 64K*16way
++ * Shared attribute override enable, 64K*16way
+ * don't call iounmap(base)
+ */
+- l2x0_init(base, 0x40470000, 0x82000fff);
++ l2x0_init(base, 0x00400000, 0xc20f0fff);
+ }
+ #endif
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-shmobile/setup-r8a7779.c linux-3.15-rc6/arch/arm/mach-shmobile/setup-r8a7779.c
+--- linux-3.15-rc6.orig/arch/arm/mach-shmobile/setup-r8a7779.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-shmobile/setup-r8a7779.c 2014-05-23 11:26:48.272939927 +0200
+@@ -700,8 +700,8 @@
+ void __init r8a7779_add_standard_devices(void)
+ {
+ #ifdef CONFIG_CACHE_L2X0
+- /* Early BRESP enable, Shared attribute override enable, 64K*16way */
+- l2x0_init(IOMEM(0xf0100000), 0x40470000, 0x82000fff);
++ /* Shared attribute override enable, 64K*16way */
++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
+ #endif
+ r8a7779_pm_init();
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-socfpga/socfpga.c linux-3.15-rc6/arch/arm/mach-socfpga/socfpga.c
+--- linux-3.15-rc6.orig/arch/arm/mach-socfpga/socfpga.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-socfpga/socfpga.c 2014-05-23 11:26:48.272939927 +0200
+@@ -100,7 +100,7 @@
+
+ static void __init socfpga_cyclone5_init(void)
+ {
+- l2x0_of_init(0, ~0UL);
++ l2x0_of_init(0, ~0);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-spear/platsmp.c linux-3.15-rc6/arch/arm/mach-spear/platsmp.c
+--- linux-3.15-rc6.orig/arch/arm/mach-spear/platsmp.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-spear/platsmp.c 2014-05-23 11:26:48.272939927 +0200
+@@ -20,6 +20,18 @@
+ #include <mach/spear.h>
+ #include "generic.h"
+
++/*
++ * Write pen_release in a way that is guaranteed to be visible to all
++ * observers, irrespective of whether they're taking part in coherency
++ * or not. This is necessary for the hotplug code to work reliably.
++ */
++static void write_pen_release(int val)
++{
++ pen_release = val;
++ smp_wmb();
++ sync_cache_w(&pen_release);
++}
++
+ static DEFINE_SPINLOCK(boot_lock);
+
+ static void __iomem *scu_base = IOMEM(VA_SCU_BASE);
+@@ -30,8 +42,7 @@
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+- pen_release = -1;
+- smp_wmb();
++ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+@@ -58,9 +69,7 @@
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+- pen_release = cpu;
+- flush_cache_all();
+- outer_flush_all();
++ write_pen_release(cpu);
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-spear/spear13xx.c linux-3.15-rc6/arch/arm/mach-spear/spear13xx.c
+--- linux-3.15-rc6.orig/arch/arm/mach-spear/spear13xx.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-spear/spear13xx.c 2014-05-23 11:26:48.272939927 +0200
+@@ -38,15 +38,15 @@
+ if (!IS_ENABLED(CONFIG_CACHE_L2X0))
+ return;
+
+- writel_relaxed(0x06, VA_L2CC_BASE + L2X0_PREFETCH_CTRL);
++ writel_relaxed(0x06, VA_L2CC_BASE + L310_PREFETCH_CTRL);
+
+ /*
+ * Program following latencies in order to make
+ * SPEAr1340 work at 600 MHz
+ */
+- writel_relaxed(0x221, VA_L2CC_BASE + L2X0_TAG_LATENCY_CTRL);
+- writel_relaxed(0x441, VA_L2CC_BASE + L2X0_DATA_LATENCY_CTRL);
+- l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff);
++ writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL);
++ writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL);
++ l2x0_init(VA_L2CC_BASE, 0x30a00001, 0xfe0fffff);
+ }
+
+ /*
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-sti/board-dt.c linux-3.15-rc6/arch/arm/mach-sti/board-dt.c
+--- linux-3.15-rc6.orig/arch/arm/mach-sti/board-dt.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-sti/board-dt.c 2014-05-23 11:26:48.272939927 +0200
+@@ -16,15 +16,9 @@
+
+ void __init stih41x_l2x0_init(void)
+ {
+- u32 way_size = 0x4;
+- u32 aux_ctrl;
+- /* may be this can be encoded in macros like BIT*() */
+- aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
+- (0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
+- (0x1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
+- (way_size << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+-
+- l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
++ l2x0_of_init(L2C_AUX_CTRL_SHARED_OVERRIDE |
++ L310_AUX_CTRL_DATA_PREFETCH |
++ L310_AUX_CTRL_INSTR_PREFETCH, 0xc00f0fff);
+ }
+
+ static void __init stih41x_machine_init(void)
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-tegra/pm.h linux-3.15-rc6/arch/arm/mach-tegra/pm.h
+--- linux-3.15-rc6.orig/arch/arm/mach-tegra/pm.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-tegra/pm.h 2014-05-23 11:26:48.272939927 +0200
+@@ -35,8 +35,6 @@
+ void tegra30_lp1_iram_hook(void);
+ void tegra30_sleep_core_init(void);
+
+-extern unsigned long l2x0_saved_regs_addr;
+-
+ void tegra_clear_cpu_in_lp2(void);
+ bool tegra_set_cpu_in_lp2(void);
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-tegra/reset-handler.S linux-3.15-rc6/arch/arm/mach-tegra/reset-handler.S
+--- linux-3.15-rc6.orig/arch/arm/mach-tegra/reset-handler.S 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-tegra/reset-handler.S 2014-05-23 11:26:48.276939940 +0200
+@@ -19,7 +19,6 @@
+
+ #include <asm/cache.h>
+ #include <asm/asm-offsets.h>
+-#include <asm/hardware/cache-l2x0.h>
+
+ #include "flowctrl.h"
+ #include "fuse.h"
+@@ -78,8 +77,10 @@
+ str r1, [r0]
+ #endif
+
++#ifdef CONFIG_CACHE_L2X0
+ /* L2 cache resume & re-enable */
+- l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr
++ bl l2c310_early_resume
++#endif
+ end_ca9_scu_l2_resume:
+ mov32 r9, 0xc0f
+ cmp r8, r9
+@@ -89,12 +90,6 @@
+ ENDPROC(tegra_resume)
+ #endif
+
+-#ifdef CONFIG_CACHE_L2X0
+- .globl l2x0_saved_regs_addr
+-l2x0_saved_regs_addr:
+- .long 0
+-#endif
+-
+ .align L1_CACHE_SHIFT
+ ENTRY(__tegra_cpu_reset_handler_start)
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-tegra/sleep.h linux-3.15-rc6/arch/arm/mach-tegra/sleep.h
+--- linux-3.15-rc6.orig/arch/arm/mach-tegra/sleep.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-tegra/sleep.h 2014-05-23 11:26:48.276939940 +0200
+@@ -120,37 +120,6 @@
+ mov \tmp1, \tmp1, lsr #8
+ .endm
+
+-/* Macro to resume & re-enable L2 cache */
+-#ifndef L2X0_CTRL_EN
+-#define L2X0_CTRL_EN 1
+-#endif
+-
+-#ifdef CONFIG_CACHE_L2X0
+-.macro l2_cache_resume, tmp1, tmp2, tmp3, phys_l2x0_saved_regs
+- W(adr) \tmp1, \phys_l2x0_saved_regs
+- ldr \tmp1, [\tmp1]
+- ldr \tmp2, [\tmp1, #L2X0_R_PHY_BASE]
+- ldr \tmp3, [\tmp2, #L2X0_CTRL]
+- tst \tmp3, #L2X0_CTRL_EN
+- bne exit_l2_resume
+- ldr \tmp3, [\tmp1, #L2X0_R_TAG_LATENCY]
+- str \tmp3, [\tmp2, #L2X0_TAG_LATENCY_CTRL]
+- ldr \tmp3, [\tmp1, #L2X0_R_DATA_LATENCY]
+- str \tmp3, [\tmp2, #L2X0_DATA_LATENCY_CTRL]
+- ldr \tmp3, [\tmp1, #L2X0_R_PREFETCH_CTRL]
+- str \tmp3, [\tmp2, #L2X0_PREFETCH_CTRL]
+- ldr \tmp3, [\tmp1, #L2X0_R_PWR_CTRL]
+- str \tmp3, [\tmp2, #L2X0_POWER_CTRL]
+- ldr \tmp3, [\tmp1, #L2X0_R_AUX_CTRL]
+- str \tmp3, [\tmp2, #L2X0_AUX_CTRL]
+- mov \tmp3, #L2X0_CTRL_EN
+- str \tmp3, [\tmp2, #L2X0_CTRL]
+-exit_l2_resume:
+-.endm
+-#else /* CONFIG_CACHE_L2X0 */
+-.macro l2_cache_resume, tmp1, tmp2, tmp3, phys_l2x0_saved_regs
+-.endm
+-#endif /* CONFIG_CACHE_L2X0 */
+ #else
+ void tegra_pen_lock(void);
+ void tegra_pen_unlock(void);
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-tegra/tegra.c linux-3.15-rc6/arch/arm/mach-tegra/tegra.c
+--- linux-3.15-rc6.orig/arch/arm/mach-tegra/tegra.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-tegra/tegra.c 2014-05-23 11:26:48.276939940 +0200
+@@ -73,27 +73,7 @@
+ static void __init tegra_init_cache(void)
+ {
+ #ifdef CONFIG_CACHE_L2X0
+- static const struct of_device_id pl310_ids[] __initconst = {
+- { .compatible = "arm,pl310-cache", },
+- {}
+- };
+-
+- struct device_node *np;
+- int ret;
+- void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+- u32 aux_ctrl, cache_type;
+-
+- np = of_find_matching_node(NULL, pl310_ids);
+- if (!np)
+- return;
+-
+- cache_type = readl(p + L2X0_CACHE_TYPE);
+- aux_ctrl = (cache_type & 0x700) << (17-8);
+- aux_ctrl |= 0x7C400001;
+-
+- ret = l2x0_of_init(aux_ctrl, 0x8200c3fe);
+- if (!ret)
+- l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs);
++ l2x0_of_init(0x3c400001, 0xc20fc3fe);
+ #endif
+ }
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-ux500/cache-l2x0.c linux-3.15-rc6/arch/arm/mach-ux500/cache-l2x0.c
+--- linux-3.15-rc6.orig/arch/arm/mach-ux500/cache-l2x0.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-ux500/cache-l2x0.c 2014-05-23 11:26:48.276939940 +0200
+@@ -35,10 +35,16 @@
+ return 0;
+ }
+
+-static int __init ux500_l2x0_init(void)
++static void ux500_l2c310_write_sec(unsigned long val, unsigned reg)
+ {
+- u32 aux_val = 0x3e000000;
++ /*
++ * We can't write to secure registers as we are in non-secure
++ * mode, until we have some SMI service available.
++ */
++}
+
++static int __init ux500_l2x0_init(void)
++{
+ if (cpu_is_u8500_family() || cpu_is_ux540_family())
+ l2x0_base = __io_address(U8500_L2CC_BASE);
+ else
+@@ -48,28 +54,12 @@
+ /* Unlock before init */
+ ux500_l2x0_unlock();
+
+- /* DBx540's L2 has 128KB way size */
+- if (cpu_is_ux540_family())
+- /* 128KB way size */
+- aux_val |= (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+- else
+- /* 64KB way size */
+- aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
++ outer_cache.write_sec = ux500_l2c310_write_sec;
+
+- /* 64KB way size, 8 way associativity, force WA */
+ if (of_have_populated_dt())
+- l2x0_of_init(aux_val, 0xc0000fff);
++ l2x0_of_init(0, ~0);
+ else
+- l2x0_init(l2x0_base, aux_val, 0xc0000fff);
+-
+- /*
+- * We can't disable l2 as we are in non secure mode, currently
+- * this seems be called only during kexec path. So let's
+- * override outer.disable with nasty assignment until we have
+- * some SMI service available.
+- */
+- outer_cache.disable = NULL;
+- outer_cache.set_debug = NULL;
++ l2x0_init(l2x0_base, 0, ~0);
+
+ return 0;
+ }
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-vexpress/ct-ca9x4.c linux-3.15-rc6/arch/arm/mach-vexpress/ct-ca9x4.c
+--- linux-3.15-rc6.orig/arch/arm/mach-vexpress/ct-ca9x4.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-vexpress/ct-ca9x4.c 2014-05-23 11:26:48.276939940 +0200
+@@ -45,6 +45,23 @@
+ iotable_init(ct_ca9x4_io_desc, ARRAY_SIZE(ct_ca9x4_io_desc));
+ }
+
++static void __init ca9x4_l2_init(void)
++{
++#ifdef CONFIG_CACHE_L2X0
++ void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K);
++
++ if (l2x0_base) {
++ /* set RAM latencies to 1 cycle for this core tile. */
++ writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
++ writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
++
++ l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff);
++ } else {
++ pr_err("L2C: unable to map L2 cache controller\n");
++ }
++#endif
++}
++
+ #ifdef CONFIG_HAVE_ARM_TWD
+ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, A9_MPCORE_TWD, IRQ_LOCALTIMER);
+
+@@ -63,6 +80,7 @@
+ gic_init(0, 29, ioremap(A9_MPCORE_GIC_DIST, SZ_4K),
+ ioremap(A9_MPCORE_GIC_CPU, SZ_256));
+ ca9x4_twd_init();
++ ca9x4_l2_init();
+ }
+
+ static int ct_ca9x4_clcd_setup(struct clcd_fb *fb)
+@@ -141,16 +159,6 @@
+ {
+ int i;
+
+-#ifdef CONFIG_CACHE_L2X0
+- void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K);
+-
+- /* set RAM latencies to 1 cycle for this core tile. */
+- writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL);
+- writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL);
+-
+- l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff);
+-#endif
+-
+ for (i = 0; i < ARRAY_SIZE(ct_ca9x4_amba_devs); i++)
+ amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource);
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mach-zynq/common.c linux-3.15-rc6/arch/arm/mach-zynq/common.c
+--- linux-3.15-rc6.orig/arch/arm/mach-zynq/common.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mach-zynq/common.c 2014-05-23 11:26:48.276939940 +0200
+@@ -70,7 +70,7 @@
+ /*
+ * 64KB way size, 8-way associativity, parity disabled
+ */
+- l2x0_of_init(0x02060000, 0xF0F0FFFF);
++ l2x0_of_init(0x02000000, 0xf0ffffff);
+
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/cache-feroceon-l2.c linux-3.15-rc6/arch/arm/mm/cache-feroceon-l2.c
+--- linux-3.15-rc6.orig/arch/arm/mm/cache-feroceon-l2.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mm/cache-feroceon-l2.c 2014-05-23 11:26:48.280939953 +0200
+@@ -350,7 +350,6 @@
+ outer_cache.inv_range = feroceon_l2_inv_range;
+ outer_cache.clean_range = feroceon_l2_clean_range;
+ outer_cache.flush_range = feroceon_l2_flush_range;
+- outer_cache.inv_all = l2_inv_all;
+
+ enable_l2();
+
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/cache-l2x0.c linux-3.15-rc6/arch/arm/mm/cache-l2x0.c
+--- linux-3.15-rc6.orig/arch/arm/mm/cache-l2x0.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mm/cache-l2x0.c 2014-05-23 11:26:48.280939953 +0200
+@@ -16,18 +16,33 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
++#include <linux/cpu.h>
+ #include <linux/err.h>
+ #include <linux/init.h>
++#include <linux/smp.h>
+ #include <linux/spinlock.h>
+ #include <linux/io.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+
+ #include <asm/cacheflush.h>
++#include <asm/cp15.h>
++#include <asm/cputype.h>
+ #include <asm/hardware/cache-l2x0.h>
+ #include "cache-tauros3.h"
+ #include "cache-aurora-l2.h"
+
++struct l2c_init_data {
++ const char *type;
++ unsigned way_size_0;
++ unsigned num_lock;
++ void (*of_parse)(const struct device_node *, u32 *, u32 *);
++ void (*enable)(void __iomem *, u32, unsigned);
++ void (*fixup)(void __iomem *, u32, struct outer_cache_fns *);
++ void (*save)(void __iomem *);
++ struct outer_cache_fns outer_cache;
++};
++
+ #define CACHE_LINE_SIZE 32
+
+ static void __iomem *l2x0_base;
+@@ -36,96 +51,116 @@
+ static u32 l2x0_size;
+ static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
+
+-/* Aurora don't have the cache ID register available, so we have to
+- * pass it though the device tree */
+-static u32 cache_id_part_number_from_dt;
+-
+ struct l2x0_regs l2x0_saved_regs;
+
+-struct l2x0_of_data {
+- void (*setup)(const struct device_node *, u32 *, u32 *);
+- void (*save)(void);
+- struct outer_cache_fns outer_cache;
+-};
+-
+-static bool of_init = false;
+-
+-static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
++/*
++ * Common code for all cache controllers.
++ */
++static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask)
+ {
+ /* wait for cache operation by line or way to complete */
+ while (readl_relaxed(reg) & mask)
+ cpu_relax();
+ }
+
+-#ifdef CONFIG_CACHE_PL310
+-static inline void cache_wait(void __iomem *reg, unsigned long mask)
++/*
++ * By default, we write directly to secure registers. Platforms must
++ * override this if they are running non-secure.
++ */
++static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg)
+ {
+- /* cache operations by line are atomic on PL310 */
++ if (val == readl_relaxed(base + reg))
++ return;
++ if (outer_cache.write_sec)
++ outer_cache.write_sec(val, reg);
++ else
++ writel_relaxed(val, base + reg);
+ }
+-#else
+-#define cache_wait cache_wait_way
+-#endif
+
+-static inline void cache_sync(void)
++/*
++ * This should only be called when we have a requirement that the
++ * register be written due to a work-around, as platforms running
++ * in non-secure mode may not be able to access this register.
++ */
++static inline void l2c_set_debug(void __iomem *base, unsigned long val)
+ {
+- void __iomem *base = l2x0_base;
+-
+- writel_relaxed(0, base + sync_reg_offset);
+- cache_wait(base + L2X0_CACHE_SYNC, 1);
++ l2c_write_sec(val, base, L2X0_DEBUG_CTRL);
+ }
+
+-static inline void l2x0_clean_line(unsigned long addr)
++static void __l2c_op_way(void __iomem *reg)
+ {
+- void __iomem *base = l2x0_base;
+- cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+- writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
++ writel_relaxed(l2x0_way_mask, reg);
++ l2c_wait_mask(reg, l2x0_way_mask);
+ }
+
+-static inline void l2x0_inv_line(unsigned long addr)
++static inline void l2c_unlock(void __iomem *base, unsigned num)
+ {
+- void __iomem *base = l2x0_base;
+- cache_wait(base + L2X0_INV_LINE_PA, 1);
+- writel_relaxed(addr, base + L2X0_INV_LINE_PA);
++ unsigned i;
++
++ for (i = 0; i < num; i++) {
++ writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE +
++ i * L2X0_LOCKDOWN_STRIDE);
++ writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE +
++ i * L2X0_LOCKDOWN_STRIDE);
++ }
+ }
+
+-#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
+-static inline void debug_writel(unsigned long val)
++/*
++ * Enable the L2 cache controller. This function must only be
++ * called when the cache controller is known to be disabled.
++ */
++static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
+ {
+- if (outer_cache.set_debug)
+- outer_cache.set_debug(val);
++ unsigned long flags;
++
++ l2c_write_sec(aux, base, L2X0_AUX_CTRL);
++
++ l2c_unlock(base, num_lock);
++
++ local_irq_save(flags);
++ __l2c_op_way(base + L2X0_INV_WAY);
++ writel_relaxed(0, base + sync_reg_offset);
++ l2c_wait_mask(base + sync_reg_offset, 1);
++ local_irq_restore(flags);
++
++ l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL);
+ }
+
+-static void pl310_set_debug(unsigned long val)
++static void l2c_disable(void)
+ {
+- writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
++ void __iomem *base = l2x0_base;
++
++ outer_cache.flush_all();
++ l2c_write_sec(0, base, L2X0_CTRL);
++ dsb(st);
+ }
+-#else
+-/* Optimised out for non-errata case */
+-static inline void debug_writel(unsigned long val)
++
++#ifdef CONFIG_CACHE_PL310
++static inline void cache_wait(void __iomem *reg, unsigned long mask)
+ {
++ /* cache operations by line are atomic on PL310 */
+ }
+-
+-#define pl310_set_debug NULL
++#else
++#define cache_wait l2c_wait_mask
+ #endif
+
+-#ifdef CONFIG_PL310_ERRATA_588369
+-static inline void l2x0_flush_line(unsigned long addr)
++static inline void cache_sync(void)
+ {
+ void __iomem *base = l2x0_base;
+
+- /* Clean by PA followed by Invalidate by PA */
+- cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+- writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
+- cache_wait(base + L2X0_INV_LINE_PA, 1);
+- writel_relaxed(addr, base + L2X0_INV_LINE_PA);
++ writel_relaxed(0, base + sync_reg_offset);
++ cache_wait(base + L2X0_CACHE_SYNC, 1);
+ }
+-#else
+
+-static inline void l2x0_flush_line(unsigned long addr)
++#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
++static inline void debug_writel(unsigned long val)
++{
++ l2c_set_debug(l2x0_base, val);
++}
++#else
++/* Optimised out for non-errata case */
++static inline void debug_writel(unsigned long val)
+ {
+- void __iomem *base = l2x0_base;
+- cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+- writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA);
+ }
+ #endif
+
+@@ -141,8 +176,7 @@
+ static void __l2x0_flush_all(void)
+ {
+ debug_writel(0x03);
+- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY);
+- cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask);
++ __l2c_op_way(l2x0_base + L2X0_CLEAN_INV_WAY);
+ cache_sync();
+ debug_writel(0x00);
+ }
+@@ -157,274 +191,882 @@
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+ }
+
+-static void l2x0_clean_all(void)
++static void l2x0_disable(void)
+ {
+ unsigned long flags;
+
+- /* clean all ways */
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
+- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
+- cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+- cache_sync();
++ __l2x0_flush_all();
++ l2c_write_sec(0, l2x0_base, L2X0_CTRL);
++ dsb(st);
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+ }
+
+-static void l2x0_inv_all(void)
++static void l2c_save(void __iomem *base)
+ {
+- unsigned long flags;
++ l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
++}
+
+- /* invalidate all ways */
+- raw_spin_lock_irqsave(&l2x0_lock, flags);
+- /* Invalidating when L2 is enabled is a nono */
+- BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN);
+- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY);
+- cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask);
+- cache_sync();
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++/*
++ * L2C-210 specific code.
++ *
++ * The L2C-2x0 PA, set/way and sync operations are atomic, but we must
++ * ensure that no background operation is running. The way operations
++ * are all background tasks.
++ *
++ * While a background operation is in progress, any new operation is
++ * ignored (unspecified whether this causes an error.) Thankfully, not
++ * used on SMP.
++ *
++ * Never has a different sync register other than L2X0_CACHE_SYNC, but
++ * we use sync_reg_offset here so we can share some of this with L2C-310.
++ */
++static void __l2c210_cache_sync(void __iomem *base)
++{
++ writel_relaxed(0, base + sync_reg_offset);
+ }
+
+-static void l2x0_inv_range(unsigned long start, unsigned long end)
++static void __l2c210_op_pa_range(void __iomem *reg, unsigned long start,
++ unsigned long end)
++{
++ while (start < end) {
++ writel_relaxed(start, reg);
++ start += CACHE_LINE_SIZE;
++ }
++}
++
++static void l2c210_inv_range(unsigned long start, unsigned long end)
+ {
+ void __iomem *base = l2x0_base;
+- unsigned long flags;
+
+- raw_spin_lock_irqsave(&l2x0_lock, flags);
+ if (start & (CACHE_LINE_SIZE - 1)) {
+ start &= ~(CACHE_LINE_SIZE - 1);
+- debug_writel(0x03);
+- l2x0_flush_line(start);
+- debug_writel(0x00);
++ writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
+ start += CACHE_LINE_SIZE;
+ }
+
+ if (end & (CACHE_LINE_SIZE - 1)) {
+ end &= ~(CACHE_LINE_SIZE - 1);
+- debug_writel(0x03);
+- l2x0_flush_line(end);
+- debug_writel(0x00);
++ writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
+ }
+
++ __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
++ __l2c210_cache_sync(base);
++}
++
++static void l2c210_clean_range(unsigned long start, unsigned long end)
++{
++ void __iomem *base = l2x0_base;
++
++ start &= ~(CACHE_LINE_SIZE - 1);
++ __l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end);
++ __l2c210_cache_sync(base);
++}
++
++static void l2c210_flush_range(unsigned long start, unsigned long end)
++{
++ void __iomem *base = l2x0_base;
++
++ start &= ~(CACHE_LINE_SIZE - 1);
++ __l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end);
++ __l2c210_cache_sync(base);
++}
++
++static void l2c210_flush_all(void)
++{
++ void __iomem *base = l2x0_base;
++
++ BUG_ON(!irqs_disabled());
++
++ __l2c_op_way(base + L2X0_CLEAN_INV_WAY);
++ __l2c210_cache_sync(base);
++}
++
++static void l2c210_sync(void)
++{
++ __l2c210_cache_sync(l2x0_base);
++}
++
++static void l2c210_resume(void)
++{
++ void __iomem *base = l2x0_base;
++
++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN))
++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 1);
++}
++
++static const struct l2c_init_data l2c210_data __initconst = {
++ .type = "L2C-210",
++ .way_size_0 = SZ_8K,
++ .num_lock = 1,
++ .enable = l2c_enable,
++ .save = l2c_save,
++ .outer_cache = {
++ .inv_range = l2c210_inv_range,
++ .clean_range = l2c210_clean_range,
++ .flush_range = l2c210_flush_range,
++ .flush_all = l2c210_flush_all,
++ .disable = l2c_disable,
++ .sync = l2c210_sync,
++ .resume = l2c210_resume,
++ },
++};
++
++/*
++ * L2C-220 specific code.
++ *
++ * All operations are background operations: they have to be waited for.
++ * Conflicting requests generate a slave error (which will cause an
++ * imprecise abort.) Never uses sync_reg_offset, so we hard-code the
++ * sync register here.
++ *
++ * However, we can re-use the l2c210_resume call.
++ */
++static inline void __l2c220_cache_sync(void __iomem *base)
++{
++ writel_relaxed(0, base + L2X0_CACHE_SYNC);
++ l2c_wait_mask(base + L2X0_CACHE_SYNC, 1);
++}
++
++static void l2c220_op_way(void __iomem *base, unsigned reg)
++{
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&l2x0_lock, flags);
++ __l2c_op_way(base + reg);
++ __l2c220_cache_sync(base);
++ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++}
++
++static unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start,
++ unsigned long end, unsigned long flags)
++{
++ raw_spinlock_t *lock = &l2x0_lock;
++
+ while (start < end) {
+ unsigned long blk_end = start + min(end - start, 4096UL);
+
+ while (start < blk_end) {
+- l2x0_inv_line(start);
++ l2c_wait_mask(reg, 1);
++ writel_relaxed(start, reg);
+ start += CACHE_LINE_SIZE;
+ }
+
+ if (blk_end < end) {
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+- raw_spin_lock_irqsave(&l2x0_lock, flags);
++ raw_spin_unlock_irqrestore(lock, flags);
++ raw_spin_lock_irqsave(lock, flags);
+ }
+ }
+- cache_wait(base + L2X0_INV_LINE_PA, 1);
+- cache_sync();
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++
++ return flags;
+ }
+
+-static void l2x0_clean_range(unsigned long start, unsigned long end)
++static void l2c220_inv_range(unsigned long start, unsigned long end)
+ {
+ void __iomem *base = l2x0_base;
+ unsigned long flags;
+
+- if ((end - start) >= l2x0_size) {
+- l2x0_clean_all();
+- return;
+- }
+-
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
+- start &= ~(CACHE_LINE_SIZE - 1);
+- while (start < end) {
+- unsigned long blk_end = start + min(end - start, 4096UL);
+-
+- while (start < blk_end) {
+- l2x0_clean_line(start);
++ if ((start | end) & (CACHE_LINE_SIZE - 1)) {
++ if (start & (CACHE_LINE_SIZE - 1)) {
++ start &= ~(CACHE_LINE_SIZE - 1);
++ writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
+ start += CACHE_LINE_SIZE;
+ }
+
+- if (blk_end < end) {
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+- raw_spin_lock_irqsave(&l2x0_lock, flags);
++ if (end & (CACHE_LINE_SIZE - 1)) {
++ end &= ~(CACHE_LINE_SIZE - 1);
++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
++ writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
+ }
+ }
+- cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+- cache_sync();
++
++ flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA,
++ start, end, flags);
++ l2c_wait_mask(base + L2X0_INV_LINE_PA, 1);
++ __l2c220_cache_sync(base);
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+ }
+
+-static void l2x0_flush_range(unsigned long start, unsigned long end)
++static void l2c220_clean_range(unsigned long start, unsigned long end)
+ {
+ void __iomem *base = l2x0_base;
+ unsigned long flags;
+
++ start &= ~(CACHE_LINE_SIZE - 1);
+ if ((end - start) >= l2x0_size) {
+- l2x0_flush_all();
++ l2c220_op_way(base, L2X0_CLEAN_WAY);
+ return;
+ }
+
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
++ flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA,
++ start, end, flags);
++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
++ __l2c220_cache_sync(base);
++ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++}
++
++static void l2c220_flush_range(unsigned long start, unsigned long end)
++{
++ void __iomem *base = l2x0_base;
++ unsigned long flags;
++
+ start &= ~(CACHE_LINE_SIZE - 1);
++ if ((end - start) >= l2x0_size) {
++ l2c220_op_way(base, L2X0_CLEAN_INV_WAY);
++ return;
++ }
++
++ raw_spin_lock_irqsave(&l2x0_lock, flags);
++ flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA,
++ start, end, flags);
++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
++ __l2c220_cache_sync(base);
++ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++}
++
++static void l2c220_flush_all(void)
++{
++ l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY);
++}
++
++static void l2c220_sync(void)
++{
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&l2x0_lock, flags);
++ __l2c220_cache_sync(l2x0_base);
++ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++}
++
++static void l2c220_enable(void __iomem *base, u32 aux, unsigned num_lock)
++{
++ /*
++ * Always enable non-secure access to the lockdown registers -
++ * we write to them as part of the L2C enable sequence so they
++ * need to be accessible.
++ */
++ aux |= L220_AUX_CTRL_NS_LOCKDOWN;
++
++ l2c_enable(base, aux, num_lock);
++}
++
++static const struct l2c_init_data l2c220_data = {
++ .type = "L2C-220",
++ .way_size_0 = SZ_8K,
++ .num_lock = 1,
++ .enable = l2c220_enable,
++ .save = l2c_save,
++ .outer_cache = {
++ .inv_range = l2c220_inv_range,
++ .clean_range = l2c220_clean_range,
++ .flush_range = l2c220_flush_range,
++ .flush_all = l2c220_flush_all,
++ .disable = l2c_disable,
++ .sync = l2c220_sync,
++ .resume = l2c210_resume,
++ },
++};
++
++/*
++ * L2C-310 specific code.
++ *
++ * Very similar to L2C-210, the PA, set/way and sync operations are atomic,
++ * and the way operations are all background tasks. However, issuing an
++ * operation while a background operation is in progress results in a
++ * SLVERR response. We can reuse:
++ *
++ * __l2c210_cache_sync (using sync_reg_offset)
++ * l2c210_sync
++ * l2c210_inv_range (if 588369 is not applicable)
++ * l2c210_clean_range
++ * l2c210_flush_range (if 588369 is not applicable)
++ * l2c210_flush_all (if 727915 is not applicable)
++ *
++ * Errata:
++ * 588369: PL310 R0P0->R1P0, fixed R2P0.
++ * Affects: all clean+invalidate operations
++ * clean and invalidate skips the invalidate step, so we need to issue
++ * separate operations. We also require the above debug workaround
++ * enclosing this code fragment on affected parts. On unaffected parts,
++ * we must not use this workaround without the debug register writes
++ * to avoid exposing a problem similar to 727915.
++ *
++ * 727915: PL310 R2P0->R3P0, fixed R3P1.
++ * Affects: clean+invalidate by way
++ * clean and invalidate by way runs in the background, and a store can
++ * hit the line between the clean operation and invalidate operation,
++ * resulting in the store being lost.
++ *
++ * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2.
++ * Affects: 8x64-bit (double fill) line fetches
++ * double fill line fetches can fail to cause dirty data to be evicted
++ * from the cache before the new data overwrites the second line.
++ *
++ * 753970: PL310 R3P0, fixed R3P1.
++ * Affects: sync
++ * prevents merging writes after the sync operation, until another L2C
++ * operation is performed (or a number of other conditions.)
++ *
++ * 769419: PL310 R0P0->R3P1, fixed R3P2.
++ * Affects: store buffer
++ * store buffer is not automatically drained.
++ */
++static void l2c310_inv_range_erratum(unsigned long start, unsigned long end)
++{
++ void __iomem *base = l2x0_base;
++
++ if ((start | end) & (CACHE_LINE_SIZE - 1)) {
++ unsigned long flags;
++
++ /* Erratum 588369 for both clean+invalidate operations */
++ raw_spin_lock_irqsave(&l2x0_lock, flags);
++ l2c_set_debug(base, 0x03);
++
++ if (start & (CACHE_LINE_SIZE - 1)) {
++ start &= ~(CACHE_LINE_SIZE - 1);
++ writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
++ writel_relaxed(start, base + L2X0_INV_LINE_PA);
++ start += CACHE_LINE_SIZE;
++ }
++
++ if (end & (CACHE_LINE_SIZE - 1)) {
++ end &= ~(CACHE_LINE_SIZE - 1);
++ writel_relaxed(end, base + L2X0_CLEAN_LINE_PA);
++ writel_relaxed(end, base + L2X0_INV_LINE_PA);
++ }
++
++ l2c_set_debug(base, 0x00);
++ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++ }
++
++ __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
++ __l2c210_cache_sync(base);
++}
++
++static void l2c310_flush_range_erratum(unsigned long start, unsigned long end)
++{
++ raw_spinlock_t *lock = &l2x0_lock;
++ unsigned long flags;
++ void __iomem *base = l2x0_base;
++
++ raw_spin_lock_irqsave(lock, flags);
+ while (start < end) {
+ unsigned long blk_end = start + min(end - start, 4096UL);
+
+- debug_writel(0x03);
++ l2c_set_debug(base, 0x03);
+ while (start < blk_end) {
+- l2x0_flush_line(start);
++ writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
++ writel_relaxed(start, base + L2X0_INV_LINE_PA);
+ start += CACHE_LINE_SIZE;
+ }
+- debug_writel(0x00);
++ l2c_set_debug(base, 0x00);
+
+ if (blk_end < end) {
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+- raw_spin_lock_irqsave(&l2x0_lock, flags);
++ raw_spin_unlock_irqrestore(lock, flags);
++ raw_spin_lock_irqsave(lock, flags);
+ }
+ }
+- cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+- cache_sync();
+- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
++ raw_spin_unlock_irqrestore(lock, flags);
++ __l2c210_cache_sync(base);
+ }
+
+-static void l2x0_disable(void)
++static void l2c310_flush_all_erratum(void)
+ {
++ void __iomem *base = l2x0_base;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
+- __l2x0_flush_all();
+- writel_relaxed(0, l2x0_base + L2X0_CTRL);
+- dsb(st);
++ l2c_set_debug(base, 0x03);
++ __l2c_op_way(base + L2X0_CLEAN_INV_WAY);
++ l2c_set_debug(base, 0x00);
++ __l2c210_cache_sync(base);
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+ }
+
+-static void l2x0_unlock(u32 cache_id)
++static void __init l2c310_save(void __iomem *base)
+ {
+- int lockregs;
+- int i;
++ unsigned revision;
+
+- switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+- case L2X0_CACHE_ID_PART_L310:
+- lockregs = 8;
+- break;
+- case AURORA_CACHE_ID:
+- lockregs = 4;
++ l2c_save(base);
++
++ l2x0_saved_regs.tag_latency = readl_relaxed(base +
++ L310_TAG_LATENCY_CTRL);
++ l2x0_saved_regs.data_latency = readl_relaxed(base +
++ L310_DATA_LATENCY_CTRL);
++ l2x0_saved_regs.filter_end = readl_relaxed(base +
++ L310_ADDR_FILTER_END);
++ l2x0_saved_regs.filter_start = readl_relaxed(base +
++ L310_ADDR_FILTER_START);
++
++ revision = readl_relaxed(base + L2X0_CACHE_ID) &
++ L2X0_CACHE_ID_RTL_MASK;
++
++ /* From r2p0, there is Prefetch offset/control register */
++ if (revision >= L310_CACHE_ID_RTL_R2P0)
++ l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
++ L310_PREFETCH_CTRL);
++
++ /* From r3p0, there is Power control register */
++ if (revision >= L310_CACHE_ID_RTL_R3P0)
++ l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
++ L310_POWER_CTRL);
++}
++
++static void l2c310_resume(void)
++{
++ void __iomem *base = l2x0_base;
++
++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
++ unsigned revision;
++
++ /* restore pl310 setup */
++ writel_relaxed(l2x0_saved_regs.tag_latency,
++ base + L310_TAG_LATENCY_CTRL);
++ writel_relaxed(l2x0_saved_regs.data_latency,
++ base + L310_DATA_LATENCY_CTRL);
++ writel_relaxed(l2x0_saved_regs.filter_end,
++ base + L310_ADDR_FILTER_END);
++ writel_relaxed(l2x0_saved_regs.filter_start,
++ base + L310_ADDR_FILTER_START);
++
++ revision = readl_relaxed(base + L2X0_CACHE_ID) &
++ L2X0_CACHE_ID_RTL_MASK;
++
++ if (revision >= L310_CACHE_ID_RTL_R2P0)
++ l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base,
++ L310_PREFETCH_CTRL);
++ if (revision >= L310_CACHE_ID_RTL_R3P0)
++ l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base,
++ L310_POWER_CTRL);
++
++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
++
++ /* Re-enable full-line-of-zeros for Cortex-A9 */
++ if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO)
++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1));
++ }
++}
++
++static int l2c310_cpu_enable_flz(struct notifier_block *nb, unsigned long act, void *data)
++{
++ switch (act & ~CPU_TASKS_FROZEN) {
++ case CPU_STARTING:
++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1));
+ break;
+- default:
+- /* L210 and unknown types */
+- lockregs = 1;
++ case CPU_DYING:
++ set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1)));
+ break;
+ }
++ return NOTIFY_OK;
++}
+
+- for (i = 0; i < lockregs; i++) {
+- writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
+- i * L2X0_LOCKDOWN_STRIDE);
+- writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
+- i * L2X0_LOCKDOWN_STRIDE);
++static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock)
++{
++ unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK;
++ bool cortex_a9 = read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9;
++
++ if (rev >= L310_CACHE_ID_RTL_R2P0) {
++ if (cortex_a9) {
++ aux |= L310_AUX_CTRL_EARLY_BRESP;
++ pr_info("L2C-310 enabling early BRESP for Cortex-A9\n");
++ } else if (aux & L310_AUX_CTRL_EARLY_BRESP) {
++ pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n");
++ aux &= ~L310_AUX_CTRL_EARLY_BRESP;
++ }
++ }
++
++ if (cortex_a9) {
++ u32 aux_cur = readl_relaxed(base + L2X0_AUX_CTRL);
++ u32 acr = get_auxcr();
++
++ pr_debug("Cortex-A9 ACR=0x%08x\n", acr);
++
++ if (acr & BIT(3) && !(aux_cur & L310_AUX_CTRL_FULL_LINE_ZERO))
++ pr_err("L2C-310: full line of zeros enabled in Cortex-A9 but not L2C-310 - invalid\n");
++
++ if (aux & L310_AUX_CTRL_FULL_LINE_ZERO && !(acr & BIT(3)))
++ pr_err("L2C-310: enabling full line of zeros but not enabled in Cortex-A9\n");
++
++ if (!(aux & L310_AUX_CTRL_FULL_LINE_ZERO) && !outer_cache.write_sec) {
++ aux |= L310_AUX_CTRL_FULL_LINE_ZERO;
++ pr_info("L2C-310 full line of zeros enabled for Cortex-A9\n");
++ }
++ } else if (aux & (L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP)) {
++ pr_err("L2C-310: disabling Cortex-A9 specific feature bits\n");
++ aux &= ~(L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP);
++ }
++
++ if (aux & (L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH)) {
++ u32 prefetch = readl_relaxed(base + L310_PREFETCH_CTRL);
++
++ pr_info("L2C-310 %s%s prefetch enabled, offset %u lines\n",
++ aux & L310_AUX_CTRL_INSTR_PREFETCH ? "I" : "",
++ aux & L310_AUX_CTRL_DATA_PREFETCH ? "D" : "",
++ 1 + (prefetch & L310_PREFETCH_CTRL_OFFSET_MASK));
++ }
++
++ /* r3p0 or later has power control register */
++ if (rev >= L310_CACHE_ID_RTL_R3P0) {
++ u32 power_ctrl;
++
++ l2c_write_sec(L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN,
++ base, L310_POWER_CTRL);
++ power_ctrl = readl_relaxed(base + L310_POWER_CTRL);
++ pr_info("L2C-310 dynamic clock gating %sabled, standby mode %sabled\n",
++ power_ctrl & L310_DYNAMIC_CLK_GATING_EN ? "en" : "dis",
++ power_ctrl & L310_STNDBY_MODE_EN ? "en" : "dis");
++ }
++
++ /*
++ * Always enable non-secure access to the lockdown registers -
++ * we write to them as part of the L2C enable sequence so they
++ * need to be accessible.
++ */
++ aux |= L310_AUX_CTRL_NS_LOCKDOWN;
++
++ l2c_enable(base, aux, num_lock);
++
++ if (aux & L310_AUX_CTRL_FULL_LINE_ZERO) {
++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1));
++ cpu_notifier(l2c310_cpu_enable_flz, 0);
+ }
+ }
+
+-void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
++static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
++ struct outer_cache_fns *fns)
+ {
+- u32 aux;
+- u32 cache_id;
+- u32 way_size = 0;
+- int ways;
+- int way_size_shift = L2X0_WAY_SIZE_SHIFT;
+- const char *type;
++ unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK;
++ const char *errata[8];
++ unsigned n = 0;
+
+- l2x0_base = base;
+- if (cache_id_part_number_from_dt)
+- cache_id = cache_id_part_number_from_dt;
+- else
+- cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
+- aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
++ if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
++ revision < L310_CACHE_ID_RTL_R2P0 &&
++ /* For bcm compatibility */
++ fns->inv_range == l2c210_inv_range) {
++ fns->inv_range = l2c310_inv_range_erratum;
++ fns->flush_range = l2c310_flush_range_erratum;
++ errata[n++] = "588369";
++ }
++
++ if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) &&
++ revision >= L310_CACHE_ID_RTL_R2P0 &&
++ revision < L310_CACHE_ID_RTL_R3P1) {
++ fns->flush_all = l2c310_flush_all_erratum;
++ errata[n++] = "727915";
++ }
++
++ if (revision >= L310_CACHE_ID_RTL_R3P0 &&
++ revision < L310_CACHE_ID_RTL_R3P2) {
++ u32 val = readl_relaxed(base + L310_PREFETCH_CTRL);
++ /* I don't think bit23 is required here... but iMX6 does so */
++ if (val & (BIT(30) | BIT(23))) {
++ val &= ~(BIT(30) | BIT(23));
++ l2c_write_sec(val, base, L310_PREFETCH_CTRL);
++ errata[n++] = "752271";
++ }
++ }
++
++ if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
++ revision == L310_CACHE_ID_RTL_R3P0) {
++ sync_reg_offset = L2X0_DUMMY_REG;
++ errata[n++] = "753970";
++ }
++
++ if (IS_ENABLED(CONFIG_PL310_ERRATA_769419))
++ errata[n++] = "769419";
++
++ if (n) {
++ unsigned i;
++
++ pr_info("L2C-310 errat%s", n > 1 ? "a" : "um");
++ for (i = 0; i < n; i++)
++ pr_cont(" %s", errata[i]);
++ pr_cont(" enabled\n");
++ }
++}
++
++static void l2c310_disable(void)
++{
++ /*
++ * If full-line-of-zeros is enabled, we must first disable it in the
++ * Cortex-A9 auxiliary control register before disabling the L2 cache.
++ */
++ if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO)
++ set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1)));
+
++ l2c_disable();
++}
++
++static const struct l2c_init_data l2c310_init_fns __initconst = {
++ .type = "L2C-310",
++ .way_size_0 = SZ_8K,
++ .num_lock = 8,
++ .enable = l2c310_enable,
++ .fixup = l2c310_fixup,
++ .save = l2c310_save,
++ .outer_cache = {
++ .inv_range = l2c210_inv_range,
++ .clean_range = l2c210_clean_range,
++ .flush_range = l2c210_flush_range,
++ .flush_all = l2c210_flush_all,
++ .disable = l2c310_disable,
++ .sync = l2c210_sync,
++ .resume = l2c310_resume,
++ },
++};
++
++static void __init __l2c_init(const struct l2c_init_data *data,
++ u32 aux_val, u32 aux_mask, u32 cache_id)
++{
++ struct outer_cache_fns fns;
++ unsigned way_size_bits, ways;
++ u32 aux, old_aux;
++
++ /*
++ * Sanity check the aux values. aux_mask is the bits we preserve
++ * from reading the hardware register, and aux_val is the bits we
++ * set.
++ */
++ if (aux_val & aux_mask)
++ pr_alert("L2C: platform provided aux values permit register corruption.\n");
++
++ old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+ aux &= aux_mask;
+ aux |= aux_val;
+
++ if (old_aux != aux)
++ pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n",
++ old_aux, aux);
++
+ /* Determine the number of ways */
+ switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+ case L2X0_CACHE_ID_PART_L310:
++ if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16))
++ pr_warn("L2C: DT/platform tries to modify or specify cache size\n");
+ if (aux & (1 << 16))
+ ways = 16;
+ else
+ ways = 8;
+- type = "L310";
+-#ifdef CONFIG_PL310_ERRATA_753970
+- /* Unmapped register. */
+- sync_reg_offset = L2X0_DUMMY_REG;
+-#endif
+- if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
+- outer_cache.set_debug = pl310_set_debug;
+ break;
++
+ case L2X0_CACHE_ID_PART_L210:
++ case L2X0_CACHE_ID_PART_L220:
+ ways = (aux >> 13) & 0xf;
+- type = "L210";
+ break;
+
+ case AURORA_CACHE_ID:
+- sync_reg_offset = AURORA_SYNC_REG;
+ ways = (aux >> 13) & 0xf;
+ ways = 2 << ((ways + 1) >> 2);
+- way_size_shift = AURORA_WAY_SIZE_SHIFT;
+- type = "Aurora";
+ break;
++
+ default:
+ /* Assume unknown chips have 8 ways */
+ ways = 8;
+- type = "L2x0 series";
+ break;
+ }
+
+ l2x0_way_mask = (1 << ways) - 1;
+
+ /*
+- * L2 cache Size = Way size * Number of ways
++ * way_size_0 is the size that a way_size value of zero would be
++ * given the calculation: way_size = way_size_0 << way_size_bits.
++ * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k,
++ * then way_size_0 would be 8k.
++ *
++ * L2 cache size = number of ways * way size.
++ */
++ way_size_bits = (aux & L2C_AUX_CTRL_WAY_SIZE_MASK) >>
++ L2C_AUX_CTRL_WAY_SIZE_SHIFT;
++ l2x0_size = ways * (data->way_size_0 << way_size_bits);
++
++ fns = data->outer_cache;
++ fns.write_sec = outer_cache.write_sec;
++ if (data->fixup)
++ data->fixup(l2x0_base, cache_id, &fns);
++
++ /*
++ * Check if l2x0 controller is already enabled. If we are booting
++ * in non-secure mode accessing the below registers will fault.
+ */
+- way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
+- way_size = 1 << (way_size + way_size_shift);
++ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
++ data->enable(l2x0_base, aux, data->num_lock);
+
+- l2x0_size = ways * way_size * SZ_1K;
++ outer_cache = fns;
+
+ /*
+- * Check if l2x0 controller is already enabled.
+- * If you are booting from non-secure mode
+- * accessing the below registers will fault.
++ * It is strange to save the register state before initialisation,
++ * but hey, this is what the DT implementations decided to do.
+ */
+- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+- /* Make sure that I&D is not locked down when starting */
+- l2x0_unlock(cache_id);
++ if (data->save)
++ data->save(l2x0_base);
++
++ /* Re-read it in case some bits are reserved. */
++ aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
++
++ pr_info("%s cache controller enabled, %d ways, %d kB\n",
++ data->type, ways, l2x0_size >> 10);
++ pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n",
++ data->type, cache_id, aux);
++}
+
+- /* l2x0 controller is disabled */
+- writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL);
++void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
++{
++ const struct l2c_init_data *data;
++ u32 cache_id;
+
+- l2x0_inv_all();
++ l2x0_base = base;
+
+- /* enable L2X0 */
+- writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
++ cache_id = readl_relaxed(base + L2X0_CACHE_ID);
++
++ switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
++ default:
++ case L2X0_CACHE_ID_PART_L210:
++ data = &l2c210_data;
++ break;
++
++ case L2X0_CACHE_ID_PART_L220:
++ data = &l2c220_data;
++ break;
++
++ case L2X0_CACHE_ID_PART_L310:
++ data = &l2c310_init_fns;
++ break;
+ }
+
+- /* Re-read it in case some bits are reserved. */
+- aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
++ __l2c_init(data, aux_val, aux_mask, cache_id);
++}
++
++#ifdef CONFIG_OF
++static int l2_wt_override;
++
++/* Aurora don't have the cache ID register available, so we have to
++ * pass it though the device tree */
++static u32 cache_id_part_number_from_dt;
++
++static void __init l2x0_of_parse(const struct device_node *np,
++ u32 *aux_val, u32 *aux_mask)
++{
++ u32 data[2] = { 0, 0 };
++ u32 tag = 0;
++ u32 dirty = 0;
++ u32 val = 0, mask = 0;
++
++ of_property_read_u32(np, "arm,tag-latency", &tag);
++ if (tag) {
++ mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
++ val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
++ }
++
++ of_property_read_u32_array(np, "arm,data-latency",
++ data, ARRAY_SIZE(data));
++ if (data[0] && data[1]) {
++ mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
++ L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
++ val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
++ ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
++ }
++
++ of_property_read_u32(np, "arm,dirty-latency", &dirty);
++ if (dirty) {
++ mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
++ val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
++ }
+
+- /* Save the value for resuming. */
+- l2x0_saved_regs.aux_ctrl = aux;
++ *aux_val &= ~mask;
++ *aux_val |= val;
++ *aux_mask &= ~mask;
++}
++
++static const struct l2c_init_data of_l2c210_data __initconst = {
++ .type = "L2C-210",
++ .way_size_0 = SZ_8K,
++ .num_lock = 1,
++ .of_parse = l2x0_of_parse,
++ .enable = l2c_enable,
++ .save = l2c_save,
++ .outer_cache = {
++ .inv_range = l2c210_inv_range,
++ .clean_range = l2c210_clean_range,
++ .flush_range = l2c210_flush_range,
++ .flush_all = l2c210_flush_all,
++ .disable = l2c_disable,
++ .sync = l2c210_sync,
++ .resume = l2c210_resume,
++ },
++};
++
++static const struct l2c_init_data of_l2c220_data __initconst = {
++ .type = "L2C-220",
++ .way_size_0 = SZ_8K,
++ .num_lock = 1,
++ .of_parse = l2x0_of_parse,
++ .enable = l2c220_enable,
++ .save = l2c_save,
++ .outer_cache = {
++ .inv_range = l2c220_inv_range,
++ .clean_range = l2c220_clean_range,
++ .flush_range = l2c220_flush_range,
++ .flush_all = l2c220_flush_all,
++ .disable = l2c_disable,
++ .sync = l2c220_sync,
++ .resume = l2c210_resume,
++ },
++};
++
++static void __init l2c310_of_parse(const struct device_node *np,
++ u32 *aux_val, u32 *aux_mask)
++{
++ u32 data[3] = { 0, 0, 0 };
++ u32 tag[3] = { 0, 0, 0 };
++ u32 filter[2] = { 0, 0 };
++
++ of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
++ if (tag[0] && tag[1] && tag[2])
++ writel_relaxed(
++ L310_LATENCY_CTRL_RD(tag[0] - 1) |
++ L310_LATENCY_CTRL_WR(tag[1] - 1) |
++ L310_LATENCY_CTRL_SETUP(tag[2] - 1),
++ l2x0_base + L310_TAG_LATENCY_CTRL);
++
++ of_property_read_u32_array(np, "arm,data-latency",
++ data, ARRAY_SIZE(data));
++ if (data[0] && data[1] && data[2])
++ writel_relaxed(
++ L310_LATENCY_CTRL_RD(data[0] - 1) |
++ L310_LATENCY_CTRL_WR(data[1] - 1) |
++ L310_LATENCY_CTRL_SETUP(data[2] - 1),
++ l2x0_base + L310_DATA_LATENCY_CTRL);
+
+- if (!of_init) {
+- outer_cache.inv_range = l2x0_inv_range;
+- outer_cache.clean_range = l2x0_clean_range;
+- outer_cache.flush_range = l2x0_flush_range;
+- outer_cache.sync = l2x0_cache_sync;
+- outer_cache.flush_all = l2x0_flush_all;
+- outer_cache.inv_all = l2x0_inv_all;
+- outer_cache.disable = l2x0_disable;
+- }
+-
+- pr_info("%s cache controller enabled\n", type);
+- pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n",
+- ways, cache_id, aux, l2x0_size >> 10);
++ of_property_read_u32_array(np, "arm,filter-ranges",
++ filter, ARRAY_SIZE(filter));
++ if (filter[1]) {
++ writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M),
++ l2x0_base + L310_ADDR_FILTER_END);
++ writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN,
++ l2x0_base + L310_ADDR_FILTER_START);
++ }
+ }
+
+-#ifdef CONFIG_OF
+-static int l2_wt_override;
++static const struct l2c_init_data of_l2c310_data __initconst = {
++ .type = "L2C-310",
++ .way_size_0 = SZ_8K,
++ .num_lock = 8,
++ .of_parse = l2c310_of_parse,
++ .enable = l2c310_enable,
++ .fixup = l2c310_fixup,
++ .save = l2c310_save,
++ .outer_cache = {
++ .inv_range = l2c210_inv_range,
++ .clean_range = l2c210_clean_range,
++ .flush_range = l2c210_flush_range,
++ .flush_all = l2c210_flush_all,
++ .disable = l2c310_disable,
++ .sync = l2c210_sync,
++ .resume = l2c310_resume,
++ },
++};
+
+ /*
+ * Note that the end addresses passed to Linux primitives are
+@@ -524,6 +1166,100 @@
+ }
+ }
+
++static void aurora_save(void __iomem *base)
++{
++ l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
++ l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL);
++}
++
++static void aurora_resume(void)
++{
++ void __iomem *base = l2x0_base;
++
++ if (!(readl(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
++ writel_relaxed(l2x0_saved_regs.aux_ctrl, base + L2X0_AUX_CTRL);
++ writel_relaxed(l2x0_saved_regs.ctrl, base + L2X0_CTRL);
++ }
++}
++
++/*
++ * For Aurora cache in no outer mode, enable via the CP15 coprocessor
++ * broadcasting of cache commands to L2.
++ */
++static void __init aurora_enable_no_outer(void __iomem *base, u32 aux,
++ unsigned num_lock)
++{
++ u32 u;
++
++ asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u));
++ u |= AURORA_CTRL_FW; /* Set the FW bit */
++ asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u));
++
++ isb();
++
++ l2c_enable(base, aux, num_lock);
++}
++
++static void __init aurora_fixup(void __iomem *base, u32 cache_id,
++ struct outer_cache_fns *fns)
++{
++ sync_reg_offset = AURORA_SYNC_REG;
++}
++
++static void __init aurora_of_parse(const struct device_node *np,
++ u32 *aux_val, u32 *aux_mask)
++{
++ u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
++ u32 mask = AURORA_ACR_REPLACEMENT_MASK;
++
++ of_property_read_u32(np, "cache-id-part",
++ &cache_id_part_number_from_dt);
++
++ /* Determine and save the write policy */
++ l2_wt_override = of_property_read_bool(np, "wt-override");
++
++ if (l2_wt_override) {
++ val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
++ mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
++ }
++
++ *aux_val &= ~mask;
++ *aux_val |= val;
++ *aux_mask &= ~mask;
++}
++
++static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
++ .type = "Aurora",
++ .way_size_0 = SZ_4K,
++ .num_lock = 4,
++ .of_parse = aurora_of_parse,
++ .enable = l2c_enable,
++ .fixup = aurora_fixup,
++ .save = aurora_save,
++ .outer_cache = {
++ .inv_range = aurora_inv_range,
++ .clean_range = aurora_clean_range,
++ .flush_range = aurora_flush_range,
++ .flush_all = l2x0_flush_all,
++ .disable = l2x0_disable,
++ .sync = l2x0_cache_sync,
++ .resume = aurora_resume,
++ },
++};
++
++static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
++ .type = "Aurora",
++ .way_size_0 = SZ_4K,
++ .num_lock = 4,
++ .of_parse = aurora_of_parse,
++ .enable = aurora_enable_no_outer,
++ .fixup = aurora_fixup,
++ .save = aurora_save,
++ .outer_cache = {
++ .resume = aurora_resume,
++ },
++};
++
+ /*
+ * For certain Broadcom SoCs, depending on the address range, different offsets
+ * need to be added to the address before passing it to L2 for
+@@ -588,16 +1324,16 @@
+
+ /* normal case, no cross section between start and end */
+ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
+- l2x0_inv_range(new_start, new_end);
++ l2c210_inv_range(new_start, new_end);
+ return;
+ }
+
+ /* They cross sections, so it can only be a cross from section
+ * 2 to section 3
+ */
+- l2x0_inv_range(new_start,
++ l2c210_inv_range(new_start,
+ bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
+- l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
++ l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+ new_end);
+ }
+
+@@ -610,26 +1346,21 @@
+ if (unlikely(end <= start))
+ return;
+
+- if ((end - start) >= l2x0_size) {
+- l2x0_clean_all();
+- return;
+- }
+-
+ new_start = bcm_l2_phys_addr(start);
+ new_end = bcm_l2_phys_addr(end);
+
+ /* normal case, no cross section between start and end */
+ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
+- l2x0_clean_range(new_start, new_end);
++ l2c210_clean_range(new_start, new_end);
+ return;
+ }
+
+ /* They cross sections, so it can only be a cross from section
+ * 2 to section 3
+ */
+- l2x0_clean_range(new_start,
++ l2c210_clean_range(new_start,
+ bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
+- l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
++ l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+ new_end);
+ }
+
+@@ -643,7 +1374,7 @@
+ return;
+
+ if ((end - start) >= l2x0_size) {
+- l2x0_flush_all();
++ outer_cache.flush_all();
+ return;
+ }
+
+@@ -652,283 +1383,67 @@
+
+ /* normal case, no cross section between start and end */
+ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
+- l2x0_flush_range(new_start, new_end);
++ l2c210_flush_range(new_start, new_end);
+ return;
+ }
+
+ /* They cross sections, so it can only be a cross from section
+ * 2 to section 3
+ */
+- l2x0_flush_range(new_start,
++ l2c210_flush_range(new_start,
+ bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
+- l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
++ l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+ new_end);
+ }
+
+-static void __init l2x0_of_setup(const struct device_node *np,
+- u32 *aux_val, u32 *aux_mask)
+-{
+- u32 data[2] = { 0, 0 };
+- u32 tag = 0;
+- u32 dirty = 0;
+- u32 val = 0, mask = 0;
+-
+- of_property_read_u32(np, "arm,tag-latency", &tag);
+- if (tag) {
+- mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
+- val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
+- }
+-
+- of_property_read_u32_array(np, "arm,data-latency",
+- data, ARRAY_SIZE(data));
+- if (data[0] && data[1]) {
+- mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
+- L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
+- val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
+- ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
+- }
+-
+- of_property_read_u32(np, "arm,dirty-latency", &dirty);
+- if (dirty) {
+- mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
+- val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
+- }
+-
+- *aux_val &= ~mask;
+- *aux_val |= val;
+- *aux_mask &= ~mask;
+-}
+-
+-static void __init pl310_of_setup(const struct device_node *np,
+- u32 *aux_val, u32 *aux_mask)
+-{
+- u32 data[3] = { 0, 0, 0 };
+- u32 tag[3] = { 0, 0, 0 };
+- u32 filter[2] = { 0, 0 };
+-
+- of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
+- if (tag[0] && tag[1] && tag[2])
+- writel_relaxed(
+- ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
+- ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
+- ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
+- l2x0_base + L2X0_TAG_LATENCY_CTRL);
+-
+- of_property_read_u32_array(np, "arm,data-latency",
+- data, ARRAY_SIZE(data));
+- if (data[0] && data[1] && data[2])
+- writel_relaxed(
+- ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
+- ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
+- ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
+- l2x0_base + L2X0_DATA_LATENCY_CTRL);
+-
+- of_property_read_u32_array(np, "arm,filter-ranges",
+- filter, ARRAY_SIZE(filter));
+- if (filter[1]) {
+- writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M),
+- l2x0_base + L2X0_ADDR_FILTER_END);
+- writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN,
+- l2x0_base + L2X0_ADDR_FILTER_START);
+- }
+-}
+-
+-static void __init pl310_save(void)
+-{
+- u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+- L2X0_CACHE_ID_RTL_MASK;
+-
+- l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base +
+- L2X0_TAG_LATENCY_CTRL);
+- l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base +
+- L2X0_DATA_LATENCY_CTRL);
+- l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base +
+- L2X0_ADDR_FILTER_END);
+- l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base +
+- L2X0_ADDR_FILTER_START);
+-
+- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
+- /*
+- * From r2p0, there is Prefetch offset/control register
+- */
+- l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base +
+- L2X0_PREFETCH_CTRL);
+- /*
+- * From r3p0, there is Power control register
+- */
+- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
+- l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base +
+- L2X0_POWER_CTRL);
+- }
+-}
++/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */
++static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
++ .type = "BCM-L2C-310",
++ .way_size_0 = SZ_8K,
++ .num_lock = 8,
++ .of_parse = l2c310_of_parse,
++ .enable = l2c310_enable,
++ .save = l2c310_save,
++ .outer_cache = {
++ .inv_range = bcm_inv_range,
++ .clean_range = bcm_clean_range,
++ .flush_range = bcm_flush_range,
++ .flush_all = l2c210_flush_all,
++ .disable = l2c310_disable,
++ .sync = l2c210_sync,
++ .resume = l2c310_resume,
++ },
++};
+
+-static void aurora_save(void)
++static void __init tauros3_save(void __iomem *base)
+ {
+- l2x0_saved_regs.ctrl = readl_relaxed(l2x0_base + L2X0_CTRL);
+- l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+-}
++ l2c_save(base);
+
+-static void __init tauros3_save(void)
+-{
+ l2x0_saved_regs.aux2_ctrl =
+- readl_relaxed(l2x0_base + TAUROS3_AUX2_CTRL);
++ readl_relaxed(base + TAUROS3_AUX2_CTRL);
+ l2x0_saved_regs.prefetch_ctrl =
+- readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
+-}
+-
+-static void l2x0_resume(void)
+-{
+- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+- /* restore aux ctrl and enable l2 */
+- l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
+-
+- writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
+- L2X0_AUX_CTRL);
+-
+- l2x0_inv_all();
+-
+- writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
+- }
+-}
+-
+-static void pl310_resume(void)
+-{
+- u32 l2x0_revision;
+-
+- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+- /* restore pl310 setup */
+- writel_relaxed(l2x0_saved_regs.tag_latency,
+- l2x0_base + L2X0_TAG_LATENCY_CTRL);
+- writel_relaxed(l2x0_saved_regs.data_latency,
+- l2x0_base + L2X0_DATA_LATENCY_CTRL);
+- writel_relaxed(l2x0_saved_regs.filter_end,
+- l2x0_base + L2X0_ADDR_FILTER_END);
+- writel_relaxed(l2x0_saved_regs.filter_start,
+- l2x0_base + L2X0_ADDR_FILTER_START);
+-
+- l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+- L2X0_CACHE_ID_RTL_MASK;
+-
+- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
+- writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
+- l2x0_base + L2X0_PREFETCH_CTRL);
+- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
+- writel_relaxed(l2x0_saved_regs.pwr_ctrl,
+- l2x0_base + L2X0_POWER_CTRL);
+- }
+- }
+-
+- l2x0_resume();
+-}
+-
+-static void aurora_resume(void)
+-{
+- if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+- writel_relaxed(l2x0_saved_regs.aux_ctrl,
+- l2x0_base + L2X0_AUX_CTRL);
+- writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL);
+- }
++ readl_relaxed(base + L310_PREFETCH_CTRL);
+ }
+
+ static void tauros3_resume(void)
+ {
+- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
++ void __iomem *base = l2x0_base;
++
++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+ writel_relaxed(l2x0_saved_regs.aux2_ctrl,
+- l2x0_base + TAUROS3_AUX2_CTRL);
++ base + TAUROS3_AUX2_CTRL);
+ writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
+- l2x0_base + L2X0_PREFETCH_CTRL);
+- }
+-
+- l2x0_resume();
+-}
+-
+-static void __init aurora_broadcast_l2_commands(void)
+-{
+- __u32 u;
+- /* Enable Broadcasting of cache commands to L2*/
+- __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
+- u |= AURORA_CTRL_FW; /* Set the FW bit */
+- __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u));
+- isb();
+-}
+-
+-static void __init aurora_of_setup(const struct device_node *np,
+- u32 *aux_val, u32 *aux_mask)
+-{
+- u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
+- u32 mask = AURORA_ACR_REPLACEMENT_MASK;
++ base + L310_PREFETCH_CTRL);
+
+- of_property_read_u32(np, "cache-id-part",
+- &cache_id_part_number_from_dt);
+-
+- /* Determine and save the write policy */
+- l2_wt_override = of_property_read_bool(np, "wt-override");
+-
+- if (l2_wt_override) {
+- val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
+- mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
+ }
+-
+- *aux_val &= ~mask;
+- *aux_val |= val;
+- *aux_mask &= ~mask;
+ }
+
+-static const struct l2x0_of_data pl310_data = {
+- .setup = pl310_of_setup,
+- .save = pl310_save,
+- .outer_cache = {
+- .resume = pl310_resume,
+- .inv_range = l2x0_inv_range,
+- .clean_range = l2x0_clean_range,
+- .flush_range = l2x0_flush_range,
+- .sync = l2x0_cache_sync,
+- .flush_all = l2x0_flush_all,
+- .inv_all = l2x0_inv_all,
+- .disable = l2x0_disable,
+- },
+-};
+-
+-static const struct l2x0_of_data l2x0_data = {
+- .setup = l2x0_of_setup,
+- .save = NULL,
+- .outer_cache = {
+- .resume = l2x0_resume,
+- .inv_range = l2x0_inv_range,
+- .clean_range = l2x0_clean_range,
+- .flush_range = l2x0_flush_range,
+- .sync = l2x0_cache_sync,
+- .flush_all = l2x0_flush_all,
+- .inv_all = l2x0_inv_all,
+- .disable = l2x0_disable,
+- },
+-};
+-
+-static const struct l2x0_of_data aurora_with_outer_data = {
+- .setup = aurora_of_setup,
+- .save = aurora_save,
+- .outer_cache = {
+- .resume = aurora_resume,
+- .inv_range = aurora_inv_range,
+- .clean_range = aurora_clean_range,
+- .flush_range = aurora_flush_range,
+- .sync = l2x0_cache_sync,
+- .flush_all = l2x0_flush_all,
+- .inv_all = l2x0_inv_all,
+- .disable = l2x0_disable,
+- },
+-};
+-
+-static const struct l2x0_of_data aurora_no_outer_data = {
+- .setup = aurora_of_setup,
+- .save = aurora_save,
+- .outer_cache = {
+- .resume = aurora_resume,
+- },
+-};
+-
+-static const struct l2x0_of_data tauros3_data = {
+- .setup = NULL,
++static const struct l2c_init_data of_tauros3_data __initconst = {
++ .type = "Tauros3",
++ .way_size_0 = SZ_8K,
++ .num_lock = 8,
++ .enable = l2c_enable,
+ .save = tauros3_save,
+ /* Tauros3 broadcasts L1 cache operations to L2 */
+ .outer_cache = {
+@@ -936,43 +1451,26 @@
+ },
+ };
+
+-static const struct l2x0_of_data bcm_l2x0_data = {
+- .setup = pl310_of_setup,
+- .save = pl310_save,
+- .outer_cache = {
+- .resume = pl310_resume,
+- .inv_range = bcm_inv_range,
+- .clean_range = bcm_clean_range,
+- .flush_range = bcm_flush_range,
+- .sync = l2x0_cache_sync,
+- .flush_all = l2x0_flush_all,
+- .inv_all = l2x0_inv_all,
+- .disable = l2x0_disable,
+- },
+-};
+-
++#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
+ static const struct of_device_id l2x0_ids[] __initconst = {
+- { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data },
+- { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data },
+- { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data },
+- { .compatible = "bcm,bcm11351-a2-pl310-cache", /* deprecated name */
+- .data = (void *)&bcm_l2x0_data},
+- { .compatible = "brcm,bcm11351-a2-pl310-cache",
+- .data = (void *)&bcm_l2x0_data},
+- { .compatible = "marvell,aurora-outer-cache",
+- .data = (void *)&aurora_with_outer_data},
+- { .compatible = "marvell,aurora-system-cache",
+- .data = (void *)&aurora_no_outer_data},
+- { .compatible = "marvell,tauros3-cache",
+- .data = (void *)&tauros3_data },
++ L2C_ID("arm,l210-cache", of_l2c210_data),
++ L2C_ID("arm,l220-cache", of_l2c220_data),
++ L2C_ID("arm,pl310-cache", of_l2c310_data),
++ L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
++ L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data),
++ L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data),
++ L2C_ID("marvell,tauros3-cache", of_tauros3_data),
++ /* Deprecated IDs */
++ L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
+ {}
+ };
+
+ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
+ {
++ const struct l2c_init_data *data;
+ struct device_node *np;
+- const struct l2x0_of_data *data;
+ struct resource res;
++ u32 cache_id, old_aux;
+
+ np = of_find_matching_node(NULL, l2x0_ids);
+ if (!np)
+@@ -989,23 +1487,29 @@
+
+ data = of_match_node(l2x0_ids, np)->data;
+
+- /* L2 configuration can only be changed if the cache is disabled */
+- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+- if (data->setup)
+- data->setup(np, &aux_val, &aux_mask);
+-
+- /* For aurora cache in no outer mode select the
+- * correct mode using the coprocessor*/
+- if (data == &aurora_no_outer_data)
+- aurora_broadcast_l2_commands();
++ old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
++ if (old_aux != ((old_aux & aux_mask) | aux_val)) {
++ pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n",
++ old_aux, (old_aux & aux_mask) | aux_val);
++ } else if (aux_mask != ~0U && aux_val != 0) {
++ pr_alert("L2C: platform provided aux values match the hardware, so have no effect. Please remove them.\n");
+ }
+
+- if (data->save)
+- data->save();
++ /* All L2 caches are unified, so this property should be specified */
++ if (!of_property_read_bool(np, "cache-unified"))
++ pr_err("L2C: device tree omits to specify unified cache\n");
++
++ /* L2 configuration can only be changed if the cache is disabled */
++ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
++ if (data->of_parse)
++ data->of_parse(np, &aux_val, &aux_mask);
++
++ if (cache_id_part_number_from_dt)
++ cache_id = cache_id_part_number_from_dt;
++ else
++ cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
+
+- of_init = true;
+- memcpy(&outer_cache, &data->outer_cache, sizeof(outer_cache));
+- l2x0_init(l2x0_base, aux_val, aux_mask);
++ __l2c_init(data, aux_val, aux_mask, cache_id);
+
+ return 0;
+ }
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/Kconfig linux-3.15-rc6/arch/arm/mm/Kconfig
+--- linux-3.15-rc6.orig/arch/arm/mm/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mm/Kconfig 2014-05-23 11:26:48.280939953 +0200
+@@ -897,6 +897,57 @@
+ This option enables optimisations for the PL310 cache
+ controller.
+
++config PL310_ERRATA_588369
++ bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines"
++ depends on CACHE_L2X0
++ help
++ The PL310 L2 cache controller implements three types of Clean &
++ Invalidate maintenance operations: by Physical Address
++ (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC).
++ They are architecturally defined to behave as the execution of a
++ clean operation followed immediately by an invalidate operation,
++ both performing to the same memory location. This functionality
++ is not correctly implemented in PL310 as clean lines are not
++ invalidated as a result of these operations.
++
++config PL310_ERRATA_727915
++ bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption"
++ depends on CACHE_L2X0
++ help
++ PL310 implements the Clean & Invalidate by Way L2 cache maintenance
++ operation (offset 0x7FC). This operation runs in background so that
++ PL310 can handle normal accesses while it is in progress. Under very
++ rare circumstances, due to this erratum, write data can be lost when
++ PL310 treats a cacheable write transaction during a Clean &
++ Invalidate by Way operation.
++
++config PL310_ERRATA_753970
++ bool "PL310 errata: cache sync operation may be faulty"
++ depends on CACHE_PL310
++ help
++ This option enables the workaround for the 753970 PL310 (r3p0) erratum.
++
++ Under some condition the effect of cache sync operation on
++ the store buffer still remains when the operation completes.
++ This means that the store buffer is always asked to drain and
++ this prevents it from merging any further writes. The workaround
++ is to replace the normal offset of cache sync operation (0x730)
++ by another offset targeting an unmapped PL310 register 0x740.
++ This has the same effect as the cache sync operation: store buffer
++ drain and waiting for all buffers empty.
++
++config PL310_ERRATA_769419
++ bool "PL310 errata: no automatic Store Buffer drain"
++ depends on CACHE_L2X0
++ help
++ On revisions of the PL310 prior to r3p2, the Store Buffer does
++ not automatically drain. This can cause normal, non-cacheable
++ writes to be retained when the memory system is idle, leading
++ to suboptimal I/O performance for drivers using coherent DMA.
++ This option adds a write barrier to the cpu_idle loop so that,
++ on systems with an outer cache, the store buffer is drained
++ explicitly.
++
+ config CACHE_TAUROS2
+ bool "Enable the Tauros2 L2 cache controller"
+ depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4)
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/l2c-common.c linux-3.15-rc6/arch/arm/mm/l2c-common.c
+--- linux-3.15-rc6.orig/arch/arm/mm/l2c-common.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/arch/arm/mm/l2c-common.c 2014-05-23 11:26:48.284939966 +0200
+@@ -0,0 +1,20 @@
++/*
++ * Copyright (C) 2010 ARM Ltd.
++ * Written by Catalin Marinas <catalin.marinas@arm.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/bug.h>
++#include <linux/smp.h>
++#include <asm/outercache.h>
++
++void outer_disable(void)
++{
++ WARN_ON(!irqs_disabled());
++ WARN_ON(num_online_cpus() > 1);
++
++ if (outer_cache.disable)
++ outer_cache.disable();
++}
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/l2c-l2x0-resume.S linux-3.15-rc6/arch/arm/mm/l2c-l2x0-resume.S
+--- linux-3.15-rc6.orig/arch/arm/mm/l2c-l2x0-resume.S 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/arch/arm/mm/l2c-l2x0-resume.S 2014-05-23 11:26:48.284939966 +0200
+@@ -0,0 +1,58 @@
++/*
++ * L2C-310 early resume code. This can be used by platforms to restore
++ * the settings of their L2 cache controller before restoring the
++ * processor state.
++ *
++ * This code can only be used to if you are running in the secure world.
++ */
++#include <linux/linkage.h>
++#include <asm/hardware/cache-l2x0.h>
++
++ .text
++
++ENTRY(l2c310_early_resume)
++ adr r0, 1f
++ ldr r2, [r0]
++ add r0, r2, r0
++
++ ldmia r0, {r1, r2, r3, r4, r5, r6, r7, r8}
++ @ r1 = phys address of L2C-310 controller
++ @ r2 = aux_ctrl
++ @ r3 = tag_latency
++ @ r4 = data_latency
++ @ r5 = filter_start
++ @ r6 = filter_end
++ @ r7 = prefetch_ctrl
++ @ r8 = pwr_ctrl
++
++ @ Check that the address has been initialised
++ teq r1, #0
++ moveq pc, lr
++
++ @ The prefetch and power control registers are revision dependent
++ @ and can be written whether or not the L2 cache is enabled
++ ldr r0, [r1, #L2X0_CACHE_ID]
++ and r0, r0, #L2X0_CACHE_ID_RTL_MASK
++ cmp r0, #L310_CACHE_ID_RTL_R2P0
++ strcs r7, [r1, #L310_PREFETCH_CTRL]
++ cmp r0, #L310_CACHE_ID_RTL_R3P0
++ strcs r8, [r1, #L310_POWER_CTRL]
++
++ @ Don't setup the L2 cache if it is already enabled
++ ldr r0, [r1, #L2X0_CTRL]
++ tst r0, #L2X0_CTRL_EN
++ movne pc, lr
++
++ str r3, [r1, #L310_TAG_LATENCY_CTRL]
++ str r4, [r1, #L310_DATA_LATENCY_CTRL]
++ str r6, [r1, #L310_ADDR_FILTER_END]
++ str r5, [r1, #L310_ADDR_FILTER_START]
++
++ str r2, [r1, #L2X0_AUX_CTRL]
++ mov r9, #L2X0_CTRL_EN
++ str r9, [r1, #L2X0_CTRL]
++ mov pc, lr
++ENDPROC(l2c310_early_resume)
++
++ .align
++1: .long l2x0_saved_regs - .
+diff -Nur linux-3.15-rc6.orig/arch/arm/mm/Makefile linux-3.15-rc6/arch/arm/mm/Makefile
+--- linux-3.15-rc6.orig/arch/arm/mm/Makefile 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/mm/Makefile 2014-05-23 11:26:48.284939966 +0200
+@@ -95,7 +95,8 @@
+ AFLAGS_proc-v6.o :=-Wa,-march=armv6
+ AFLAGS_proc-v7.o :=-Wa,-march=armv7-a
+
++obj-$(CONFIG_OUTER_CACHE) += l2c-common.o
+ obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
+-obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
++obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o l2c-l2x0-resume.o
+ obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o
+ obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o
+diff -Nur linux-3.15-rc6.orig/arch/arm/plat-samsung/s5p-sleep.S linux-3.15-rc6/arch/arm/plat-samsung/s5p-sleep.S
+--- linux-3.15-rc6.orig/arch/arm/plat-samsung/s5p-sleep.S 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/arch/arm/plat-samsung/s5p-sleep.S 2014-05-23 11:26:48.284939966 +0200
+@@ -22,7 +22,6 @@
+ */
+
+ #include <linux/linkage.h>
+-#include <asm/asm-offsets.h>
+
+ .data
+ .align
+diff -Nur linux-3.15-rc6.orig/Documentation/devicetree/bindings/leds/leds-pwm.txt linux-3.15-rc6/Documentation/devicetree/bindings/leds/leds-pwm.txt
+--- linux-3.15-rc6.orig/Documentation/devicetree/bindings/leds/leds-pwm.txt 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/Documentation/devicetree/bindings/leds/leds-pwm.txt 2014-05-23 11:26:48.284939966 +0200
+@@ -13,6 +13,8 @@
+ For the pwms and pwm-names property please refer to:
+ Documentation/devicetree/bindings/pwm/pwm.txt
+ - max-brightness : Maximum brightness possible for the LED
++- active-low : (optional) For PWMs where the LED is wired to supply
++ rather than ground.
+ - label : (optional)
+ see Documentation/devicetree/bindings/leds/common.txt
+ - linux,default-trigger : (optional)
+diff -Nur linux-3.15-rc6.orig/Documentation/devicetree/bindings/mmc/mmc.txt linux-3.15-rc6/Documentation/devicetree/bindings/mmc/mmc.txt
+--- linux-3.15-rc6.orig/Documentation/devicetree/bindings/mmc/mmc.txt 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/Documentation/devicetree/bindings/mmc/mmc.txt 2014-05-23 11:26:48.284939966 +0200
+@@ -5,6 +5,8 @@
+ Interpreted by the OF core:
+ - reg: Registers location and length.
+ - interrupts: Interrupts used by the MMC controller.
++- clocks: Clocks needed for the host controller, if any.
++- clock-names: Goes with clocks above.
+
+ Card detection:
+ If no property below is supplied, host native card detect is used.
+@@ -39,6 +41,15 @@
+ - mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
+ - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
+
++Card power and reset control:
++The following properties can be specified for cases where the MMC
++peripheral needs additional reset, regulator and clock lines. It is for
++example common for WiFi/BT adapters to have these separate from the main
++MMC bus:
++ - card-reset-gpios: Specify GPIOs for card reset (reset active low)
++ - card-external-vcc-supply: Regulator to drive (independent) card VCC
++ - clock with name "card_ext_clock": External clock provided to the card
++
+ *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
+ polarity properties, we have to fix the meaning of the "normal" and "inverted"
+ line levels. We choose to follow the SDHCI standard, which specifies both those
+diff -Nur linux-3.15-rc6.orig/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt linux-3.15-rc6/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
+--- linux-3.15-rc6.orig/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt 2014-05-23 11:26:48.288939979 +0200
+@@ -60,7 +60,8 @@
+ - compatible: Should be "fsl,imx-parallel-display"
+ Optional properties:
+ - interface_pix_fmt: How this display is connected to the
+- display interface. Currently supported types: "rgb24", "rgb565", "bgr666"
++ display interface. Currently supported types: "rgb24", "rgb565", "bgr666",
++ "rgb666"
+ - edid: verbatim EDID data block describing attached display.
+ - ddc: phandle describing the i2c bus handling the display data
+ channel
+diff -Nur linux-3.15-rc6.orig/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml linux-3.15-rc6/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
+--- linux-3.15-rc6.orig/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml 2014-05-23 11:26:48.288939979 +0200
+@@ -279,6 +279,45 @@
+ <entry></entry>
+ <entry></entry>
+ </row>
++ <row id="V4L2-PIX-FMT-RGB666">
++ <entry><constant>V4L2_PIX_FMT_RGB666</constant></entry>
++ <entry>'RGBH'</entry>
++ <entry></entry>
++ <entry>r<subscript>5</subscript></entry>
++ <entry>r<subscript>4</subscript></entry>
++ <entry>r<subscript>3</subscript></entry>
++ <entry>r<subscript>2</subscript></entry>
++ <entry>r<subscript>1</subscript></entry>
++ <entry>r<subscript>0</subscript></entry>
++ <entry>g<subscript>5</subscript></entry>
++ <entry>g<subscript>4</subscript></entry>
++ <entry></entry>
++ <entry>g<subscript>3</subscript></entry>
++ <entry>g<subscript>2</subscript></entry>
++ <entry>g<subscript>1</subscript></entry>
++ <entry>g<subscript>0</subscript></entry>
++ <entry>b<subscript>5</subscript></entry>
++ <entry>b<subscript>4</subscript></entry>
++ <entry>b<subscript>3</subscript></entry>
++ <entry>b<subscript>2</subscript></entry>
++ <entry></entry>
++ <entry>b<subscript>1</subscript></entry>
++ <entry>b<subscript>0</subscript></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ <entry></entry>
++ </row>
+ <row id="V4L2-PIX-FMT-BGR24">
+ <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
+ <entry>'BGR3'</entry>
+diff -Nur linux-3.15-rc6.orig/drivers/ata/ahci_imx.c linux-3.15-rc6/drivers/ata/ahci_imx.c
+--- linux-3.15-rc6.orig/drivers/ata/ahci_imx.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/ata/ahci_imx.c 2014-05-23 11:26:48.288939979 +0200
+@@ -62,6 +62,7 @@
+ struct regmap *gpr;
+ bool no_device;
+ bool first_time;
++ u32 phy_params;
+ };
+
+ static int ahci_imx_hotplug;
+@@ -246,14 +247,7 @@
+ IMX6Q_GPR13_SATA_TX_LVL_MASK |
+ IMX6Q_GPR13_SATA_MPLL_CLK_EN |
+ IMX6Q_GPR13_SATA_TX_EDGE_RATE,
+- IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB |
+- IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
+- IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
+- IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
+- IMX6Q_GPR13_SATA_MPLL_SS_EN |
+- IMX6Q_GPR13_SATA_TX_ATTEN_9_16 |
+- IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB |
+- IMX6Q_GPR13_SATA_TX_LVL_1_025_V);
++ imxpriv->phy_params);
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
+ IMX6Q_GPR13_SATA_MPLL_CLK_EN,
+ IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+@@ -324,6 +318,10 @@
+ writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR);
+ imx_sata_disable(hpriv);
+ imxpriv->no_device = true;
++
++ dev_info(ap->dev, "no device found, disabling link.\n");
++ dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX
++ ".hotplug=1 to enable hotplug\n");
+ }
+
+ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class,
+@@ -364,6 +362,165 @@
+ };
+ MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
+
++struct reg_value {
++ u32 of_value;
++ u32 reg_value;
++};
++
++struct reg_property {
++ const char *name;
++ const struct reg_value *values;
++ size_t num_values;
++ u32 def_value;
++ u32 set_value;
++};
++
++static const struct reg_value gpr13_tx_level[] = {
++ { 937, IMX6Q_GPR13_SATA_TX_LVL_0_937_V },
++ { 947, IMX6Q_GPR13_SATA_TX_LVL_0_947_V },
++ { 957, IMX6Q_GPR13_SATA_TX_LVL_0_957_V },
++ { 966, IMX6Q_GPR13_SATA_TX_LVL_0_966_V },
++ { 976, IMX6Q_GPR13_SATA_TX_LVL_0_976_V },
++ { 986, IMX6Q_GPR13_SATA_TX_LVL_0_986_V },
++ { 996, IMX6Q_GPR13_SATA_TX_LVL_0_996_V },
++ { 1005, IMX6Q_GPR13_SATA_TX_LVL_1_005_V },
++ { 1015, IMX6Q_GPR13_SATA_TX_LVL_1_015_V },
++ { 1025, IMX6Q_GPR13_SATA_TX_LVL_1_025_V },
++ { 1035, IMX6Q_GPR13_SATA_TX_LVL_1_035_V },
++ { 1045, IMX6Q_GPR13_SATA_TX_LVL_1_045_V },
++ { 1054, IMX6Q_GPR13_SATA_TX_LVL_1_054_V },
++ { 1064, IMX6Q_GPR13_SATA_TX_LVL_1_064_V },
++ { 1074, IMX6Q_GPR13_SATA_TX_LVL_1_074_V },
++ { 1084, IMX6Q_GPR13_SATA_TX_LVL_1_084_V },
++ { 1094, IMX6Q_GPR13_SATA_TX_LVL_1_094_V },
++ { 1104, IMX6Q_GPR13_SATA_TX_LVL_1_104_V },
++ { 1113, IMX6Q_GPR13_SATA_TX_LVL_1_113_V },
++ { 1123, IMX6Q_GPR13_SATA_TX_LVL_1_123_V },
++ { 1133, IMX6Q_GPR13_SATA_TX_LVL_1_133_V },
++ { 1143, IMX6Q_GPR13_SATA_TX_LVL_1_143_V },
++ { 1152, IMX6Q_GPR13_SATA_TX_LVL_1_152_V },
++ { 1162, IMX6Q_GPR13_SATA_TX_LVL_1_162_V },
++ { 1172, IMX6Q_GPR13_SATA_TX_LVL_1_172_V },
++ { 1182, IMX6Q_GPR13_SATA_TX_LVL_1_182_V },
++ { 1191, IMX6Q_GPR13_SATA_TX_LVL_1_191_V },
++ { 1201, IMX6Q_GPR13_SATA_TX_LVL_1_201_V },
++ { 1211, IMX6Q_GPR13_SATA_TX_LVL_1_211_V },
++ { 1221, IMX6Q_GPR13_SATA_TX_LVL_1_221_V },
++ { 1230, IMX6Q_GPR13_SATA_TX_LVL_1_230_V },
++ { 1240, IMX6Q_GPR13_SATA_TX_LVL_1_240_V }
++};
++
++static const struct reg_value gpr13_tx_boost[] = {
++ { 0, IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB },
++ { 370, IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB },
++ { 740, IMX6Q_GPR13_SATA_TX_BOOST_0_74_DB },
++ { 111, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB },
++ { 148, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB },
++ { 185, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB },
++ { 222, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB },
++ { 259, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB },
++ { 296, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB },
++ { 333, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB },
++ { 370, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB },
++ { 407, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB },
++ { 444, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB },
++ { 481, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB },
++ { 528, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB },
++ { 575, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB }
++};
++
++static const struct reg_value gpr13_tx_atten[] = {
++ { 8, IMX6Q_GPR13_SATA_TX_ATTEN_8_16 },
++ { 9, IMX6Q_GPR13_SATA_TX_ATTEN_9_16 },
++ { 10, IMX6Q_GPR13_SATA_TX_ATTEN_10_16 },
++ { 12, IMX6Q_GPR13_SATA_TX_ATTEN_12_16 },
++ { 14, IMX6Q_GPR13_SATA_TX_ATTEN_14_16 },
++ { 16, IMX6Q_GPR13_SATA_TX_ATTEN_16_16 },
++};
++
++static const struct reg_value gpr13_rx_eq[] = {
++ { 500, IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB },
++ { 1000, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB },
++ { 1500, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_5_DB },
++ { 2000, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_0_DB },
++ { 2500, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_5_DB },
++ { 3000, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB },
++ { 3500, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB },
++ { 4000, IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB },
++};
++
++static const struct reg_property gpr13_props[] = {
++ {
++ .name = "fsl,transmit-level-mV",
++ .values = gpr13_tx_level,
++ .num_values = ARRAY_SIZE(gpr13_tx_level),
++ .def_value = IMX6Q_GPR13_SATA_TX_LVL_1_025_V,
++ }, {
++ .name = "fsl,transmit-boost-mdB",
++ .values = gpr13_tx_boost,
++ .num_values = ARRAY_SIZE(gpr13_tx_boost),
++ .def_value = IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB,
++ }, {
++ .name = "fsl,transmit-atten-16ths",
++ .values = gpr13_tx_atten,
++ .num_values = ARRAY_SIZE(gpr13_tx_atten),
++ .def_value = IMX6Q_GPR13_SATA_TX_ATTEN_9_16,
++ }, {
++ .name = "fsl,receive-eq-mdB",
++ .values = gpr13_rx_eq,
++ .num_values = ARRAY_SIZE(gpr13_rx_eq),
++ .def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB,
++ }, {
++ .name = "fsl,no-spread-spectrum",
++ .def_value = IMX6Q_GPR13_SATA_MPLL_SS_EN,
++ .set_value = 0,
++ },
++};
++
++static u32 imx_ahci_parse_props(struct device *dev,
++ const struct reg_property *prop, size_t num)
++{
++ struct device_node *np = dev->of_node;
++ u32 reg_value = 0;
++ int i, j;
++
++ for (i = 0; i < num; i++, prop++) {
++ u32 of_val;
++
++ if (prop->num_values == 0) {
++ if (of_property_read_bool(np, prop->name))
++ reg_value |= prop->set_value;
++ else
++ reg_value |= prop->def_value;
++ continue;
++ }
++
++ if (of_property_read_u32(np, prop->name, &of_val)) {
++ dev_info(dev, "%s not specified, using %08x\n",
++ prop->name, prop->def_value);
++ reg_value |= prop->def_value;
++ continue;
++ }
++
++ for (j = 0; j < prop->num_values; j++) {
++ if (prop->values[j].of_value == of_val) {
++ dev_info(dev, "%s value %u, using %08x\n",
++ prop->name, of_val, prop->values[j].reg_value);
++ reg_value |= prop->values[j].reg_value;
++ break;
++ }
++ }
++
++ if (j == prop->num_values) {
++ dev_err(dev, "DT property %s is not a valid value\n",
++ prop->name);
++ reg_value |= prop->def_value;
++ }
++ }
++
++ return reg_value;
++}
++
+ static int imx_ahci_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -392,6 +549,8 @@
+ }
+
+ if (imxpriv->type == AHCI_IMX6Q) {
++ u32 reg_value;
++
+ imxpriv->gpr = syscon_regmap_lookup_by_compatible(
+ "fsl,imx6q-iomuxc-gpr");
+ if (IS_ERR(imxpriv->gpr)) {
+@@ -399,6 +558,15 @@
+ "failed to find fsl,imx6q-iomux-gpr regmap\n");
+ return PTR_ERR(imxpriv->gpr);
+ }
++
++ reg_value = imx_ahci_parse_props(dev, gpr13_props,
++ ARRAY_SIZE(gpr13_props));
++
++ imxpriv->phy_params =
++ IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
++ IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
++ IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
++ reg_value;
+ }
+
+ hpriv = ahci_platform_get_resources(pdev);
+diff -Nur linux-3.15-rc6.orig/drivers/cec/cec-dev.c linux-3.15-rc6/drivers/cec/cec-dev.c
+--- linux-3.15-rc6.orig/drivers/cec/cec-dev.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/cec/cec-dev.c 2014-05-23 11:26:48.296940005 +0200
+@@ -0,0 +1,384 @@
++/*
++ * HDMI Consumer Electronics Control
++ *
++ * This provides the user API for communication with HDMI CEC complaint
++ * devices in kernel drivers, and is based upon the protocol developed
++ * by Freescale for their i.MX SoCs.
++ *
++ * 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/cec-dev.h>
++#include <linux/device.h>
++#include <linux/fs.h>
++#include <linux/module.h>
++#include <linux/poll.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++
++struct cec_event {
++ struct cec_user_event usr;
++ struct list_head node;
++};
++
++static struct class *cec_class;
++static int cec_major;
++
++static void cec_dev_send_message(struct cec_dev *cec_dev, u8 *msg,
++ size_t count)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&cec_dev->lock, flags);
++ cec_dev->retries = 5;
++ cec_dev->write_busy = 1;
++ cec_dev->send_message(cec_dev, msg, count);
++ spin_unlock_irqrestore(&cec_dev->lock, flags);
++}
++
++void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len)
++{
++ struct cec_event *event;
++ unsigned long flags;
++
++ event = kzalloc(sizeof(*event), GFP_ATOMIC);
++ if (event) {
++ event->usr.event_type = type;
++ event->usr.msg_len = len;
++ if (msg)
++ memcpy(event->usr.msg, msg, len);
++
++ spin_lock_irqsave(&cec_dev->lock, flags);
++ list_add_tail(&event->node, &cec_dev->events);
++ spin_unlock_irqrestore(&cec_dev->lock, flags);
++ wake_up(&cec_dev->waitq);
++ }
++}
++EXPORT_SYMBOL_GPL(cec_dev_event);
++
++static int cec_dev_lock_write(struct cec_dev *cec_dev, struct file *file)
++ __acquires(cec_dev->mutex)
++{
++ int ret;
++
++ do {
++ if (file->f_flags & O_NONBLOCK) {
++ if (cec_dev->write_busy)
++ return -EAGAIN;
++ } else {
++ ret = wait_event_interruptible(cec_dev->waitq,
++ !cec_dev->write_busy);
++ if (ret)
++ break;
++ }
++
++ ret = mutex_lock_interruptible(&cec_dev->mutex);
++ if (ret)
++ break;
++
++ if (!cec_dev->write_busy)
++ break;
++
++ mutex_unlock(&cec_dev->mutex);
++ } while (1);
++
++ return ret;
++}
++
++static ssize_t cec_dev_read(struct file *file, char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ struct cec_dev *cec_dev = file->private_data;
++ ssize_t ret;
++
++ if (count > sizeof(struct cec_user_event))
++ count = sizeof(struct cec_user_event);
++
++ if (!access_ok(VERIFY_WRITE, buf, count))
++ return -EFAULT;
++
++ do {
++ struct cec_event *event = NULL;
++ unsigned long flags;
++
++ spin_lock_irqsave(&cec_dev->lock, flags);
++ if (!list_empty(&cec_dev->events)) {
++ event = list_first_entry(&cec_dev->events,
++ struct cec_event, node);
++ list_del(&event->node);
++ }
++ spin_unlock_irqrestore(&cec_dev->lock, flags);
++
++ if (event) {
++ ret = __copy_to_user(buf, &event->usr, count) ?
++ -EFAULT : count;
++ kfree(event);
++ break;
++ }
++
++ if (file->f_flags & O_NONBLOCK) {
++ ret = -EAGAIN;
++ break;
++ }
++
++ ret = wait_event_interruptible(cec_dev->waitq,
++ !list_empty(&cec_dev->events));
++ if (ret)
++ break;
++ } while (1);
++
++ return ret;
++}
++
++static ssize_t cec_dev_write(struct file *file, const char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ struct cec_dev *cec_dev = file->private_data;
++ u8 msg[MAX_MESSAGE_LEN];
++ int ret;
++
++ if (count > sizeof(msg))
++ return -E2BIG;
++
++ if (copy_from_user(msg, buf, count))
++ return -EFAULT;
++
++ ret = cec_dev_lock_write(cec_dev, file);
++ if (ret)
++ return ret;
++
++ cec_dev_send_message(cec_dev, msg, count);
++
++ mutex_unlock(&cec_dev->mutex);
++
++ return count;
++}
++
++static long cec_dev_ioctl(struct file *file, u_int cmd, unsigned long arg)
++{
++ struct cec_dev *cec_dev = file->private_data;
++ int ret;
++
++ switch (cmd) {
++ case HDMICEC_IOC_O_SETLOGICALADDRESS:
++ case HDMICEC_IOC_SETLOGICALADDRESS:
++ if (arg > 15) {
++ ret = -EINVAL;
++ break;
++ }
++
++ ret = cec_dev_lock_write(cec_dev, file);
++ if (ret == 0) {
++ unsigned char msg[1];
++
++ cec_dev->addresses = BIT(arg);
++ cec_dev->set_address(cec_dev, cec_dev->addresses);
++
++ /*
++ * Send a ping message with the source and destination
++ * set to our address; the result indicates whether
++ * unit has chosen our address simultaneously.
++ */
++ msg[0] = arg << 4 | arg;
++ cec_dev_send_message(cec_dev, msg, sizeof(msg));
++ mutex_unlock(&cec_dev->mutex);
++ }
++ break;
++
++ case HDMICEC_IOC_STARTDEVICE:
++ ret = mutex_lock_interruptible(&cec_dev->mutex);
++ if (ret == 0) {
++ cec_dev->addresses = BIT(15);
++ cec_dev->set_address(cec_dev, cec_dev->addresses);
++ mutex_unlock(&cec_dev->mutex);
++ }
++ break;
++
++ case HDMICEC_IOC_STOPDEVICE:
++ ret = 0;
++ break;
++
++ case HDMICEC_IOC_GETPHYADDRESS:
++ ret = put_user(cec_dev->physical, (u16 __user *)arg);
++ ret = -ENOIOCTLCMD;
++ break;
++
++ default:
++ ret = -ENOIOCTLCMD;
++ break;
++ }
++
++ return ret;
++}
++
++static unsigned cec_dev_poll(struct file *file, poll_table *wait)
++{
++ struct cec_dev *cec_dev = file->private_data;
++ unsigned mask = 0;
++
++ poll_wait(file, &cec_dev->waitq, wait);
++
++ if (cec_dev->write_busy == 0)
++ mask |= POLLOUT | POLLWRNORM;
++ if (!list_empty(&cec_dev->events))
++ mask |= POLLIN | POLLRDNORM;
++
++ return mask;
++}
++
++static int cec_dev_release(struct inode *inode, struct file *file)
++{
++ struct cec_dev *cec_dev = file->private_data;
++
++ mutex_lock(&cec_dev->mutex);
++ if (cec_dev->users >= 1)
++ cec_dev->users -= 1;
++ if (cec_dev->users == 0) {
++ /*
++ * Wait for any write to complete before shutting down.
++ * A message should complete in a maximum of 2.75ms *
++ * 160 bits + 4.7ms, or 444.7ms. Let's call that 500ms.
++ * If we time out, shutdown anyway.
++ */
++ wait_event_timeout(cec_dev->waitq, !cec_dev->write_busy,
++ msecs_to_jiffies(500));
++
++ cec_dev->release(cec_dev);
++
++ while (!list_empty(&cec_dev->events)) {
++ struct cec_event *event;
++
++ event = list_first_entry(&cec_dev->events,
++ struct cec_event, node);
++ list_del(&event->node);
++ kfree(event);
++ }
++ }
++ mutex_unlock(&cec_dev->mutex);
++ return 0;
++}
++
++static int cec_dev_open(struct inode *inode, struct file *file)
++{
++ struct cec_dev *cec_dev = container_of(inode->i_cdev, struct cec_dev,
++ cdev);
++ int ret = 0;
++
++ nonseekable_open(inode, file);
++
++ file->private_data = cec_dev;
++
++ ret = mutex_lock_interruptible(&cec_dev->mutex);
++ if (ret)
++ return ret;
++
++ if (cec_dev->users++ == 0) {
++ cec_dev->addresses = BIT(15);
++
++ ret = cec_dev->open(cec_dev);
++ if (ret < 0)
++ cec_dev->users = 0;
++ }
++ mutex_unlock(&cec_dev->mutex);
++
++ return ret;
++}
++
++static const struct file_operations hdmi_cec_fops = {
++ .owner = THIS_MODULE,
++ .read = cec_dev_read,
++ .write = cec_dev_write,
++ .open = cec_dev_open,
++ .unlocked_ioctl = cec_dev_ioctl,
++ .release = cec_dev_release,
++ .poll = cec_dev_poll,
++};
++
++void cec_dev_init(struct cec_dev *cec_dev, struct module *module)
++{
++ cec_dev->devn = MKDEV(cec_major, 0);
++
++ INIT_LIST_HEAD(&cec_dev->events);
++ init_waitqueue_head(&cec_dev->waitq);
++ spin_lock_init(&cec_dev->lock);
++ mutex_init(&cec_dev->mutex);
++
++ cec_dev->addresses = BIT(15);
++
++ cdev_init(&cec_dev->cdev, &hdmi_cec_fops);
++ cec_dev->cdev.owner = module;
++}
++EXPORT_SYMBOL_GPL(cec_dev_init);
++
++int cec_dev_add(struct cec_dev *cec_dev, struct device *dev, const char *name)
++{
++ struct device *cd;
++ int ret;
++
++ ret = cdev_add(&cec_dev->cdev, cec_dev->devn, 1);
++ if (ret < 0)
++ goto err_cdev;
++
++ cd = device_create(cec_class, dev, cec_dev->devn, NULL, name);
++ if (IS_ERR(cd)) {
++ ret = PTR_ERR(cd);
++ dev_err(dev, "can't create device: %d\n", ret);
++ goto err_dev;
++ }
++
++ return 0;
++
++ err_dev:
++ cdev_del(&cec_dev->cdev);
++ err_cdev:
++ return ret;
++}
++EXPORT_SYMBOL_GPL(cec_dev_add);
++
++void cec_dev_remove(struct cec_dev *cec_dev)
++{
++ device_destroy(cec_class, cec_dev->devn);
++ cdev_del(&cec_dev->cdev);
++}
++EXPORT_SYMBOL_GPL(cec_dev_remove);
++
++static int cec_init(void)
++{
++ dev_t dev;
++ int ret;
++
++ cec_class = class_create(THIS_MODULE, "hdmi-cec");
++ if (IS_ERR(cec_class)) {
++ ret = PTR_ERR(cec_class);
++ pr_err("cec: can't create cec class: %d\n", ret);
++ goto err_class;
++ }
++
++ ret = alloc_chrdev_region(&dev, 0, 1, "hdmi-cec");
++ if (ret) {
++ pr_err("cec: can't create character devices: %d\n", ret);
++ goto err_chrdev;
++ }
++
++ cec_major = MAJOR(dev);
++
++ return 0;
++
++ err_chrdev:
++ class_destroy(cec_class);
++ err_class:
++ return ret;
++}
++subsys_initcall(cec_init);
++
++static void cec_exit(void)
++{
++ unregister_chrdev_region(MKDEV(cec_major, 0), 1);
++ class_destroy(cec_class);
++}
++module_exit(cec_exit);
++
++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
++MODULE_DESCRIPTION("Generic HDMI CEC driver");
++MODULE_LICENSE("GPL");
+diff -Nur linux-3.15-rc6.orig/drivers/cec/Kconfig linux-3.15-rc6/drivers/cec/Kconfig
+--- linux-3.15-rc6.orig/drivers/cec/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/cec/Kconfig 2014-05-23 11:26:48.296940005 +0200
+@@ -0,0 +1,14 @@
++#
++# Consumer Electroncs Control support
++#
++
++menu "Consumer Electronics Control devices"
++
++config CEC
++ bool
++
++config HDMI_CEC_CORE
++ tristate
++ select CEC
++
++endmenu
+diff -Nur linux-3.15-rc6.orig/drivers/cec/Makefile linux-3.15-rc6/drivers/cec/Makefile
+--- linux-3.15-rc6.orig/drivers/cec/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/cec/Makefile 2014-05-23 11:26:48.296940005 +0200
+@@ -0,0 +1 @@
++obj-$(CONFIG_HDMI_CEC_CORE) += cec-dev.o
+diff -Nur linux-3.15-rc6.orig/drivers/gpu/drm/drm_crtc_helper.c linux-3.15-rc6/drivers/gpu/drm/drm_crtc_helper.c
+--- linux-3.15-rc6.orig/drivers/gpu/drm/drm_crtc_helper.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/gpu/drm/drm_crtc_helper.c 2014-05-23 11:26:48.296940005 +0200
+@@ -140,16 +140,10 @@
+ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
+ {
+ struct drm_encoder *encoder;
+- struct drm_connector *connector;
+ struct drm_crtc *crtc;
+
+ drm_warn_on_modeset_not_all_locked(dev);
+
+- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+- if (!connector->encoder)
+- continue;
+- }
+-
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (!drm_helper_encoder_in_use(encoder)) {
+ drm_encoder_disable(encoder);
+diff -Nur linux-3.15-rc6.orig/drivers/Kconfig linux-3.15-rc6/drivers/Kconfig
+--- linux-3.15-rc6.orig/drivers/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/Kconfig 2014-05-23 11:26:48.296940005 +0200
+@@ -174,4 +174,6 @@
+
+ source "drivers/mcb/Kconfig"
+
++source "drivers/cec/Kconfig"
++
+ endmenu
+diff -Nur linux-3.15-rc6.orig/drivers/leds/leds-pwm.c linux-3.15-rc6/drivers/leds/leds-pwm.c
+--- linux-3.15-rc6.orig/drivers/leds/leds-pwm.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/leds/leds-pwm.c 2014-05-23 11:26:48.296940005 +0200
+@@ -69,6 +69,10 @@
+
+ duty *= brightness;
+ do_div(duty, max);
++
++ if (led_dat->active_low)
++ duty = led_dat->period - duty;
++
+ led_dat->duty = duty;
+
+ if (led_dat->can_sleep)
+@@ -92,55 +96,75 @@
+ }
+ }
+
+-static int led_pwm_create_of(struct platform_device *pdev,
+- struct led_pwm_priv *priv)
++static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
++ struct led_pwm *led, struct device_node *child)
+ {
+- struct device_node *child;
++ struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
+ int ret;
+
+- for_each_child_of_node(pdev->dev.of_node, child) {
+- struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
++ led_data->active_low = led->active_low;
++ led_data->period = led->pwm_period_ns;
++ led_data->cdev.name = led->name;
++ led_data->cdev.default_trigger = led->default_trigger;
++ led_data->cdev.brightness_set = led_pwm_set;
++ led_data->cdev.brightness = LED_OFF;
++ led_data->cdev.max_brightness = led->max_brightness;
++ led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
++
++ if (child)
++ led_data->pwm = devm_of_pwm_get(dev, child, NULL);
++ else
++ led_data->pwm = devm_pwm_get(dev, led->name);
++ if (IS_ERR(led_data->pwm)) {
++ ret = PTR_ERR(led_data->pwm);
++ dev_err(dev, "unable to request PWM for %s: %d\n",
++ led->name, ret);
++ return ret;
++ }
+
+- led_dat->cdev.name = of_get_property(child, "label",
+- NULL) ? : child->name;
++ if (child)
++ led_data->period = pwm_get_period(led_data->pwm);
+
+- led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
+- if (IS_ERR(led_dat->pwm)) {
+- dev_err(&pdev->dev, "unable to request PWM for %s\n",
+- led_dat->cdev.name);
+- ret = PTR_ERR(led_dat->pwm);
+- goto err;
+- }
+- /* Get the period from PWM core when n*/
+- led_dat->period = pwm_get_period(led_dat->pwm);
++ led_data->can_sleep = pwm_can_sleep(led_data->pwm);
++ if (led_data->can_sleep)
++ INIT_WORK(&led_data->work, led_pwm_work);
+
+- led_dat->cdev.default_trigger = of_get_property(child,
++ ret = led_classdev_register(dev, &led_data->cdev);
++ if (ret == 0) {
++ priv->num_leds++;
++ } else {
++ dev_err(dev, "failed to register PWM led for %s: %d\n",
++ led->name, ret);
++ }
++
++ return ret;
++}
++
++static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
++{
++ struct device_node *child;
++ struct led_pwm led;
++ int ret = 0;
++
++ memset(&led, 0, sizeof(led));
++
++ for_each_child_of_node(dev->of_node, child) {
++ led.name = of_get_property(child, "label", NULL) ? :
++ child->name;
++
++ led.default_trigger = of_get_property(child,
+ "linux,default-trigger", NULL);
++ led.active_low = of_property_read_bool(child, "active-low");
+ of_property_read_u32(child, "max-brightness",
+- &led_dat->cdev.max_brightness);
++ &led.max_brightness);
+
+- led_dat->cdev.brightness_set = led_pwm_set;
+- led_dat->cdev.brightness = LED_OFF;
+- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+-
+- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+- if (led_dat->can_sleep)
+- INIT_WORK(&led_dat->work, led_pwm_work);
+-
+- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
+- if (ret < 0) {
+- dev_err(&pdev->dev, "failed to register for %s\n",
+- led_dat->cdev.name);
++ ret = led_pwm_add(dev, priv, &led, child);
++ if (ret) {
+ of_node_put(child);
+- goto err;
++ break;
+ }
+- priv->num_leds++;
+ }
+
+- return 0;
+-err:
+- led_pwm_cleanup(priv);
+-
+ return ret;
+ }
+
+@@ -166,51 +190,23 @@
+
+ if (pdata) {
+ for (i = 0; i < count; i++) {
+- struct led_pwm *cur_led = &pdata->leds[i];
+- struct led_pwm_data *led_dat = &priv->leds[i];
+-
+- led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
+- if (IS_ERR(led_dat->pwm)) {
+- ret = PTR_ERR(led_dat->pwm);
+- dev_err(&pdev->dev,
+- "unable to request PWM for %s\n",
+- cur_led->name);
+- goto err;
+- }
+-
+- led_dat->cdev.name = cur_led->name;
+- led_dat->cdev.default_trigger = cur_led->default_trigger;
+- led_dat->active_low = cur_led->active_low;
+- led_dat->period = cur_led->pwm_period_ns;
+- led_dat->cdev.brightness_set = led_pwm_set;
+- led_dat->cdev.brightness = LED_OFF;
+- led_dat->cdev.max_brightness = cur_led->max_brightness;
+- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+-
+- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+- if (led_dat->can_sleep)
+- INIT_WORK(&led_dat->work, led_pwm_work);
+-
+- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
+- if (ret < 0)
+- goto err;
++ ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
++ NULL);
++ if (ret)
++ break;
+ }
+- priv->num_leds = count;
+ } else {
+- ret = led_pwm_create_of(pdev, priv);
+- if (ret)
+- return ret;
++ ret = led_pwm_create_of(&pdev->dev, priv);
++ }
++
++ if (ret) {
++ led_pwm_cleanup(priv);
++ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+-
+-err:
+- priv->num_leds = i;
+- led_pwm_cleanup(priv);
+-
+- return ret;
+ }
+
+ static int led_pwm_remove(struct platform_device *pdev)
+diff -Nur linux-3.15-rc6.orig/drivers/Makefile linux-3.15-rc6/drivers/Makefile
+--- linux-3.15-rc6.orig/drivers/Makefile 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/Makefile 2014-05-23 11:26:48.296940005 +0200
+@@ -157,3 +157,4 @@
+ obj-$(CONFIG_FMC) += fmc/
+ obj-$(CONFIG_POWERCAP) += powercap/
+ obj-$(CONFIG_MCB) += mcb/
++obj-$(CONFIG_CEC) += cec/
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/core/core.c linux-3.15-rc6/drivers/mmc/core/core.c
+--- linux-3.15-rc6.orig/drivers/mmc/core/core.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/core/core.c 2014-05-23 11:26:48.300940018 +0200
+@@ -13,11 +13,13 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
++#include <linux/clk.h>
+ #include <linux/completion.h>
+ #include <linux/device.h>
+ #include <linux/delay.h>
+ #include <linux/pagemap.h>
+ #include <linux/err.h>
++#include <linux/gpio.h>
+ #include <linux/leds.h>
+ #include <linux/scatterlist.h>
+ #include <linux/log2.h>
+@@ -1504,6 +1506,43 @@
+ mmc_host_clk_release(host);
+ }
+
++static void mmc_card_power_up(struct mmc_host *host)
++{
++ int i;
++ struct gpio_desc **gds = host->card_reset_gpios;
++
++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
++ if (gds[i]) {
++ dev_dbg(host->parent, "Asserting reset line %d", i);
++ gpiod_set_value(gds[i], 1);
++ }
++ }
++
++ if (host->card_regulator) {
++ dev_dbg(host->parent, "Enabling external regulator");
++ if (regulator_enable(host->card_regulator))
++ dev_err(host->parent, "Failed to enable external regulator");
++ }
++
++ if (host->card_clk) {
++ dev_dbg(host->parent, "Enabling external clock");
++ clk_prepare_enable(host->card_clk);
++ }
++
++ /* 2ms delay to let clocks and power settle */
++ mmc_delay(20);
++
++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
++ if (gds[i]) {
++ dev_dbg(host->parent, "Deasserting reset line %d", i);
++ gpiod_set_value(gds[i], 0);
++ }
++ }
++
++ /* 2ms delay to after reset release */
++ mmc_delay(20);
++}
++
+ /*
+ * Apply power to the MMC stack. This is a two-stage process.
+ * First, we enable power to the card without the clock running.
+@@ -1520,6 +1559,9 @@
+ if (host->ios.power_mode == MMC_POWER_ON)
+ return;
+
++ /* Power up the card/module first, if needed */
++ mmc_card_power_up(host);
++
+ mmc_host_clk_hold(host);
+
+ host->ios.vdd = fls(ocr) - 1;
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/core/host.c linux-3.15-rc6/drivers/mmc/core/host.c
+--- linux-3.15-rc6.orig/drivers/mmc/core/host.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/core/host.c 2014-05-23 11:26:48.300940018 +0200
+@@ -12,14 +12,18 @@
+ * MMC host class device management
+ */
+
++#include <linux/kernel.h>
++#include <linux/clk.h>
+ #include <linux/device.h>
+ #include <linux/err.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/idr.h>
+ #include <linux/of.h>
+ #include <linux/of_gpio.h>
+ #include <linux/pagemap.h>
+ #include <linux/export.h>
+ #include <linux/leds.h>
++#include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/suspend.h>
+
+@@ -457,6 +461,66 @@
+
+ EXPORT_SYMBOL(mmc_of_parse);
+
++static int mmc_of_parse_child(struct mmc_host *host)
++{
++ struct device_node *np;
++ struct clk *clk;
++ int i;
++
++ if (!host->parent || !host->parent->of_node)
++ return 0;
++
++ np = host->parent->of_node;
++
++ host->card_regulator = regulator_get(host->parent, "card-external-vcc");
++ if (IS_ERR(host->card_regulator)) {
++ if (PTR_ERR(host->card_regulator) == -EPROBE_DEFER)
++ return PTR_ERR(host->card_regulator);
++ host->card_regulator = NULL;
++ }
++
++ /* Parse card power/reset/clock control */
++ if (of_find_property(np, "card-reset-gpios", NULL)) {
++ struct gpio_desc *gpd;
++ int level = 0;
++
++ /*
++ * If the regulator is enabled, then we can hold the
++ * card in reset with an active high resets. Otherwise,
++ * hold the resets low.
++ */
++ if (host->card_regulator && regulator_is_enabled(host->card_regulator))
++ level = 1;
++
++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
++ gpd = devm_gpiod_get_index(host->parent, "card-reset", i);
++ if (IS_ERR(gpd)) {
++ if (PTR_ERR(gpd) == -EPROBE_DEFER)
++ return PTR_ERR(gpd);
++ break;
++ }
++ gpiod_direction_output(gpd, gpiod_is_active_low(gpd) | level);
++ host->card_reset_gpios[i] = gpd;
++ }
++
++ gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios));
++ if (!IS_ERR(gpd)) {
++ dev_warn(host->parent, "More reset gpios than we can handle");
++ gpiod_put(gpd);
++ }
++ }
++
++ clk = of_clk_get_by_name(np, "card_ext_clock");
++ if (IS_ERR(clk)) {
++ if (PTR_ERR(clk) == -EPROBE_DEFER)
++ return PTR_ERR(clk);
++ clk = NULL;
++ }
++ host->card_clk = clk;
++
++ return 0;
++}
++
+ /**
+ * mmc_alloc_host - initialise the per-host structure.
+ * @extra: sizeof private data structure
+@@ -536,6 +600,10 @@
+ {
+ int err;
+
++ err = mmc_of_parse_child(host);
++ if (err)
++ return err;
++
+ WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
+ !host->ops->enable_sdio_irq);
+
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/core/sdio_irq.c linux-3.15-rc6/drivers/mmc/core/sdio_irq.c
+--- linux-3.15-rc6.orig/drivers/mmc/core/sdio_irq.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/core/sdio_irq.c 2014-05-23 11:26:48.300940018 +0200
+@@ -90,6 +90,15 @@
+ return ret;
+ }
+
++void sdio_run_irqs(struct mmc_host *host)
++{
++ mmc_claim_host(host);
++ host->sdio_irq_pending = true;
++ process_sdio_pending_irqs(host);
++ mmc_release_host(host);
++}
++EXPORT_SYMBOL_GPL(sdio_run_irqs);
++
+ static int sdio_irq_thread(void *_host)
+ {
+ struct mmc_host *host = _host;
+@@ -189,14 +198,20 @@
+ WARN_ON(!host->claimed);
+
+ if (!host->sdio_irqs++) {
+- atomic_set(&host->sdio_irq_thread_abort, 0);
+- host->sdio_irq_thread =
+- kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
+- mmc_hostname(host));
+- if (IS_ERR(host->sdio_irq_thread)) {
+- int err = PTR_ERR(host->sdio_irq_thread);
+- host->sdio_irqs--;
+- return err;
++ if (!(host->caps2 & MMC_CAP2_SDIO_NOTHREAD)) {
++ atomic_set(&host->sdio_irq_thread_abort, 0);
++ host->sdio_irq_thread =
++ kthread_run(sdio_irq_thread, host,
++ "ksdioirqd/%s", mmc_hostname(host));
++ if (IS_ERR(host->sdio_irq_thread)) {
++ int err = PTR_ERR(host->sdio_irq_thread);
++ host->sdio_irqs--;
++ return err;
++ }
++ } else {
++ mmc_host_clk_hold(host);
++ host->ops->enable_sdio_irq(host, 1);
++ mmc_host_clk_release(host);
+ }
+ }
+
+@@ -211,8 +226,14 @@
+ BUG_ON(host->sdio_irqs < 1);
+
+ if (!--host->sdio_irqs) {
+- atomic_set(&host->sdio_irq_thread_abort, 1);
+- kthread_stop(host->sdio_irq_thread);
++ if (!(host->caps2 & MMC_CAP2_SDIO_NOTHREAD)) {
++ atomic_set(&host->sdio_irq_thread_abort, 1);
++ kthread_stop(host->sdio_irq_thread);
++ } else {
++ mmc_host_clk_hold(host);
++ host->ops->enable_sdio_irq(host, 0);
++ mmc_host_clk_release(host);
++ }
+ }
+
+ return 0;
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/dw_mmc.c linux-3.15-rc6/drivers/mmc/host/dw_mmc.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/dw_mmc.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/dw_mmc.c 2014-05-23 11:26:48.300940018 +0200
+@@ -2140,6 +2140,8 @@
+ if (!mmc)
+ return -ENOMEM;
+
++ mmc_of_parse(mmc);
++
+ slot = mmc_priv(mmc);
+ slot->id = id;
+ slot->mmc = mmc;
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/Kconfig linux-3.15-rc6/drivers/mmc/host/Kconfig
+--- linux-3.15-rc6.orig/drivers/mmc/host/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/Kconfig 2014-05-23 11:26:48.300940018 +0200
+@@ -25,8 +25,7 @@
+ If unsure, say N.
+
+ config MMC_SDHCI
+- tristate "Secure Digital Host Controller Interface support"
+- depends on HAS_DMA
++ tristate
+ help
+ This selects the generic Secure Digital Host Controller Interface.
+ It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
+@@ -59,7 +58,8 @@
+
+ config MMC_SDHCI_PCI
+ tristate "SDHCI support on PCI bus"
+- depends on MMC_SDHCI && PCI
++ depends on PCI && HAS_DMA
++ select MMC_SDHCI
+ help
+ This selects the PCI Secure Digital Host Controller Interface.
+ Most controllers found today are PCI devices.
+@@ -83,7 +83,8 @@
+
+ config MMC_SDHCI_ACPI
+ tristate "SDHCI support for ACPI enumerated SDHCI controllers"
+- depends on MMC_SDHCI && ACPI
++ depends on ACPI && HAS_DMA
++ select MMC_SDHCI
+ help
+ This selects support for ACPI enumerated SDHCI controllers,
+ identified by ACPI Compatibility ID PNP0D40 or specific
+@@ -94,8 +95,8 @@
+ If unsure, say N.
+
+ config MMC_SDHCI_PLTFM
+- tristate "SDHCI platform and OF driver helper"
+- depends on MMC_SDHCI
++ tristate
++ select MMC_SDHCI
+ help
+ This selects the common helper functions support for Secure Digital
+ Host Controller Interface based platform and OF drivers.
+@@ -106,8 +107,8 @@
+
+ config MMC_SDHCI_OF_ARASAN
+ tristate "SDHCI OF support for the Arasan SDHCI controllers"
+- depends on MMC_SDHCI_PLTFM
+- depends on OF
++ depends on OF && HAS_DMA
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Arasan Secure Digital Host Controller Interface
+ (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
+@@ -118,9 +119,9 @@
+
+ config MMC_SDHCI_OF_ESDHC
+ tristate "SDHCI OF support for the Freescale eSDHC controller"
+- depends on MMC_SDHCI_PLTFM
+- depends on PPC_OF
++ depends on PPC_OF && HAS_DMA
+ select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Freescale eSDHC controller support.
+
+@@ -130,9 +131,9 @@
+
+ config MMC_SDHCI_OF_HLWD
+ tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers"
+- depends on MMC_SDHCI_PLTFM
+- depends on PPC_OF
++ depends on PPC_OF && HAS_DMA
+ select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ found in the "Hollywood" chipset of the Nintendo Wii video game
+@@ -144,8 +145,8 @@
+
+ config MMC_SDHCI_CNS3XXX
+ tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
+- depends on ARCH_CNS3XXX
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_CNS3XXX && HAS_DMA
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the SDHCI support for CNS3xxx System-on-Chip devices.
+
+@@ -155,9 +156,9 @@
+
+ config MMC_SDHCI_ESDHC_IMX
+ tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
+- depends on ARCH_MXC
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_MXC && HAS_DMA
+ select MMC_SDHCI_IO_ACCESSORS
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Freescale eSDHC/uSDHC controller support
+ found on i.MX25, i.MX35 i.MX5x and i.MX6x.
+@@ -168,9 +169,9 @@
+
+ config MMC_SDHCI_DOVE
+ tristate "SDHCI support on Marvell's Dove SoC"
+- depends on ARCH_DOVE
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_DOVE && HAS_DMA
+ select MMC_SDHCI_IO_ACCESSORS
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Secure Digital Host Controller Interface in
+ Marvell's Dove SoC.
+@@ -181,9 +182,9 @@
+
+ config MMC_SDHCI_TEGRA
+ tristate "SDHCI platform support for the Tegra SD/MMC Controller"
+- depends on ARCH_TEGRA
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_TEGRA && HAS_DMA
+ select MMC_SDHCI_IO_ACCESSORS
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the Tegra SD/MMC controller. If you have a Tegra
+ platform with SD or MMC devices, say Y or M here.
+@@ -192,7 +193,8 @@
+
+ config MMC_SDHCI_S3C
+ tristate "SDHCI support on Samsung S3C SoC"
+- depends on MMC_SDHCI && PLAT_SAMSUNG
++ depends on PLAT_SAMSUNG && HAS_DMA
++ select MMC_SDHCI
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ often referrered to as the HSMMC block in some of the Samsung S3C
+@@ -204,8 +206,8 @@
+
+ config MMC_SDHCI_SIRF
+ tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
+- depends on ARCH_SIRF
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_SIRF && HAS_DMA
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the SDHCI support for SiRF System-on-Chip devices.
+
+@@ -215,8 +217,7 @@
+
+ config MMC_SDHCI_PXAV3
+ tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
+- depends on CLKDEV_LOOKUP
+- select MMC_SDHCI
++ depends on CLKDEV_LOOKUP && HAS_DMA
+ select MMC_SDHCI_PLTFM
+ default CPU_MMP2
+ help
+@@ -228,8 +229,7 @@
+
+ config MMC_SDHCI_PXAV2
+ tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
+- depends on CLKDEV_LOOKUP
+- select MMC_SDHCI
++ depends on CLKDEV_LOOKUP && HAS_DMA
+ select MMC_SDHCI_PLTFM
+ default CPU_PXA910
+ help
+@@ -241,7 +241,8 @@
+
+ config MMC_SDHCI_SPEAR
+ tristate "SDHCI support on ST SPEAr platform"
+- depends on MMC_SDHCI && PLAT_SPEAR
++ depends on PLAT_SPEAR && HAS_DMA
++ select MMC_SDHCI
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ often referrered to as the HSMMC block in some of the ST SPEAR range
+@@ -263,7 +264,7 @@
+
+ config MMC_SDHCI_BCM_KONA
+ tristate "SDHCI support on Broadcom KONA platform"
+- depends on ARCH_BCM_MOBILE
++ depends on ARCH_BCM_MOBILE && HAS_DMA
+ select MMC_SDHCI_PLTFM
+ help
+ This selects the Broadcom Kona Secure Digital Host Controller
+@@ -274,9 +275,9 @@
+
+ config MMC_SDHCI_BCM2835
+ tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
+- depends on ARCH_BCM2835
+- depends on MMC_SDHCI_PLTFM
++ depends on ARCH_BCM2835 && HAS_DMA
+ select MMC_SDHCI_IO_ACCESSORS
++ select MMC_SDHCI_PLTFM
+ help
+ This selects the BCM2835 SD/MMC controller. If you have a BCM2835
+ platform with SD or MMC devices, say Y or M here.
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-acpi.c linux-3.15-rc6/drivers/mmc/host/sdhci-acpi.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-acpi.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-acpi.c 2014-05-23 11:26:48.300940018 +0200
+@@ -102,11 +102,19 @@
+ }
+
+ static const struct sdhci_ops sdhci_acpi_ops_dflt = {
++ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_acpi_enable_dma,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_ops sdhci_acpi_ops_int = {
++ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_acpi_enable_dma,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .hw_reset = sdhci_acpi_int_hw_reset,
+ };
+
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-bcm2835.c linux-3.15-rc6/drivers/mmc/host/sdhci-bcm2835.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-bcm2835.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-bcm2835.c 2014-05-23 11:26:48.300940018 +0200
+@@ -131,8 +131,12 @@
+ .read_l = bcm2835_sdhci_readl,
+ .read_w = bcm2835_sdhci_readw,
+ .read_b = bcm2835_sdhci_readb,
++ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_min_clock = bcm2835_sdhci_get_min_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-bcm-kona.c linux-3.15-rc6/drivers/mmc/host/sdhci-bcm-kona.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-bcm-kona.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-bcm-kona.c 2014-05-23 11:26:48.300940018 +0200
+@@ -206,9 +206,13 @@
+ }
+
+ static struct sdhci_ops sdhci_bcm_kona_ops = {
++ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_bcm_kona_get_max_clk,
+ .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
+ .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .card_event = sdhci_bcm_kona_card_event,
+ };
+
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci.c linux-3.15-rc6/drivers/mmc/host/sdhci.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci.c 2014-05-23 11:26:48.304940032 +0200
+@@ -44,6 +44,8 @@
+
+ #define MAX_TUNING_LOOP 40
+
++#define ADMA_SIZE ((128 * 2 + 1) * 4)
++
+ static unsigned int debug_quirks = 0;
+ static unsigned int debug_quirks2;
+
+@@ -131,43 +133,26 @@
+ * *
+ \*****************************************************************************/
+
+-static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+-{
+- u32 ier;
+-
+- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+- ier &= ~clear;
+- ier |= set;
+- sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+- sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+-}
+-
+-static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
+-{
+- sdhci_clear_set_irqs(host, 0, irqs);
+-}
+-
+-static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
+-{
+- sdhci_clear_set_irqs(host, irqs, 0);
+-}
+-
+ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
+ {
+- u32 present, irqs;
++ u32 present;
+
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
+ (host->mmc->caps & MMC_CAP_NONREMOVABLE))
+ return;
+
+- present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+- SDHCI_CARD_PRESENT;
+- irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
++ if (enable) {
++ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
++ SDHCI_CARD_PRESENT;
+
+- if (enable)
+- sdhci_unmask_irqs(host, irqs);
+- else
+- sdhci_mask_irqs(host, irqs);
++ host->ier |= present ? SDHCI_INT_CARD_REMOVE :
++ SDHCI_INT_CARD_INSERT;
++ } else {
++ host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
++ }
++
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ }
+
+ static void sdhci_enable_card_detection(struct sdhci_host *host)
+@@ -180,22 +165,9 @@
+ sdhci_set_card_detection(host, false);
+ }
+
+-static void sdhci_reset(struct sdhci_host *host, u8 mask)
++void sdhci_reset(struct sdhci_host *host, u8 mask)
+ {
+ unsigned long timeout;
+- u32 uninitialized_var(ier);
+-
+- if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+- SDHCI_CARD_PRESENT))
+- return;
+- }
+-
+- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+-
+- if (host->ops->platform_reset_enter)
+- host->ops->platform_reset_enter(host, mask);
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+@@ -220,16 +192,27 @@
+ timeout--;
+ mdelay(1);
+ }
++}
++EXPORT_SYMBOL_GPL(sdhci_reset);
++
++static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
++{
++ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
++ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
++ SDHCI_CARD_PRESENT))
++ return;
++ }
+
+- if (host->ops->platform_reset_exit)
+- host->ops->platform_reset_exit(host, mask);
++ host->ops->reset(host, mask);
+
+- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
++ if (mask & SDHCI_RESET_ALL) {
++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
++ if (host->ops->enable_dma)
++ host->ops->enable_dma(host);
++ }
+
+- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+- if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
+- host->ops->enable_dma(host);
++ /* Resetting the controller clears many */
++ host->preset_enabled = false;
+ }
+ }
+
+@@ -238,15 +221,18 @@
+ static void sdhci_init(struct sdhci_host *host, int soft)
+ {
+ if (soft)
+- sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
++ sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+ else
+- sdhci_reset(host, SDHCI_RESET_ALL);
++ sdhci_do_reset(host, SDHCI_RESET_ALL);
++
++ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
++ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT |
++ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC |
++ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
++ SDHCI_INT_RESPONSE;
+
+- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+- SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+- SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
+- SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
+- SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ if (soft) {
+ /* force clock reconfiguration */
+@@ -502,11 +488,6 @@
+ else
+ direction = DMA_TO_DEVICE;
+
+- /*
+- * The ADMA descriptor table is mapped further down as we
+- * need to fill it with data first.
+- */
+-
+ host->align_addr = dma_map_single(mmc_dev(host->mmc),
+ host->align_buffer, 128 * 4, direction);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
+@@ -567,7 +548,7 @@
+ * If this triggers then we have a calculation bug
+ * somewhere. :/
+ */
+- WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4);
++ WARN_ON((desc - host->adma_desc) > ADMA_SIZE);
+ }
+
+ if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
+@@ -595,17 +576,8 @@
+ host->align_addr, 128 * 4, direction);
+ }
+
+- host->adma_addr = dma_map_single(mmc_dev(host->mmc),
+- host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+- if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr))
+- goto unmap_entries;
+- BUG_ON(host->adma_addr & 0x3);
+-
+ return 0;
+
+-unmap_entries:
+- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+- data->sg_len, direction);
+ unmap_align:
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+@@ -623,19 +595,25 @@
+ u8 *align;
+ char *buffer;
+ unsigned long flags;
++ bool has_unaligned;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+- dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
+- (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+-
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+
+- if (data->flags & MMC_DATA_READ) {
++ /* Do a quick scan of the SG list for any unaligned mappings */
++ has_unaligned = false;
++ for_each_sg(data->sg, sg, host->sg_count, i)
++ if (sg_dma_address(sg) & 3) {
++ has_unaligned = true;
++ break;
++ }
++
++ if (has_unaligned && data->flags & MMC_DATA_READ) {
+ dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+
+@@ -721,9 +699,12 @@
+ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+- sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
++ host->ier = (host->ier & ~pio_irqs) | dma_irqs;
+ else
+- sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
++ host->ier = (host->ier & ~dma_irqs) | pio_irqs;
++
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ }
+
+ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+@@ -976,8 +957,8 @@
+ * upon error conditions.
+ */
+ if (data->error) {
+- sdhci_reset(host, SDHCI_RESET_CMD);
+- sdhci_reset(host, SDHCI_RESET_DATA);
++ sdhci_do_reset(host, SDHCI_RESET_CMD);
++ sdhci_do_reset(host, SDHCI_RESET_DATA);
+ }
+
+ sdhci_send_command(host, data->stop);
+@@ -1107,24 +1088,23 @@
+
+ static u16 sdhci_get_preset_value(struct sdhci_host *host)
+ {
+- u16 ctrl, preset = 0;
++ u16 preset = 0;
+
+- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+-
+- switch (ctrl & SDHCI_CTRL_UHS_MASK) {
+- case SDHCI_CTRL_UHS_SDR12:
++ switch (host->timing) {
++ case MMC_TIMING_UHS_SDR12:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);
+ break;
+- case SDHCI_CTRL_UHS_SDR25:
++ case MMC_TIMING_UHS_SDR25:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25);
+ break;
+- case SDHCI_CTRL_UHS_SDR50:
++ case MMC_TIMING_UHS_SDR50:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50);
+ break;
+- case SDHCI_CTRL_UHS_SDR104:
++ case MMC_TIMING_UHS_SDR104:
++ case MMC_TIMING_MMC_HS200:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);
+ break;
+- case SDHCI_CTRL_UHS_DDR50:
++ case MMC_TIMING_UHS_DDR50:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
+ break;
+ default:
+@@ -1136,32 +1116,22 @@
+ return preset;
+ }
+
+-static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
++void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+ {
+ int div = 0; /* Initialized for compiler warning */
+ int real_div = div, clk_mul = 1;
+ u16 clk = 0;
+ unsigned long timeout;
+
+- if (clock && clock == host->clock)
+- return;
+-
+ host->mmc->actual_clock = 0;
+
+- if (host->ops->set_clock) {
+- host->ops->set_clock(host, clock);
+- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
+- return;
+- }
+-
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+- goto out;
++ return;
+
+ if (host->version >= SDHCI_SPEC_300) {
+- if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+- SDHCI_CTRL_PRESET_VAL_ENABLE) {
++ if (host->preset_enabled) {
+ u16 pre_val;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+@@ -1247,26 +1217,16 @@
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+-
+-out:
+- host->clock = clock;
+-}
+-
+-static inline void sdhci_update_clock(struct sdhci_host *host)
+-{
+- unsigned int clock;
+-
+- clock = host->clock;
+- host->clock = 0;
+- sdhci_set_clock(host, clock);
+ }
++EXPORT_SYMBOL_GPL(sdhci_set_clock);
+
+-static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
++static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
++ unsigned short vdd)
+ {
+ u8 pwr = 0;
+
+- if (power != (unsigned short)-1) {
+- switch (1 << power) {
++ if (mode != MMC_POWER_OFF) {
++ switch (1 << vdd) {
+ case MMC_VDD_165_195:
+ pwr = SDHCI_POWER_180;
+ break;
+@@ -1284,7 +1244,7 @@
+ }
+
+ if (host->pwr == pwr)
+- return -1;
++ return;
+
+ host->pwr = pwr;
+
+@@ -1292,38 +1252,43 @@
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_off(host);
+- return 0;
+- }
+-
+- /*
+- * Spec says that we should clear the power reg before setting
+- * a new value. Some controllers don't seem to like this though.
+- */
+- if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
+- sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
++ vdd = 0;
++ } else {
++ /*
++ * Spec says that we should clear the power reg before setting
++ * a new value. Some controllers don't seem to like this though.
++ */
++ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
++ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+- /*
+- * At least the Marvell CaFe chip gets confused if we set the voltage
+- * and set turn on power at the same time, so set the voltage first.
+- */
+- if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
+- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
++ /*
++ * At least the Marvell CaFe chip gets confused if we set the
++ * voltage and set turn on power at the same time, so set the
++ * voltage first.
++ */
++ if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
++ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+- pwr |= SDHCI_POWER_ON;
++ pwr |= SDHCI_POWER_ON;
+
+- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
++ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+- if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+- sdhci_runtime_pm_bus_on(host);
++ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
++ sdhci_runtime_pm_bus_on(host);
+
+- /*
+- * Some controllers need an extra 10ms delay of 10ms before they
+- * can apply clock after applying power
+- */
+- if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
+- mdelay(10);
++ /*
++ * Some controllers need an extra 10ms delay of 10ms before
++ * they can apply clock after applying power
++ */
++ if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
++ mdelay(10);
++ }
+
+- return power;
++ if (host->vmmc) {
++ spin_unlock_irq(&host->lock);
++ mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd);
++ spin_lock_irq(&host->lock);
++ }
+ }
+
+ /*****************************************************************************\
+@@ -1427,10 +1392,52 @@
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
++void sdhci_set_bus_width(struct sdhci_host *host, int width)
++{
++ u8 ctrl;
++
++ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
++ if (width == MMC_BUS_WIDTH_8) {
++ ctrl &= ~SDHCI_CTRL_4BITBUS;
++ if (host->version >= SDHCI_SPEC_300)
++ ctrl |= SDHCI_CTRL_8BITBUS;
++ } else {
++ if (host->version >= SDHCI_SPEC_300)
++ ctrl &= ~SDHCI_CTRL_8BITBUS;
++ if (width == MMC_BUS_WIDTH_4)
++ ctrl |= SDHCI_CTRL_4BITBUS;
++ else
++ ctrl &= ~SDHCI_CTRL_4BITBUS;
++ }
++ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
++}
++EXPORT_SYMBOL_GPL(sdhci_set_bus_width);
++
++void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
++{
++ u16 ctrl_2;
++
++ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
++ /* Select Bus Speed Mode for host */
++ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
++ if ((timing == MMC_TIMING_MMC_HS200) ||
++ (timing == MMC_TIMING_UHS_SDR104))
++ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
++ else if (timing == MMC_TIMING_UHS_SDR12)
++ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
++ else if (timing == MMC_TIMING_UHS_SDR25)
++ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
++ else if (timing == MMC_TIMING_UHS_SDR50)
++ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
++ else if (timing == MMC_TIMING_UHS_DDR50)
++ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
++ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
++}
++EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
++
+ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
+ {
+ unsigned long flags;
+- int vdd_bit = -1;
+ u8 ctrl;
+
+ spin_lock_irqsave(&host->lock, flags);
+@@ -1456,45 +1463,17 @@
+ !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+ sdhci_enable_preset_value(host, false);
+
+- sdhci_set_clock(host, ios->clock);
+-
+- if (ios->power_mode == MMC_POWER_OFF)
+- vdd_bit = sdhci_set_power(host, -1);
+- else
+- vdd_bit = sdhci_set_power(host, ios->vdd);
+-
+- if (host->vmmc && vdd_bit != -1) {
+- spin_unlock_irqrestore(&host->lock, flags);
+- mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
+- spin_lock_irqsave(&host->lock, flags);
++ if (!ios->clock || ios->clock != host->clock) {
++ host->ops->set_clock(host, ios->clock);
++ host->clock = ios->clock;
+ }
+
++ sdhci_set_power(host, ios->power_mode, ios->vdd);
++
+ if (host->ops->platform_send_init_74_clocks)
+ host->ops->platform_send_init_74_clocks(host, ios->power_mode);
+
+- /*
+- * If your platform has 8-bit width support but is not a v3 controller,
+- * or if it requires special setup code, you should implement that in
+- * platform_bus_width().
+- */
+- if (host->ops->platform_bus_width) {
+- host->ops->platform_bus_width(host, ios->bus_width);
+- } else {
+- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+- if (ios->bus_width == MMC_BUS_WIDTH_8) {
+- ctrl &= ~SDHCI_CTRL_4BITBUS;
+- if (host->version >= SDHCI_SPEC_300)
+- ctrl |= SDHCI_CTRL_8BITBUS;
+- } else {
+- if (host->version >= SDHCI_SPEC_300)
+- ctrl &= ~SDHCI_CTRL_8BITBUS;
+- if (ios->bus_width == MMC_BUS_WIDTH_4)
+- ctrl |= SDHCI_CTRL_4BITBUS;
+- else
+- ctrl &= ~SDHCI_CTRL_4BITBUS;
+- }
+- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+- }
++ host->ops->set_bus_width(host, ios->bus_width);
+
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+
+@@ -1516,13 +1495,13 @@
+ (ios->timing == MMC_TIMING_UHS_SDR25))
+ ctrl |= SDHCI_CTRL_HISPD;
+
+- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+- if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
++ if (!host->preset_enabled) {
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ /*
+ * We only need to set Driver Strength if the
+ * preset value enable is not set.
+ */
++ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
+ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+@@ -1546,34 +1525,11 @@
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
+ /* Re-enable SD Clock */
+- sdhci_update_clock(host);
++ host->ops->set_clock(host, host->clock);
+ }
+
+-
+- /* Reset SD Clock Enable */
+- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+- clk &= ~SDHCI_CLOCK_CARD_EN;
+- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+-
+- if (host->ops->set_uhs_signaling)
+- host->ops->set_uhs_signaling(host, ios->timing);
+- else {
+- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+- /* Select Bus Speed Mode for host */
+- ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+- if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+- (ios->timing == MMC_TIMING_UHS_SDR104))
+- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+- else if (ios->timing == MMC_TIMING_UHS_SDR12)
+- ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+- else if (ios->timing == MMC_TIMING_UHS_SDR25)
+- ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+- else if (ios->timing == MMC_TIMING_UHS_SDR50)
+- ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+- else if (ios->timing == MMC_TIMING_UHS_DDR50)
+- ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+- sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+- }
++ host->ops->set_uhs_signaling(host, ios->timing);
++ host->timing = ios->timing;
+
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&
+ ((ios->timing == MMC_TIMING_UHS_SDR12) ||
+@@ -1588,9 +1544,6 @@
+ ios->drv_type = (preset & SDHCI_PRESET_DRV_MASK)
+ >> SDHCI_PRESET_DRV_SHIFT;
+ }
+-
+- /* Re-enable SD Clock */
+- sdhci_update_clock(host);
+ } else
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
+@@ -1600,7 +1553,7 @@
+ * it on each ios seems to solve the problem.
+ */
+ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
++ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+@@ -1709,24 +1662,16 @@
+
+ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
+ {
+- if (host->flags & SDHCI_DEVICE_DEAD)
+- goto out;
+-
+- if (enable)
+- host->flags |= SDHCI_SDIO_IRQ_ENABLED;
+- else
+- host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
+-
+- /* SDIO IRQ will be enabled as appropriate in runtime resume */
+- if (host->runtime_suspended)
+- goto out;
++ if (!(host->flags & SDHCI_DEVICE_DEAD)) {
++ if (enable)
++ host->ier |= SDHCI_INT_CARD_INT;
++ else
++ host->ier &= ~SDHCI_INT_CARD_INT;
+
+- if (enable)
+- sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
+- else
+- sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
+-out:
+- mmiowb();
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
++ mmiowb();
++ }
+ }
+
+ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+@@ -1734,9 +1679,18 @@
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
++ sdhci_runtime_pm_get(host);
++
+ spin_lock_irqsave(&host->lock, flags);
++ if (enable)
++ host->flags |= SDHCI_SDIO_IRQ_ENABLED;
++ else
++ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
++
+ sdhci_enable_sdio_irq_nolock(host, enable);
+ spin_unlock_irqrestore(&host->lock, flags);
++
++ sdhci_runtime_pm_put(host);
+ }
+
+ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
+@@ -1798,9 +1752,6 @@
+ ctrl |= SDHCI_CTRL_VDD_180;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+- /* Wait for 5ms */
+- usleep_range(5000, 5500);
+-
+ /* 1.8V regulator output should be stable within 5 ms */
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (ctrl & SDHCI_CTRL_VDD_180)
+@@ -1855,22 +1806,16 @@
+
+ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+ {
+- struct sdhci_host *host;
++ struct sdhci_host *host = mmc_priv(mmc);
+ u16 ctrl;
+- u32 ier;
+ int tuning_loop_counter = MAX_TUNING_LOOP;
+ unsigned long timeout;
+ int err = 0;
+- bool requires_tuning_nonuhs = false;
+ unsigned long flags;
+
+- host = mmc_priv(mmc);
+-
+ sdhci_runtime_pm_get(host);
+ spin_lock_irqsave(&host->lock, flags);
+
+- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+-
+ /*
+ * The Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in the
+@@ -1878,15 +1823,18 @@
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+- (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
+- host->flags & SDHCI_SDR104_NEEDS_TUNING))
+- requires_tuning_nonuhs = true;
+-
+- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
+- requires_tuning_nonuhs)
+- ctrl |= SDHCI_CTRL_EXEC_TUNING;
+- else {
++ switch (host->timing) {
++ case MMC_TIMING_MMC_HS200:
++ case MMC_TIMING_UHS_SDR104:
++ break;
++
++ case MMC_TIMING_UHS_SDR50:
++ if (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
++ host->flags & SDHCI_SDR104_NEEDS_TUNING)
++ break;
++ /* FALLTHROUGH */
++
++ default:
+ spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_runtime_pm_put(host);
+ return 0;
+@@ -1899,6 +1847,8 @@
+ return err;
+ }
+
++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
++ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ /*
+@@ -1911,8 +1861,8 @@
+ * to make sure we don't hit a controller bug, we _only_
+ * enable Buffer Read Ready interrupt here.
+ */
+- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+- sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
+
+ /*
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+@@ -2044,7 +1994,8 @@
+ if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
+ err = 0;
+
+- sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_runtime_pm_put(host);
+
+@@ -2054,26 +2005,30 @@
+
+ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
+ {
+- u16 ctrl;
+-
+ /* Host Controller v3.00 defines preset value registers */
+ if (host->version < SDHCI_SPEC_300)
+ return;
+
+- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+-
+ /*
+ * We only enable or disable Preset Value if they are not already
+ * enabled or disabled respectively. Otherwise, we bail out.
+ */
+- if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+- ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
+- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+- host->flags |= SDHCI_PV_ENABLED;
+- } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+- ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
++ if (host->preset_enabled != enable) {
++ u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
++
++ if (enable)
++ ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
++ else
++ ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
++
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+- host->flags &= ~SDHCI_PV_ENABLED;
++
++ if (enable)
++ host->flags |= SDHCI_PV_ENABLED;
++ else
++ host->flags &= ~SDHCI_PV_ENABLED;
++
++ host->preset_enabled = enable;
+ }
+ }
+
+@@ -2095,8 +2050,8 @@
+ pr_err("%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+- sdhci_reset(host, SDHCI_RESET_CMD);
+- sdhci_reset(host, SDHCI_RESET_DATA);
++ sdhci_do_reset(host, SDHCI_RESET_CMD);
++ sdhci_do_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+@@ -2124,15 +2079,6 @@
+ * *
+ \*****************************************************************************/
+
+-static void sdhci_tasklet_card(unsigned long param)
+-{
+- struct sdhci_host *host = (struct sdhci_host*)param;
+-
+- sdhci_card_event(host->mmc);
+-
+- mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+-}
+-
+ static void sdhci_tasklet_finish(unsigned long param)
+ {
+ struct sdhci_host *host;
+@@ -2169,12 +2115,12 @@
+ /* Some controllers need this kick or reset won't work here */
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
+ /* This is to force an update */
+- sdhci_update_clock(host);
++ host->ops->set_clock(host, host->clock);
+
+ /* Spec says we should do both at the same time, but Ricoh
+ controllers do not like that. */
+- sdhci_reset(host, SDHCI_RESET_CMD);
+- sdhci_reset(host, SDHCI_RESET_DATA);
++ sdhci_do_reset(host, SDHCI_RESET_CMD);
++ sdhci_do_reset(host, SDHCI_RESET_DATA);
+ }
+
+ host->mrq = NULL;
+@@ -2424,101 +2370,94 @@
+
+ static irqreturn_t sdhci_irq(int irq, void *dev_id)
+ {
+- irqreturn_t result;
++ irqreturn_t result = IRQ_NONE;
+ struct sdhci_host *host = dev_id;
+- u32 intmask, unexpected = 0;
+- int cardint = 0, max_loops = 16;
++ u32 intmask, mask, unexpected = 0;
++ int max_loops = 16;
+
+ spin_lock(&host->lock);
+
+- if (host->runtime_suspended) {
++ if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) {
+ spin_unlock(&host->lock);
+ return IRQ_NONE;
+ }
+
+ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+-
+ if (!intmask || intmask == 0xffffffff) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+-again:
+- DBG("*** %s got interrupt: 0x%08x\n",
+- mmc_hostname(host->mmc), intmask);
+-
+- if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+- u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+- SDHCI_CARD_PRESENT;
+-
+- /*
+- * There is a observation on i.mx esdhc. INSERT bit will be
+- * immediately set again when it gets cleared, if a card is
+- * inserted. We have to mask the irq to prevent interrupt
+- * storm which will freeze the system. And the REMOVE gets
+- * the same situation.
+- *
+- * More testing are needed here to ensure it works for other
+- * platforms though.
+- */
+- sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
+- SDHCI_INT_CARD_REMOVE);
+- sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
+- SDHCI_INT_CARD_INSERT);
+-
+- sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
+- SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+- tasklet_schedule(&host->card_tasklet);
+- }
+-
+- if (intmask & SDHCI_INT_CMD_MASK) {
+- sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
+- SDHCI_INT_STATUS);
+- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+- }
++ do {
++ /* Clear selected interrupts. */
++ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
++ SDHCI_INT_BUS_POWER);
++ sdhci_writel(host, mask, SDHCI_INT_STATUS);
++
++ DBG("*** %s got interrupt: 0x%08x\n",
++ mmc_hostname(host->mmc), intmask);
++
++ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
++ u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
++ SDHCI_CARD_PRESENT;
+
+- if (intmask & SDHCI_INT_DATA_MASK) {
+- sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
+- SDHCI_INT_STATUS);
+- sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+- }
++ /*
++ * There is a observation on i.mx esdhc. INSERT
++ * bit will be immediately set again when it gets
++ * cleared, if a card is inserted. We have to mask
++ * the irq to prevent interrupt storm which will
++ * freeze the system. And the REMOVE gets the
++ * same situation.
++ *
++ * More testing are needed here to ensure it works
++ * for other platforms though.
++ */
++ host->ier &= ~(SDHCI_INT_CARD_INSERT |
++ SDHCI_INT_CARD_REMOVE);
++ host->ier |= present ? SDHCI_INT_CARD_REMOVE :
++ SDHCI_INT_CARD_INSERT;
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+- intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
++ sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
++ SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+
+- intmask &= ~SDHCI_INT_ERROR;
++ host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT |
++ SDHCI_INT_CARD_REMOVE);
++ result = IRQ_WAKE_THREAD;
++ }
+
+- if (intmask & SDHCI_INT_BUS_POWER) {
+- pr_err("%s: Card is consuming too much power!\n",
+- mmc_hostname(host->mmc));
+- sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
+- }
++ if (intmask & SDHCI_INT_CMD_MASK)
++ sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+
+- intmask &= ~SDHCI_INT_BUS_POWER;
++ if (intmask & SDHCI_INT_DATA_MASK)
++ sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+
+- if (intmask & SDHCI_INT_CARD_INT)
+- cardint = 1;
++ if (intmask & SDHCI_INT_BUS_POWER)
++ pr_err("%s: Card is consuming too much power!\n",
++ mmc_hostname(host->mmc));
+
+- intmask &= ~SDHCI_INT_CARD_INT;
++ if (intmask & SDHCI_INT_CARD_INT) {
++ sdhci_enable_sdio_irq_nolock(host, false);
++ host->thread_isr |= SDHCI_INT_CARD_INT;
++ result = IRQ_WAKE_THREAD;
++ }
+
+- if (intmask) {
+- unexpected |= intmask;
+- sdhci_writel(host, intmask, SDHCI_INT_STATUS);
+- }
++ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
++ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
++ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
++ SDHCI_INT_CARD_INT);
+
+- result = IRQ_HANDLED;
++ if (intmask) {
++ unexpected |= intmask;
++ sdhci_writel(host, intmask, SDHCI_INT_STATUS);
++ }
+
+- intmask = sdhci_readl(host, SDHCI_INT_STATUS);
++ if (result == IRQ_NONE)
++ result = IRQ_HANDLED;
+
+- /*
+- * If we know we'll call the driver to signal SDIO IRQ, disregard
+- * further indications of Card Interrupt in the status to avoid a
+- * needless loop.
+- */
+- if (cardint)
+- intmask &= ~SDHCI_INT_CARD_INT;
+- if (intmask && --max_loops)
+- goto again;
++ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
++ } while (intmask && --max_loops);
+ out:
+ spin_unlock(&host->lock);
+
+@@ -2527,15 +2466,38 @@
+ mmc_hostname(host->mmc), unexpected);
+ sdhci_dumpregs(host);
+ }
+- /*
+- * We have to delay this as it calls back into the driver.
+- */
+- if (cardint)
+- mmc_signal_sdio_irq(host->mmc);
+
+ return result;
+ }
+
++static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
++{
++ struct sdhci_host *host = dev_id;
++ unsigned long flags;
++ u32 isr;
++
++ spin_lock_irqsave(&host->lock, flags);
++ isr = host->thread_isr;
++ host->thread_isr = 0;
++ spin_unlock_irqrestore(&host->lock, flags);
++
++ if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
++ sdhci_card_event(host->mmc);
++ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
++ }
++
++ if (isr & SDHCI_INT_CARD_INT) {
++ sdio_run_irqs(host->mmc);
++
++ spin_lock_irqsave(&host->lock, flags);
++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
++ sdhci_enable_sdio_irq_nolock(host, true);
++ spin_unlock_irqrestore(&host->lock, flags);
++ }
++
++ return isr ? IRQ_HANDLED : IRQ_NONE;
++}
++
+ /*****************************************************************************\
+ * *
+ * Suspend/resume *
+@@ -2572,9 +2534,6 @@
+
+ int sdhci_suspend_host(struct sdhci_host *host)
+ {
+- if (host->ops->platform_suspend)
+- host->ops->platform_suspend(host);
+-
+ sdhci_disable_card_detection(host);
+
+ /* Disable tuning since we are suspending */
+@@ -2584,7 +2543,9 @@
+ }
+
+ if (!device_may_wakeup(mmc_dev(host->mmc))) {
+- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
++ host->ier = 0;
++ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+ free_irq(host->irq, host);
+ } else {
+ sdhci_enable_irq_wakeups(host);
+@@ -2605,8 +2566,9 @@
+ }
+
+ if (!device_may_wakeup(mmc_dev(host->mmc))) {
+- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+- mmc_hostname(host->mmc), host);
++ ret = request_threaded_irq(host->irq, sdhci_irq,
++ sdhci_thread_irq, IRQF_SHARED,
++ mmc_hostname(host->mmc), host);
+ if (ret)
+ return ret;
+ } else {
+@@ -2628,9 +2590,6 @@
+
+ sdhci_enable_card_detection(host);
+
+- if (host->ops->platform_resume)
+- host->ops->platform_resume(host);
+-
+ /* Set the re-tuning expiration flag */
+ if (host->flags & SDHCI_USING_RETUNING_TIMER)
+ host->flags |= SDHCI_NEEDS_RETUNING;
+@@ -2682,10 +2641,12 @@
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
++ host->ier &= SDHCI_INT_CARD_INT;
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+- synchronize_irq(host->irq);
++ synchronize_hardirq(host->irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = true;
+@@ -2729,7 +2690,7 @@
+ host->runtime_suspended = false;
+
+ /* Enable SDIO IRQ */
+- if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
+ sdhci_enable_sdio_irq_nolock(host, true);
+
+ /* Enable Card Detection */
+@@ -2788,7 +2749,7 @@
+ if (debug_quirks2)
+ host->quirks2 = debug_quirks2;
+
+- sdhci_reset(host, SDHCI_RESET_ALL);
++ sdhci_do_reset(host, SDHCI_RESET_ALL);
+
+ host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+ host->version = (host->version & SDHCI_SPEC_VER_MASK)
+@@ -2848,15 +2809,29 @@
+ * (128) and potentially one alignment transfer for
+ * each of those entries.
+ */
+- host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
++ host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc),
++ ADMA_SIZE, &host->adma_addr,
++ GFP_KERNEL);
+ host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
+ if (!host->adma_desc || !host->align_buffer) {
+- kfree(host->adma_desc);
++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
++ host->adma_desc, host->adma_addr);
+ kfree(host->align_buffer);
+ pr_warning("%s: Unable to allocate ADMA "
+ "buffers. Falling back to standard DMA.\n",
+ mmc_hostname(mmc));
+ host->flags &= ~SDHCI_USE_ADMA;
++ host->adma_desc = NULL;
++ host->align_buffer = NULL;
++ } else if (host->adma_addr & 3) {
++ pr_warning("%s: unable to allocate aligned ADMA descriptor\n",
++ mmc_hostname(mmc));
++ host->flags &= ~SDHCI_USE_ADMA;
++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
++ host->adma_desc, host->adma_addr);
++ kfree(host->align_buffer);
++ host->adma_desc = NULL;
++ host->align_buffer = NULL;
+ }
+ }
+
+@@ -2941,6 +2916,7 @@
+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
+
+ mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
++ mmc->caps2 |= MMC_CAP2_SDIO_NOTHREAD;
+
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+ host->flags |= SDHCI_AUTO_CMD12;
+@@ -3212,8 +3188,6 @@
+ /*
+ * Init tasklets.
+ */
+- tasklet_init(&host->card_tasklet,
+- sdhci_tasklet_card, (unsigned long)host);
+ tasklet_init(&host->finish_tasklet,
+ sdhci_tasklet_finish, (unsigned long)host);
+
+@@ -3230,8 +3204,8 @@
+
+ sdhci_init(host, 0);
+
+- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+- mmc_hostname(mmc), host);
++ ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
++ IRQF_SHARED, mmc_hostname(mmc), host);
+ if (ret) {
+ pr_err("%s: Failed to request IRQ %d: %d\n",
+ mmc_hostname(mmc), host->irq, ret);
+@@ -3273,12 +3247,12 @@
+
+ #ifdef SDHCI_USE_LEDS_CLASS
+ reset:
+- sdhci_reset(host, SDHCI_RESET_ALL);
+- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
++ sdhci_do_reset(host, SDHCI_RESET_ALL);
++ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+ free_irq(host->irq, host);
+ #endif
+ untasklet:
+- tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ return ret;
+@@ -3315,14 +3289,14 @@
+ #endif
+
+ if (!dead)
+- sdhci_reset(host, SDHCI_RESET_ALL);
++ sdhci_do_reset(host, SDHCI_RESET_ALL);
+
+- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
++ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+ free_irq(host->irq, host);
+
+ del_timer_sync(&host->timer);
+
+- tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ if (host->vmmc) {
+@@ -3335,7 +3309,9 @@
+ regulator_put(host->vqmmc);
+ }
+
+- kfree(host->adma_desc);
++ if (host->adma_desc)
++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
++ host->adma_desc, host->adma_addr);
+ kfree(host->align_buffer);
+
+ host->adma_desc = NULL;
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-cns3xxx.c linux-3.15-rc6/drivers/mmc/host/sdhci-cns3xxx.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-cns3xxx.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-cns3xxx.c 2014-05-23 11:26:48.304940032 +0200
+@@ -30,13 +30,12 @@
+ u16 clk;
+ unsigned long timeout;
+
+- if (clock == host->clock)
+- return;
++ host->mmc->actual_clock = 0;
+
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+- goto out;
++ return;
+
+ while (host->max_clk / div > clock) {
+ /*
+@@ -75,13 +74,14 @@
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+-out:
+- host->clock = clock;
+ }
+
+ static const struct sdhci_ops sdhci_cns3xxx_ops = {
+ .get_max_clock = sdhci_cns3xxx_get_max_clk,
+ .set_clock = sdhci_cns3xxx_set_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
+@@ -90,8 +90,7 @@
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+- SDHCI_QUIRK_NONSTANDARD_CLOCK,
++ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ };
+
+ static int sdhci_cns3xxx_probe(struct platform_device *pdev)
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-dove.c linux-3.15-rc6/drivers/mmc/host/sdhci-dove.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-dove.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-dove.c 2014-05-23 11:26:48.304940032 +0200
+@@ -86,6 +86,10 @@
+ static const struct sdhci_ops sdhci_dove_ops = {
+ .read_w = sdhci_dove_readw,
+ .read_l = sdhci_dove_readl,
++ .set_clock = sdhci_set_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_pltfm_data sdhci_dove_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-esdhc.h linux-3.15-rc6/drivers/mmc/host/sdhci-esdhc.h
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-esdhc.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-esdhc.h 2014-05-23 11:26:48.304940032 +0200
+@@ -20,10 +20,8 @@
+
+ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \
+ SDHCI_QUIRK_NO_BUSY_IRQ | \
+- SDHCI_QUIRK_NONSTANDARD_CLOCK | \
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \
+- SDHCI_QUIRK_PIO_NEEDS_DELAY | \
+- SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
++ SDHCI_QUIRK_PIO_NEEDS_DELAY)
+
+ #define ESDHC_SYSTEM_CONTROL 0x2c
+ #define ESDHC_CLOCK_MASK 0x0000fff0
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-esdhc-imx.c linux-3.15-rc6/drivers/mmc/host/sdhci-esdhc-imx.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-esdhc-imx.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-esdhc-imx.c 2014-05-23 11:26:48.304940032 +0200
+@@ -160,7 +160,6 @@
+ MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
+ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
+ } multiblock_status;
+- u32 uhs_mode;
+ u32 is_ddr;
+ };
+
+@@ -382,7 +381,6 @@
+ if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
+ ret |= SDHCI_CTRL_TUNED_CLK;
+
+- ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
+ ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+ return ret;
+@@ -429,7 +427,6 @@
+ else
+ new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
+ writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+- imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
+ if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+ new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ if (val & SDHCI_CTRL_TUNED_CLK)
+@@ -600,12 +597,14 @@
+ u32 temp, val;
+
+ if (clock == 0) {
++ host->mmc->actual_clock = 0;
++
+ if (esdhc_is_usdhc(imx_data)) {
+ val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+ writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+ host->ioaddr + ESDHC_VENDOR_SPEC);
+ }
+- goto out;
++ return;
+ }
+
+ if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr)
+@@ -645,8 +644,6 @@
+ }
+
+ mdelay(1);
+-out:
+- host->clock = clock;
+ }
+
+ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
+@@ -668,7 +665,7 @@
+ return -ENOSYS;
+ }
+
+-static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
++static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
+ {
+ u32 ctrl;
+
+@@ -686,8 +683,6 @@
+
+ esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
+ SDHCI_HOST_CONTROL);
+-
+- return 0;
+ }
+
+ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
+@@ -697,6 +692,7 @@
+ /* FIXME: delay a bit for card to be ready for next tuning due to errors */
+ mdelay(1);
+
++ /* This is balanced by the runtime put in sdhci_tasklet_finish */
+ pm_runtime_get_sync(host->mmc->parent);
+ reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
+@@ -713,13 +709,12 @@
+ complete(&mrq->completion);
+ }
+
+-static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
++static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode,
++ struct scatterlist *sg)
+ {
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {NULL};
+ struct mmc_data data = {0};
+- struct scatterlist sg;
+- char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+@@ -728,11 +723,9 @@
+ data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+- data.sg = &sg;
++ data.sg = sg;
+ data.sg_len = 1;
+
+- sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
+-
+ mrq.cmd = &cmd;
+ mrq.cmd->mrq = &mrq;
+ mrq.data = &data;
+@@ -742,14 +735,12 @@
+ mrq.done = esdhc_request_done;
+ init_completion(&(mrq.completion));
+
+- disable_irq(host->irq);
+- spin_lock(&host->lock);
++ spin_lock_irq(&host->lock);
+ host->mrq = &mrq;
+
+ sdhci_send_command(host, mrq.cmd);
+
+- spin_unlock(&host->lock);
+- enable_irq(host->irq);
++ spin_unlock_irq(&host->lock);
+
+ wait_for_completion(&mrq.completion);
+
+@@ -772,13 +763,21 @@
+
+ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
+ {
++ struct scatterlist sg;
++ char *tuning_pattern;
+ int min, max, avg, ret;
+
++ tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL);
++ if (!tuning_pattern)
++ return -ENOMEM;
++
++ sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN);
++
+ /* find the mininum delay first which can pass tuning */
+ min = ESDHC_TUNE_CTRL_MIN;
+ while (min < ESDHC_TUNE_CTRL_MAX) {
+ esdhc_prepare_tuning(host, min);
+- if (!esdhc_send_tuning_cmd(host, opcode))
++ if (!esdhc_send_tuning_cmd(host, opcode, &sg))
+ break;
+ min += ESDHC_TUNE_CTRL_STEP;
+ }
+@@ -787,7 +786,7 @@
+ max = min + ESDHC_TUNE_CTRL_STEP;
+ while (max < ESDHC_TUNE_CTRL_MAX) {
+ esdhc_prepare_tuning(host, max);
+- if (esdhc_send_tuning_cmd(host, opcode)) {
++ if (esdhc_send_tuning_cmd(host, opcode, &sg)) {
+ max -= ESDHC_TUNE_CTRL_STEP;
+ break;
+ }
+@@ -797,9 +796,11 @@
+ /* use average delay to get the best timing */
+ avg = (min + max) / 2;
+ esdhc_prepare_tuning(host, avg);
+- ret = esdhc_send_tuning_cmd(host, opcode);
++ ret = esdhc_send_tuning_cmd(host, opcode, &sg);
+ esdhc_post_tuning(host);
+
++ kfree(tuning_pattern);
++
+ dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
+ ret ? "failed" : "passed", avg, ret);
+
+@@ -837,28 +838,20 @@
+ return pinctrl_select_state(imx_data->pinctrl, pinctrl);
+ }
+
+-static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
++static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
+ {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+- switch (uhs) {
++ switch (timing) {
+ case MMC_TIMING_UHS_SDR12:
+- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12;
+- break;
+ case MMC_TIMING_UHS_SDR25:
+- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25;
+- break;
+ case MMC_TIMING_UHS_SDR50:
+- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
+- break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+- imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50;
+ writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
+ ESDHC_MIX_CTRL_DDREN,
+ host->ioaddr + ESDHC_MIX_CTRL);
+@@ -875,7 +868,15 @@
+ break;
+ }
+
+- return esdhc_change_pinstate(host, uhs);
++ esdhc_change_pinstate(host, timing);
++}
++
++static void esdhc_reset(struct sdhci_host *host, u8 mask)
++{
++ sdhci_reset(host, mask);
++
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ }
+
+ static struct sdhci_ops sdhci_esdhc_ops = {
+@@ -888,8 +889,9 @@
+ .get_max_clock = esdhc_pltfm_get_max_clock,
+ .get_min_clock = esdhc_pltfm_get_min_clock,
+ .get_ro = esdhc_pltfm_get_ro,
+- .platform_bus_width = esdhc_pltfm_bus_width,
++ .set_bus_width = esdhc_pltfm_set_bus_width,
+ .set_uhs_signaling = esdhc_set_uhs_signaling,
++ .reset = esdhc_reset,
+ };
+
+ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
+@@ -1170,8 +1172,10 @@
+
+ ret = sdhci_runtime_suspend_host(host);
+
+- clk_disable_unprepare(imx_data->clk_per);
+- clk_disable_unprepare(imx_data->clk_ipg);
++ if (!sdhci_sdio_irq_enabled(host)) {
++ clk_disable_unprepare(imx_data->clk_per);
++ clk_disable_unprepare(imx_data->clk_ipg);
++ }
+ clk_disable_unprepare(imx_data->clk_ahb);
+
+ return ret;
+@@ -1183,8 +1187,10 @@
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
+- clk_prepare_enable(imx_data->clk_per);
+- clk_prepare_enable(imx_data->clk_ipg);
++ if (!sdhci_sdio_irq_enabled(host)) {
++ clk_prepare_enable(imx_data->clk_per);
++ clk_prepare_enable(imx_data->clk_ipg);
++ }
+ clk_prepare_enable(imx_data->clk_ahb);
+
+ return sdhci_runtime_resume_host(host);
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci.h linux-3.15-rc6/drivers/mmc/host/sdhci.h
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci.h 2014-05-23 11:26:48.304940032 +0200
+@@ -281,18 +281,14 @@
+ unsigned int (*get_max_clock)(struct sdhci_host *host);
+ unsigned int (*get_min_clock)(struct sdhci_host *host);
+ unsigned int (*get_timeout_clock)(struct sdhci_host *host);
+- int (*platform_bus_width)(struct sdhci_host *host,
+- int width);
++ void (*set_bus_width)(struct sdhci_host *host, int width);
+ void (*platform_send_init_74_clocks)(struct sdhci_host *host,
+ u8 power_mode);
+ unsigned int (*get_ro)(struct sdhci_host *host);
+- void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
+- void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
++ void (*reset)(struct sdhci_host *host, u8 mask);
+ int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
+- int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
++ void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
+ void (*hw_reset)(struct sdhci_host *host);
+- void (*platform_suspend)(struct sdhci_host *host);
+- void (*platform_resume)(struct sdhci_host *host);
+ void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
+ void (*platform_init)(struct sdhci_host *host);
+ void (*card_event)(struct sdhci_host *host);
+@@ -397,6 +393,16 @@
+ extern void sdhci_send_command(struct sdhci_host *host,
+ struct mmc_command *cmd);
+
++static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
++{
++ return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
++}
++
++void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
++void sdhci_set_bus_width(struct sdhci_host *host, int width);
++void sdhci_reset(struct sdhci_host *host, u8 mask);
++void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
++
+ #ifdef CONFIG_PM
+ extern int sdhci_suspend_host(struct sdhci_host *host);
+ extern int sdhci_resume_host(struct sdhci_host *host);
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-arasan.c linux-3.15-rc6/drivers/mmc/host/sdhci-of-arasan.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-arasan.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-of-arasan.c 2014-05-23 11:26:48.304940032 +0200
+@@ -52,8 +52,12 @@
+ }
+
+ static struct sdhci_ops sdhci_arasan_ops = {
++ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_timeout_clock = sdhci_arasan_get_timeout_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static struct sdhci_pltfm_data sdhci_arasan_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-esdhc.c linux-3.15-rc6/drivers/mmc/host/sdhci-of-esdhc.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-esdhc.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-of-esdhc.c 2014-05-23 11:26:48.304940032 +0200
+@@ -199,13 +199,14 @@
+
+ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
+ {
+-
+ int pre_div = 2;
+ int div = 1;
+ u32 temp;
+
++ host->mmc->actual_clock = 0;
++
+ if (clock == 0)
+- goto out;
++ return;
+
+ /* Workaround to reduce the clock frequency for p1010 esdhc */
+ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
+@@ -238,24 +239,8 @@
+ | (pre_div << ESDHC_PREDIV_SHIFT));
+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+ mdelay(1);
+-out:
+- host->clock = clock;
+ }
+
+-#ifdef CONFIG_PM
+-static u32 esdhc_proctl;
+-static void esdhc_of_suspend(struct sdhci_host *host)
+-{
+- esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
+-}
+-
+-static void esdhc_of_resume(struct sdhci_host *host)
+-{
+- esdhc_of_enable_dma(host);
+- sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
+-}
+-#endif
+-
+ static void esdhc_of_platform_init(struct sdhci_host *host)
+ {
+ u32 vvn;
+@@ -269,7 +254,7 @@
+ host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
+ }
+
+-static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
++static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
+ {
+ u32 ctrl;
+
+@@ -289,8 +274,6 @@
+
+ clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
+ ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
+-
+- return 0;
+ }
+
+ static const struct sdhci_ops sdhci_esdhc_ops = {
+@@ -305,13 +288,46 @@
+ .get_max_clock = esdhc_of_get_max_clock,
+ .get_min_clock = esdhc_of_get_min_clock,
+ .platform_init = esdhc_of_platform_init,
+-#ifdef CONFIG_PM
+- .platform_suspend = esdhc_of_suspend,
+- .platform_resume = esdhc_of_resume,
+-#endif
+ .adma_workaround = esdhci_of_adma_workaround,
+- .platform_bus_width = esdhc_pltfm_bus_width,
++ .set_bus_width = esdhc_pltfm_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
++};
++
++#ifdef CONFIG_PM
++
++static u32 esdhc_proctl;
++static int esdhc_of_suspend(struct device *dev)
++{
++ struct sdhci_host *host = dev_get_drvdata(dev);
++
++ esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
++
++ return sdhci_suspend_host(host);
++}
++
++static void esdhc_of_resume(device *dev)
++{
++ struct sdhci_host *host = dev_get_drvdata(dev);
++ int ret = sdhci_resume_host(host);
++
++ if (ret == 0) {
++ /* Isn't this already done by sdhci_resume_host() ? --rmk */
++ esdhc_of_enable_dma(host);
++ sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
++ }
++
++ return ret;
++}
++
++static const struct dev_pm_ops esdhc_pmops = {
++ .suspend = esdhci_of_suspend,
++ .resume = esdhci_of_resume,
+ };
++#define ESDHC_PMOPS (&esdhc_pmops)
++#else
++#define ESDHC_PMOPS NULL
++#endif
+
+ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
+ /*
+@@ -374,7 +390,7 @@
+ .name = "sdhci-esdhc",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_esdhc_of_match,
+- .pm = SDHCI_PLTFM_PMOPS,
++ .pm = ESDHC_PMOPS,
+ },
+ .probe = sdhci_esdhc_probe,
+ .remove = sdhci_esdhc_remove,
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-hlwd.c linux-3.15-rc6/drivers/mmc/host/sdhci-of-hlwd.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-of-hlwd.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-of-hlwd.c 2014-05-23 11:26:48.304940032 +0200
+@@ -58,6 +58,10 @@
+ .write_l = sdhci_hlwd_writel,
+ .write_w = sdhci_hlwd_writew,
+ .write_b = sdhci_hlwd_writeb,
++ .set_clock = sdhci_set_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pci.c linux-3.15-rc6/drivers/mmc/host/sdhci-pci.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pci.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-pci.c 2014-05-23 11:26:48.304940032 +0200
+@@ -1031,7 +1031,7 @@
+ return 0;
+ }
+
+-static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
++static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width)
+ {
+ u8 ctrl;
+
+@@ -1052,8 +1052,6 @@
+ }
+
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+-
+- return 0;
+ }
+
+ static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host)
+@@ -1080,8 +1078,11 @@
+ }
+
+ static const struct sdhci_ops sdhci_pci_ops = {
++ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_pci_enable_dma,
+- .platform_bus_width = sdhci_pci_bus_width,
++ .set_bus_width = sdhci_pci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .hw_reset = sdhci_pci_hw_reset,
+ };
+
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pltfm.c linux-3.15-rc6/drivers/mmc/host/sdhci-pltfm.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pltfm.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-pltfm.c 2014-05-23 11:26:48.308940045 +0200
+@@ -45,6 +45,10 @@
+ EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
+
+ static const struct sdhci_ops sdhci_pltfm_ops = {
++ .set_clock = sdhci_set_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ #ifdef CONFIG_OF
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pxav2.c linux-3.15-rc6/drivers/mmc/host/sdhci-pxav2.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pxav2.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-pxav2.c 2014-05-23 11:26:48.308940045 +0200
+@@ -51,11 +51,13 @@
+ #define MMC_CARD 0x1000
+ #define MMC_WIDTH 0x0100
+
+-static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
++static void pxav2_reset(struct sdhci_host *host, u8 mask)
+ {
+ struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+
++ sdhci_reset(host, mask);
++
+ if (mask == SDHCI_RESET_ALL) {
+ u16 tmp = 0;
+
+@@ -88,7 +90,7 @@
+ }
+ }
+
+-static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
++static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
+ {
+ u8 ctrl;
+ u16 tmp;
+@@ -107,14 +109,14 @@
+ }
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+-
+- return 0;
+ }
+
+ static const struct sdhci_ops pxav2_sdhci_ops = {
++ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+- .platform_reset_exit = pxav2_set_private_registers,
+- .platform_bus_width = pxav2_mmc_set_width,
++ .set_bus_width = pxav2_mmc_set_bus_width,
++ .reset = pxav2_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ #ifdef CONFIG_OF
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pxav3.c linux-3.15-rc6/drivers/mmc/host/sdhci-pxav3.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-pxav3.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-pxav3.c 2014-05-23 11:26:48.308940045 +0200
+@@ -112,11 +112,13 @@
+ return 0;
+ }
+
+-static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
++static void pxav3_reset(struct sdhci_host *host, u8 mask)
+ {
+ struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+ struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+
++ sdhci_reset(host, mask);
++
+ if (mask == SDHCI_RESET_ALL) {
+ /*
+ * tune timing of read data/command when crc error happen
+@@ -184,7 +186,7 @@
+ pxa->power_mode = power_mode;
+ }
+
+-static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
++static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
+ {
+ u16 ctrl_2;
+
+@@ -218,15 +220,16 @@
+ dev_dbg(mmc_dev(host->mmc),
+ "%s uhs = %d, ctrl_2 = %04X\n",
+ __func__, uhs, ctrl_2);
+-
+- return 0;
+ }
+
+ static const struct sdhci_ops pxav3_sdhci_ops = {
+- .platform_reset_exit = pxav3_set_private_registers,
++ .set_clock = sdhci_set_clock,
+ .set_uhs_signaling = pxav3_set_uhs_signaling,
+ .platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = pxav3_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static struct sdhci_pltfm_data sdhci_pxav3_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-s3c.c linux-3.15-rc6/drivers/mmc/host/sdhci-s3c.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-s3c.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-s3c.c 2014-05-23 11:26:48.308940045 +0200
+@@ -58,6 +58,8 @@
+ struct clk *clk_io;
+ struct clk *clk_bus[MAX_BUS_CLK];
+ unsigned long clk_rates[MAX_BUS_CLK];
++
++ bool no_divider;
+ };
+
+ /**
+@@ -70,6 +72,7 @@
+ */
+ struct sdhci_s3c_drv_data {
+ unsigned int sdhci_quirks;
++ bool no_divider;
+ };
+
+ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
+@@ -119,7 +122,7 @@
+ * If controller uses a non-standard clock division, find the best clock
+ * speed possible with selected clock source and skip the division.
+ */
+- if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
++ if (ourhost->no_divider) {
+ rate = clk_round_rate(clksrc, wanted);
+ return wanted - rate;
+ }
+@@ -161,9 +164,13 @@
+ int src;
+ u32 ctrl;
+
++ host->mmc->actual_clock = 0;
++
+ /* don't bother if the clock is going off. */
+- if (clock == 0)
++ if (clock == 0) {
++ sdhci_set_clock(host, clock);
+ return;
++ }
+
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ delta = sdhci_s3c_consider_clock(ourhost, src, clock);
+@@ -215,6 +222,8 @@
+ if (clock < 25 * 1000000)
+ ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);
+ writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3);
++
++ sdhci_set_clock(host, clock);
+ }
+
+ /**
+@@ -295,10 +304,11 @@
+ unsigned long timeout;
+ u16 clk = 0;
+
++ host->mmc->actual_clock = 0;
++
+ /* If the clock is going off, set to 0 at clock control register */
+ if (clock == 0) {
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+- host->clock = clock;
+ return;
+ }
+
+@@ -306,8 +316,6 @@
+
+ clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
+
+- host->clock = clock;
+-
+ clk = SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+@@ -329,14 +337,14 @@
+ }
+
+ /**
+- * sdhci_s3c_platform_bus_width - support 8bit buswidth
++ * sdhci_s3c_set_bus_width - support 8bit buswidth
+ * @host: The SDHCI host being queried
+ * @width: MMC_BUS_WIDTH_ macro for the bus width being requested
+ *
+ * We have 8-bit width support but is not a v3 controller.
+ * So we add platform_bus_width() and support 8bit width.
+ */
+-static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)
++static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width)
+ {
+ u8 ctrl;
+
+@@ -358,15 +366,15 @@
+ }
+
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+-
+- return 0;
+ }
+
+ static struct sdhci_ops sdhci_s3c_ops = {
+ .get_max_clock = sdhci_s3c_get_max_clk,
+ .set_clock = sdhci_s3c_set_clock,
+ .get_min_clock = sdhci_s3c_get_min_clock,
+- .platform_bus_width = sdhci_s3c_platform_bus_width,
++ .set_bus_width = sdhci_s3c_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
+@@ -606,8 +614,10 @@
+ /* Setup quirks for the controller */
+ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+- if (drv_data)
++ if (drv_data) {
+ host->quirks |= drv_data->sdhci_quirks;
++ sc->no_divider = drv_data->no_divider;
++ }
+
+ #ifndef CONFIG_MMC_SDHCI_S3C_DMA
+
+@@ -656,7 +666,7 @@
+ * If controller does not have internal clock divider,
+ * we can use overriding functions instead of default.
+ */
+- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
++ if (sc->no_divider) {
+ sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
+ sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
+ sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
+@@ -797,7 +807,7 @@
+
+ #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
+ static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
+- .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK,
++ .no_divider = true,
+ };
+ #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
+ #else
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-sirf.c linux-3.15-rc6/drivers/mmc/host/sdhci-sirf.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-sirf.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-sirf.c 2014-05-23 11:26:48.308940045 +0200
+@@ -28,7 +28,11 @@
+ }
+
+ static struct sdhci_ops sdhci_sirf_ops = {
++ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_sirf_get_max_clk,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static struct sdhci_pltfm_data sdhci_sirf_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-spear.c linux-3.15-rc6/drivers/mmc/host/sdhci-spear.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-spear.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-spear.c 2014-05-23 11:26:48.308940045 +0200
+@@ -38,7 +38,10 @@
+
+ /* sdhci ops */
+ static const struct sdhci_ops sdhci_pltfm_ops = {
+- /* Nothing to do for now. */
++ .set_clock = sdhci_set_clock,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ #ifdef CONFIG_OF
+diff -Nur linux-3.15-rc6.orig/drivers/mmc/host/sdhci-tegra.c linux-3.15-rc6/drivers/mmc/host/sdhci-tegra.c
+--- linux-3.15-rc6.orig/drivers/mmc/host/sdhci-tegra.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/mmc/host/sdhci-tegra.c 2014-05-23 11:26:48.308940045 +0200
+@@ -48,19 +48,6 @@
+ int power_gpio;
+ };
+
+-static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
+-{
+- u32 val;
+-
+- if (unlikely(reg == SDHCI_PRESENT_STATE)) {
+- /* Use wp_gpio here instead? */
+- val = readl(host->ioaddr + reg);
+- return val | SDHCI_WRITE_PROTECT;
+- }
+-
+- return readl(host->ioaddr + reg);
+-}
+-
+ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
+ {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+@@ -108,12 +95,14 @@
+ return mmc_gpio_get_ro(host->mmc);
+ }
+
+-static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
++static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
+ {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+
++ sdhci_reset(host, mask);
++
+ if (!(mask & SDHCI_RESET_ALL))
+ return;
+
+@@ -127,7 +116,7 @@
+ }
+ }
+
+-static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
++static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
+ {
+ u32 ctrl;
+
+@@ -144,16 +133,16 @@
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ }
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+- return 0;
+ }
+
+ static const struct sdhci_ops tegra_sdhci_ops = {
+ .get_ro = tegra_sdhci_get_ro,
+- .read_l = tegra_sdhci_readl,
+ .read_w = tegra_sdhci_readw,
+ .write_l = tegra_sdhci_writel,
+- .platform_bus_width = tegra_sdhci_buswidth,
+- .platform_reset_exit = tegra_sdhci_reset_exit,
++ .set_clock = sdhci_set_clock,
++ .set_bus_width = tegra_sdhci_set_bus_width,
++ .reset = tegra_sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
+ static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
+diff -Nur linux-3.15-rc6.orig/drivers/net/ethernet/freescale/fec.h linux-3.15-rc6/drivers/net/ethernet/freescale/fec.h
+--- linux-3.15-rc6.orig/drivers/net/ethernet/freescale/fec.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/net/ethernet/freescale/fec.h 2014-05-23 11:26:48.308940045 +0200
+@@ -14,6 +14,7 @@
+ /****************************************************************************/
+
+ #include <linux/clocksource.h>
++#include <linux/mutex.h>
+ #include <linux/net_tstamp.h>
+ #include <linux/ptp_clock_kernel.h>
+
+@@ -170,6 +171,11 @@
+ unsigned short res0[4];
+ };
+
++union bufdesc_u {
++ struct bufdesc bd;
++ struct bufdesc_ex ebd;
++};
++
+ /*
+ * The following definitions courtesy of commproc.h, which where
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net).
+@@ -202,6 +208,7 @@
+ #define BD_ENET_RX_OV ((ushort)0x0002)
+ #define BD_ENET_RX_CL ((ushort)0x0001)
+ #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
++#define BD_ENET_RX_ERROR ((ushort)0x003f)
+
+ /* Enhanced buffer descriptor control/status used by Ethernet receive */
+ #define BD_ENET_RX_VLAN 0x00000004
+@@ -224,10 +231,17 @@
+ #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
+
+ /*enhanced buffer descriptor control/status used by Ethernet transmit*/
+-#define BD_ENET_TX_INT 0x40000000
+-#define BD_ENET_TX_TS 0x20000000
+-#define BD_ENET_TX_PINS 0x10000000
+-#define BD_ENET_TX_IINS 0x08000000
++#define BD_ENET_TX_INT BIT(30)
++#define BD_ENET_TX_TS BIT(29)
++#define BD_ENET_TX_PINS BIT(28)
++#define BD_ENET_TX_IINS BIT(27)
++#define BD_ENET_TX_TXE BIT(15)
++#define BD_ENET_TX_UE BIT(13)
++#define BD_ENET_TX_EE BIT(12)
++#define BD_ENET_TX_FE BIT(11)
++#define BD_ENET_TX_LCE BIT(10)
++#define BD_ENET_TX_OE BIT(9)
++#define BD_ENET_TX_TSE BIT(8)
+
+
+ /* This device has up to three irqs on some platforms */
+@@ -240,28 +254,20 @@
+ * the skbuffer directly.
+ */
+
+-#define FEC_ENET_RX_PAGES 8
++#define FEC_ENET_RX_PAGES 64
+ #define FEC_ENET_RX_FRSIZE 2048
+ #define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE)
+ #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
+ #define FEC_ENET_TX_FRSIZE 2048
+ #define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
+-#define TX_RING_SIZE 16 /* Must be power of two */
+-#define TX_RING_MOD_MASK 15 /* for this to work */
++#define TX_RING_SIZE 128 /* Must be power of two */
+
+ #define BD_ENET_RX_INT 0x00800000
+ #define BD_ENET_RX_PTP ((ushort)0x0400)
+ #define BD_ENET_RX_ICE 0x00000020
+ #define BD_ENET_RX_PCR 0x00000010
+-#define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR)
+ #define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR)
+
+-struct fec_enet_delayed_work {
+- struct delayed_work delay_work;
+- bool timeout;
+- bool trig_tx;
+-};
+-
+ /* The FEC buffer descriptors track the ring buffers. The rx_bd_base and
+ * tx_bd_base always point to the base of the buffer descriptors. The
+ * cur_rx and cur_tx point to the currently available buffer.
+@@ -281,27 +287,33 @@
+ struct clk *clk_enet_out;
+ struct clk *clk_ptp;
+
++ unsigned char tx_page_map[TX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ unsigned char *tx_bounce[TX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+
+ /* CPM dual port RAM relative addresses */
+- dma_addr_t bd_dma;
++ dma_addr_t rx_bd_dma;
++ dma_addr_t tx_bd_dma;
+ /* Address of Rx and Tx buffers */
+- struct bufdesc *rx_bd_base;
+- struct bufdesc *tx_bd_base;
++ union bufdesc_u *rx_bd_base;
++ union bufdesc_u *tx_bd_base;
+ /* The next free ring entry */
+- struct bufdesc *cur_rx, *cur_tx;
+- /* The ring entries to be free()ed */
+- struct bufdesc *dirty_tx;
++ unsigned short tx_next;
++ unsigned short tx_dirty;
++ unsigned short tx_min;
++ unsigned short rx_next;
+
+ unsigned short tx_ring_size;
+ unsigned short rx_ring_size;
+
++ unsigned char flags;
++
++ struct mutex mutex;
++
+ struct platform_device *pdev;
+
+- int opened;
+ int dev_id;
+
+ /* Phylib and MDIO interface */
+@@ -315,11 +327,12 @@
+ int speed;
+ struct completion mdio_done;
+ int irq[FEC_IRQ_NUM];
+- int bufdesc_ex;
+- int pause_flag;
++ unsigned short pause_flag;
++ unsigned short pause_mode;
+
+ struct napi_struct napi;
+- int csum_flags;
++
++ struct work_struct tx_timeout_work;
+
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_caps;
+@@ -333,8 +346,8 @@
+ int hwts_rx_en;
+ int hwts_tx_en;
+ struct timer_list time_keep;
+- struct fec_enet_delayed_work delay_work;
+ struct regulator *reg_phy;
++ unsigned long quirks;
+ };
+
+ void fec_ptp_init(struct platform_device *pdev);
+diff -Nur linux-3.15-rc6.orig/drivers/net/ethernet/freescale/fec_main.c linux-3.15-rc6/drivers/net/ethernet/freescale/fec_main.c
+--- linux-3.15-rc6.orig/drivers/net/ethernet/freescale/fec_main.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/net/ethernet/freescale/fec_main.c 2014-05-23 11:26:48.312940058 +0200
+@@ -33,12 +33,6 @@
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/skbuff.h>
+-#include <linux/in.h>
+-#include <linux/ip.h>
+-#include <net/ip.h>
+-#include <linux/tcp.h>
+-#include <linux/udp.h>
+-#include <linux/icmp.h>
+ #include <linux/spinlock.h>
+ #include <linux/workqueue.h>
+ #include <linux/bitops.h>
+@@ -91,16 +85,8 @@
+ #define FEC_QUIRK_HAS_CSUM (1 << 5)
+ /* Controller has hardware vlan support */
+ #define FEC_QUIRK_HAS_VLAN (1 << 6)
+-/* ENET IP errata ERR006358
+- *
+- * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously
+- * detected as not set during a prior frame transmission, then the
+- * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs
+- * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in
+- * frames not being transmitted until there is a 0-to-1 transition on
+- * ENET_TDAR[TDAR].
+- */
+-#define FEC_QUIRK_ERR006358 (1 << 7)
++/* Controller has ability to offset rx packets */
++#define FEC_QUIRK_RX_SHIFT16 (1 << 8)
+
+ static struct platform_device_id fec_devtype[] = {
+ {
+@@ -120,7 +106,7 @@
+ .name = "imx6q-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+- FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358,
++ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_RX_SHIFT16,
+ }, {
+ .name = "mvf600-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC,
+@@ -172,9 +158,15 @@
+ #endif
+ #endif /* CONFIG_M5272 */
+
+-#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
+-#error "FEC: descriptor ring size constants too large"
++#if RX_RING_SIZE * 32 > PAGE_SIZE
++#error "FEC: receive descriptor ring size too large"
+ #endif
++#if TX_RING_SIZE * 32 > PAGE_SIZE
++#error "FEC: transmit descriptor ring size too large"
++#endif
++
++/* Minimum TX ring size when using NETIF_F_SG */
++#define TX_RING_SIZE_MIN_SG (2 * (MAX_SKB_FRAGS + 1))
+
+ /* Interrupt events/masks. */
+ #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
+@@ -200,6 +192,7 @@
+ /* FEC receive acceleration */
+ #define FEC_RACC_IPDIS (1 << 1)
+ #define FEC_RACC_PRODIS (1 << 2)
++#define FEC_RACC_SHIFT16 BIT(7)
+ #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS)
+
+ /*
+@@ -228,62 +221,60 @@
+ /* Transmitter timeout */
+ #define TX_TIMEOUT (2 * HZ)
+
+-#define FEC_PAUSE_FLAG_AUTONEG 0x1
+-#define FEC_PAUSE_FLAG_ENABLE 0x2
++/* pause mode/flag */
++#define FEC_PAUSE_FLAG_AUTONEG BIT(0)
++#define FEC_PAUSE_FLAG_RX BIT(1)
++#define FEC_PAUSE_FLAG_TX BIT(2)
++
++/* flags */
++#define FEC_FLAG_BUFDESC_EX BIT(0)
++#define FEC_FLAG_RX_CSUM BIT(1)
++#define FEC_FLAG_RX_VLAN BIT(2)
+
+ static int mii_cnt;
+
+-static inline
+-struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
++static unsigned copybreak = 200;
++module_param(copybreak, uint, 0644);
++MODULE_PARM_DESC(copybreak,
++ "Maximum size of packet that is copied to a new buffer on receive");
++
++static bool fec_enet_rx_zerocopy(struct fec_enet_private *fep, unsigned pktlen)
+ {
+- struct bufdesc *new_bd = bdp + 1;
+- struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1;
+- struct bufdesc_ex *ex_base;
+- struct bufdesc *base;
+- int ring_size;
+-
+- if (bdp >= fep->tx_bd_base) {
+- base = fep->tx_bd_base;
+- ring_size = fep->tx_ring_size;
+- ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+- } else {
+- base = fep->rx_bd_base;
+- ring_size = fep->rx_ring_size;
+- ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+- }
++#ifndef CONFIG_M5272
++ if (fep->quirks & FEC_QUIRK_RX_SHIFT16 && pktlen >= copybreak)
++ return true;
++#endif
++ return false;
++}
+
+- if (fep->bufdesc_ex)
+- return (struct bufdesc *)((ex_new_bd >= (ex_base + ring_size)) ?
+- ex_base : ex_new_bd);
++static union bufdesc_u *
++fec_enet_tx_get(unsigned index, struct fec_enet_private *fep)
++{
++ union bufdesc_u *base = fep->tx_bd_base;
++ union bufdesc_u *bdp;
++
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
++ bdp = (union bufdesc_u *)(&base->ebd + index);
+ else
+- return (new_bd >= (base + ring_size)) ?
+- base : new_bd;
++ bdp = (union bufdesc_u *)(&base->bd + index);
++
++ return bdp;
+ }
+
+-static inline
+-struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
++static union bufdesc_u *
++fec_enet_rx_get(unsigned index, struct fec_enet_private *fep)
+ {
+- struct bufdesc *new_bd = bdp - 1;
+- struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1;
+- struct bufdesc_ex *ex_base;
+- struct bufdesc *base;
+- int ring_size;
+-
+- if (bdp >= fep->tx_bd_base) {
+- base = fep->tx_bd_base;
+- ring_size = fep->tx_ring_size;
+- ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+- } else {
+- base = fep->rx_bd_base;
+- ring_size = fep->rx_ring_size;
+- ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+- }
++ union bufdesc_u *base = fep->rx_bd_base;
++ union bufdesc_u *bdp;
++
++ index &= fep->rx_ring_size - 1;
+
+- if (fep->bufdesc_ex)
+- return (struct bufdesc *)((ex_new_bd < ex_base) ?
+- (ex_new_bd + ring_size) : ex_new_bd);
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
++ bdp = (union bufdesc_u *)(&base->ebd + index);
+ else
+- return (new_bd < base) ? (new_bd + ring_size) : new_bd;
++ bdp = (union bufdesc_u *)(&base->bd + index);
++
++ return bdp;
+ }
+
+ static void *swap_buffer(void *bufaddr, int len)
+@@ -297,13 +288,47 @@
+ return bufaddr;
+ }
+
++static void fec_dump(struct net_device *ndev)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ union bufdesc_u *bdp;
++ unsigned index = 0;
++
++ netdev_info(ndev, "TX ring dump\n");
++ pr_info("Nr SC addr len SKB\n");
++
++ for (index = 0; index < fep->tx_ring_size; index++) {
++ bdp = fec_enet_tx_get(index, fep);
++
++ pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p",
++ index,
++ index == fep->tx_next ? 'S' : ' ',
++ index == fep->tx_dirty ? 'H' : ' ',
++ bdp->bd.cbd_sc, bdp->bd.cbd_bufaddr,
++ bdp->bd.cbd_datlen,
++ fep->tx_skbuff[index]);
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
++ pr_cont(" %08lx", bdp->ebd.cbd_esc);
++ pr_cont("\n");
++ }
++}
++
+ static int
+ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
+ {
++ int csum_start;
++
+ /* Only run for packets requiring a checksum. */
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
++ csum_start = skb_checksum_start_offset(skb);
++ if (csum_start + skb->csum_offset > skb_headlen(skb)) {
++ netdev_err(ndev, "checksum outside skb head: headlen %u start %u offset %u\n",
++ skb_headlen(skb), csum_start, skb->csum_offset);
++ return -1;
++ }
++
+ if (unlikely(skb_cow_head(skb, 0)))
+ return -1;
+
+@@ -312,23 +337,56 @@
+ return 0;
+ }
+
++static void
++fec_enet_tx_unmap(unsigned index, union bufdesc_u *bdp, struct fec_enet_private *fep)
++{
++ dma_addr_t addr = bdp->bd.cbd_bufaddr;
++ unsigned length = bdp->bd.cbd_datlen;
++
++ bdp->bd.cbd_bufaddr = 0;
++
++ if (fep->tx_page_map[index])
++ dma_unmap_page(&fep->pdev->dev, addr, length, DMA_TO_DEVICE);
++ else
++ dma_unmap_single(&fep->pdev->dev, addr, length, DMA_TO_DEVICE);
++}
++
++static void
++fec_enet_tx_unmap_range(unsigned index, unsigned last, struct fec_enet_private *fep)
++{
++ union bufdesc_u *bdp;
++
++ do {
++ if (last == 0)
++ last = fep->tx_ring_size;
++ last--;
++
++ bdp = fec_enet_tx_get(last, fep);
++ fec_enet_tx_unmap(last, bdp, fep);
++ } while (index != last);
++}
++
++static unsigned ring_free(unsigned ins, unsigned rem, unsigned size)
++{
++ int num = rem - ins;
++ return num < 0 ? num + size : num;
++}
++
+ static netdev_tx_t
+ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+- struct bufdesc *bdp, *bdp_pre;
++ union bufdesc_u *bdp;
+ void *bufaddr;
+ unsigned short status;
+- unsigned int index;
++ unsigned index, last, length, cbd_esc;
++ int f, nr_frags = skb_shinfo(skb)->nr_frags;
++ dma_addr_t addr;
+
+ /* Fill in a Tx ring entry */
+- bdp = fep->cur_tx;
+-
+- status = bdp->cbd_sc;
++ index = fep->tx_next;
+
+- if (status & BD_ENET_TX_READY) {
++ if (ring_free(index, fep->tx_dirty, fep->tx_ring_size) < 1 + nr_frags) {
+ /* Ooops. All transmit buffers are full. Bail out.
+ * This should not happen, since ndev->tbusy should be set.
+ */
+@@ -342,26 +400,17 @@
+ return NETDEV_TX_OK;
+ }
+
+- /* Clear all of the status flags */
+- status &= ~BD_ENET_TX_STATS;
+-
+ /* Set buffer length and buffer pointer */
+ bufaddr = skb->data;
+- bdp->cbd_datlen = skb->len;
++ length = skb_headlen(skb);
+
+ /*
+ * On some FEC implementations data must be aligned on
+ * 4-byte boundaries. Use bounce buffers to copy data
+ * and get it aligned. Ugh.
+ */
+- if (fep->bufdesc_ex)
+- index = (struct bufdesc_ex *)bdp -
+- (struct bufdesc_ex *)fep->tx_bd_base;
+- else
+- index = bdp - fep->tx_bd_base;
+-
+ if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
+- memcpy(fep->tx_bounce[index], skb->data, skb->len);
++ memcpy(fep->tx_bounce[index], skb->data, length);
+ bufaddr = fep->tx_bounce[index];
+ }
+
+@@ -370,75 +419,127 @@
+ * the system that it's running on. As the result, driver has to
+ * swap every frame going to and coming from the controller.
+ */
+- if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+- swap_buffer(bufaddr, skb->len);
++ if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
++ swap_buffer(bufaddr, length);
+
+- /* Save skb pointer */
+- fep->tx_skbuff[index] = skb;
++ /* Push the data cache so the CPM does not get stale memory data. */
++ addr = dma_map_single(&fep->pdev->dev, bufaddr, length, DMA_TO_DEVICE);
++ if (dma_mapping_error(&fep->pdev->dev, addr))
++ goto release;
++
++ bdp = fec_enet_tx_get(index, fep);
++ bdp->bd.cbd_datlen = length;
++ bdp->bd.cbd_bufaddr = addr;
+
+- /* Push the data cache so the CPM does not get stale memory
+- * data.
+- */
+- bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+- skb->len, DMA_TO_DEVICE);
+- if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+- bdp->cbd_bufaddr = 0;
+- fep->tx_skbuff[index] = NULL;
+- dev_kfree_skb_any(skb);
+- if (net_ratelimit())
+- netdev_err(ndev, "Tx DMA memory map failed\n");
+- return NETDEV_TX_OK;
+- }
++ fep->tx_page_map[index] = 0;
+
+- if (fep->bufdesc_ex) {
+-
+- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+- ebdp->cbd_bdu = 0;
++ cbd_esc = BD_ENET_TX_INT;
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
+ fep->hwts_tx_en)) {
+- ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
++ cbd_esc |= BD_ENET_TX_TS;
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ } else {
+- ebdp->cbd_esc = BD_ENET_TX_INT;
+-
+ /* Enable protocol checksum flags
+ * We do not bother with the IP Checksum bits as they
+ * are done by the kernel
+ */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+- ebdp->cbd_esc |= BD_ENET_TX_PINS;
++ cbd_esc |= BD_ENET_TX_PINS;
++ }
++ bdp->ebd.cbd_bdu = 0;
++ bdp->ebd.cbd_esc = cbd_esc;
++ }
++
++ for (last = index, f = 0; f < nr_frags; f++) {
++ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[f];
++
++ if (++last >= fep->tx_ring_size)
++ last = 0;
++
++ length = skb_frag_size(frag);
++
++ /* If the alignment is unsuitable, we need to bounce. */
++ if (frag->page_offset & FEC_ALIGNMENT) {
++ unsigned char *bounce = fep->tx_bounce[last];
++
++ /* FIXME: highdma? */
++ memcpy(bounce, skb_frag_address(frag), length);
++
++ addr = dma_map_single(&fep->pdev->dev, bounce,
++ length, DMA_TO_DEVICE);
++ fep->tx_page_map[last] = 0;
++ } else {
++ addr = skb_frag_dma_map(&fep->pdev->dev, frag, 0,
++ length, DMA_TO_DEVICE);
++ fep->tx_page_map[last] = 1;
++ }
++
++ if (dma_mapping_error(&fep->pdev->dev, addr))
++ goto release_frags;
++
++ bdp = fec_enet_tx_get(last, fep);
++ bdp->bd.cbd_datlen = length;
++ bdp->bd.cbd_bufaddr = addr;
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
++ bdp->ebd.cbd_esc = cbd_esc;
++ bdp->ebd.cbd_bdu = 0;
+ }
+ }
+
++ /* Save skb pointer */
++ fep->tx_skbuff[last] = skb;
++
++ /*
++ * We need the preceding stores to the descriptor to complete
++ * before updating the status field, which hands it over to the
++ * hardware. The corresponding rmb() is "in the hardware".
++ */
++ wmb();
++
+ /* Send it on its way. Tell FEC it's ready, interrupt when done,
+ * it's the last BD of the frame, and to put the CRC on the end.
+ */
+- status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
+- | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+- bdp->cbd_sc = status;
+-
+- bdp_pre = fec_enet_get_prevdesc(bdp, fep);
+- if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
+- !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
+- fep->delay_work.trig_tx = true;
+- schedule_delayed_work(&(fep->delay_work.delay_work),
+- msecs_to_jiffies(1));
++ status = bdp->bd.cbd_sc & BD_ENET_TX_WRAP;
++ bdp->bd.cbd_sc = status | BD_ENET_TX_READY | BD_ENET_TX_INTR |
++ BD_ENET_TX_LAST | BD_ENET_TX_TC;
++
++ /* Now walk backwards setting the TX_READY on each fragment */
++ for (f = nr_frags - 1; f >= 0; f--) {
++ unsigned i = index + f;
++
++ if (i >= fep->tx_ring_size)
++ i -= fep->tx_ring_size;
++
++ bdp = fec_enet_tx_get(i, fep);
++ status = bdp->bd.cbd_sc & BD_ENET_TX_WRAP;
++ bdp->bd.cbd_sc = status | BD_ENET_TX_READY | BD_ENET_TX_INTR;
+ }
+
+- /* If this was the last BD in the ring, start at the beginning again. */
+- bdp = fec_enet_get_nextdesc(bdp, fep);
+-
+ skb_tx_timestamp(skb);
++ netdev_sent_queue(ndev, skb->len);
++
++ if (++last >= fep->tx_ring_size)
++ last = 0;
+
+- fep->cur_tx = bdp;
++ fep->tx_next = last;
+
+- if (fep->cur_tx == fep->dirty_tx)
++ if (ring_free(last, fep->tx_dirty, fep->tx_ring_size) < fep->tx_min)
+ netif_stop_queue(ndev);
+
+ /* Trigger transmission start */
+- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
++ if (readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
++ writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+ return NETDEV_TX_OK;
++
++ release_frags:
++ fec_enet_tx_unmap_range(index, last, fep);
++ release:
++ dev_kfree_skb_any(skb);
++ if (net_ratelimit())
++ netdev_err(ndev, "Tx DMA memory map failed\n");
++ return NETDEV_TX_OK;
+ }
+
+ /* Init RX & TX buffer descriptors
+@@ -446,71 +547,60 @@
+ static void fec_enet_bd_init(struct net_device *dev)
+ {
+ struct fec_enet_private *fep = netdev_priv(dev);
+- struct bufdesc *bdp;
++ union bufdesc_u *bdp;
+ unsigned int i;
+
+ /* Initialize the receive buffer descriptors. */
+- bdp = fep->rx_bd_base;
+ for (i = 0; i < fep->rx_ring_size; i++) {
++ bdp = fec_enet_rx_get(i, fep);
+
+ /* Initialize the BD for every fragment in the page. */
+- if (bdp->cbd_bufaddr)
+- bdp->cbd_sc = BD_ENET_RX_EMPTY;
++ if (bdp->bd.cbd_bufaddr)
++ bdp->bd.cbd_sc = BD_ENET_RX_EMPTY;
+ else
+- bdp->cbd_sc = 0;
+- bdp = fec_enet_get_nextdesc(bdp, fep);
+- }
++ bdp->bd.cbd_sc = 0;
+
+- /* Set the last buffer to wrap */
+- bdp = fec_enet_get_prevdesc(bdp, fep);
+- bdp->cbd_sc |= BD_SC_WRAP;
++ if (i == fep->rx_ring_size - 1)
++ bdp->bd.cbd_sc |= BD_SC_WRAP;
++ }
+
+- fep->cur_rx = fep->rx_bd_base;
++ fep->rx_next = 0;
+
+ /* ...and the same for transmit */
+- bdp = fep->tx_bd_base;
+- fep->cur_tx = bdp;
+ for (i = 0; i < fep->tx_ring_size; i++) {
++ bdp = fec_enet_tx_get(i, fep);
+
+ /* Initialize the BD for every fragment in the page. */
+- bdp->cbd_sc = 0;
+- if (bdp->cbd_bufaddr && fep->tx_skbuff[i]) {
++ if (i == fep->tx_ring_size - 1)
++ bdp->bd.cbd_sc = BD_SC_WRAP;
++ else
++ bdp->bd.cbd_sc = 0;
++ if (bdp->bd.cbd_bufaddr)
++ fec_enet_tx_unmap(i, bdp, fep);
++ if (fep->tx_skbuff[i]) {
+ dev_kfree_skb_any(fep->tx_skbuff[i]);
+ fep->tx_skbuff[i] = NULL;
+ }
+- bdp->cbd_bufaddr = 0;
+- bdp = fec_enet_get_nextdesc(bdp, fep);
+ }
+
+- /* Set the last buffer to wrap */
+- bdp = fec_enet_get_prevdesc(bdp, fep);
+- bdp->cbd_sc |= BD_SC_WRAP;
+- fep->dirty_tx = bdp;
++ fep->tx_next = 0;
++ fep->tx_dirty = fep->tx_ring_size - 1;
+ }
+
+-/* This function is called to start or restart the FEC during a link
+- * change. This only happens when switching between half and full
+- * duplex.
++/*
++ * This function is called to start or restart the FEC during a link
++ * change, transmit timeout, or to reconfigure the FEC. The network
++ * packet processing for this device must be stopped before this call.
+ */
+ static void
+-fec_restart(struct net_device *ndev, int duplex)
++fec_restart(struct net_device *ndev)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+- int i;
+ u32 val;
+ u32 temp_mac[2];
+ u32 rcntl = OPT_FRAME_SIZE | 0x04;
+ u32 ecntl = 0x2; /* ETHEREN */
+
+- if (netif_running(ndev)) {
+- netif_device_detach(ndev);
+- napi_disable(&fep->napi);
+- netif_stop_queue(ndev);
+- netif_tx_lock_bh(ndev);
+- }
+-
+ /* Whack a reset. We should wait for this. */
+ writel(1, fep->hwp + FEC_ECNTRL);
+ udelay(10);
+@@ -519,7 +609,7 @@
+ * enet-mac reset will reset mac address registers too,
+ * so need to reconfigure it.
+ */
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
++ if (fep->quirks & FEC_QUIRK_ENET_MAC) {
+ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
+ writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
+ writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
+@@ -531,27 +621,16 @@
+ /* Set maximum receive buffer size. */
+ writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
+
+- fec_enet_bd_init(ndev);
++ if (fep->rx_bd_base)
++ fec_enet_bd_init(ndev);
++ netdev_reset_queue(ndev);
+
+ /* Set receive and transmit descriptor base. */
+- writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
+- if (fep->bufdesc_ex)
+- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
+- * fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
+- else
+- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
+- * fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
+-
+-
+- for (i = 0; i <= TX_RING_MOD_MASK; i++) {
+- if (fep->tx_skbuff[i]) {
+- dev_kfree_skb_any(fep->tx_skbuff[i]);
+- fep->tx_skbuff[i] = NULL;
+- }
+- }
++ writel(fep->rx_bd_dma, fep->hwp + FEC_R_DES_START);
++ writel(fep->tx_bd_dma, fep->hwp + FEC_X_DES_START);
+
+ /* Enable MII mode */
+- if (duplex) {
++ if (fep->full_duplex == DUPLEX_FULL) {
+ /* FD enable */
+ writel(0x04, fep->hwp + FEC_X_CNTRL);
+ } else {
+@@ -560,15 +639,15 @@
+ writel(0x0, fep->hwp + FEC_X_CNTRL);
+ }
+
+- fep->full_duplex = duplex;
+-
+ /* Set MII speed */
+ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+
+ #if !defined(CONFIG_M5272)
+ /* set RX checksum */
+ val = readl(fep->hwp + FEC_RACC);
+- if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
++ if (fep->quirks & FEC_QUIRK_RX_SHIFT16)
++ val |= FEC_RACC_SHIFT16;
++ if (fep->flags & FEC_FLAG_RX_CSUM)
+ val |= FEC_RACC_OPTIONS;
+ else
+ val &= ~FEC_RACC_OPTIONS;
+@@ -579,9 +658,9 @@
+ * The phy interface and speed need to get configured
+ * differently on enet-mac.
+ */
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
++ if (fep->quirks & FEC_QUIRK_ENET_MAC) {
+ /* Enable flow control and length check */
+- rcntl |= 0x40000000 | 0x00000020;
++ rcntl |= 0x40000000;
+
+ /* RGMII, RMII or MII */
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
+@@ -602,7 +681,7 @@
+ }
+ } else {
+ #ifdef FEC_MIIGSK_ENR
+- if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
++ if (fep->quirks & FEC_QUIRK_USE_GASKET) {
+ u32 cfgr;
+ /* disable the gasket and wait */
+ writel(0, fep->hwp + FEC_MIIGSK_ENR);
+@@ -627,22 +706,24 @@
+ }
+
+ #if !defined(CONFIG_M5272)
+- /* enable pause frame*/
+- if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
+- ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
+- fep->phy_dev && fep->phy_dev->pause)) {
+- rcntl |= FEC_ENET_FCE;
+-
+- /* set FIFO threshold parameter to reduce overrun */
+- writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
+- writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
+- writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
+- writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
++ if (fep->full_duplex == DUPLEX_FULL) {
++ /*
++ * Configure pause modes according to the current status.
++ * Must only be enabled for full duplex links.
++ */
++ if (fep->pause_mode & FEC_PAUSE_FLAG_RX)
++ rcntl |= FEC_ENET_FCE;
+
+- /* OPD */
+- writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
+- } else {
+- rcntl &= ~FEC_ENET_FCE;
++ if (fep->pause_mode & FEC_PAUSE_FLAG_TX) {
++ /* set FIFO threshold parameter to reduce overrun */
++ writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
++ writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
++ writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
++ writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
++
++ /* OPD */
++ writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
++ }
+ }
+ #endif /* !defined(CONFIG_M5272) */
+
+@@ -655,14 +736,14 @@
+ writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
+ #endif
+
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
++ if (fep->quirks & FEC_QUIRK_ENET_MAC) {
+ /* enable ENET endian swap */
+ ecntl |= (1 << 8);
+ /* enable ENET store and forward mode */
+ writel(1 << 8, fep->hwp + FEC_X_WMRK);
+ }
+
+- if (fep->bufdesc_ex)
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
+ ecntl |= (1 << 4);
+
+ #ifndef CONFIG_M5272
+@@ -674,26 +755,17 @@
+ writel(ecntl, fep->hwp + FEC_ECNTRL);
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+
+- if (fep->bufdesc_ex)
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
+ fec_ptp_start_cyclecounter(ndev);
+
+ /* Enable interrupts we wish to service */
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+-
+- if (netif_running(ndev)) {
+- netif_tx_unlock_bh(ndev);
+- netif_wake_queue(ndev);
+- napi_enable(&fep->napi);
+- netif_device_attach(ndev);
+- }
+ }
+
+ static void
+ fec_stop(struct net_device *ndev)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+ u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
+
+ /* We cannot expect a graceful transmit stop without link !!! */
+@@ -711,7 +783,7 @@
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+ /* We have to keep ENET enabled to have MII interrupt stay working */
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
++ if (fep->quirks & FEC_QUIRK_ENET_MAC) {
+ writel(2, fep->hwp + FEC_ECNTRL);
+ writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
+ }
+@@ -723,127 +795,312 @@
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
++ fec_dump(ndev);
++
+ ndev->stats.tx_errors++;
+
+- fep->delay_work.timeout = true;
+- schedule_delayed_work(&(fep->delay_work.delay_work), 0);
++ schedule_work(&fep->tx_timeout_work);
+ }
+
+-static void fec_enet_work(struct work_struct *work)
++static void fec_enet_timeout_work(struct work_struct *work)
+ {
+ struct fec_enet_private *fep =
+- container_of(work,
+- struct fec_enet_private,
+- delay_work.delay_work.work);
+-
+- if (fep->delay_work.timeout) {
+- fep->delay_work.timeout = false;
+- fec_restart(fep->netdev, fep->full_duplex);
+- netif_wake_queue(fep->netdev);
+- }
++ container_of(work, struct fec_enet_private, tx_timeout_work);
++ struct net_device *ndev = fep->netdev;
+
+- if (fep->delay_work.trig_tx) {
+- fep->delay_work.trig_tx = false;
+- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
++ rtnl_lock();
++ if (netif_device_present(ndev) || netif_running(ndev)) {
++ mutex_lock(&fep->mutex);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
++ fec_restart(ndev);
++ netif_wake_queue(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ mutex_unlock(&fep->mutex);
+ }
++ rtnl_unlock();
+ }
+
+ static void
++fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
++ struct skb_shared_hwtstamps *hwtstamps)
++{
++ unsigned long flags;
++ u64 ns;
++
++ spin_lock_irqsave(&fep->tmreg_lock, flags);
++ ns = timecounter_cyc2time(&fep->tc, ts);
++ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
++
++ memset(hwtstamps, 0, sizeof(*hwtstamps));
++ hwtstamps->hwtstamp = ns_to_ktime(ns);
++}
++
++static void noinline
+ fec_enet_tx(struct net_device *ndev)
+ {
+- struct fec_enet_private *fep;
+- struct bufdesc *bdp;
+- unsigned short status;
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ union bufdesc_u *bdp;
+ struct sk_buff *skb;
+- int index = 0;
+-
+- fep = netdev_priv(ndev);
+- bdp = fep->dirty_tx;
++ unsigned index = fep->tx_dirty;
++ unsigned pkts_compl, bytes_compl;
+
+- /* get next bdp of dirty_tx */
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ pkts_compl = bytes_compl = 0;
++ do {
++ unsigned status, cbd_esc;
+
+- while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
++ if (++index >= fep->tx_ring_size)
++ index = 0;
+
+ /* current queue is empty */
+- if (bdp == fep->cur_tx)
++ if (index == fep->tx_next)
+ break;
+
+- if (fep->bufdesc_ex)
+- index = (struct bufdesc_ex *)bdp -
+- (struct bufdesc_ex *)fep->tx_bd_base;
+- else
+- index = bdp - fep->tx_bd_base;
++ bdp = fec_enet_tx_get(index, fep);
++
++ status = bdp->bd.cbd_sc;
++ if (status & BD_ENET_TX_READY)
++ break;
++
++ fec_enet_tx_unmap(index, bdp, fep);
+
+ skb = fep->tx_skbuff[index];
+- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
+- DMA_TO_DEVICE);
+- bdp->cbd_bufaddr = 0;
++ fep->tx_skbuff[index] = NULL;
+
+ /* Check for errors. */
+- if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+- BD_ENET_TX_RL | BD_ENET_TX_UN |
+- BD_ENET_TX_CSL)) {
+- ndev->stats.tx_errors++;
+- if (status & BD_ENET_TX_HB) /* No heartbeat */
+- ndev->stats.tx_heartbeat_errors++;
+- if (status & BD_ENET_TX_LC) /* Late collision */
+- ndev->stats.tx_window_errors++;
+- if (status & BD_ENET_TX_RL) /* Retrans limit */
+- ndev->stats.tx_aborted_errors++;
+- if (status & BD_ENET_TX_UN) /* Underrun */
+- ndev->stats.tx_fifo_errors++;
+- if (status & BD_ENET_TX_CSL) /* Carrier lost */
+- ndev->stats.tx_carrier_errors++;
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
++ cbd_esc = bdp->ebd.cbd_esc;
++ if (cbd_esc & BD_ENET_TX_TXE) {
++ ndev->stats.tx_errors++;
++ if (cbd_esc & BD_ENET_TX_EE) { /* excess collision */
++ ndev->stats.collisions += 16;
++ ndev->stats.tx_aborted_errors++;
++ }
++ if (cbd_esc & BD_ENET_TX_LCE) /* late collision error */
++ ndev->stats.tx_window_errors++;
++ if (cbd_esc & (BD_ENET_TX_UE | BD_ENET_TX_FE | BD_ENET_TX_OE))
++ ndev->stats.tx_fifo_errors++;
++ goto next;
++ }
+ } else {
+- ndev->stats.tx_packets++;
+- ndev->stats.tx_bytes += bdp->cbd_datlen;
++ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
++ BD_ENET_TX_RL | BD_ENET_TX_UN |
++ BD_ENET_TX_CSL)) {
++ ndev->stats.tx_errors++;
++ if (status & BD_ENET_TX_HB) /* No heartbeat */
++ ndev->stats.tx_heartbeat_errors++;
++ if (status & BD_ENET_TX_LC) /* Late collision */
++ ndev->stats.tx_window_errors++;
++ if (status & BD_ENET_TX_RL) /* Retrans limit */
++ ndev->stats.tx_aborted_errors++;
++ if (status & BD_ENET_TX_UN) /* Underrun */
++ ndev->stats.tx_fifo_errors++;
++ if (status & BD_ENET_TX_CSL) /* Carrier lost */
++ ndev->stats.tx_carrier_errors++;
++ goto next;
++ }
+ }
+
+- if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
+- fep->bufdesc_ex) {
+- struct skb_shared_hwtstamps shhwtstamps;
+- unsigned long flags;
+- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+-
+- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+- spin_lock_irqsave(&fep->tmreg_lock, flags);
+- shhwtstamps.hwtstamp = ns_to_ktime(
+- timecounter_cyc2time(&fep->tc, ebdp->ts));
+- spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+- skb_tstamp_tx(skb, &shhwtstamps);
++ if (skb) {
++ ndev->stats.tx_packets++;
++ ndev->stats.tx_bytes += skb->len;
+ }
+
+- if (status & BD_ENET_TX_READY)
+- netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n");
+-
+ /* Deferred means some collisions occurred during transmit,
+ * but we eventually sent the packet OK.
+ */
+ if (status & BD_ENET_TX_DEF)
+ ndev->stats.collisions++;
++ next:
++ if (skb) {
++ if (fep->flags & FEC_FLAG_BUFDESC_EX &&
++ unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) {
++ struct skb_shared_hwtstamps shhwtstamps;
+
+- /* Free the sk buffer associated with this last transmit */
+- dev_kfree_skb_any(skb);
+- fep->tx_skbuff[index] = NULL;
++ fec_enet_hwtstamp(fep, bdp->ebd.ts, &shhwtstamps);
++ skb_tstamp_tx(skb, &shhwtstamps);
++ }
+
+- fep->dirty_tx = bdp;
++ pkts_compl++;
++ bytes_compl += skb->len;
+
+- /* Update pointer to next buffer descriptor to be transmitted */
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ /* Free the sk buffer associated with this last transmit */
++ dev_kfree_skb_any(skb);
++ }
+
+- /* Since we have freed up a buffer, the ring is no longer full
+- */
+- if (fep->dirty_tx != fep->cur_tx) {
+- if (netif_queue_stopped(ndev))
+- netif_wake_queue(ndev);
++ fep->tx_dirty = index;
++ } while (1);
++
++ netdev_completed_queue(ndev, pkts_compl, bytes_compl);
++
++ /* ERR006538: Keep the transmitter going */
++ if (index != fep->tx_next && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
++ writel(0, fep->hwp + FEC_X_DES_ACTIVE);
++
++ if (netif_queue_stopped(ndev) &&
++ ring_free(fep->tx_next, fep->tx_dirty, fep->tx_ring_size) >=
++ fep->tx_min)
++ netif_wake_queue(ndev);
++}
++
++
++static void
++fec_enet_receive(struct sk_buff *skb, union bufdesc_u *bdp, struct net_device *ndev)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++
++ skb->protocol = eth_type_trans(skb, ndev);
++
++ /* Get receive timestamp from the skb */
++ if (fep->hwts_rx_en && fep->flags & FEC_FLAG_BUFDESC_EX)
++ fec_enet_hwtstamp(fep, bdp->ebd.ts, skb_hwtstamps(skb));
++
++ if (fep->flags & FEC_FLAG_RX_CSUM) {
++ if (!(bdp->ebd.cbd_esc & FLAG_RX_CSUM_ERROR)) {
++ /* don't check it */
++ skb->ip_summed = CHECKSUM_UNNECESSARY;
++ } else {
++ skb_checksum_none_assert(skb);
+ }
+ }
+- return;
++
++ napi_gro_receive(&fep->napi, skb);
++}
++
++static void
++fec_enet_receive_copy(unsigned pkt_len, unsigned index, union bufdesc_u *bdp, struct net_device *ndev)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ struct sk_buff *skb;
++ unsigned char *data;
++ bool vlan_packet_rcvd = false;
++
++ /*
++ * Detect the presence of the VLAN tag, and adjust
++ * the packet length appropriately.
++ */
++ if (fep->flags & FEC_FLAG_RX_VLAN &&
++ bdp->ebd.cbd_esc & BD_ENET_RX_VLAN) {
++ pkt_len -= VLAN_HLEN;
++ vlan_packet_rcvd = true;
++ }
++
++ /* This does 16 byte alignment, exactly what we need. */
++ skb = netdev_alloc_skb(ndev, pkt_len + NET_IP_ALIGN);
++ if (unlikely(!skb)) {
++ ndev->stats.rx_dropped++;
++ return;
++ }
++
++ dma_sync_single_for_cpu(&fep->pdev->dev, bdp->bd.cbd_bufaddr,
++ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
++
++ data = fep->rx_skbuff[index]->data;
++
++#ifndef CONFIG_M5272
++ /*
++ * If we have enabled this feature, we need to discard
++ * the two bytes at the beginning of the packet before
++ * copying it.
++ */
++ if (fep->quirks & FEC_QUIRK_RX_SHIFT16) {
++ pkt_len -= 2;
++ data += 2;
++ }
++#endif
++
++ if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
++ swap_buffer(data, pkt_len);
++
++ skb_reserve(skb, NET_IP_ALIGN);
++ skb_put(skb, pkt_len); /* Make room */
++
++ /* If this is a VLAN packet remove the VLAN Tag */
++ if (vlan_packet_rcvd) {
++ struct vlan_hdr *vlan = (struct vlan_hdr *)(data + ETH_HLEN);
++
++ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
++ ntohs(vlan->h_vlan_TCI));
++
++ /* Extract the frame data without the VLAN header. */
++ skb_copy_to_linear_data(skb, data, 2 * ETH_ALEN);
++ skb_copy_to_linear_data_offset(skb, 2 * ETH_ALEN,
++ data + 2 * ETH_ALEN + VLAN_HLEN,
++ pkt_len - 2 * ETH_ALEN);
++ } else {
++ skb_copy_to_linear_data(skb, data, pkt_len);
++ }
++
++ dma_sync_single_for_device(&fep->pdev->dev, bdp->bd.cbd_bufaddr,
++ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
++
++ fec_enet_receive(skb, bdp, ndev);
+ }
+
++static void
++fec_enet_receive_nocopy(unsigned pkt_len, unsigned index, union bufdesc_u *bdp,
++ struct net_device *ndev)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ struct sk_buff *skb, *skb_new;
++ unsigned char *data;
++ dma_addr_t addr;
++
++ skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
++ if (!skb_new) {
++ ndev->stats.rx_dropped++;
++ return;
++ }
++
++ addr = dma_map_single(&fep->pdev->dev, skb_new->data,
++ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
++ if (dma_mapping_error(&fep->pdev->dev, addr)) {
++ dev_kfree_skb(skb_new);
++ ndev->stats.rx_dropped++;
++ return;
++ }
+
+-/* During a receive, the cur_rx points to the current incoming buffer.
++ /* We have the new skb, so proceed to deal with the received data. */
++ dma_unmap_single(&fep->pdev->dev, bdp->bd.cbd_bufaddr,
++ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
++
++ skb = fep->rx_skbuff[index];
++
++ /* Now subsitute in the new skb */
++ fep->rx_skbuff[index] = skb_new;
++ bdp->bd.cbd_bufaddr = addr;
++
++ /*
++ * Update the skb length according to the raw packet length.
++ * Then remove the two bytes of additional padding.
++ */
++ skb_put(skb, pkt_len);
++ data = skb_pull_inline(skb, 2);
++
++ if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
++ swap_buffer(data, skb->len);
++
++ /*
++ * Now juggle things for the VLAN tag - if the hardware
++ * flags this as present, we need to read the tag, and
++ * then shuffle the ethernet addresses up.
++ */
++ if (ndev->features & NETIF_F_HW_VLAN_CTAG_RX &&
++ bdp->ebd.cbd_esc & BD_ENET_RX_VLAN) {
++ struct vlan_hdr *vlan = (struct vlan_hdr *)(data + ETH_HLEN);
++
++ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
++ ntohs(vlan->h_vlan_TCI));
++
++ memmove(data + VLAN_HLEN, data, 2 * ETH_ALEN);
++ skb_pull_inline(skb, VLAN_HLEN);
++ }
++
++ fec_enet_receive(skb, bdp, ndev);
++}
++
++/* During a receive, the rx_next points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+@@ -852,18 +1109,9 @@
+ fec_enet_rx(struct net_device *ndev, int budget)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+- struct bufdesc *bdp;
+- unsigned short status;
+- struct sk_buff *skb;
+ ushort pkt_len;
+- __u8 *data;
+ int pkt_received = 0;
+- struct bufdesc_ex *ebdp = NULL;
+- bool vlan_packet_rcvd = false;
+- u16 vlan_tag;
+- int index = 0;
++ unsigned index = fep->rx_next;
+
+ #ifdef CONFIG_M532x
+ flush_cache_all();
+@@ -872,12 +1120,17 @@
+ /* First, grab all of the stats for the incoming packet.
+ * These get messed up if we get called due to a busy condition.
+ */
+- bdp = fep->cur_rx;
++ do {
++ union bufdesc_u *bdp = fec_enet_rx_get(index, fep);
++ unsigned status;
+
+- while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
++ status = bdp->bd.cbd_sc;
++ if (status & BD_ENET_RX_EMPTY)
++ break;
+
+ if (pkt_received >= budget)
+ break;
++
+ pkt_received++;
+
+ /* Since we have allocated space to hold a complete frame,
+@@ -886,155 +1139,81 @@
+ if ((status & BD_ENET_RX_LAST) == 0)
+ netdev_err(ndev, "rcv is not +last\n");
+
+- if (!fep->opened)
+- goto rx_processing_done;
++ writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
+
+ /* Check for errors. */
+- if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+- BD_ENET_RX_CR | BD_ENET_RX_OV)) {
++ if (status & BD_ENET_RX_ERROR) {
+ ndev->stats.rx_errors++;
+- if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+- /* Frame too long or too short. */
+- ndev->stats.rx_length_errors++;
+- }
+- if (status & BD_ENET_RX_NO) /* Frame alignment */
+- ndev->stats.rx_frame_errors++;
+- if (status & BD_ENET_RX_CR) /* CRC Error */
+- ndev->stats.rx_crc_errors++;
+- if (status & BD_ENET_RX_OV) /* FIFO overrun */
+- ndev->stats.rx_fifo_errors++;
+- }
+
+- /* Report late collisions as a frame error.
+- * On this error, the BD is closed, but we don't know what we
+- * have in the buffer. So, just drop this frame on the floor.
+- */
+- if (status & BD_ENET_RX_CL) {
+- ndev->stats.rx_errors++;
+- ndev->stats.rx_frame_errors++;
++ /*
++ * Report late collisions as a frame error. On this
++ * error, the BD is closed, but we don't know what we
++ * have in the buffer. So, just drop this frame on
++ * the floor.
++ */
++ if (status & BD_ENET_RX_CL) {
++ ndev->stats.rx_frame_errors++;
++ } else {
++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH))
++ /* Frame too long or too short. */
++ ndev->stats.rx_length_errors++;
++ if (status & BD_ENET_RX_NO) /* Frame alignment */
++ ndev->stats.rx_frame_errors++;
++ if (status & BD_ENET_RX_CR) /* CRC Error */
++ ndev->stats.rx_crc_errors++;
++ if (status & BD_ENET_RX_OV) /* FIFO overrun */
++ ndev->stats.rx_fifo_errors++;
++ }
+ goto rx_processing_done;
+ }
+
+ /* Process the incoming frame. */
+ ndev->stats.rx_packets++;
+- pkt_len = bdp->cbd_datlen;
+- ndev->stats.rx_bytes += pkt_len;
+-
+- if (fep->bufdesc_ex)
+- index = (struct bufdesc_ex *)bdp -
+- (struct bufdesc_ex *)fep->rx_bd_base;
+- else
+- index = bdp - fep->rx_bd_base;
+- data = fep->rx_skbuff[index]->data;
+- dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
+- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+-
+- if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+- swap_buffer(data, pkt_len);
+
+- /* Extract the enhanced buffer descriptor */
+- ebdp = NULL;
+- if (fep->bufdesc_ex)
+- ebdp = (struct bufdesc_ex *)bdp;
+-
+- /* If this is a VLAN packet remove the VLAN Tag */
+- vlan_packet_rcvd = false;
+- if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+- fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
+- /* Push and remove the vlan tag */
+- struct vlan_hdr *vlan_header =
+- (struct vlan_hdr *) (data + ETH_HLEN);
+- vlan_tag = ntohs(vlan_header->h_vlan_TCI);
+- pkt_len -= VLAN_HLEN;
+-
+- vlan_packet_rcvd = true;
+- }
+-
+- /* This does 16 byte alignment, exactly what we need.
+- * The packet length includes FCS, but we don't want to
+- * include that when passing upstream as it messes up
+- * bridging applications.
++ /*
++ * The packet length includes FCS, but we don't want
++ * to include that when passing upstream as it messes
++ * up bridging applications.
+ */
+- skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
++ pkt_len = bdp->bd.cbd_datlen - 4;
++ ndev->stats.rx_bytes += pkt_len;
+
+- if (unlikely(!skb)) {
+- ndev->stats.rx_dropped++;
++ if (fec_enet_rx_zerocopy(fep, pkt_len)) {
++ fec_enet_receive_nocopy(pkt_len, index, bdp, ndev);
+ } else {
+- int payload_offset = (2 * ETH_ALEN);
+- skb_reserve(skb, NET_IP_ALIGN);
+- skb_put(skb, pkt_len - 4); /* Make room */
+-
+- /* Extract the frame data without the VLAN header. */
+- skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
+- if (vlan_packet_rcvd)
+- payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
+- skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
+- data + payload_offset,
+- pkt_len - 4 - (2 * ETH_ALEN));
+-
+- skb->protocol = eth_type_trans(skb, ndev);
+-
+- /* Get receive timestamp from the skb */
+- if (fep->hwts_rx_en && fep->bufdesc_ex) {
+- struct skb_shared_hwtstamps *shhwtstamps =
+- skb_hwtstamps(skb);
+- unsigned long flags;
+-
+- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+-
+- spin_lock_irqsave(&fep->tmreg_lock, flags);
+- shhwtstamps->hwtstamp = ns_to_ktime(
+- timecounter_cyc2time(&fep->tc, ebdp->ts));
+- spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+- }
+-
+- if (fep->bufdesc_ex &&
+- (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
+- if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
+- /* don't check it */
+- skb->ip_summed = CHECKSUM_UNNECESSARY;
+- } else {
+- skb_checksum_none_assert(skb);
+- }
+- }
+-
+- /* Handle received VLAN packets */
+- if (vlan_packet_rcvd)
+- __vlan_hwaccel_put_tag(skb,
+- htons(ETH_P_8021Q),
+- vlan_tag);
+-
+- napi_gro_receive(&fep->napi, skb);
++ fec_enet_receive_copy(pkt_len, index, bdp, ndev);
+ }
+
+- dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr,
+- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+ rx_processing_done:
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
++ bdp->ebd.cbd_esc = BD_ENET_RX_INT;
++ bdp->ebd.cbd_prot = 0;
++ bdp->ebd.cbd_bdu = 0;
++ }
++
++ /*
++ * Ensure that the previous writes have completed before
++ * the status update becomes visible.
++ */
++ wmb();
++
+ /* Clear the status flags for this buffer */
+ status &= ~BD_ENET_RX_STATS;
+
+ /* Mark the buffer empty */
+ status |= BD_ENET_RX_EMPTY;
+- bdp->cbd_sc = status;
+-
+- if (fep->bufdesc_ex) {
+- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+-
+- ebdp->cbd_esc = BD_ENET_RX_INT;
+- ebdp->cbd_prot = 0;
+- ebdp->cbd_bdu = 0;
+- }
+-
+- /* Update BD pointer to next entry */
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ bdp->bd.cbd_sc = status;
+
+ /* Doing this here will keep the FEC running while we process
+ * incoming frames. On a heavily loaded network, we should be
+ * able to keep up at the expense of system resources.
+ */
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+- }
+- fep->cur_rx = bdp;
++
++ if (++index >= fep->rx_ring_size)
++ index = 0;
++ } while (1);
++ fep->rx_next = index;
+
+ return pkt_received;
+ }
+@@ -1044,29 +1223,25 @@
+ {
+ struct net_device *ndev = dev_id;
+ struct fec_enet_private *fep = netdev_priv(ndev);
++ const unsigned napi_mask = FEC_ENET_RXF | FEC_ENET_TXF;
+ uint int_events;
+ irqreturn_t ret = IRQ_NONE;
+
+- do {
+- int_events = readl(fep->hwp + FEC_IEVENT);
+- writel(int_events, fep->hwp + FEC_IEVENT);
++ int_events = readl(fep->hwp + FEC_IEVENT);
++ writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT);
+
+- if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
+- ret = IRQ_HANDLED;
++ if (int_events & napi_mask) {
++ ret = IRQ_HANDLED;
+
+- /* Disable the RX interrupt */
+- if (napi_schedule_prep(&fep->napi)) {
+- writel(FEC_RX_DISABLED_IMASK,
+- fep->hwp + FEC_IMASK);
+- __napi_schedule(&fep->napi);
+- }
+- }
++ /* Disable the NAPI interrupts */
++ writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
++ napi_schedule(&fep->napi);
++ }
+
+- if (int_events & FEC_ENET_MII) {
+- ret = IRQ_HANDLED;
+- complete(&fep->mdio_done);
+- }
+- } while (int_events);
++ if (int_events & FEC_ENET_MII) {
++ ret = IRQ_HANDLED;
++ complete(&fep->mdio_done);
++ }
+
+ return ret;
+ }
+@@ -1074,8 +1249,16 @@
+ static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
+ {
+ struct net_device *ndev = napi->dev;
+- int pkts = fec_enet_rx(ndev, budget);
+ struct fec_enet_private *fep = netdev_priv(ndev);
++ int pkts;
++
++ /*
++ * Clear any pending transmit or receive interrupts before
++ * processing the rings to avoid racing with the hardware.
++ */
++ writel(FEC_ENET_RXF | FEC_ENET_TXF, fep->hwp + FEC_IEVENT);
++
++ pkts = fec_enet_rx(ndev, budget);
+
+ fec_enet_tx(ndev);
+
+@@ -1173,26 +1356,78 @@
+ return;
+ }
+
+- if (phy_dev->link) {
++ /*
++ * If the netdev is down, or is going down, we're not interested
++ * in link state events, so just mark our idea of the link as down
++ * and ignore the event.
++ */
++ if (!netif_running(ndev) || !netif_device_present(ndev)) {
++ fep->link = 0;
++ } else if (phy_dev->link) {
+ if (!fep->link) {
+ fep->link = phy_dev->link;
+ status_change = 1;
+ }
+
+- if (fep->full_duplex != phy_dev->duplex)
++ if (fep->full_duplex != phy_dev->duplex) {
++ fep->full_duplex = phy_dev->duplex;
+ status_change = 1;
++ }
+
+ if (phy_dev->speed != fep->speed) {
+ fep->speed = phy_dev->speed;
+ status_change = 1;
+ }
+
++ if (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) {
++ u32 lcl_adv = phy_dev->advertising;
++ u32 rmt_adv = phy_dev->lp_advertising;
++ unsigned mode = 0;
++
++ if (lcl_adv & rmt_adv & ADVERTISED_Pause) {
++ /*
++ * Local Device Link Partner
++ * Pause AsymDir Pause AsymDir Result
++ * 1 X 1 X TX+RX
++ */
++ mode = FEC_PAUSE_FLAG_TX | FEC_PAUSE_FLAG_RX;
++ } else if (lcl_adv & rmt_adv & ADVERTISED_Asym_Pause) {
++ /*
++ * 0 1 1 1 RX
++ * 1 1 0 1 TX
++ */
++ if (rmt_adv & ADVERTISED_Pause)
++ mode = FEC_PAUSE_FLAG_RX;
++ else
++ mode = FEC_PAUSE_FLAG_TX;
++ }
++
++ if (mode != fep->pause_mode) {
++ fep->pause_mode = mode;
++ status_change = 1;
++ }
++ }
++
+ /* if any of the above changed restart the FEC */
+- if (status_change)
+- fec_restart(ndev, phy_dev->duplex);
++ if (status_change) {
++ mutex_lock(&fep->mutex);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
++ fec_restart(ndev);
++ netif_wake_queue(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ mutex_unlock(&fep->mutex);
++ }
+ } else {
+ if (fep->link) {
++ mutex_lock(&fep->mutex);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
+ fec_stop(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ mutex_unlock(&fep->mutex);
+ fep->link = phy_dev->link;
+ status_change = 1;
+ }
+@@ -1202,23 +1437,35 @@
+ phy_print_status(phy_dev);
+ }
+
+-static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
++static unsigned long fec_enet_mdio_op(struct fec_enet_private *fep,
++ unsigned data)
+ {
+- struct fec_enet_private *fep = bus->priv;
+ unsigned long time_left;
+
+ fep->mii_timeout = 0;
+ init_completion(&fep->mdio_done);
+
+- /* start a read op */
+- writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+- FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+- FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
++ mutex_lock(&fep->mutex);
++
++ /* start operation */
++ writel(data, fep->hwp + FEC_MII_DATA);
+
+ /* wait for end of transfer */
+ time_left = wait_for_completion_timeout(&fep->mdio_done,
+ usecs_to_jiffies(FEC_MII_TIMEOUT));
+- if (time_left == 0) {
++
++ mutex_unlock(&fep->mutex);
++
++ return time_left;
++}
++
++static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
++{
++ struct fec_enet_private *fep = bus->priv;
++
++ if (fec_enet_mdio_op(fep, FEC_MMFR_ST | FEC_MMFR_OP_READ |
++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
++ FEC_MMFR_TA) == 0) {
+ fep->mii_timeout = 1;
+ netdev_err(fep->netdev, "MDIO read timeout\n");
+ return -ETIMEDOUT;
+@@ -1232,21 +1479,10 @@
+ u16 value)
+ {
+ struct fec_enet_private *fep = bus->priv;
+- unsigned long time_left;
+-
+- fep->mii_timeout = 0;
+- init_completion(&fep->mdio_done);
+-
+- /* start a write op */
+- writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
+- FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+- FEC_MMFR_TA | FEC_MMFR_DATA(value),
+- fep->hwp + FEC_MII_DATA);
+
+- /* wait for end of transfer */
+- time_left = wait_for_completion_timeout(&fep->mdio_done,
+- usecs_to_jiffies(FEC_MII_TIMEOUT));
+- if (time_left == 0) {
++ if (fec_enet_mdio_op(fep, FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
++ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
++ FEC_MMFR_TA | FEC_MMFR_DATA(value)) == 0) {
+ fep->mii_timeout = 1;
+ netdev_err(fep->netdev, "MDIO write timeout\n");
+ return -ETIMEDOUT;
+@@ -1255,11 +1491,37 @@
+ return 0;
+ }
+
++static void fec_enet_phy_config(struct net_device *ndev)
++{
++#ifndef CONFIG_M5272
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ struct phy_device *phy = fep->phy_dev;
++ unsigned pause = 0;
++
++ /*
++ * Pause advertisment logic is weird. We don't advertise the raw
++ * "can tx" and "can rx" modes, but instead it is whether we support
++ * symmetric flow or asymmetric flow.
++ *
++ * Symmetric flow means we can only support both transmit and receive
++ * flow control frames together. Asymmetric flow means we can
++ * independently control each. Note that there is no bit encoding
++ * for "I can only receive flow control frames."
++ */
++ if (fep->pause_flag & FEC_PAUSE_FLAG_RX)
++ pause |= ADVERTISED_Asym_Pause | ADVERTISED_Pause;
++ if (fep->pause_flag & FEC_PAUSE_FLAG_TX)
++ pause |= ADVERTISED_Asym_Pause;
++
++ pause &= phy->supported;
++ pause |= phy->advertising & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
++ phy->advertising = pause;
++#endif
++}
++
+ static int fec_enet_mii_probe(struct net_device *ndev)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+ struct phy_device *phy_dev = NULL;
+ char mdio_bus_id[MII_BUS_ID_SIZE];
+ char phy_name[MII_BUS_ID_SIZE + 3];
+@@ -1297,10 +1559,11 @@
+ }
+
+ /* mask with MAC supported features */
+- if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
++ if (fep->quirks & FEC_QUIRK_HAS_GBIT) {
+ phy_dev->supported &= PHY_GBIT_FEATURES;
++ phy_dev->supported &= ~SUPPORTED_1000baseT_Half;
+ #if !defined(CONFIG_M5272)
+- phy_dev->supported |= SUPPORTED_Pause;
++ phy_dev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+ #endif
+ }
+ else
+@@ -1312,6 +1575,8 @@
+ fep->link = 0;
+ fep->full_duplex = 0;
+
++ fec_enet_phy_config(ndev);
++
+ netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
+ fep->phy_dev->irq);
+@@ -1324,8 +1589,6 @@
+ static struct mii_bus *fec0_mii_bus;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+ int err = -ENXIO, i;
+
+ /*
+@@ -1344,7 +1607,7 @@
+ * mdio interface in board design, and need to be configured by
+ * fec0 mii_bus.
+ */
+- if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
++ if ((fep->quirks & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
+ /* fec1 uses fec0 mii_bus */
+ if (mii_cnt && fec0_mii_bus) {
+ fep->mii_bus = fec0_mii_bus;
+@@ -1365,7 +1628,7 @@
+ * document.
+ */
+ fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
++ if (fep->quirks & FEC_QUIRK_ENET_MAC)
+ fep->phy_speed--;
+ fep->phy_speed <<= 1;
+ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+@@ -1399,7 +1662,7 @@
+ mii_cnt++;
+
+ /* save fec0 mii_bus */
+- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
++ if (fep->quirks & FEC_QUIRK_ENET_MAC)
+ fec0_mii_bus = fep->mii_bus;
+
+ return 0;
+@@ -1461,7 +1724,7 @@
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+- if (fep->bufdesc_ex) {
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
+
+ info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+@@ -1485,6 +1748,51 @@
+ }
+ }
+
++static void fec_enet_get_ringparam(struct net_device *ndev,
++ struct ethtool_ringparam *ring)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++
++ ring->rx_max_pending = RX_RING_SIZE;
++ ring->tx_max_pending = TX_RING_SIZE;
++ ring->rx_pending = fep->rx_ring_size;
++ ring->tx_pending = fep->tx_ring_size;
++}
++
++static int fec_enet_set_ringparam(struct net_device *ndev,
++ struct ethtool_ringparam *ring)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++ unsigned rx, tx, tx_min;
++
++ tx_min = ndev->features & NETIF_F_SG ? TX_RING_SIZE_MIN_SG : 16;
++
++ rx = clamp_t(u32, ring->rx_pending, 16, RX_RING_SIZE);
++ tx = clamp_t(u32, ring->tx_pending, tx_min, TX_RING_SIZE);
++
++ if (tx == fep->tx_ring_size && rx == fep->rx_ring_size)
++ return 0;
++
++ /* Setting the ring size while the interface is down is easy */
++ if (!netif_running(ndev)) {
++ fep->tx_ring_size = tx;
++ fep->rx_ring_size = rx;
++ } else {
++ return -EINVAL;
++
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
++ fec_stop(ndev);
++ /* reallocate ring */
++ fec_restart(ndev);
++ netif_wake_queue(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ }
++
++ return 0;
++}
++
+ #if !defined(CONFIG_M5272)
+
+ static void fec_enet_get_pauseparam(struct net_device *ndev,
+@@ -1493,42 +1801,81 @@
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
+- pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
+- pause->rx_pause = pause->tx_pause;
++ pause->rx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_RX) != 0;
++ pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_TX) != 0;
+ }
+
+ static int fec_enet_set_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *pause)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
++ unsigned pause_flag, changed;
++ struct phy_device *phy = fep->phy_dev;
+
+- if (pause->tx_pause != pause->rx_pause) {
+- netdev_info(ndev,
+- "hardware only support enable/disable both tx and rx");
++ if (!phy)
++ return -ENODEV;
++ if (!(phy->supported & SUPPORTED_Pause))
++ return -EINVAL;
++ if (!(phy->supported & SUPPORTED_Asym_Pause) &&
++ pause->rx_pause != pause->tx_pause)
+ return -EINVAL;
+- }
+
+- fep->pause_flag = 0;
++ pause_flag = 0;
++ if (pause->autoneg)
++ pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
++ if (pause->rx_pause)
++ pause_flag |= FEC_PAUSE_FLAG_RX;
++ if (pause->tx_pause)
++ pause_flag |= FEC_PAUSE_FLAG_TX;
++
++ changed = fep->pause_flag ^ pause_flag;
++ fep->pause_flag = pause_flag;
++
++ /* configure the phy advertisment according to our new options */
++ fec_enet_phy_config(ndev);
++
++ if (changed) {
++ if (pause_flag & FEC_PAUSE_FLAG_AUTONEG) {
++ if (netif_running(ndev))
++ phy_start_aneg(fep->phy_dev);
++ } else {
++ int adv, old_adv;
+
+- /* tx pause must be same as rx pause */
+- fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
+- fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
+-
+- if (pause->rx_pause || pause->autoneg) {
+- fep->phy_dev->supported |= ADVERTISED_Pause;
+- fep->phy_dev->advertising |= ADVERTISED_Pause;
+- } else {
+- fep->phy_dev->supported &= ~ADVERTISED_Pause;
+- fep->phy_dev->advertising &= ~ADVERTISED_Pause;
+- }
++ /*
++ * Even if we are not in autonegotiate mode, we
++ * still update the phy with our capabilities so
++ * our link parter can make the appropriate
++ * decision. PHYLIB provides no way to do this.
++ */
++ adv = phy_read(phy, MII_ADVERTISE);
++ if (adv >= 0) {
++ old_adv = adv;
++ adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
++ if (phy->advertising & ADVERTISED_Pause)
++ adv |= ADVERTISE_PAUSE_CAP;
++ if (phy->advertising & ADVERTISED_Asym_Pause)
++ adv |= ADVERTISE_PAUSE_ASYM;
+
+- if (pause->autoneg) {
+- if (netif_running(ndev))
+- fec_stop(ndev);
+- phy_start_aneg(fep->phy_dev);
++ if (old_adv != adv)
++ phy_write(phy, MII_ADVERTISE, adv);
++ }
++
++ /* Forced pause mode */
++ fep->pause_mode = fep->pause_flag;
++
++ if (netif_running(ndev)) {
++ mutex_lock(&fep->mutex);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
++ fec_stop(ndev);
++ fec_restart(ndev);
++ netif_wake_queue(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ mutex_unlock(&fep->mutex);
++ }
++ }
+ }
+- if (netif_running(ndev))
+- fec_restart(ndev, 0);
+
+ return 0;
+ }
+@@ -1645,21 +1992,21 @@
+ }
+
+ static const struct ethtool_ops fec_enet_ethtool_ops = {
+-#if !defined(CONFIG_M5272)
+- .get_pauseparam = fec_enet_get_pauseparam,
+- .set_pauseparam = fec_enet_set_pauseparam,
+-#endif
+ .get_settings = fec_enet_get_settings,
+ .set_settings = fec_enet_set_settings,
+ .get_drvinfo = fec_enet_get_drvinfo,
+- .get_link = ethtool_op_get_link,
+- .get_ts_info = fec_enet_get_ts_info,
+ .nway_reset = fec_enet_nway_reset,
++ .get_link = ethtool_op_get_link,
++ .get_ringparam = fec_enet_get_ringparam,
++ .set_ringparam = fec_enet_set_ringparam,
+ #ifndef CONFIG_M5272
+- .get_ethtool_stats = fec_enet_get_ethtool_stats,
++ .get_pauseparam = fec_enet_get_pauseparam,
++ .set_pauseparam = fec_enet_set_pauseparam,
+ .get_strings = fec_enet_get_strings,
++ .get_ethtool_stats = fec_enet_get_ethtool_stats,
+ .get_sset_count = fec_enet_get_sset_count,
+ #endif
++ .get_ts_info = fec_enet_get_ts_info,
+ };
+
+ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+@@ -1673,7 +2020,7 @@
+ if (!phydev)
+ return -ENODEV;
+
+- if (fep->bufdesc_ex) {
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
+ if (cmd == SIOCSHWTSTAMP)
+ return fec_ptp_set(ndev, rq);
+ if (cmd == SIOCGHWTSTAMP)
+@@ -1688,23 +2035,33 @@
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ unsigned int i;
+ struct sk_buff *skb;
+- struct bufdesc *bdp;
++ union bufdesc_u *bdp;
+
+- bdp = fep->rx_bd_base;
+ for (i = 0; i < fep->rx_ring_size; i++) {
+- skb = fep->rx_skbuff[i];
++ bdp = fec_enet_rx_get(i, fep);
+
+- if (bdp->cbd_bufaddr)
+- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
++ skb = fep->rx_skbuff[i];
++ fep->rx_skbuff[i] = NULL;
++ if (skb) {
++ dma_unmap_single(&fep->pdev->dev, bdp->bd.cbd_bufaddr,
+ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+- if (skb)
+ dev_kfree_skb(skb);
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ }
+ }
+
+- bdp = fep->tx_bd_base;
+- for (i = 0; i < fep->tx_ring_size; i++)
++ for (i = 0; i < fep->tx_ring_size; i++) {
++ bdp = fec_enet_tx_get(i, fep);
++ if (bdp->bd.cbd_bufaddr)
++ fec_enet_tx_unmap(i, bdp, fep);
+ kfree(fep->tx_bounce[i]);
++ fep->tx_bounce[i] = NULL;
++ skb = fep->tx_skbuff[i];
++ fep->tx_skbuff[i] = NULL;
++ if (skb)
++ dev_kfree_skb(skb);
++ }
++
++ dma_free_coherent(NULL, PAGE_SIZE, fep->rx_bd_base, fep->rx_bd_dma);
+ }
+
+ static int fec_enet_alloc_buffers(struct net_device *ndev)
+@@ -1712,59 +2069,82 @@
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ unsigned int i;
+ struct sk_buff *skb;
+- struct bufdesc *bdp;
++ union bufdesc_u *bdp;
++ union bufdesc_u *rx_cbd_cpu, *tx_cbd_cpu;
++ dma_addr_t rx_cbd_dma, tx_cbd_dma;
++
++ /* Allocate memory for buffer descriptors. */
++ rx_cbd_cpu = dma_alloc_coherent(NULL, PAGE_SIZE, &rx_cbd_dma,
++ GFP_KERNEL);
++ tx_cbd_cpu = dma_alloc_coherent(NULL, PAGE_SIZE, &tx_cbd_dma,
++ GFP_KERNEL);
++ if (!rx_cbd_cpu || !tx_cbd_cpu) {
++ if (rx_cbd_cpu)
++ dma_free_coherent(NULL, PAGE_SIZE, rx_cbd_cpu, rx_cbd_dma);
++ if (tx_cbd_cpu)
++ dma_free_coherent(NULL, PAGE_SIZE, tx_cbd_cpu, tx_cbd_dma);
++ return -ENOMEM;
++ }
++
++ memset(rx_cbd_cpu, 0, PAGE_SIZE);
++ memset(tx_cbd_cpu, 0, PAGE_SIZE);
++
++ /* Set receive and transmit descriptor base. */
++ fep->rx_bd_base = rx_cbd_cpu;
++ fep->rx_bd_dma = rx_cbd_dma;
++ fep->tx_bd_base = tx_cbd_cpu;
++ fep->tx_bd_dma = tx_cbd_dma;
+
+- bdp = fep->rx_bd_base;
+ for (i = 0; i < fep->rx_ring_size; i++) {
++ dma_addr_t addr;
++
+ skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
+- if (!skb) {
+- fec_enet_free_buffers(ndev);
+- return -ENOMEM;
+- }
+- fep->rx_skbuff[i] = skb;
++ if (!skb)
++ goto err_alloc;
+
+- bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
++ addr = dma_map_single(&fep->pdev->dev, skb->data,
+ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+- if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+- fec_enet_free_buffers(ndev);
++ if (dma_mapping_error(&fep->pdev->dev, addr)) {
++ dev_kfree_skb(skb);
+ if (net_ratelimit())
+ netdev_err(ndev, "Rx DMA memory map failed\n");
+- return -ENOMEM;
++ goto err_alloc;
+ }
+- bdp->cbd_sc = BD_ENET_RX_EMPTY;
+
+- if (fep->bufdesc_ex) {
+- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+- ebdp->cbd_esc = BD_ENET_RX_INT;
+- }
++ fep->rx_skbuff[i] = skb;
++ bdp = fec_enet_rx_get(i, fep);
++ bdp->bd.cbd_bufaddr = addr;
++ bdp->bd.cbd_sc = BD_ENET_RX_EMPTY;
++ /* Set the last buffer to wrap. */
++ if (i == fep->rx_ring_size - 1)
++ bdp->bd.cbd_sc |= BD_SC_WRAP;
+
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
++ bdp->ebd.cbd_esc = BD_ENET_RX_INT;
+ }
+
+- /* Set the last buffer to wrap. */
+- bdp = fec_enet_get_prevdesc(bdp, fep);
+- bdp->cbd_sc |= BD_SC_WRAP;
+-
+- bdp = fep->tx_bd_base;
+ for (i = 0; i < fep->tx_ring_size; i++) {
++ bdp = fec_enet_tx_get(i, fep);
+ fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
++ if (!fep->tx_bounce[i])
++ goto err_alloc;
+
+- bdp->cbd_sc = 0;
+- bdp->cbd_bufaddr = 0;
+-
+- if (fep->bufdesc_ex) {
+- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+- ebdp->cbd_esc = BD_ENET_TX_INT;
+- }
++ /* Set the last buffer to wrap. */
++ if (i == fep->tx_ring_size - 1)
++ bdp->bd.cbd_sc = BD_SC_WRAP;
++ else
++ bdp->bd.cbd_sc = 0;
++ bdp->bd.cbd_bufaddr = 0;
+
+- bdp = fec_enet_get_nextdesc(bdp, fep);
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
++ bdp->ebd.cbd_esc = BD_ENET_TX_INT;
+ }
+
+- /* Set the last buffer to wrap. */
+- bdp = fec_enet_get_prevdesc(bdp, fep);
+- bdp->cbd_sc |= BD_SC_WRAP;
+-
+ return 0;
++
++ err_alloc:
++ fec_enet_free_buffers(ndev);
++ return -ENOMEM;
+ }
+
+ static int
+@@ -1788,10 +2168,12 @@
+ return ret;
+ }
+
++ mutex_lock(&fep->mutex);
++ fec_restart(ndev);
++ mutex_unlock(&fep->mutex);
+ napi_enable(&fep->napi);
+ phy_start(fep->phy_dev);
+ netif_start_queue(ndev);
+- fep->opened = 1;
+ return 0;
+ }
+
+@@ -1800,17 +2182,19 @@
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+- /* Don't know what to do yet. */
+- napi_disable(&fep->napi);
+- fep->opened = 0;
+- netif_stop_queue(ndev);
+- fec_stop(ndev);
++ phy_stop(fep->phy_dev);
+
+- if (fep->phy_dev) {
+- phy_stop(fep->phy_dev);
+- phy_disconnect(fep->phy_dev);
++ if (netif_device_present(ndev)) {
++ napi_disable(&fep->napi);
++ netif_tx_disable(ndev);
++ mutex_lock(&fep->mutex);
++ fec_stop(ndev);
++ mutex_unlock(&fep->mutex);
+ }
+
++ phy_disconnect(fep->phy_dev);
++ fep->phy_dev = NULL;
++
+ fec_enet_free_buffers(ndev);
+
+ return 0;
+@@ -1935,28 +2319,67 @@
+ }
+ #endif
+
++static netdev_features_t fec_fix_features(struct net_device *ndev,
++ netdev_features_t features)
++{
++ struct fec_enet_private *fep = netdev_priv(ndev);
++
++ /*
++ * NETIF_F_SG requires a minimum transmit ring size. If we
++ * have less than this size, we can't support this feature.
++ */
++ if (fep->tx_ring_size < TX_RING_SIZE_MIN_SG)
++ features &= ~NETIF_F_SG;
++
++ return features;
++}
++
++#define FEATURES_NEED_QUIESCE (NETIF_F_RXCSUM | NETIF_F_SG)
++
+ static int fec_set_features(struct net_device *netdev,
+ netdev_features_t features)
+ {
+ struct fec_enet_private *fep = netdev_priv(netdev);
+ netdev_features_t changed = features ^ netdev->features;
+
++ /* Quiesce the device if necessary */
++ if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
++ mutex_lock(&fep->mutex);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(netdev);
++ fec_stop(netdev);
++ }
++
+ netdev->features = features;
+
+ /* Receive checksum has been changed */
+ if (changed & NETIF_F_RXCSUM) {
+ if (features & NETIF_F_RXCSUM)
+- fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
++ fep->flags |= FEC_FLAG_RX_CSUM;
+ else
+- fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
++ fep->flags &= ~FEC_FLAG_RX_CSUM;
++ }
+
+- if (netif_running(netdev)) {
+- fec_stop(netdev);
+- fec_restart(netdev, fep->phy_dev->duplex);
+- netif_wake_queue(netdev);
+- } else {
+- fec_restart(netdev, fep->phy_dev->duplex);
+- }
++ if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
++ if (features & NETIF_F_HW_VLAN_CTAG_RX)
++ fep->flags |= FEC_FLAG_RX_VLAN;
++ else
++ fep->flags &= ~FEC_FLAG_RX_VLAN;
++ }
++
++ /* Set the appropriate minimum transmit ring free threshold */
++ if (features & NETIF_F_SG)
++ fep->tx_min = MAX_SKB_FRAGS + 1;
++ else
++ fep->tx_min = 1;
++
++ /* Resume the device after updates */
++ if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
++ fec_restart(netdev);
++ netif_wake_queue(netdev);
++ netif_tx_unlock_bh(netdev);
++ napi_enable(&fep->napi);
++ mutex_unlock(&fep->mutex);
+ }
+
+ return 0;
+@@ -1975,27 +2398,13 @@
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = fec_poll_controller,
+ #endif
++ .ndo_fix_features = fec_fix_features,
+ .ndo_set_features = fec_set_features,
+ };
+
+- /*
+- * XXX: We need to clean up on failure exits here.
+- *
+- */
+-static int fec_enet_init(struct net_device *ndev)
++static void fec_enet_init(struct net_device *ndev)
+ {
+ struct fec_enet_private *fep = netdev_priv(ndev);
+- const struct platform_device_id *id_entry =
+- platform_get_device_id(fep->pdev);
+- struct bufdesc *cbd_base;
+-
+- /* Allocate memory for buffer descriptors. */
+- cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+- GFP_KERNEL);
+- if (!cbd_base)
+- return -ENOMEM;
+-
+- memset(cbd_base, 0, PAGE_SIZE);
+
+ fep->netdev = ndev;
+
+@@ -2008,13 +2417,8 @@
+ fep->tx_ring_size = TX_RING_SIZE;
+ fep->rx_ring_size = RX_RING_SIZE;
+
+- /* Set receive and transmit descriptor base. */
+- fep->rx_bd_base = cbd_base;
+- if (fep->bufdesc_ex)
+- fep->tx_bd_base = (struct bufdesc *)
+- (((struct bufdesc_ex *)cbd_base) + fep->rx_ring_size);
+- else
+- fep->tx_bd_base = cbd_base + fep->rx_ring_size;
++ fep->rx_bd_base = fep->tx_bd_base = NULL;
++ fep->rx_bd_dma = fep->tx_bd_dma = 0;
+
+ /* The FEC Ethernet specific entries in the device structure */
+ ndev->watchdog_timeo = TX_TIMEOUT;
+@@ -2024,24 +2428,37 @@
+ writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
+ netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
+
+- if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
+- /* enable hw VLAN support */
+- ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+- ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+- }
+-
+- if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
+- /* enable hw accelerator */
+- ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+- | NETIF_F_RXCSUM);
+- ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+- | NETIF_F_RXCSUM);
+- fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
++ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
++ /* Features which require the enhanced buffer descriptors */
++ if (fep->quirks & FEC_QUIRK_HAS_VLAN) {
++ /* enable hw VLAN support */
++ ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
++ ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
++ fep->flags |= FEC_FLAG_RX_VLAN;
++ }
++
++ if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
++ /* enable hw accelerator */
++ ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
++ | NETIF_F_RXCSUM);
++ ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
++ | NETIF_F_RXCSUM);
++ fep->flags |= FEC_FLAG_RX_CSUM;
++ }
+ }
+
+- fec_restart(ndev, 0);
++ if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME)) {
++ /* don't enable SG if we need to swap frames */
++ ndev->features |= NETIF_F_SG;
++ ndev->hw_features |= NETIF_F_SG;
++ }
+
+- return 0;
++ if (ndev->features & NETIF_F_SG)
++ fep->tx_min = MAX_SKB_FRAGS + 1;
++ else
++ fep->tx_min = 1;
++
++ fec_restart(ndev);
+ }
+
+ #ifdef CONFIG_OF
+@@ -2107,11 +2524,16 @@
+ /* setup board info structure */
+ fep = netdev_priv(ndev);
+
++ mutex_init(&fep->mutex);
++
++ if (pdev->id_entry)
++ fep->quirks = pdev->id_entry->driver_data;
+ #if !defined(CONFIG_M5272)
+ /* default enable pause frame auto negotiation */
+- if (pdev->id_entry &&
+- (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
+- fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
++ if (fep->quirks & FEC_QUIRK_HAS_GBIT)
++ fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG |
++ FEC_PAUSE_FLAG_TX |
++ FEC_PAUSE_FLAG_RX;
+ #endif
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+@@ -2124,7 +2546,9 @@
+ fep->pdev = pdev;
+ fep->dev_id = dev_id++;
+
+- fep->bufdesc_ex = 0;
++ fep->flags = 0;
++ if (pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX)
++ fep->flags |= FEC_FLAG_BUFDESC_EX;
+
+ platform_set_drvdata(pdev, ndev);
+
+@@ -2157,11 +2581,9 @@
+ fep->clk_enet_out = NULL;
+
+ fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
+- fep->bufdesc_ex =
+- pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
+ if (IS_ERR(fep->clk_ptp)) {
+ fep->clk_ptp = NULL;
+- fep->bufdesc_ex = 0;
++ fep->flags &= ~FEC_FLAG_BUFDESC_EX;
+ }
+
+ ret = clk_prepare_enable(fep->clk_ahb);
+@@ -2198,12 +2620,10 @@
+
+ fec_reset_phy(pdev);
+
+- if (fep->bufdesc_ex)
++ if (fep->flags & FEC_FLAG_BUFDESC_EX)
+ fec_ptp_init(pdev);
+
+- ret = fec_enet_init(ndev);
+- if (ret)
+- goto failed_init;
++ fec_enet_init(ndev);
+
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ irq = platform_get_irq(pdev, i);
+@@ -2230,17 +2650,16 @@
+ if (ret)
+ goto failed_register;
+
+- if (fep->bufdesc_ex && fep->ptp_clock)
++ if (fep->flags & FEC_FLAG_BUFDESC_EX && fep->ptp_clock)
+ netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
+
+- INIT_DELAYED_WORK(&(fep->delay_work.delay_work), fec_enet_work);
++ INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
+ return 0;
+
+ failed_register:
+ fec_enet_mii_remove(fep);
+ failed_mii_init:
+ failed_irq:
+-failed_init:
+ if (fep->reg_phy)
+ regulator_disable(fep->reg_phy);
+ failed_regulator:
+@@ -2266,7 +2685,7 @@
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+- cancel_delayed_work_sync(&(fep->delay_work.delay_work));
++ cancel_work_sync(&fep->tx_timeout_work);
+ unregister_netdev(ndev);
+ fec_enet_mii_remove(fep);
+ del_timer_sync(&fep->time_keep);
+@@ -2292,10 +2711,19 @@
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
++ rtnl_lock();
+ if (netif_running(ndev)) {
+- fec_stop(ndev);
++ phy_stop(fep->phy_dev);
++ napi_disable(&fep->napi);
++ netif_tx_lock_bh(ndev);
+ netif_device_detach(ndev);
++ netif_tx_unlock_bh(ndev);
++ mutex_lock(&fep->mutex);
++ fec_stop(ndev);
++ mutex_unlock(&fep->mutex);
+ }
++ rtnl_unlock();
++
+ if (fep->clk_ptp)
+ clk_disable_unprepare(fep->clk_ptp);
+ if (fep->clk_enet_out)
+@@ -2342,10 +2770,18 @@
+ goto failed_clk_ptp;
+ }
+
++ rtnl_lock();
+ if (netif_running(ndev)) {
+- fec_restart(ndev, fep->full_duplex);
++ mutex_lock(&fep->mutex);
++ fec_restart(ndev);
++ mutex_unlock(&fep->mutex);
++ netif_tx_lock_bh(ndev);
+ netif_device_attach(ndev);
++ netif_tx_unlock_bh(ndev);
++ napi_enable(&fep->napi);
++ phy_start(fep->phy_dev);
+ }
++ rtnl_unlock();
+
+ return 0;
+
+diff -Nur linux-3.15-rc6.orig/drivers/regulator/anatop-regulator.c linux-3.15-rc6/drivers/regulator/anatop-regulator.c
+--- linux-3.15-rc6.orig/drivers/regulator/anatop-regulator.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/regulator/anatop-regulator.c 2014-05-23 11:26:48.312940058 +0200
+@@ -267,6 +267,7 @@
+ config.driver_data = sreg;
+ config.of_node = pdev->dev.of_node;
+ config.regmap = sreg->anatop;
++ config.ena_gpio = -EINVAL;
+
+ /* Only core regulators have the ramp up delay configuration. */
+ if (sreg->control_reg && sreg->delay_bit_width) {
+diff -Nur linux-3.15-rc6.orig/drivers/regulator/core.c linux-3.15-rc6/drivers/regulator/core.c
+--- linux-3.15-rc6.orig/drivers/regulator/core.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/regulator/core.c 2014-05-23 11:26:48.312940058 +0200
+@@ -3459,7 +3459,7 @@
+
+ dev_set_drvdata(&rdev->dev, rdev);
+
+- if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
++ if (gpio_is_valid(config->ena_gpio)) {
+ ret = regulator_ena_gpio_request(rdev, config);
+ if (ret != 0) {
+ rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
+diff -Nur linux-3.15-rc6.orig/drivers/regulator/dummy.c linux-3.15-rc6/drivers/regulator/dummy.c
+--- linux-3.15-rc6.orig/drivers/regulator/dummy.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/regulator/dummy.c 2014-05-23 11:26:48.312940058 +0200
+@@ -48,6 +48,7 @@
+
+ config.dev = &pdev->dev;
+ config.init_data = &dummy_initdata;
++ config.ena_gpio = -EINVAL;
+
+ dummy_regulator_rdev = regulator_register(&dummy_desc, &config);
+ if (IS_ERR(dummy_regulator_rdev)) {
+diff -Nur linux-3.15-rc6.orig/drivers/regulator/fixed.c linux-3.15-rc6/drivers/regulator/fixed.c
+--- linux-3.15-rc6.orig/drivers/regulator/fixed.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/regulator/fixed.c 2014-05-23 11:26:48.312940058 +0200
+@@ -161,9 +161,7 @@
+ drvdata->desc.n_voltages = 1;
+
+ drvdata->desc.fixed_uV = config->microvolts;
+-
+- if (config->gpio >= 0)
+- cfg.ena_gpio = config->gpio;
++ cfg.ena_gpio = config->gpio;
+ cfg.ena_gpio_invert = !config->enable_high;
+ if (config->enabled_at_boot) {
+ if (config->enable_high)
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/drm-ddc-connector.c linux-3.15-rc6/drivers/staging/imx-drm/drm-ddc-connector.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/drm-ddc-connector.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/drm-ddc-connector.c 2014-05-23 11:26:48.312940058 +0200
+@@ -0,0 +1,92 @@
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <drm/drmP.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_edid.h>
++
++#include "drm-ddc-connector.h"
++
++static enum drm_connector_status
++drm_ddc_connector_detect(struct drm_connector *connector, bool force)
++{
++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector);
++
++ return ddc_conn->detect ? ddc_conn->detect(connector, force) :
++ connector_status_connected;
++}
++
++int drm_ddc_connector_get_modes(struct drm_connector *connector)
++{
++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector);
++ struct edid *edid;
++ int ret = 0;
++
++ if (!ddc_conn->ddc)
++ return 0;
++
++ edid = drm_get_edid(connector, ddc_conn->ddc);
++ if (edid) {
++ drm_mode_connector_update_edid_property(connector, edid);
++ ret = drm_add_edid_modes(connector, edid);
++ /* Store the ELD */
++ drm_edid_to_eld(connector, edid);
++ kfree(edid);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(drm_ddc_connector_get_modes);
++
++static void drm_ddc_connector_destroy(struct drm_connector *connector)
++{
++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector);
++
++ drm_sysfs_connector_remove(connector);
++ drm_connector_cleanup(connector);
++ if (ddc_conn->ddc)
++ i2c_put_adapter(ddc_conn->ddc);
++}
++
++static const struct drm_connector_funcs drm_ddc_connector_funcs = {
++ .dpms = drm_helper_connector_dpms,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .detect = drm_ddc_connector_detect,
++ .destroy = drm_ddc_connector_destroy,
++};
++
++int drm_ddc_connector_add(struct drm_device *drm,
++ struct drm_ddc_connector *ddc_conn, int connector_type)
++{
++ drm_connector_init(drm, &ddc_conn->connector, &drm_ddc_connector_funcs,
++ connector_type);
++ return 0;
++}
++EXPORT_SYMBOL_GPL(drm_ddc_connector_add);
++
++struct drm_ddc_connector *drm_ddc_connector_create(struct drm_device *drm,
++ struct device_node *np, void *private)
++{
++ struct drm_ddc_connector *ddc_conn;
++ struct device_node *ddc_node;
++
++ ddc_conn = devm_kzalloc(drm->dev, sizeof(*ddc_conn), GFP_KERNEL);
++ if (!ddc_conn)
++ return ERR_PTR(-ENOMEM);
++
++ ddc_conn->private = private;
++
++ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
++ if (ddc_node) {
++ ddc_conn->ddc = of_find_i2c_adapter_by_node(ddc_node);
++ of_node_put(ddc_node);
++ if (!ddc_conn->ddc)
++ return ERR_PTR(-EPROBE_DEFER);
++ }
++
++ return ddc_conn;
++}
++EXPORT_SYMBOL_GPL(drm_ddc_connector_create);
++
++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
++MODULE_DESCRIPTION("Generic DRM DDC connector module");
++MODULE_LICENSE("GPL v2");
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/drm-ddc-connector.h linux-3.15-rc6/drivers/staging/imx-drm/drm-ddc-connector.h
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/drm-ddc-connector.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/drm-ddc-connector.h 2014-05-23 11:26:48.312940058 +0200
+@@ -0,0 +1,26 @@
++#ifndef DRM_DDC_CONNECTOR_H
++#define DRM_DDC_CONNECTOR_H
++
++struct drm_ddc_connector {
++ struct i2c_adapter *ddc;
++ struct drm_connector connector;
++ enum drm_connector_status (*detect)(struct drm_connector *, bool);
++ void *private;
++};
++
++#define to_ddc_conn(c) container_of(c, struct drm_ddc_connector, connector)
++
++int drm_ddc_connector_get_modes(struct drm_connector *connector);
++int drm_ddc_connector_add(struct drm_device *drm,
++ struct drm_ddc_connector *ddc_conn, int connector_type);
++struct drm_ddc_connector *drm_ddc_connector_create(struct drm_device *drm,
++ struct device_node *np, void *private);
++
++static inline void *drm_ddc_private(struct drm_connector *connector)
++{
++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector);
++
++ return ddc_conn->private;
++}
++
++#endif
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-audio.c linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-audio.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-audio.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-audio.c 2014-05-23 11:26:48.316940071 +0200
+@@ -0,0 +1,654 @@
++/*
++ * DesignWare HDMI audio driver
++ *
++ * 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.
++ *
++ * Written and tested against the (alleged) DW HDMI Tx found in iMX6S.
++ */
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/asoundef.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++
++#include "dw-hdmi-audio.h"
++
++#define DRIVER_NAME "dw-hdmi-audio"
++
++/* Provide some bits rather than bit offsets */
++enum {
++ HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
++ HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
++ HDMI_AHB_DMA_START_START = BIT(0),
++ HDMI_AHB_DMA_STOP_STOP = BIT(0),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
++ HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
++ HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
++ HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
++ HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
++ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
++ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
++ HDMI_IH_AHBDMAAUD_STAT0_ALL =
++ HDMI_IH_AHBDMAAUD_STAT0_ERROR |
++ HDMI_IH_AHBDMAAUD_STAT0_LOST |
++ HDMI_IH_AHBDMAAUD_STAT0_RETRY |
++ HDMI_IH_AHBDMAAUD_STAT0_DONE |
++ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
++ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
++ HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
++ HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
++ HDMI_AHB_DMA_CONF0_INCR4 = 0,
++ HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
++ HDMI_AHB_DMA_MASK_DONE = BIT(7),
++ HDMI_REVISION_ID = 0x0001,
++ HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
++ HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
++ HDMI_AUD_N1 = 0x3200,
++ HDMI_AUD_CTS1 = 0x3203,
++ HDMI_AHB_DMA_CONF0 = 0x3600,
++ HDMI_AHB_DMA_START = 0x3601,
++ HDMI_AHB_DMA_STOP = 0x3602,
++ HDMI_AHB_DMA_THRSLD = 0x3603,
++ HDMI_AHB_DMA_STRADDR0 = 0x3604,
++ HDMI_AHB_DMA_STPADDR0 = 0x3608,
++ HDMI_AHB_DMA_STAT = 0x3612,
++ HDMI_AHB_DMA_STAT_FULL = BIT(1),
++ HDMI_AHB_DMA_MASK = 0x3614,
++ HDMI_AHB_DMA_POL = 0x3615,
++ HDMI_AHB_DMA_CONF1 = 0x3616,
++ HDMI_AHB_DMA_BUFFPOL = 0x361a,
++};
++
++struct snd_dw_hdmi {
++ struct snd_card *card;
++ struct snd_pcm *pcm;
++ struct dw_hdmi_audio_data data;
++ struct snd_pcm_substream *substream;
++ void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
++ void *buf_src;
++ void *buf_dst;
++ dma_addr_t buf_addr;
++ unsigned buf_offset;
++ unsigned buf_period;
++ unsigned buf_size;
++ unsigned channels;
++ uint8_t revision;
++ uint8_t iec_offset;
++ uint8_t cs[192][8];
++};
++
++static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
++{
++ writeb_relaxed(val, ptr);
++ writeb_relaxed(val >> 8, ptr + 1);
++ writeb_relaxed(val >> 16, ptr + 2);
++ writeb_relaxed(val >> 24, ptr + 3);
++}
++
++/*
++ * Convert to hardware format: The userspace buffer contains IEC958 samples,
++ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4. We
++ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
++ * samples in 23..0.
++ *
++ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
++ *
++ * Ideally, we could do with having the data properly formatted in userspace.
++ */
++static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
++ size_t offset, size_t bytes)
++{
++ uint32_t *src = dw->buf_src + offset;
++ uint32_t *dst = dw->buf_dst + offset;
++ uint32_t *end = dw->buf_src + offset + bytes;
++
++ do {
++ uint32_t b, sample = *src++;
++
++ b = (sample & 8) << (28 - 3);
++
++ sample >>= 4;
++
++ *dst++ = sample | b;
++ } while (src < end);
++}
++
++static uint32_t parity(uint32_t sample)
++{
++ sample ^= sample >> 16;
++ sample ^= sample >> 8;
++ sample ^= sample >> 4;
++ sample ^= sample >> 2;
++ sample ^= sample >> 1;
++ return (sample & 1) << 27;
++}
++
++static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
++ size_t offset, size_t bytes)
++{
++ uint32_t *src = dw->buf_src + offset;
++ uint32_t *dst = dw->buf_dst + offset;
++ uint32_t *end = dw->buf_src + offset + bytes;
++
++ do {
++ unsigned i;
++ uint8_t *cs;
++
++ cs = dw->cs[dw->iec_offset++];
++ if (dw->iec_offset >= 192)
++ dw->iec_offset = 0;
++
++ i = dw->channels;
++ do {
++ uint32_t sample = *src++;
++
++ sample &= ~0xff000000;
++ sample |= *cs++ << 24;
++ sample |= parity(sample & ~0xf8000000);
++
++ *dst++ = sample;
++ } while (--i);
++ } while (src < end);
++}
++
++static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
++ struct snd_pcm_runtime *runtime)
++{
++ uint8_t cs[4];
++ unsigned ch, i, j;
++
++ cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
++ cs[1] = IEC958_AES1_CON_GENERAL;
++ cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC;
++ cs[3] = IEC958_AES3_CON_CLOCK_1000PPM;
++
++ switch (runtime->rate) {
++ case 32000:
++ cs[3] |= IEC958_AES3_CON_FS_32000;
++ break;
++ case 44100:
++ cs[3] |= IEC958_AES3_CON_FS_44100;
++ break;
++ case 48000:
++ cs[3] |= IEC958_AES3_CON_FS_48000;
++ break;
++ case 88200:
++ cs[3] |= IEC958_AES3_CON_FS_88200;
++ break;
++ case 96000:
++ cs[3] |= IEC958_AES3_CON_FS_96000;
++ break;
++ case 176400:
++ cs[3] |= IEC958_AES3_CON_FS_176400;
++ break;
++ case 192000:
++ cs[3] |= IEC958_AES3_CON_FS_192000;
++ break;
++ }
++
++ memset(dw->cs, 0, sizeof(dw->cs));
++
++ for (ch = 0; ch < 8; ch++) {
++ cs[2] &= ~IEC958_AES2_CON_CHANNEL;
++ cs[2] |= (ch + 1) << 4;
++
++ for (i = 0; i < ARRAY_SIZE(cs); i++) {
++ unsigned c = cs[i];
++
++ for (j = 0; j < 8; j++, c >>= 1)
++ dw->cs[i * 8 + j][ch] = (c & 1) << 2;
++ }
++ }
++ dw->cs[0][0] |= BIT(4);
++}
++
++static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
++{
++ void __iomem *base = dw->data.base;
++ unsigned offset = dw->buf_offset;
++ unsigned period = dw->buf_period;
++ u32 start, stop;
++
++ dw->reformat(dw, offset, period);
++
++ /* Clear all irqs before enabling irqs and starting DMA */
++ writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
++ base + HDMI_IH_AHBDMAAUD_STAT0);
++
++ start = dw->buf_addr + offset;
++ stop = start + period - 1;
++
++ /* Setup the hardware start/stop addresses */
++ dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
++ dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
++
++ writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
++ writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
++
++ offset += period;
++ if (offset >= dw->buf_size)
++ offset = 0;
++ dw->buf_offset = offset;
++}
++
++static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
++{
++ dw->substream = NULL;
++
++ /* Disable interrupts before disabling DMA */
++ writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
++ writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
++}
++
++static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
++{
++ struct snd_dw_hdmi *dw = data;
++ struct snd_pcm_substream *substream;
++ unsigned stat;
++
++ stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
++ if (!stat)
++ return IRQ_NONE;
++
++ writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
++
++ substream = dw->substream;
++ if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
++ snd_pcm_period_elapsed(substream);
++ if (dw->substream)
++ dw_hdmi_start_dma(dw);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static struct snd_pcm_hardware dw_hdmi_hw = {
++ .info = SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID,
++ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
++ SNDRV_PCM_FMTBIT_S24_LE,
++ .rates = SNDRV_PCM_RATE_32000 |
++ SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000 |
++ SNDRV_PCM_RATE_88200 |
++ SNDRV_PCM_RATE_96000 |
++ SNDRV_PCM_RATE_176400 |
++ SNDRV_PCM_RATE_192000,
++ .channels_min = 2,
++ .channels_max = 8,
++ .buffer_bytes_max = 64 * 1024,
++ .period_bytes_min = 256,
++ .period_bytes_max = 8192, /* ERR004323: must limit to 8k */
++ .periods_min = 2,
++ .periods_max = 16,
++ .fifo_size = 0,
++};
++
++static unsigned rates_mask[] = {
++ SNDRV_PCM_RATE_32000,
++ SNDRV_PCM_RATE_44100,
++ SNDRV_PCM_RATE_48000,
++ SNDRV_PCM_RATE_88200,
++ SNDRV_PCM_RATE_96000,
++ SNDRV_PCM_RATE_176400,
++ SNDRV_PCM_RATE_192000,
++};
++
++static void dw_hdmi_parse_eld(struct snd_dw_hdmi *dw,
++ struct snd_pcm_runtime *runtime)
++{
++ u8 *sad, *eld = dw->data.eld;
++ unsigned eld_ver, mnl, sad_count, rates, rate_mask, i;
++ unsigned max_channels;
++
++ eld_ver = eld[0] >> 3;
++ if (eld_ver != 2 && eld_ver != 31)
++ return;
++
++ mnl = eld[4] & 0x1f;
++ if (mnl > 16)
++ return;
++
++ sad_count = eld[5] >> 4;
++ sad = eld + 20 + mnl;
++
++ /* Start from the basic audio settings */
++ max_channels = 2;
++ rates = 7;
++ while (sad_count > 0) {
++ switch (sad[0] & 0x78) {
++ case 0x08: /* PCM */
++ max_channels = max(max_channels, (sad[0] & 7) + 1u);
++ rates |= sad[1];
++ break;
++ }
++ sad += 3;
++ sad_count -= 1;
++ }
++
++ for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++)
++ if (rates & 1 << i)
++ rate_mask |= rates_mask[i];
++
++ runtime->hw.rates &= rate_mask;
++ runtime->hw.channels_max = min(runtime->hw.channels_max, max_channels);
++}
++
++static int dw_hdmi_open(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_dw_hdmi *dw = substream->private_data;
++ void __iomem *base = dw->data.base;
++ int ret;
++
++ /* Clear FIFO */
++ writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
++ base + HDMI_AHB_DMA_CONF0);
++
++ /* Configure interrupt polarities */
++ writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
++ writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
++
++ /* Keep interrupts masked, and clear any pending */
++ writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
++ writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
++
++ ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
++ "dw-hdmi-audio", dw);
++ if (ret)
++ return ret;
++
++ /* Un-mute done interrupt */
++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
++ ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
++ base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
++
++ runtime->hw = dw_hdmi_hw;
++ dw_hdmi_parse_eld(dw, runtime);
++ snd_pcm_limit_hw_rates(runtime);
++
++ return 0;
++}
++
++static int dw_hdmi_close(struct snd_pcm_substream *substream)
++{
++ struct snd_dw_hdmi *dw = substream->private_data;
++
++ /* Mute all interrupts */
++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
++ dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
++
++ free_irq(dw->data.irq, dw);
++
++ return 0;
++}
++
++static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_vmalloc_buffer(substream);
++}
++
++static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
++ params_buffer_bytes(params));
++}
++
++static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_dw_hdmi *dw = substream->private_data;
++ uint8_t threshold, conf0, conf1;
++
++ /* Setup as per 3.0.5 FSL 4.1.0 BSP */
++ switch (dw->revision) {
++ case 0x0a:
++ conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
++ HDMI_AHB_DMA_CONF0_INCR4;
++ if (runtime->channels == 2)
++ threshold = 126;
++ else
++ threshold = 124;
++ break;
++ case 0x1a:
++ conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
++ HDMI_AHB_DMA_CONF0_INCR8;
++ threshold = 128;
++ break;
++ default:
++ /* NOTREACHED */
++ return -EINVAL;
++ }
++
++ dw->data.set_sample_rate(dw->data.hdmi, runtime->rate);
++
++ /* Minimum number of bytes in the fifo. */
++ runtime->hw.fifo_size = threshold * 32;
++
++ conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
++ conf1 = (1 << runtime->channels) - 1;
++
++ writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
++ writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
++ writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
++
++ switch (runtime->format) {
++ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
++ dw->reformat = dw_hdmi_reformat_iec958;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ dw_hdmi_create_cs(dw, runtime);
++ dw->reformat = dw_hdmi_reformat_s24;
++ break;
++ }
++ dw->iec_offset = 0;
++ dw->channels = runtime->channels;
++ dw->buf_src = runtime->dma_area;
++ dw->buf_dst = substream->dma_buffer.area;
++ dw->buf_addr = substream->dma_buffer.addr;
++ dw->buf_period = snd_pcm_lib_period_bytes(substream);
++ dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
++
++ return 0;
++}
++
++static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_dw_hdmi *dw = substream->private_data;
++ void __iomem *base = dw->data.base;
++ unsigned n[3], cts[3];
++ int ret = 0, i;
++ bool err005174;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ err005174 = dw->revision == 0x0a;
++ if (err005174) {
++ for (i = 2; i >= 1; i--) {
++ n[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
++ cts[i] = readb_relaxed(base + HDMI_AUD_CTS1 + i);
++ writeb_relaxed(0, base + HDMI_AUD_N1 + i);
++ writeb_relaxed(0, base + HDMI_AUD_CTS1 + i);
++ }
++ }
++
++ dw->buf_offset = 0;
++ dw->substream = substream;
++ dw_hdmi_start_dma(dw);
++
++ if (err005174) {
++ for (i = 2; i >= 1; i--)
++ writeb_relaxed(cts[i], base + HDMI_AUD_CTS1 + i);
++ for (i = 2; i >= 1; i--)
++ writeb_relaxed(n[i], base + HDMI_AUD_N1 + i);
++ }
++
++ substream->runtime->delay = substream->runtime->period_size;
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ dw_hdmi_stop_dma(dw);
++ break;
++
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_dw_hdmi *dw = substream->private_data;
++
++ return bytes_to_frames(runtime, dw->buf_offset);
++}
++
++static struct snd_pcm_ops snd_dw_hdmi_ops = {
++ .open = dw_hdmi_open,
++ .close = dw_hdmi_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = dw_hdmi_hw_params,
++ .hw_free = dw_hdmi_hw_free,
++ .prepare = dw_hdmi_prepare,
++ .trigger = dw_hdmi_trigger,
++ .pointer = dw_hdmi_pointer,
++ .page = snd_pcm_lib_get_vmalloc_page,
++};
++
++static int snd_dw_hdmi_probe(struct platform_device *pdev)
++{
++ const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
++ struct device *dev = pdev->dev.parent;
++ struct snd_dw_hdmi *dw;
++ struct snd_card *card;
++ struct snd_pcm *pcm;
++ unsigned revision;
++ int ret;
++
++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
++ data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
++ revision = readb_relaxed(data->base + HDMI_REVISION_ID);
++ if (revision != 0x0a && revision != 0x1a) {
++ dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
++ revision);
++ return -ENXIO;
++ }
++
++ ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
++ THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
++ if (ret < 0)
++ return ret;
++
++ snd_card_set_dev(card, dev);
++
++ strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
++ strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
++ snprintf(card->longname, sizeof(card->longname),
++ "%s rev 0x%02x, irq %d", card->shortname, revision,
++ data->irq);
++
++ dw = card->private_data;
++ dw->card = card;
++ dw->data = *data;
++ dw->revision = revision;
++
++ ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
++ if (ret < 0)
++ goto err;
++
++ dw->pcm = pcm;
++ pcm->private_data = dw;
++ strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
++
++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++ dev, 64 * 1024, 64 * 1024);
++
++ ret = snd_card_register(card);
++ if (ret < 0)
++ goto err;
++
++ platform_set_drvdata(pdev, dw);
++
++ return 0;
++
++err:
++ snd_card_free(card);
++ return ret;
++}
++
++static int snd_dw_hdmi_remove(struct platform_device *pdev)
++{
++ struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
++
++ snd_card_free(dw->card);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int snd_dw_hdmi_suspend(struct device *dev)
++{
++ struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
++
++ snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
++ snd_pcm_suspend_all(dw->pcm);
++
++ return 0;
++}
++
++static int snd_dw_hdmi_resume(struct device *dev)
++{
++ struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
++
++ snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
++ snd_dw_hdmi_resume);
++#define PM_OPS &snd_dw_hdmi_pm
++#else
++#define PM_OPS NULL
++#endif
++
++static struct platform_driver snd_dw_hdmi_driver = {
++ .probe = snd_dw_hdmi_probe,
++ .remove = snd_dw_hdmi_remove,
++ .driver = {
++ .name = "dw-hdmi-audio",
++ .owner = THIS_MODULE,
++ .pm = PM_OPS,
++ },
++};
++
++module_platform_driver(snd_dw_hdmi_driver);
++
++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
++MODULE_LICENSE("GPL");
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-audio.h linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-audio.h
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-audio.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-audio.h 2014-05-23 11:26:48.316940071 +0200
+@@ -0,0 +1,15 @@
++#ifndef DW_HDMI_AUDIO_H
++#define DW_HDMI_AUDIO_H
++
++struct imx_hdmi;
++
++struct dw_hdmi_audio_data {
++ phys_addr_t phys;
++ void __iomem *base;
++ int irq;
++ struct imx_hdmi *hdmi;
++ u8 *eld;
++ void (*set_sample_rate)(struct imx_hdmi *, unsigned);
++};
++
++#endif
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-cec.c linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-cec.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-cec.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-cec.c 2014-05-23 11:26:48.316940071 +0200
+@@ -0,0 +1,205 @@
++/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
++#include <linux/cec-dev.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++
++#include "imx-hdmi.h"
++#include "dw-hdmi-cec.h"
++
++#define DEV_NAME "mxc_hdmi_cec"
++
++enum {
++ CEC_STAT_DONE = BIT(0),
++ CEC_STAT_EOM = BIT(1),
++ CEC_STAT_NACK = BIT(2),
++ CEC_STAT_ARBLOST = BIT(3),
++ CEC_STAT_ERROR_INIT = BIT(4),
++ CEC_STAT_ERROR_FOLL = BIT(5),
++ CEC_STAT_WAKEUP = BIT(6),
++
++ CEC_CTRL_START = BIT(0),
++ CEC_CTRL_NORMAL = 1 << 1,
++};
++
++struct dw_hdmi_cec {
++ struct cec_dev cec;
++
++ struct device *dev;
++ void __iomem *base;
++ const struct dw_hdmi_cec_ops *ops;
++ void *ops_data;
++ int irq;
++};
++
++static void dw_hdmi_set_address(struct cec_dev *cec_dev, unsigned addresses)
++{
++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
++
++ writeb(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
++ writeb(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
++}
++
++static void dw_hdmi_send_message(struct cec_dev *cec_dev, u8 *msg,
++ size_t count)
++{
++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
++ unsigned i;
++
++ for (i = 0; i < count; i++)
++ writeb(msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
++
++ writeb(count, cec->base + HDMI_CEC_TX_CNT);
++ writeb(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
++}
++
++static irqreturn_t dw_hdmi_cec_irq(int irq, void *data)
++{
++ struct dw_hdmi_cec *cec = data;
++ struct cec_dev *cec_dev = &cec->cec;
++ unsigned stat = readb(cec->base + HDMI_IH_CEC_STAT0);
++
++ if (stat == 0)
++ return IRQ_NONE;
++
++ writeb(stat, cec->base + HDMI_IH_CEC_STAT0);
++
++ if (stat & CEC_STAT_ERROR_INIT) {
++ if (cec->cec.retries) {
++ unsigned v = readb(cec->base + HDMI_CEC_CTRL);
++ writeb(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
++ cec->cec.retries -= 1;
++ } else {
++ cec->cec.write_busy = 0;
++ cec_dev_event(cec_dev, MESSAGE_TYPE_SEND_ERROR, NULL, 0);
++ }
++ } else if (stat & (CEC_STAT_DONE | CEC_STAT_NACK))
++ cec_dev_send_complete(cec_dev, stat & CEC_STAT_DONE);
++
++ if (stat & CEC_STAT_EOM) {
++ unsigned len, i;
++ u8 msg[MAX_MESSAGE_LEN];
++
++ len = readb(cec->base + HDMI_CEC_RX_CNT);
++ if (len > sizeof(msg))
++ len = sizeof(msg);
++
++ for (i = 0; i < len; i++)
++ msg[i] = readb(cec->base + HDMI_CEC_RX_DATA0 + i);
++
++ writeb(0, cec->base + HDMI_CEC_LOCK);
++
++ cec_dev_receive(cec_dev, msg, len);
++ }
++
++ return IRQ_HANDLED;
++}
++EXPORT_SYMBOL(dw_hdmi_cec_irq);
++
++static void dw_hdmi_cec_release(struct cec_dev *cec_dev)
++{
++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
++
++ writeb(~0, cec->base + HDMI_CEC_MASK);
++ writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
++ writeb(0, cec->base + HDMI_CEC_POLARITY);
++
++ free_irq(cec->irq, cec);
++
++ cec->ops->disable(cec->ops_data);
++}
++
++static int dw_hdmi_cec_open(struct cec_dev *cec_dev)
++{
++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
++ unsigned irqs;
++ int ret;
++
++ writeb(0, cec->base + HDMI_CEC_CTRL);
++ writeb(~0, cec->base + HDMI_IH_CEC_STAT0);
++ writeb(0, cec->base + HDMI_CEC_LOCK);
++
++ ret = request_irq(cec->irq, dw_hdmi_cec_irq, IRQF_SHARED,
++ DEV_NAME, cec);
++ if (ret < 0)
++ return ret;
++
++ dw_hdmi_set_address(cec_dev, cec_dev->addresses);
++
++ cec->ops->enable(cec->ops_data);
++
++ irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
++ CEC_STAT_DONE;
++ writeb(irqs, cec->base + HDMI_CEC_POLARITY);
++ writeb(~irqs, cec->base + HDMI_CEC_MASK);
++ writeb(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
++
++ return 0;
++}
++
++static int dw_hdmi_cec_probe(struct platform_device *pdev)
++{
++ struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
++ struct dw_hdmi_cec *cec;
++
++ if (!data)
++ return -ENXIO;
++
++ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
++ if (!cec)
++ return -ENOMEM;
++
++ cec->dev = &pdev->dev;
++ cec->base = data->base;
++ cec->irq = data->irq;
++ cec->ops = data->ops;
++ cec->ops_data = data->ops_data;
++ cec->cec.open = dw_hdmi_cec_open;
++ cec->cec.release = dw_hdmi_cec_release;
++ cec->cec.send_message = dw_hdmi_send_message;
++ cec->cec.set_address = dw_hdmi_set_address;
++
++ cec_dev_init(&cec->cec, THIS_MODULE);
++
++ /* FIXME: soft-reset the CEC interface */
++
++ dw_hdmi_set_address(&cec->cec, cec->cec.addresses);
++ writeb(0, cec->base + HDMI_CEC_TX_CNT);
++ writeb(~0, cec->base + HDMI_CEC_MASK);
++ writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
++ writeb(0, cec->base + HDMI_CEC_POLARITY);
++
++ /*
++ * Our device is just a convenience - we want to link to the real
++ * hardware device here, so that userspace can see the association
++ * between the HDMI hardware and its associated CEC chardev.
++ */
++ return cec_dev_add(&cec->cec, cec->dev->parent, DEV_NAME);
++}
++
++static int dw_hdmi_cec_remove(struct platform_device *pdev)
++{
++ struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
++
++ cec_dev_remove(&cec->cec);
++
++ return 0;
++}
++
++static struct platform_driver dw_hdmi_cec_driver = {
++ .probe = dw_hdmi_cec_probe,
++ .remove = dw_hdmi_cec_remove,
++ .driver = {
++ .name = "dw-hdmi-cec",
++ .owner = THIS_MODULE,
++ },
++};
++module_platform_driver(dw_hdmi_cec_driver);
++
++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
++MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-cec.h linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-cec.h
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/dw-hdmi-cec.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/drivers/staging/imx-drm/dw-hdmi-cec.h 2014-05-23 11:26:48.316940071 +0200
+@@ -0,0 +1,16 @@
++#ifndef DW_HDMI_CEC_H
++#define DW_HDMI_CEC_H
++
++struct dw_hdmi_cec_ops {
++ void (*enable)(void *);
++ void (*disable)(void *);
++};
++
++struct dw_hdmi_cec_data {
++ void __iomem *base;
++ int irq;
++ const struct dw_hdmi_cec_ops *ops;
++ void *ops_data;
++};
++
++#endif
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-hdmi.c linux-3.15-rc6/drivers/staging/imx-drm/imx-hdmi.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-hdmi.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/imx-hdmi.c 2014-05-23 11:26:48.316940071 +0200
+@@ -28,6 +28,9 @@
+ #include <drm/drm_edid.h>
+ #include <drm/drm_encoder_slave.h>
+
++#include "drm-ddc-connector.h"
++#include "dw-hdmi-audio.h"
++#include "dw-hdmi-cec.h"
+ #include "ipu-v3/imx-ipu-v3.h"
+ #include "imx-hdmi.h"
+ #include "imx-drm.h"
+@@ -112,27 +115,27 @@
+ };
+
+ struct imx_hdmi {
+- struct drm_connector connector;
++ struct drm_ddc_connector *ddc_conn;
+ struct drm_encoder encoder;
+
++ struct platform_device *audio;
++ struct platform_device *cec;
+ enum imx_hdmi_devtype dev_type;
+ struct device *dev;
+ struct clk *isfr_clk;
+ struct clk *iahb_clk;
+
+- enum drm_connector_status connector_status;
+-
+ struct hdmi_data_info hdmi_data;
+ int vic;
+
+ u8 edid[HDMI_EDID_LEN];
++ u8 mc_clkdis;
+ bool cable_plugin;
+
+ bool phy_enabled;
+ struct drm_display_mode previous_mode;
+
+ struct regmap *regmap;
+- struct i2c_adapter *ddc;
+ void __iomem *regs;
+
+ unsigned int sample_rate;
+@@ -362,6 +365,12 @@
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
+ }
+
++static void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned rate)
++{
++ hdmi->sample_rate = rate;
++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
++}
++
+ /*
+ * this submodule is responsible for the video data synchronization.
+ * for example, for RGB 4:4:4 input, the data map is defined as
+@@ -1148,8 +1157,6 @@
+ /* HDMI Initialization Step B.4 */
+ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
+ {
+- u8 clkdis;
+-
+ /* control period minimum duration */
+ hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
+ hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
+@@ -1161,23 +1168,28 @@
+ hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+
+ /* Enable pixel clock and tmds data path */
+- clkdis = 0x7F;
+- clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
++ HDMI_MC_CLKDIS_CSCCLK_DISABLE |
++ HDMI_MC_CLKDIS_AUDCLK_DISABLE |
++ HDMI_MC_CLKDIS_PREPCLK_DISABLE |
++ HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+- clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+ /* Enable csc path */
+ if (is_color_space_conversion(hdmi)) {
+- clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ }
+ }
+
+ static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
+ {
+- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ }
+
+ /* Workaround to clear the overflow condition */
+@@ -1380,41 +1392,16 @@
+ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
+ *connector, bool force)
+ {
+- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+- connector);
+- return hdmi->connector_status;
+-}
+-
+-static int imx_hdmi_connector_get_modes(struct drm_connector *connector)
+-{
+- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+- connector);
+- struct edid *edid;
+- int ret;
++ struct imx_hdmi *hdmi = drm_ddc_private(connector);
+
+- if (!hdmi->ddc)
+- return 0;
+-
+- edid = drm_get_edid(connector, hdmi->ddc);
+- if (edid) {
+- dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+- edid->width_cm, edid->height_cm);
+-
+- drm_mode_connector_update_edid_property(connector, edid);
+- ret = drm_add_edid_modes(connector, edid);
+- kfree(edid);
+- } else {
+- dev_dbg(hdmi->dev, "failed to get edid\n");
+- }
+-
+- return 0;
++ return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
++ connector_status_connected : connector_status_disconnected;
+ }
+
+ static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector
+ *connector)
+ {
+- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+- connector);
++ struct imx_hdmi *hdmi = drm_ddc_private(connector);
+
+ return &hdmi->encoder;
+ }
+@@ -1483,15 +1470,8 @@
+ .disable = imx_hdmi_encoder_disable,
+ };
+
+-static struct drm_connector_funcs imx_hdmi_connector_funcs = {
+- .dpms = drm_helper_connector_dpms,
+- .fill_modes = drm_helper_probe_single_connector_modes,
+- .detect = imx_hdmi_connector_detect,
+- .destroy = imx_drm_connector_destroy,
+-};
+-
+ static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
+- .get_modes = imx_hdmi_connector_get_modes,
++ .get_modes = drm_ddc_connector_get_modes,
+ .mode_valid = imx_drm_connector_mode_valid,
+ .best_encoder = imx_hdmi_connector_best_encoder,
+ };
+@@ -1524,7 +1504,6 @@
+
+ hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
+
+- hdmi->connector_status = connector_status_connected;
+ imx_hdmi_poweron(hdmi);
+ } else {
+ dev_dbg(hdmi->dev, "EVENT=plugout\n");
+@@ -1532,10 +1511,9 @@
+ hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
+ HDMI_PHY_POL0);
+
+- hdmi->connector_status = connector_status_disconnected;
+ imx_hdmi_poweroff(hdmi);
+ }
+- drm_helper_hpd_irq_event(hdmi->connector.dev);
++ drm_helper_hpd_irq_event(hdmi->ddc_conn->connector.dev);
+ }
+
+ hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+@@ -1553,24 +1531,42 @@
+ if (ret)
+ return ret;
+
+- hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
++ hdmi->ddc_conn->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs);
+ drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+- drm_connector_helper_add(&hdmi->connector,
++ drm_connector_helper_add(&hdmi->ddc_conn->connector,
+ &imx_hdmi_connector_helper_funcs);
+- drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs,
+- DRM_MODE_CONNECTOR_HDMIA);
+-
+- hdmi->connector.encoder = &hdmi->encoder;
++ drm_ddc_connector_add(drm, hdmi->ddc_conn, DRM_MODE_CONNECTOR_HDMIA);
+
+- drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
++ drm_mode_connector_attach_encoder(&hdmi->ddc_conn->connector, &hdmi->encoder);
+
+ return 0;
+ }
+
++static void imx_hdmi_cec_enable(void *data)
++{
++ struct imx_hdmi *hdmi = data;
++
++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
++}
++
++static void imx_hdmi_cec_disable(void *data)
++{
++ struct imx_hdmi *hdmi = data;
++
++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
++}
++
++static const struct dw_hdmi_cec_ops imx_hdmi_cec_ops = {
++ .enable = imx_hdmi_cec_enable,
++ .disable = imx_hdmi_cec_disable,
++};
++
+ static struct platform_device_id imx_hdmi_devtype[] = {
+ {
+ .name = "imx6q-hdmi",
+@@ -1592,11 +1588,13 @@
+ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
++ struct platform_device_info pdevinfo;
+ const struct of_device_id *of_id =
+ of_match_device(imx_hdmi_dt_ids, dev);
+ struct drm_device *drm = data;
+ struct device_node *np = dev->of_node;
+- struct device_node *ddc_node;
++ struct dw_hdmi_audio_data audio;
++ struct dw_hdmi_cec_data cec;
+ struct imx_hdmi *hdmi;
+ struct resource *iores;
+ int ret, irq;
+@@ -1605,27 +1603,22 @@
+ if (!hdmi)
+ return -ENOMEM;
+
++ hdmi->ddc_conn = drm_ddc_connector_create(drm, np, hdmi);
++ if (IS_ERR(hdmi->ddc_conn))
++ return PTR_ERR(hdmi->ddc_conn);
++
++ hdmi->ddc_conn->detect = imx_hdmi_connector_detect;
++
+ hdmi->dev = dev;
+- hdmi->connector_status = connector_status_disconnected;
+ hdmi->sample_rate = 48000;
+ hdmi->ratio = 100;
++ hdmi->mc_clkdis = 0x7f;
+
+ if (of_id) {
+ const struct platform_device_id *device_id = of_id->data;
+ hdmi->dev_type = device_id->driver_data;
+ }
+
+- ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+- if (ddc_node) {
+- hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+- if (!hdmi->ddc)
+- dev_dbg(hdmi->dev, "failed to read ddc node\n");
+-
+- of_node_put(ddc_node);
+- } else {
+- dev_dbg(hdmi->dev, "no ddc property found\n");
+- }
+-
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -EINVAL;
+@@ -1711,6 +1704,35 @@
+ /* Unmute interrupts */
+ hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
++ memset(&pdevinfo, 0, sizeof(pdevinfo));
++ pdevinfo.parent = dev;
++ pdevinfo.id = PLATFORM_DEVID_AUTO;
++
++ audio.phys = iores->start;
++ audio.base = hdmi->regs;
++ audio.irq = irq;
++ audio.hdmi = hdmi;
++ audio.eld = hdmi->ddc_conn->connector.eld;
++ audio.set_sample_rate = imx_hdmi_set_sample_rate;
++
++ pdevinfo.name = "dw-hdmi-audio";
++ pdevinfo.data = &audio;
++ pdevinfo.size_data = sizeof(audio);
++ pdevinfo.dma_mask = DMA_BIT_MASK(32);
++ hdmi->audio = platform_device_register_full(&pdevinfo);
++
++ cec.base = hdmi->regs;
++ cec.irq = irq;
++ cec.ops = &imx_hdmi_cec_ops;
++ cec.ops_data = hdmi;
++
++ pdevinfo.name = "dw-hdmi-cec";
++ pdevinfo.data = &cec;
++ pdevinfo.size_data = sizeof(cec);
++ pdevinfo.dma_mask = 0;
++
++ hdmi->cec = platform_device_register_full(&pdevinfo);
++
+ dev_set_drvdata(dev, hdmi);
+
+ return 0;
+@@ -1728,15 +1750,19 @@
+ {
+ struct imx_hdmi *hdmi = dev_get_drvdata(dev);
+
++ if (!IS_ERR(hdmi->audio))
++ platform_device_unregister(hdmi->audio);
++ if (!IS_ERR(hdmi->cec))
++ platform_device_unregister(hdmi->cec);
++
+ /* Disable all interrupts */
+ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
+- hdmi->connector.funcs->destroy(&hdmi->connector);
++ hdmi->ddc_conn->connector.funcs->destroy(&hdmi->ddc_conn->connector);
+ hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+ clk_disable_unprepare(hdmi->iahb_clk);
+ clk_disable_unprepare(hdmi->isfr_clk);
+- i2c_put_adapter(hdmi->ddc);
+ }
+
+ static const struct component_ops hdmi_ops = {
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-ldb.c linux-3.15-rc6/drivers/staging/imx-drm/imx-ldb.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-ldb.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/imx-ldb.c 2014-05-23 11:26:48.316940071 +0200
+@@ -24,6 +24,7 @@
+ #include <drm/drmP.h>
+ #include <drm/drm_fb_helper.h>
+ #include <drm/drm_crtc_helper.h>
++#include <drm/drm_panel.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+ #include <linux/of_address.h>
+@@ -60,6 +61,7 @@
+ struct imx_ldb *ldb;
+ struct drm_connector connector;
+ struct drm_encoder encoder;
++ struct drm_panel *panel;
+ struct device_node *child;
+ int chno;
+ void *edid;
+@@ -96,6 +98,13 @@
+ struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
+ int num_modes = 0;
+
++ if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
++ imx_ldb_ch->panel->funcs->get_modes) {
++ num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
++ if (num_modes > 0)
++ return num_modes;
++ }
++
+ if (imx_ldb_ch->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ imx_ldb_ch->edid);
+@@ -243,6 +252,8 @@
+ }
+
+ regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
++
++ drm_panel_enable(imx_ldb_ch->panel);
+ }
+
+ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
+@@ -294,6 +305,8 @@
+ (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
+ return;
+
++ drm_panel_disable(imx_ldb_ch->panel);
++
+ if (imx_ldb_ch == &ldb->channel[0])
+ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+ else if (imx_ldb_ch == &ldb->channel[1])
+@@ -379,6 +392,9 @@
+ drm_connector_init(drm, &imx_ldb_ch->connector,
+ &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+
++ if (imx_ldb_ch->panel)
++ drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
++
+ drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
+ &imx_ldb_ch->encoder);
+
+@@ -493,6 +509,7 @@
+
+ for_each_child_of_node(np, child) {
+ struct imx_ldb_channel *channel;
++ struct device_node *panel_node;
+
+ ret = of_property_read_u32(child, "reg", &i);
+ if (ret || i < 0 || i > 1)
+@@ -556,6 +573,10 @@
+ return -EINVAL;
+ }
+
++ panel_node = of_parse_phandle(child, "fsl,panel", 0);
++ if (panel_node)
++ channel->panel = of_drm_find_panel(panel_node);
++
+ ret = imx_ldb_register(drm, channel);
+ if (ret)
+ return ret;
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-tve.c linux-3.15-rc6/drivers/staging/imx-drm/imx-tve.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/imx-tve.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/imx-tve.c 2014-05-23 11:28:50.689342803 +0200
+@@ -22,7 +22,6 @@
+ #include <linux/clk-provider.h>
+ #include <linux/component.h>
+ #include <linux/module.h>
+-#include <linux/i2c.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/spinlock.h>
+@@ -31,6 +30,7 @@
+ #include <drm/drm_fb_helper.h>
+ #include <drm/drm_crtc_helper.h>
+
++#include "drm-ddc-connector.h"
+ #include "ipu-v3/imx-ipu-v3.h"
+ #include "imx-drm.h"
+
+@@ -111,7 +111,7 @@
+ };
+
+ struct imx_tve {
+- struct drm_connector connector;
++ struct drm_ddc_connector *ddc_conn;
+ struct drm_encoder encoder;
+ struct device *dev;
+ spinlock_t lock; /* register lock */
+@@ -120,7 +120,6 @@
+
+ struct regmap *regmap;
+ struct regulator *dac_reg;
+- struct i2c_adapter *ddc;
+ struct clk *clk;
+ struct clk *di_sel_clk;
+ struct clk_hw clk_hw_di;
+@@ -219,35 +218,10 @@
+ return 0;
+ }
+
+-static enum drm_connector_status imx_tve_connector_detect(
+- struct drm_connector *connector, bool force)
+-{
+- return connector_status_connected;
+-}
+-
+-static int imx_tve_connector_get_modes(struct drm_connector *connector)
+-{
+- struct imx_tve *tve = con_to_tve(connector);
+- struct edid *edid;
+- int ret = 0;
+-
+- if (!tve->ddc)
+- return 0;
+-
+- edid = drm_get_edid(connector, tve->ddc);
+- if (edid) {
+- drm_mode_connector_update_edid_property(connector, edid);
+- ret = drm_add_edid_modes(connector, edid);
+- kfree(edid);
+- }
+-
+- return ret;
+-}
+-
+ static int imx_tve_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+ {
+- struct imx_tve *tve = con_to_tve(connector);
++ struct imx_tve *tve = to_ddc_conn(connector)->private;
+ unsigned long rate;
+ int ret;
+
+@@ -274,7 +248,7 @@
+ static struct drm_encoder *imx_tve_connector_best_encoder(
+ struct drm_connector *connector)
+ {
+- struct imx_tve *tve = con_to_tve(connector);
++ struct imx_tve *tve = drm_ddc_private(connector);
+
+ return &tve->encoder;
+ }
+@@ -362,15 +336,8 @@
+ tve_disable(tve);
+ }
+
+-static struct drm_connector_funcs imx_tve_connector_funcs = {
+- .dpms = drm_helper_connector_dpms,
+- .fill_modes = drm_helper_probe_single_connector_modes,
+- .detect = imx_tve_connector_detect,
+- .destroy = imx_drm_connector_destroy,
+-};
+-
+ static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = {
+- .get_modes = imx_tve_connector_get_modes,
++ .get_modes = drm_ddc_connector_get_modes,
+ .best_encoder = imx_tve_connector_best_encoder,
+ .mode_valid = imx_tve_connector_mode_valid,
+ };
+@@ -513,12 +480,11 @@
+ drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs,
+ encoder_type);
+
+- drm_connector_helper_add(&tve->connector,
++ drm_connector_helper_add(&tve->ddc_conn->connector,
+ &imx_tve_connector_helper_funcs);
+- drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs,
+- DRM_MODE_CONNECTOR_VGA);
++ drm_ddc_connector_add(drm, tve->ddc_conn, DRM_MODE_CONNECTOR_VGA);
+
+- drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder);
++ drm_mode_connector_attach_encoder(&tve->ddc_conn->connector, &tve->encoder);
+
+ return 0;
+ }
+@@ -567,7 +533,6 @@
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct device_node *np = dev->of_node;
+- struct device_node *ddc_node;
+ struct imx_tve *tve;
+ struct resource *res;
+ void __iomem *base;
+@@ -579,15 +544,13 @@
+ if (!tve)
+ return -ENOMEM;
+
++ tve->ddc_conn = drm_ddc_connector_create(drm, np, tve);
++ if (IS_ERR(tve->ddc_conn))
++ return PTR_ERR(tve->ddc_conn);
++
+ tve->dev = dev;
+ spin_lock_init(&tve->lock);
+
+- ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+- if (ddc_node) {
+- tve->ddc = of_find_i2c_adapter_by_node(ddc_node);
+- of_node_put(ddc_node);
+- }
+-
+ tve->mode = of_get_tve_mode(np);
+ if (tve->mode != TVE_MODE_VGA) {
+ dev_err(dev, "only VGA mode supported, currently\n");
+@@ -694,7 +657,7 @@
+ {
+ struct imx_tve *tve = dev_get_drvdata(dev);
+
+- tve->connector.funcs->destroy(&tve->connector);
++ tve->ddc_conn->connector.funcs->destroy(&tve->ddc_conn->connector);
+ tve->encoder.funcs->destroy(&tve->encoder);
+
+ if (!IS_ERR(tve->dac_reg))
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h 2014-05-23 11:26:48.316940071 +0200
+@@ -76,6 +76,7 @@
+ IPU_IRQ_EOS = 192,
+ };
+
++int ipu_map_irq(struct ipu_soc *ipu, int irq);
+ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq);
+
+@@ -114,8 +115,10 @@
+ void ipu_dc_put(struct ipu_dc *dc);
+ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width);
++void ipu_dc_enable(struct ipu_soc *ipu);
+ void ipu_dc_enable_channel(struct ipu_dc *dc);
+ void ipu_dc_disable_channel(struct ipu_dc *dc);
++void ipu_dc_disable(struct ipu_soc *ipu);
+
+ /*
+ * IPU Display Interface (di) functions
+@@ -152,8 +155,10 @@
+
+ struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
+ void ipu_dp_put(struct ipu_dp *);
++int ipu_dp_enable(struct ipu_soc *ipu);
+ int ipu_dp_enable_channel(struct ipu_dp *dp);
+ void ipu_dp_disable_channel(struct ipu_dp *dp);
++void ipu_dp_disable(struct ipu_soc *ipu);
+ int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in, enum ipu_color_space out);
+ int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-common.c linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-common.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-common.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-common.c 2014-05-23 11:26:48.352940189 +0200
+@@ -697,6 +697,12 @@
+ }
+ EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
+
++bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
++{
++ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
++}
++EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
++
+ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
+ {
+ struct ipu_soc *ipu = channel->ipu;
+@@ -714,6 +720,22 @@
+ }
+ EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
+
++int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
++{
++ unsigned long timeout;
++
++ timeout = jiffies + msecs_to_jiffies(ms);
++ ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
++ while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
++ if (time_after(jiffies, timeout))
++ return -ETIMEDOUT;
++ cpu_relax();
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
++
+ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
+ {
+ struct ipu_soc *ipu = channel->ipu;
+@@ -933,15 +955,22 @@
+ chained_irq_exit(chip, desc);
+ }
+
+-int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+- enum ipu_channel_irq irq_type)
++int ipu_map_irq(struct ipu_soc *ipu, int irq)
+ {
+- int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num);
++ int virq;
+
+- if (!irq)
+- irq = irq_create_mapping(ipu->domain, irq_type + channel->num);
++ virq = irq_linear_revmap(ipu->domain, irq);
++ if (!virq)
++ virq = irq_create_mapping(ipu->domain, irq);
+
+- return irq;
++ return virq;
++}
++EXPORT_SYMBOL_GPL(ipu_map_irq);
++
++int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
++ enum ipu_channel_irq irq_type)
++{
++ return ipu_map_irq(ipu, irq_type + channel->num);
+ }
+ EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
+
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dc.c linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dc.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dc.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dc.c 2014-05-23 11:26:48.360940215 +0200
+@@ -18,6 +18,7 @@
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/delay.h>
++#include <linux/interrupt.h>
+ #include <linux/io.h>
+
+ #include "../imx-drm.h"
+@@ -92,6 +93,7 @@
+ IPU_DC_MAP_GBR24, /* TVEv2 */
+ IPU_DC_MAP_BGR666,
+ IPU_DC_MAP_BGR24,
++ IPU_DC_MAP_RGB666,
+ };
+
+ struct ipu_dc {
+@@ -110,6 +112,9 @@
+ struct device *dev;
+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
+ struct mutex mutex;
++ struct completion comp;
++ int dc_irq;
++ int dp_irq;
+ };
+
+ static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
+@@ -155,6 +160,8 @@
+ return IPU_DC_MAP_BGR666;
+ case V4L2_PIX_FMT_BGR24:
+ return IPU_DC_MAP_BGR24;
++ case V4L2_PIX_FMT_RGB666:
++ return IPU_DC_MAP_RGB666;
+ default:
+ return -EINVAL;
+ }
+@@ -220,12 +227,16 @@
+ writel(0x0, dc->base + DC_WR_CH_ADDR);
+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
+
+- ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
+-
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
+
++void ipu_dc_enable(struct ipu_soc *ipu)
++{
++ ipu_module_enable(ipu, IPU_CONF_DC_EN);
++}
++EXPORT_SYMBOL_GPL(ipu_dc_enable);
++
+ void ipu_dc_enable_channel(struct ipu_dc *dc)
+ {
+ int di;
+@@ -239,41 +250,55 @@
+ }
+ EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
+
++static irqreturn_t dc_irq_handler(int irq, void *dev_id)
++{
++ struct ipu_dc *dc = dev_id;
++ u32 reg;
++
++ reg = readl(dc->base + DC_WR_CH_CONF);
++ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
++ writel(reg, dc->base + DC_WR_CH_CONF);
++
++ /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */
++
++ complete(&dc->priv->comp);
++ return IRQ_HANDLED;
++}
++
+ void ipu_dc_disable_channel(struct ipu_dc *dc)
+ {
+ struct ipu_dc_priv *priv = dc->priv;
++ int irq, ret;
+ u32 val;
+- int irq = 0, timeout = 50;
+
++ /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
+ if (dc->chno == 1)
+- irq = IPU_IRQ_DC_FC_1;
++ irq = priv->dc_irq;
+ else if (dc->chno == 5)
+- irq = IPU_IRQ_DP_SF_END;
++ irq = priv->dp_irq;
+ else
+ return;
+
+- /* should wait for the interrupt here */
+- mdelay(50);
+-
+- if (dc->di == 0)
+- val = 0x00000002;
+- else
+- val = 0x00000020;
+-
+- /* Wait for DC triple buffer to empty */
+- while ((readl(priv->dc_reg + DC_STAT) & val) != val) {
+- usleep_range(2000, 20000);
+- timeout -= 2;
+- if (timeout <= 0)
+- break;
++ init_completion(&priv->comp);
++ enable_irq(irq);
++ ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50));
++ disable_irq(irq);
++ if (ret <= 0) {
++ dev_warn(priv->dev, "DC stop timeout after 50 ms\n");
++
++ val = readl(dc->base + DC_WR_CH_CONF);
++ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
++ writel(val, dc->base + DC_WR_CH_CONF);
+ }
+-
+- val = readl(dc->base + DC_WR_CH_CONF);
+- val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+- writel(val, dc->base + DC_WR_CH_CONF);
+ }
+ EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
+
++void ipu_dc_disable(struct ipu_soc *ipu)
++{
++ ipu_module_disable(ipu, IPU_CONF_DC_EN);
++}
++EXPORT_SYMBOL_GPL(ipu_dc_disable);
++
+ static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
+ int byte_num, int offset, int mask)
+ {
+@@ -340,7 +365,7 @@
+ struct ipu_dc_priv *priv;
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
+ 0x78, 0, 0x94, 0xb4};
+- int i;
++ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -361,6 +386,23 @@
+ priv->channels[i].base = priv->dc_reg + channel_offsets[i];
+ }
+
++ priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1);
++ if (!priv->dc_irq)
++ return -EINVAL;
++ ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL,
++ &priv->channels[1]);
++ if (ret < 0)
++ return ret;
++ disable_irq(priv->dc_irq);
++ priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END);
++ if (!priv->dp_irq)
++ return -EINVAL;
++ ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL,
++ &priv->channels[5]);
++ if (ret < 0)
++ return ret;
++ disable_irq(priv->dp_irq);
++
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
+ DC_WR_CH_CONF_PROG_DI_ID,
+ priv->channels[1].base + DC_WR_CH_CONF);
+@@ -404,6 +446,12 @@
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
+
++ /* rgb666 */
++ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB666);
++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 0, 5, 0xfc); /* blue */
++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 1, 11, 0xfc); /* green */
++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 2, 17, 0xfc); /* red */
++
+ return 0;
+ }
+
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-di.c linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-di.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-di.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-di.c 2014-05-23 11:26:48.372940255 +0200
+@@ -595,7 +595,7 @@
+ }
+ }
+
+- if (!sig->clk_pol)
++ if (sig->clk_pol)
+ di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c 2014-05-23 11:26:48.380940281 +0200
+@@ -28,7 +28,12 @@
+ #define DMFC_GENERAL1 0x0014
+ #define DMFC_GENERAL2 0x0018
+ #define DMFC_IC_CTRL 0x001c
+-#define DMFC_STAT 0x0020
++#define DMFC_WR_CHAN_ALT 0x0020
++#define DMFC_WR_CHAN_DEF_ALT 0x0024
++#define DMFC_DP_CHAN_ALT 0x0028
++#define DMFC_DP_CHAN_DEF_ALT 0x002c
++#define DMFC_GENERAL1_ALT 0x0030
++#define DMFC_STAT 0x0034
+
+ #define DMFC_WR_CHAN_1_28 0
+ #define DMFC_WR_CHAN_2_41 8
+@@ -133,6 +138,20 @@
+ }
+ EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
+
++static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
++{
++ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
++
++ while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
++ if (time_after(jiffies, timeout)) {
++ dev_warn(priv->dev,
++ "Timeout waiting for DMFC FIFOs to clear\n");
++ break;
++ }
++ cpu_relax();
++ }
++}
++
+ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+ {
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+@@ -141,8 +160,10 @@
+
+ priv->use_count--;
+
+- if (!priv->use_count)
++ if (!priv->use_count) {
++ ipu_dmfc_wait_fifos(priv);
+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
++ }
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dp.c linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dp.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-dp.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-dp.c 2014-05-23 11:26:48.388940307 +0200
+@@ -215,10 +215,9 @@
+ }
+ EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
+
+-int ipu_dp_enable_channel(struct ipu_dp *dp)
++int ipu_dp_enable(struct ipu_soc *ipu)
+ {
+- struct ipu_flow *flow = to_flow(dp);
+- struct ipu_dp_priv *priv = flow->priv;
++ struct ipu_dp_priv *priv = ipu->dp_priv;
+
+ mutex_lock(&priv->mutex);
+
+@@ -227,15 +226,28 @@
+
+ priv->use_count++;
+
+- if (dp->foreground) {
+- u32 reg;
++ mutex_unlock(&priv->mutex);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(ipu_dp_enable);
++
++int ipu_dp_enable_channel(struct ipu_dp *dp)
++{
++ struct ipu_flow *flow = to_flow(dp);
++ struct ipu_dp_priv *priv = flow->priv;
++ u32 reg;
++
++ if (!dp->foreground)
++ return 0;
++
++ mutex_lock(&priv->mutex);
+
+- reg = readl(flow->base + DP_COM_CONF);
+- reg |= DP_COM_CONF_FG_EN;
+- writel(reg, flow->base + DP_COM_CONF);
++ reg = readl(flow->base + DP_COM_CONF);
++ reg |= DP_COM_CONF_FG_EN;
++ writel(reg, flow->base + DP_COM_CONF);
+
+- ipu_srm_dp_sync_update(priv->ipu);
+- }
++ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+@@ -247,25 +259,38 @@
+ {
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
++ u32 reg, csc;
++
++ if (!dp->foreground)
++ return;
+
+ mutex_lock(&priv->mutex);
+
+- priv->use_count--;
++ reg = readl(flow->base + DP_COM_CONF);
++ csc = reg & DP_COM_CONF_CSC_DEF_MASK;
++ if (csc == DP_COM_CONF_CSC_DEF_FG)
++ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
++
++ reg &= ~DP_COM_CONF_FG_EN;
++ writel(reg, flow->base + DP_COM_CONF);
++
++ writel(0, flow->base + DP_FG_POS);
++ ipu_srm_dp_sync_update(priv->ipu);
++
++ if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
++ ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);
++
++ mutex_unlock(&priv->mutex);
++}
++EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
+
+- if (dp->foreground) {
+- u32 reg, csc;
++void ipu_dp_disable(struct ipu_soc *ipu)
++{
++ struct ipu_dp_priv *priv = ipu->dp_priv;
+
+- reg = readl(flow->base + DP_COM_CONF);
+- csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+- if (csc == DP_COM_CONF_CSC_DEF_FG)
+- reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+-
+- reg &= ~DP_COM_CONF_FG_EN;
+- writel(reg, flow->base + DP_COM_CONF);
+-
+- writel(0, flow->base + DP_FG_POS);
+- ipu_srm_dp_sync_update(priv->ipu);
+- }
++ mutex_lock(&priv->mutex);
++
++ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
+@@ -275,7 +300,7 @@
+
+ mutex_unlock(&priv->mutex);
+ }
+-EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
++EXPORT_SYMBOL_GPL(ipu_dp_disable);
+
+ struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
+ {
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-prv.h linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-prv.h
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipu-v3/ipu-prv.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipu-v3/ipu-prv.h 2014-05-23 11:26:48.396940333 +0200
+@@ -185,6 +185,9 @@
+ int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
+ int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
+
++bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno);
++int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms);
++
+ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base, u32 module, struct clk *ipu_clk);
+ void ipu_di_exit(struct ipu_soc *ipu, int id);
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipuv3-crtc.c linux-3.15-rc6/drivers/staging/imx-drm/ipuv3-crtc.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipuv3-crtc.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipuv3-crtc.c 2014-05-23 11:26:48.396940333 +0200
+@@ -60,24 +60,32 @@
+
+ static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
+ {
++ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
++
+ if (ipu_crtc->enabled)
+ return;
+
+- ipu_di_enable(ipu_crtc->di);
+- ipu_dc_enable_channel(ipu_crtc->dc);
++ ipu_dc_enable(ipu);
+ ipu_plane_enable(ipu_crtc->plane[0]);
++ /* Start DC channel and DI after IDMAC */
++ ipu_dc_enable_channel(ipu_crtc->dc);
++ ipu_di_enable(ipu_crtc->di);
+
+ ipu_crtc->enabled = 1;
+ }
+
+ static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
+ {
++ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
++
+ if (!ipu_crtc->enabled)
+ return;
+
+- ipu_plane_disable(ipu_crtc->plane[0]);
++ /* Stop DC channel and DI before IDMAC */
+ ipu_dc_disable_channel(ipu_crtc->dc);
+ ipu_di_disable(ipu_crtc->di);
++ ipu_plane_disable(ipu_crtc->plane[0]);
++ ipu_dc_disable(ipu);
+
+ ipu_crtc->enabled = 0;
+ }
+@@ -158,7 +166,7 @@
+ sig_cfg.Vsync_pol = 1;
+
+ sig_cfg.enable_pol = 1;
+- sig_cfg.clk_pol = 1;
++ sig_cfg.clk_pol = 0;
+ sig_cfg.width = mode->hdisplay;
+ sig_cfg.height = mode->vdisplay;
+ sig_cfg.pixel_fmt = out_pixel_fmt;
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/ipuv3-plane.c linux-3.15-rc6/drivers/staging/imx-drm/ipuv3-plane.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/ipuv3-plane.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/ipuv3-plane.c 2014-05-23 11:26:48.396940333 +0200
+@@ -239,6 +239,8 @@
+
+ void ipu_plane_enable(struct ipu_plane *ipu_plane)
+ {
++ if (ipu_plane->dp)
++ ipu_dp_enable(ipu_plane->ipu);
+ ipu_dmfc_enable_channel(ipu_plane->dmfc);
+ ipu_idmac_enable_channel(ipu_plane->ipu_ch);
+ if (ipu_plane->dp)
+@@ -257,6 +259,8 @@
+ ipu_dp_disable_channel(ipu_plane->dp);
+ ipu_idmac_disable_channel(ipu_plane->ipu_ch);
+ ipu_dmfc_disable_channel(ipu_plane->dmfc);
++ if (ipu_plane->dp)
++ ipu_dp_disable(ipu_plane->ipu);
+ }
+
+ static void ipu_plane_dpms(struct ipu_plane *ipu_plane, int mode)
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/Kconfig linux-3.15-rc6/drivers/staging/imx-drm/Kconfig
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/Kconfig 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/Kconfig 2014-05-23 11:26:48.396940333 +0200
+@@ -35,6 +35,7 @@
+ config DRM_IMX_LDB
+ tristate "Support for LVDS displays"
+ depends on DRM_IMX && MFD_SYSCON
++ select DRM_PANEL
+ help
+ Choose this to enable the internal LVDS Display Bridge (LDB)
+ found on i.MX53 and i.MX6 processors.
+@@ -60,3 +61,20 @@
+ depends on DRM_IMX
+ help
+ Choose this if you want to use HDMI on i.MX6.
++
++config DRM_DW_HDMI_AUDIO
++ tristate "Synopsis Designware Audio interface"
++ depends on DRM_IMX_HDMI != n
++ help
++ Support the Audio interface which is part of the Synopsis
++ Designware HDMI block. This is used in conjunction with
++ the i.MX HDMI driver.
++
++config DRM_DW_HDMI_CEC
++ tristate "Synopsis Designware CEC interface"
++ depends on DRM_IMX_HDMI != n
++ select HDMI_CEC_CORE
++ help
++ Support the CEC interface which is part of the Synposis
++ Designware HDMI block. This is used in conjunction with
++ the i.MX HDMI driver.
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/Makefile linux-3.15-rc6/drivers/staging/imx-drm/Makefile
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/Makefile 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/Makefile 2014-05-23 11:26:48.396940333 +0200
+@@ -3,6 +3,7 @@
+
+ obj-$(CONFIG_DRM_IMX) += imxdrm.o
+
++obj-$(CONFIG_DRM_IMX) += drm-ddc-connector.o
+ obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
+ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
+ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
+@@ -11,3 +12,5 @@
+ imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o
+ obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o
+ obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
++obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o
++obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
+diff -Nur linux-3.15-rc6.orig/drivers/staging/imx-drm/parallel-display.c linux-3.15-rc6/drivers/staging/imx-drm/parallel-display.c
+--- linux-3.15-rc6.orig/drivers/staging/imx-drm/parallel-display.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/drivers/staging/imx-drm/parallel-display.c 2014-05-23 11:26:48.396940333 +0200
+@@ -219,6 +219,8 @@
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565;
+ else if (!strcmp(fmt, "bgr666"))
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666;
++ else if (!strcmp(fmt, "rgb666"))
++ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB666;
+ }
+
+ panel_node = of_parse_phandle(np, "fsl,panel", 0);
+diff -Nur linux-3.15-rc6.orig/include/linux/cec-dev.h linux-3.15-rc6/include/linux/cec-dev.h
+--- linux-3.15-rc6.orig/include/linux/cec-dev.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/include/linux/cec-dev.h 2014-05-23 11:26:48.396940333 +0200
+@@ -0,0 +1,69 @@
++#ifndef _LINUX_CEC_DEV_H
++#define _LINUX_CEC_DEV_H
++
++#include <linux/cdev.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/spinlock.h>
++#include <linux/wait.h>
++
++#include <uapi/linux/cec-dev.h>
++
++struct device;
++
++struct cec_dev {
++ struct cdev cdev;
++ dev_t devn;
++
++ struct mutex mutex;
++ unsigned users;
++
++ spinlock_t lock;
++ wait_queue_head_t waitq;
++ struct list_head events;
++ u8 write_busy;
++
++ u8 retries;
++ u16 addresses;
++ u16 physical;
++
++ int (*open)(struct cec_dev *);
++ void (*release)(struct cec_dev *);
++ void (*send_message)(struct cec_dev *, u8 *, size_t);
++ void (*set_address)(struct cec_dev *, unsigned);
++};
++
++void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len);
++
++static inline void cec_dev_receive(struct cec_dev *cec_dev, u8 *msg,
++ unsigned len)
++{
++ cec_dev_event(cec_dev, MESSAGE_TYPE_RECEIVE_SUCCESS, msg, len);
++}
++
++static inline void cec_dev_send_complete(struct cec_dev *cec_dev, int ack)
++{
++ cec_dev->retries = 0;
++ cec_dev->write_busy = 0;
++
++ cec_dev_event(cec_dev, ack ? MESSAGE_TYPE_SEND_SUCCESS :
++ MESSAGE_TYPE_NOACK, NULL, 0);
++}
++
++static inline void cec_dev_disconnect(struct cec_dev *cec_dev)
++{
++ cec_dev->physical = 0;
++ cec_dev_event(cec_dev, MESSAGE_TYPE_DISCONNECTED, NULL, 0);
++}
++
++static inline void cec_dev_connect(struct cec_dev *cec_dev, u32 phys)
++{
++ cec_dev->physical = phys;
++ cec_dev_event(cec_dev, MESSAGE_TYPE_CONNECTED, NULL, 0);
++}
++
++void cec_dev_init(struct cec_dev *cec_dev, struct module *);
++int cec_dev_add(struct cec_dev *cec_dev, struct device *, const char *name);
++void cec_dev_remove(struct cec_dev *cec_dev);
++
++#endif
+diff -Nur linux-3.15-rc6.orig/include/linux/mmc/host.h linux-3.15-rc6/include/linux/mmc/host.h
+--- linux-3.15-rc6.orig/include/linux/mmc/host.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/include/linux/mmc/host.h 2014-05-23 11:26:48.400940347 +0200
+@@ -278,6 +278,7 @@
+ #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
+ MMC_CAP2_PACKED_WR)
+ #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
++#define MMC_CAP2_SDIO_NOTHREAD (1 << 15)
+
+ mmc_pm_flag_t pm_caps; /* supported pm features */
+
+@@ -293,6 +294,11 @@
+ unsigned long clkgate_delay;
+ #endif
+
++ /* card specific properties to deal with power and reset */
++ struct regulator *card_regulator; /* External VCC needed by the card */
++ struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */
++ struct clk *card_clk; /* External clock needed by the card */
++
+ /* host specific block data */
+ unsigned int max_seg_size; /* see blk_queue_max_segment_size */
+ unsigned short max_segs; /* see blk_queue_max_segments */
+@@ -391,6 +397,8 @@
+ wake_up_process(host->sdio_irq_thread);
+ }
+
++void sdio_run_irqs(struct mmc_host *host);
++
+ #ifdef CONFIG_REGULATOR
+ int mmc_regulator_get_ocrmask(struct regulator *supply);
+ int mmc_regulator_set_ocr(struct mmc_host *mmc,
+diff -Nur linux-3.15-rc6.orig/include/linux/mmc/sdhci.h linux-3.15-rc6/include/linux/mmc/sdhci.h
+--- linux-3.15-rc6.orig/include/linux/mmc/sdhci.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/include/linux/mmc/sdhci.h 2014-05-23 11:26:48.400940347 +0200
+@@ -57,12 +57,8 @@
+ #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
+ /* Controller reports inverted write-protect state */
+ #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
+-/* Controller has nonstandard clock management */
+-#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
+ /* Controller does not like fast PIO transfers */
+ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
+-/* Controller losing signal/interrupt enable states after reset */
+-#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
+ /* Controller has to be forced to use block size of 2048 bytes */
+ #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
+ /* Controller cannot do multi-block transfers */
+@@ -147,6 +143,7 @@
+
+ bool runtime_suspended; /* Host is runtime suspended */
+ bool bus_on; /* Bus power prevents runtime suspend */
++ bool preset_enabled; /* Preset is enabled */
+
+ struct mmc_request *mrq; /* Current request */
+ struct mmc_command *cmd; /* Current command */
+@@ -164,8 +161,7 @@
+ dma_addr_t adma_addr; /* Mapped ADMA descr. table */
+ dma_addr_t align_addr; /* Mapped bounce buffer */
+
+- struct tasklet_struct card_tasklet; /* Tasklet structures */
+- struct tasklet_struct finish_tasklet;
++ struct tasklet_struct finish_tasklet; /* Tasklet structures */
+
+ struct timer_list timer; /* Timer for timeouts */
+
+@@ -177,6 +173,13 @@
+ unsigned int ocr_avail_mmc;
+ u32 ocr_mask; /* available voltages */
+
++ unsigned timing; /* Current timing */
++
++ u32 thread_isr;
++
++ /* cached registers */
++ u32 ier;
++
+ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
+ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
+
+diff -Nur linux-3.15-rc6.orig/include/uapi/linux/cec-dev.h linux-3.15-rc6/include/uapi/linux/cec-dev.h
+--- linux-3.15-rc6.orig/include/uapi/linux/cec-dev.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-3.15-rc6/include/uapi/linux/cec-dev.h 2014-05-23 11:26:48.400940347 +0200
+@@ -0,0 +1,34 @@
++#ifndef _UAPI_LINUX_CEC_DEV_H
++#define _UAPI_LINUX_CEC_DEV_H
++
++#include <linux/ioctl.h>
++#include <linux/types.h>
++
++#define MAX_MESSAGE_LEN 16
++
++enum {
++ HDMICEC_IOC_MAGIC = 'H',
++ /* This is wrong: we pass the argument as a number, not a pointer */
++ HDMICEC_IOC_O_SETLOGICALADDRESS = _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char),
++ HDMICEC_IOC_SETLOGICALADDRESS = _IO(HDMICEC_IOC_MAGIC, 1),
++ HDMICEC_IOC_STARTDEVICE = _IO(HDMICEC_IOC_MAGIC, 2),
++ HDMICEC_IOC_STOPDEVICE = _IO(HDMICEC_IOC_MAGIC, 3),
++ HDMICEC_IOC_GETPHYADDRESS = _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]),
++};
++
++enum {
++ MESSAGE_TYPE_RECEIVE_SUCCESS = 1,
++ MESSAGE_TYPE_NOACK,
++ MESSAGE_TYPE_DISCONNECTED,
++ MESSAGE_TYPE_CONNECTED,
++ MESSAGE_TYPE_SEND_SUCCESS,
++ MESSAGE_TYPE_SEND_ERROR,
++};
++
++struct cec_user_event {
++ __u32 event_type;
++ __u32 msg_len;
++ __u8 msg[MAX_MESSAGE_LEN];
++};
++
++#endif
+diff -Nur linux-3.15-rc6.orig/include/uapi/linux/videodev2.h linux-3.15-rc6/include/uapi/linux/videodev2.h
+--- linux-3.15-rc6.orig/include/uapi/linux/videodev2.h 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/include/uapi/linux/videodev2.h 2014-05-23 11:26:48.400940347 +0200
+@@ -299,6 +299,7 @@
+ #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */
+ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */
+ #define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */
++#define V4L2_PIX_FMT_RGB666 v4l2_fourcc('R', 'G', 'B', 'H') /* 18 RGB-6-6-6 */
+ #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */
+ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */
+ #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */
+diff -Nur linux-3.15-rc6.orig/sound/soc/fsl/imx-pcm-dma.c linux-3.15-rc6/sound/soc/fsl/imx-pcm-dma.c
+--- linux-3.15-rc6.orig/sound/soc/fsl/imx-pcm-dma.c 2014-05-21 23:42:02.000000000 +0200
++++ linux-3.15-rc6/sound/soc/fsl/imx-pcm-dma.c 2014-05-23 11:26:48.400940347 +0200
+@@ -44,7 +44,7 @@
+ .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 65535, /* Limited by SDMA engine */
+- .periods_min = 2,
++ .periods_min = 4,
+ .periods_max = 255,
+ .fifo_size = 0,
+ };