Merge tag 'v4.4.121' into toradex_vf_4.4-next Colibri-VF_LXDE-Image_2.8b2.97-20180331
authorMax Krummenacher <max.krummenacher@toradex.com>
Tue, 13 Mar 2018 10:32:58 +0000 (11:32 +0100)
committerMax Krummenacher <max.krummenacher@toradex.com>
Tue, 13 Mar 2018 10:32:58 +0000 (11:32 +0100)
This is the 4.4.121 stable release

132 files changed:
Documentation/ABI/testing/sysfs-bus-iio-vf610
Documentation/devicetree/bindings/arm/freescale/fsl,vf610-ddrmc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/fsl,dcu.txt
Documentation/devicetree/bindings/display/fsl,tcon.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/logic,lt161010-2nhc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/iio/dac/vf610-dac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/fusion_F0710A.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/Makefile
arch/arm/boot/dts/vf-colibri-aster.dtsi [new file with mode: 0644]
arch/arm/boot/dts/vf-colibri-dual-eth.dtsi [new file with mode: 0644]
arch/arm/boot/dts/vf-colibri-eval-v3.dtsi
arch/arm/boot/dts/vf-colibri.dtsi
arch/arm/boot/dts/vf500-colibri-aster.dts [new file with mode: 0644]
arch/arm/boot/dts/vf500-colibri-dual-eth.dts [new file with mode: 0644]
arch/arm/boot/dts/vf500-colibri.dtsi
arch/arm/boot/dts/vf500.dtsi
arch/arm/boot/dts/vf610-colibri-aster.dts [new file with mode: 0644]
arch/arm/boot/dts/vf610-colibri-dual-eth.dts [new file with mode: 0644]
arch/arm/boot/dts/vf610-colibri.dtsi
arch/arm/boot/dts/vf610-twr.dts
arch/arm/boot/dts/vf610.dtsi
arch/arm/boot/dts/vfxxx.dtsi
arch/arm/configs/colibri_vf_defconfig [new file with mode: 0644]
arch/arm/kernel/irq.c
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/common.h
arch/arm/mach-imx/mach-vf610.c
arch/arm/mach-imx/pm-vf610.c [new file with mode: 0644]
arch/arm/mach-imx/suspend-vf610.S [new file with mode: 0644]
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/vf610_sema4.c [new file with mode: 0644]
drivers/clk/imx/clk-gate2.c
drivers/clk/imx/clk-vf610.c
drivers/clk/imx/clk.h
drivers/clocksource/vf_pit_timer.c
drivers/dma/fsl-edma.c
drivers/gpio/gpio-vf610.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_modeset_lock.c
drivers/gpu/drm/fsl-dcu/Makefile
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
drivers/gpu/drm/fsl-dcu/fsl_tcon.c [new file with mode: 0644]
drivers/gpu/drm/fsl-dcu/fsl_tcon.h [new file with mode: 0644]
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/sti/sti_drv.c
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/i2c/busses/i2c-imx.c
drivers/iio/dac/Kconfig
drivers/iio/dac/Makefile
drivers/iio/dac/vf610_dac.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/colibri-vf50-ts.c
drivers/input/touchscreen/fusion_F0710A.c [new file with mode: 0644]
drivers/input/touchscreen/fusion_F0710A.h [new file with mode: 0644]
drivers/irqchip/Makefile
drivers/irqchip/irq-vf610-gpc.c [new file with mode: 0644]
drivers/irqchip/irq-vf610-mscm-ir.c
drivers/mfd/syscon.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-pltfm.c
drivers/mmc/host/sdhci-pltfm.h
drivers/net/ethernet/freescale/Kconfig
drivers/net/ethernet/freescale/Makefile
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fsl_l2_switch.c [new file with mode: 0644]
drivers/net/ethernet/freescale/fsl_l2_switch.h [new file with mode: 0644]
drivers/net/phy/micrel.c
drivers/pinctrl/freescale/pinctrl-imx.c
drivers/pinctrl/freescale/pinctrl-imx.h
drivers/pinctrl/freescale/pinctrl-vf610.c
drivers/remoteproc/Kconfig
drivers/remoteproc/Makefile
drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/vf610_cm4_rproc.c [new file with mode: 0644]
drivers/rpmsg/Kconfig
drivers/rpmsg/Makefile
drivers/rpmsg/imx_rpmsg_pingpong.c [new file with mode: 0644]
drivers/rpmsg/imx_rpmsg_tty.c [new file with mode: 0644]
drivers/rpmsg/vf610_rpmsg.c [new file with mode: 0644]
drivers/rtc/rtc-ds1307.c
drivers/soc/Kconfig
drivers/soc/Makefile
drivers/soc/fsl/Kconfig [new file with mode: 0644]
drivers/soc/fsl/Makefile [new file with mode: 0644]
drivers/soc/fsl/soc-vf610.c [new file with mode: 0644]
drivers/spi/spi-fsl-dspi.c
drivers/spi/spidev.c
drivers/tty/serial/fsl_lpuart.c
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/otg.c
drivers/usb/gadget/configfs.c
drivers/video/logo/Kconfig
drivers/video/logo/Makefile
drivers/video/logo/logo.c
include/drm/drm_atomic_helper.h
include/drm/drm_crtc.h
include/drm/drm_fb_cma_helper.h
include/drm/drm_modeset_lock.h
include/dt-bindings/clock/vf610-clock.h
include/linux/input/fusion_F0710A.h [new file with mode: 0644]
include/linux/linux_logo.h
include/linux/mfd/syscon.h
include/linux/usb/chipidea.h
include/linux/vf610_mscm.h [new file with mode: 0644]
include/linux/vf610_sema4.h [new file with mode: 0644]
sound/soc/codecs/wm9712.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl_sai.h
sound/soc/fsl/fsl_sai_ac97.c [new file with mode: 0644]
sound/soc/fsl/fsl_sai_clk.c [new file with mode: 0644]
sound/soc/fsl/fsl_sai_wm9712.c [new file with mode: 0644]

index ecbc1f4af921c3daba3b9ba7618b42ad03eb8356..308a6756d3bf3fab4812b2edb735b5978ccc4e5a 100644 (file)
@@ -5,3 +5,12 @@ Description:
                Specifies the hardware conversion mode used. The three
                available modes are "normal", "high-speed" and "low-power",
                where the last is the default mode.
+
+
+What:          /sys/bus/iio/devices/iio:deviceX/out_conversion_mode
+KernelVersion: 4.6
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Specifies the hardware conversion mode used within DAC.
+               The two available modes are "high-power" and "low-power",
+               where "low-power" mode is the default mode.
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-ddrmc.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-ddrmc.txt
new file mode 100644 (file)
index 0000000..56a71d6
--- /dev/null
@@ -0,0 +1,23 @@
+Freescale Vybrid LPDDR2/DDR3 SDRAM Memory Controller
+
+The memory controller supports high performance applications for 16-bit or
+8-bit DDR2, or LPDDR SDRAM memories.
+
+Required properties:
+- compatible:  "fsl,vf610-ddrmc"
+- reg:         the register range of the DDRMC registers
+- clocks:      DDRMC main clock to clock memory and access registers.
+- clock-names: Must contain "ddrc", matching entry in the clocks property.
+- fsl,has-cke-reset-pulls:
+               States whether pull-down/up are populated on DDR CKE/RESET
+               signals to allow using DDR self-refresh modes (see Vybrid
+               Hardware Development Guide for details).
+
+Example:
+       ddrmc: ddrmc@400ae000 {
+               compatible = "fsl,vf610-ddrmc";
+               reg = <0x400ae000 0x1000>;
+               clocks = <&clks VF610_CLK_DDRMC>;
+               clock-names = "ddrc";
+               fsl,has-cke-reset-pulls;
+       }
index ebf1be9ae3936e2bd05437398ed816d00dd6f343..62c167e3da21037173d5dc4d1e7710c3e852a10c 100644 (file)
@@ -11,6 +11,11 @@ Required properties:
 - big-endian           Boolean property, LS1021A DCU registers are big-endian.
 - fsl,panel:           The phandle to panel node.
 
+Optional properties:
+- fsl,tcon:            The phandle to the timing controller node.
+- clocks:              Second handle for pixel clock.
+- clock-names:         Second name "pix" for pixel clock.
+
 Examples:
 dcu: dcu@2ce0000 {
        compatible = "fsl,ls1021a-dcu";
@@ -19,4 +24,5 @@ dcu: dcu@2ce0000 {
        clock-names = "dcu";
        big-endian;
        fsl,panel = <&panel>;
+       fsl,tcon = <&tcon>;
 };
diff --git a/Documentation/devicetree/bindings/display/fsl,tcon.txt b/Documentation/devicetree/bindings/display/fsl,tcon.txt
new file mode 100644 (file)
index 0000000..2e1015e
--- /dev/null
@@ -0,0 +1,18 @@
+Device Tree bindings for Freescale TCON Driver
+
+Required properties:
+- compatible:          Should be one of
+       * "fsl,vf610-tcon".
+
+- reg:                 Address and length of the register set for dcu.
+- clocks:              From common clock binding: handle to tcon ipg clock.
+- clock-names:         From common clock binding: Shall be "ipg".
+
+Examples:
+timing-controller@4003d000 {
+       compatible = "fsl,vf610-tcon";
+       reg = <0x4003d000 0x1000>;
+       clocks = <&clks VF610_CLK_TCON0>;
+       clock-names = "ipg";
+       status = "okay";
+};
diff --git a/Documentation/devicetree/bindings/display/panel/logic,lt161010-2nhc.txt b/Documentation/devicetree/bindings/display/panel/logic,lt161010-2nhc.txt
new file mode 100644 (file)
index 0000000..09fa2aa
--- /dev/null
@@ -0,0 +1,7 @@
+Logic Technologies LT161010-2NHC TFT 7" WVGA 800x480 panel.
+
+Required properties:
+- compatible: should be "logic,lt161010-2nhc"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt b/Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt
new file mode 100644 (file)
index 0000000..a2613b9
--- /dev/null
@@ -0,0 +1,8 @@
+TPK U.S.A. LLC Fusion 7" integrated projected capacitive touch display with,
+800 x 480 (WVGA) LCD panel.
+
+Required properties:
+- compatible: should be "tpk,f07a-0102"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt b/Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt
new file mode 100644 (file)
index 0000000..b9d0511
--- /dev/null
@@ -0,0 +1,8 @@
+TPK U.S.A. LLC Fusion 10.1" integrated projected capacitive touch display with,
+1024 x 600 (WSVGA) LCD panel.
+
+Required properties:
+- compatible: should be "tpk,f10a-0102"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
index c50cf13c852e518cff4664370eb1a72947e9cca7..aee63eb6a3b666623c59121368b79665a05bbe6a 100644 (file)
@@ -92,6 +92,7 @@ sgx,vz89x             SGX Sensortech VZ89X Sensors
 sii,s35390a            2-wire CMOS real-time clock
 skyworks,sky81452      Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
 st-micro,24c256                i2c serial eeprom  (24cxx)
+st,m41t0               Serial real-time clock (RTC)
 stm,m41t00             Serial Access TIMEKEEPER
 stm,m41t62             Serial real-time clock (RTC) with alarm
 stm,m41t80             M41T80 - SERIAL ACCESS RTC WITH ALARMS
diff --git a/Documentation/devicetree/bindings/iio/dac/vf610-dac.txt b/Documentation/devicetree/bindings/iio/dac/vf610-dac.txt
new file mode 100644 (file)
index 0000000..20c6c7a
--- /dev/null
@@ -0,0 +1,20 @@
+Freescale vf610 Digital to Analog Converter bindings
+
+The devicetree bindings are for the new DAC driver written for
+vf610 SoCs from Freescale.
+
+Required properties:
+- compatible: Should contain "fsl,vf610-dac"
+- reg: Offset and length of the register set for the device
+- interrupts: Should contain the interrupt for the device
+- clocks: The clock is needed by the DAC controller
+- clock-names: Must contain "dac" matching entry in the clocks property.
+
+Example:
+dac0: dac@400cc000 {
+       compatible = "fsl,vf610-dac";
+       reg = <0x400cc000 0x1000>;
+       interrupts = <55 IRQ_TYPE_LEVEL_HIGH>;
+       clock-names = "dac";
+       clocks = <&clks VF610_CLK_DAC0>;
+};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/fusion_F0710A.txt b/Documentation/devicetree/bindings/input/touchscreen/fusion_F0710A.txt
new file mode 100644 (file)
index 0000000..6fe730d
--- /dev/null
@@ -0,0 +1,23 @@
+* TouchRevolution Fusion 7" and 10" multi-touch controller
+
+Required properties:
+- compatible: must be "touchrevolution,fusion-f0710a"
+- reg: I2C address of the chip
+- gpios: The interrupt and reset GPIO's.(see GPIO binding[1] for more details)
+
+[1]: Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+       &i2c1 {
+               status = "okay";
+
+               pcap@10 {
+                       compatible = "touchrevolution,fusion-f0710a";
+                       reg = <0x10>;
+                       gpios = <&gpio6 10 0
+                                &gpio6  9 0
+                               >;
+               };
+
+               /* ... */
+       };
index ab65bbecb159279675cf5bb63e64f1feabf13f44..06deecd09eb8a1e540e963cac7197a9b90cb150a 100644 (file)
@@ -3681,7 +3681,7 @@ F:        include/drm/exynos*
 F:     include/uapi/drm/exynos*
 
 DRM DRIVERS FOR FREESCALE DCU
-M:     Jianwei Wang <jianwei.wang.chn@gmail.com>
+M:     Stefan Agner <stefan@agner.ch>
 M:     Alison Wang <alison.wang@freescale.com>
 L:     dri-devel@lists.freedesktop.org
 S:     Supported
index 30bbc3746130a56e54fa665a763894fe4ec02e6a..a5738197983acb746d1189beec553daa3f6850d5 100644 (file)
@@ -357,7 +357,11 @@ dtb-$(CONFIG_SOC_LS1021A) += \
 dtb-$(CONFIG_SOC_VF610) += \
        vf500-colibri-eval-v3.dtb \
        vf610-colibri-eval-v3.dtb \
+       vf500-colibri-aster.dtb \
+       vf610-colibri-aster.dtb \
        vf610m4-colibri.dtb \
+       vf500-colibri-dual-eth.dtb \
+       vf610-colibri-dual-eth.dtb \
        vf610-cosmic.dtb \
        vf610-twr.dtb
 dtb-$(CONFIG_ARCH_MXS) += \
diff --git a/arch/arm/boot/dts/vf-colibri-aster.dtsi b/arch/arm/boot/dts/vf-colibri-aster.dtsi
new file mode 100644 (file)
index 0000000..7710d56
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2017 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <dt-bindings/input/input.h>
+
+/ {
+       chosen {
+               stdout-path = "serial0:115200n8";
+       };
+
+       aliases {
+               ethernet0 = &fec1;
+               ethernet1 = &fec0;
+       };
+
+       panel: panel {
+               compatible = "edt,et057090dhu";
+               backlight = <&bl>;
+               power-supply =  <&reg_3v3>;
+       };
+
+       extcon_usbc_det: usbc_det {
+               compatible = "linux,extcon-usb-gpio";
+               debounce = <25>;
+               id-gpio = <&gpio3 6 GPIO_ACTIVE_HIGH>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_usbc_det>;
+       };
+
+       reg_3v3: regulator-3v3 {
+               compatible = "regulator-fixed";
+               regulator-name = "3.3V";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               regulator-always-on;
+       };
+
+       reg_5v0: regulator-5v0 {
+               compatible = "regulator-fixed";
+               regulator-name = "5V";
+               regulator-min-microvolt = <5000000>;
+               regulator-max-microvolt = <5000000>;
+               regulator-always-on;
+       };
+
+       reg_usbh_vbus: regulator-usbh-vbus {
+               compatible = "regulator-fixed";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_usbh1_reg>;
+               regulator-name = "VCC_USB[1-4]";
+               regulator-min-microvolt = <5000000>;
+               regulator-max-microvolt = <5000000>;
+               gpio = <&gpio2 19 GPIO_ACTIVE_LOW>; /* USBH_PEN resp. USBH_P_EN */
+               vin-supply = <&reg_5v0>;
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_gpiokeys>;
+
+               power {
+                       label = "Wake-Up";
+                       gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
+                       linux,code = <KEY_WAKEUP>;
+                       debounce-interval = <10>;
+                       gpio-key,wakeup;
+               };
+       };
+};
+
+&bl {
+       brightness-levels = <0 4 8 16 32 64 128 255>;
+       default-brightness-level = <6>;
+       power-supply =  <&reg_3v3>;
+       status  = "okay";
+};
+
+&dcu0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_dcu0_1>;
+       fsl,panel = <&panel>;
+       status = "okay";
+};
+
+&dspi1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_aster_dspi1 &pinctrl_aster_spi_gpio>;
+       status = "okay";
+
+       spidev0: spidev@0 {
+               compatible = "toradex,evalspi";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+       };
+};
+
+&esdhc1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_esdhc1>;
+       bus-width = <4>;
+       status = "okay";
+};
+
+&fec1 {
+       phy-mode = "rmii";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_fec1>;
+       status = "okay";
+};
+
+&i2c0 {
+       status = "okay";
+
+       /* Atmel maxtouch controller */
+       atmel_mxt_ts: atmel_mxt_ts@4a {
+               compatible = "atmel,maxtouch";
+               reg = <0x4a>;
+               interrupt-parent = <&gpio2>;
+               interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+               status = "disabled";
+       };
+
+       /* M41T0M6 real time clock on carrier board */
+       rtc: m41t0m6@68 {
+               compatible = "st,m41t0";
+               reg = <0x68>;
+       };
+};
+
+&pwm0 {
+       status = "okay";
+};
+
+&pwm1 {
+       status = "okay";
+};
+
+&reg_module_3v3 {
+       vin-supply = <&reg_3v3>;
+};
+
+&tcon0 {
+       status = "okay";
+};
+
+&uart0 {
+       status = "okay";
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&uart2 {
+       status = "okay";
+};
+
+&usbdev0 {
+       extcon = <&extcon_usbc_det>, <&extcon_usbc_det>;
+};
+
+&usbh1 {
+       vbus-supply = <&reg_usbh_vbus>;
+};
+
+&iomuxc {
+       vf610-colibri {
+               pinctrl_gpiokeys: gpiokeys {
+                       fsl,pins = <
+                               VF610_PAD_PTB19__GPIO_41        0x218d
+                       >;
+               };
+
+               pinctrl_aster_dspi1: dspi1grp {
+                       fsl,pins = <
+                               VF610_PAD_PTD6__DSPI1_SIN               0x33e1
+                               VF610_PAD_PTD7__DSPI1_SOUT              0x33e2
+                               VF610_PAD_PTD8__DSPI1_SCK               0x33e2
+                       >;
+               };
+
+               pinctrl_aster_spi_gpio: spigpios {
+                       fsl,pins = <
+                               /* CS0 */
+                               VF610_PAD_PTD5__GPIO_84                 0x22ed
+                               /* CS1 */
+                               VF610_PAD_PTB18__GPIO_40                0x22ed
+                       >;
+               };
+       };
+};
diff --git a/arch/arm/boot/dts/vf-colibri-dual-eth.dtsi b/arch/arm/boot/dts/vf-colibri-dual-eth.dtsi
new file mode 100644 (file)
index 0000000..dfa19d6
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/ {
+       chosen {
+               bootargs = "console=ttyLP0,115200";
+               stdout-path = "serial0:115200n8";
+       };
+
+       aliases {
+               ethernet0 = &fec0;
+               ethernet1 = &fec1;
+       };
+};
+
+&fec0 {
+       phy-mode = "rmii";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_fec0>;
+       status = "okay";
+};
+
+&fec1 {
+       status = "okay";
+};
+
+&uart0 {
+       status = "okay";
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&uart2 {
+       status = "okay";
+};
+
+&uart3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_uart3>;
+       status = "okay";
+};
+
+&uart4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_uart4>;
+       status = "okay";
+};
+
+&iomuxc {
+       pinctrl-0 = <&pinctrl_hog_1>;
+
+       vf610-colibri {
+               pinctrl_fec0: fec0grp {
+                       fsl,pins = <
+                               VF610_PAD_PTA9__RMII_CLKOUT             0x30d2
+                               VF610_PAD_PTC0__ENET_RMII0_MDC          0x30d2
+                               VF610_PAD_PTC1__ENET_RMII0_MDIO         0x30d3
+                               VF610_PAD_PTC2__ENET_RMII0_CRS          0x30d1
+                               VF610_PAD_PTC3__ENET_RMII0_RXD1         0x30d1
+                               VF610_PAD_PTC4__ENET_RMII0_RXD0         0x30d1
+                               VF610_PAD_PTC5__ENET_RMII0_RXER         0x30d1
+                               VF610_PAD_PTC6__ENET_RMII0_TXD1         0x30d2
+                               VF610_PAD_PTC7__ENET_RMII0_TXD0         0x30d2
+                               VF610_PAD_PTC8__ENET_RMII0_TXEN         0x30d2
+                               /* Disable pads multiplexed with PTC7/PTC6 */
+                               VF610_PAD_PTB0__GPIO_22                 0x0000
+                               VF610_PAD_PTB9__GPIO_31                 0x0000
+                       >;
+               };
+
+               pinctrl_uart3: uart3grp {
+                       fsl,pins = <
+                               VF610_PAD_PTA20__UART3_TX               0x21a2
+                               VF610_PAD_PTA21__UART3_RX               0x21a1
+                       >;
+               };
+
+               pinctrl_uart4: uart4grp {
+                       fsl,pins = <
+                               VF610_PAD_PTA28__UART4_TX               0x21a2
+                               VF610_PAD_PTA29__UART4_RX               0x21a1
+                       >;
+               };
+       };
+};
index ed65e0f7dfc0ac96057325c8c7a2582789accb67..dc703043d3ac4c9ac630f969cfc04c14333e09ec 100644 (file)
@@ -7,42 +7,76 @@
  * (at your option) any later version.
  */
 
+#include <dt-bindings/input/input.h>
+
 / {
        chosen {
                stdout-path = "serial0:115200n8";
        };
 
+       aliases {
+               ethernet0 = &fec1;
+               ethernet1 = &fec0;
+       };
+
        clk16m: clk16m {
                compatible = "fixed-clock";
                #clock-cells = <0>;
                clock-frequency = <16000000>;
        };
 
-       regulators {
-               compatible = "simple-bus";
-               #address-cells = <1>;
-               #size-cells = <0>;
-
-               sys_5v0_reg: regulator@0 {
-                       compatible = "regulator-fixed";
-                       reg = <0>;
-                       regulator-name = "5v0";
-                       regulator-min-microvolt = <5000000>;
-                       regulator-max-microvolt = <5000000>;
-                       regulator-always-on;
-               };
+       panel: panel {
+               compatible = "edt,et057090dhu";
+               backlight = <&bl>;
+               power-supply =  <&reg_3v3>;
+       };
+
+       extcon_usbc_det: usbc_det {
+               compatible = "linux,extcon-usb-gpio";
+               debounce = <25>;
+               id-gpio = <&gpio3 6 GPIO_ACTIVE_HIGH>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_usbc_det>;
+       };
+
+       reg_3v3: regulator-3v3 {
+               compatible = "regulator-fixed";
+               regulator-name = "3.3V";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               regulator-always-on;
+       };
+
+       reg_5v0: regulator-5v0 {
+               compatible = "regulator-fixed";
+               regulator-name = "5V";
+               regulator-min-microvolt = <5000000>;
+               regulator-max-microvolt = <5000000>;
+               regulator-always-on;
+       };
+
+       reg_usbh_vbus: regulator-usbh-vbus {
+               compatible = "regulator-fixed";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_usbh1_reg>;
+               regulator-name = "VCC_USB[1-4]";
+               regulator-min-microvolt = <5000000>;
+               regulator-max-microvolt = <5000000>;
+               gpio = <&gpio2 19 GPIO_ACTIVE_LOW>; /* USBH_PEN resp. USBH_P_EN */
+               vin-supply = <&reg_5v0>;
+       };
 
-               /* USBH_PEN */
-               usbh_vbus_reg: regulator@1 {
-                       compatible = "regulator-fixed";
-                       pinctrl-names = "default";
-                       pinctrl-0 = <&pinctrl_usbh1_reg>;
-                       reg = <1>;
-                       regulator-name = "usbh_vbus";
-                       regulator-min-microvolt = <5000000>;
-                       regulator-max-microvolt = <5000000>;
-                       gpio = <&gpio2 19 GPIO_ACTIVE_LOW>;
-                       vin-supply = <&sys_5v0_reg>;
+       gpio-keys {
+               compatible = "gpio-keys";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_gpiokeys>;
+
+               power {
+                       label = "Wake-Up";
+                       gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
+                       linux,code = <KEY_WAKEUP>;
+                       debounce-interval = <10>;
+                       gpio-key,wakeup;
                };
        };
 };
 &bl {
        brightness-levels = <0 4 8 16 32 64 128 255>;
        default-brightness-level = <6>;
+       power-supply =  <&reg_3v3>;
        status  = "okay";
 };
 
+&dcu0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_dcu0_1>;
+       fsl,panel = <&panel>;
+       status = "okay";
+};
+
 &dspi1 {
        status = "okay";
 
+
+       spidev0: spidev@0 {
+               compatible = "toradex,evalspi";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+       };
+
        mcp2515can: can@0 {
                compatible = "microchip,mcp2515";
                pinctrl-names = "default";
                spi-max-frequency = <10000000>;
                interrupt-parent = <&gpio1>;
                interrupts = <11 GPIO_ACTIVE_LOW>;
+               status = "disabled";
        };
 };
 
 &i2c0 {
        status = "okay";
 
+       /* Atmel maxtouch controller */
+       atmel_mxt_ts: atmel_mxt_ts@4a {
+               compatible = "atmel,maxtouch";
+               reg = <0x4a>;
+               interrupt-parent = <&gpio2>;
+               interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+               status = "disabled";
+       };
+
+       /* TouchRevolution Fusion 7 and 10 multi-touch controller */
+       touch: touchrevf0710a@10 {
+               compatible = "touchrevolution,fusion-f0710a";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_gpiotouch>;
+               reg = <0x10>;
+               gpios = <&gpio0 30 GPIO_ACTIVE_HIGH /* SO-DIMM 28, Pen down interrupt */
+                               &gpio0 23 GPIO_ACTIVE_LOW /* SO-DIMM 30, Reset interrupt */
+                               >;
+               status = "disabled";
+       };
+
        /* M41T0M6 real time clock on carrier board */
        rtc: m41t0m6@68 {
-               compatible = "st,m41t00";
+               compatible = "st,m41t0";
                reg = <0x68>;
        };
 };
        status = "okay";
 };
 
+&reg_module_3v3 {
+       vin-supply = <&reg_3v3>;
+};
+
+&tcon0 {
+       status = "okay";
+};
+
 &uart0 {
        status = "okay";
 };
        status = "okay";
 };
 
+&usbdev0 {
+       extcon = <&extcon_usbc_det>, <&extcon_usbc_det>;
+};
+
 &usbh1 {
-       vbus-supply = <&usbh_vbus_reg>;
+       vbus-supply = <&reg_usbh_vbus>;
 };
 
 &iomuxc {
                                VF610_PAD_PTB21__GPIO_43        0x22ed
                        >;
                };
+
+               pinctrl_gpiokeys: gpiokeys {
+                       fsl,pins = <
+                               VF610_PAD_PTB19__GPIO_41        0x218d
+                       >;
+               };
+
+               pinctrl_gpiotouch: touchgpios {
+                       fsl,pins = <
+                               VF610_PAD_PTB8__GPIO_30         0x6f
+                               VF610_PAD_PTB1__GPIO_23         0x4f
+                       >;
+               };
        };
 };
index e5949b9349453394688ba62bd4073c817049ac58..7d11341cc567edfa9c2cc9fa4305d6ffdf34e709 100644 (file)
 / {
        bl: backlight {
                compatible = "pwm-backlight";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_gpio_bl_on>;
                pwms = <&pwm0 0 5000000 0>;
+               enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
                status = "disabled";
        };
+
+       reg_module_3v3: regulator-module-3v3 {
+               compatible = "regulator-fixed";
+               regulator-name = "+V3.3";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               regulator-always-on;
+       };
+
+       reg_module_3v3_avdd: regulator-module-3v3-avdd {
+               compatible = "regulator-fixed";
+               regulator-name = "+V3.3_AVDD_AUDIO";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               regulator-always-on;
+       };
+
+       soc {
+               fsl,use-lpm-poweroff;
+       };
 };
 
 &adc0 {
        status = "okay";
+       vref-supply = <&reg_module_3v3_avdd>;
 };
 
 &adc1 {
        status = "okay";
+       vref-supply = <&reg_module_3v3_avdd>;
+};
+
+&can0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_flexcan0>;
+       status = "disabled";
+};
+
+&can1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_flexcan1>;
+       status = "disabled";
+};
+
+&clks {
+       assigned-clocks = <&clks VF610_CLK_ENET_SEL>,
+                         <&clks VF610_CLK_ENET_TS_SEL>;
+       assigned-clock-parents = <&clks VF610_CLK_ENET_50M>,
+                                <&clks VF610_CLK_ENET_50M>;
 };
 
 &dspi1 {
        status = "okay";
 };
 
+&edma1 {
+       status = "okay";
+};
+
 &esdhc1 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_esdhc1>;
        bus-width = <4>;
        cd-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
+       disable-wp;
 };
 
 &fec1 {
        phy-mode = "rmii";
+       phy-supply = <&reg_module_3v3>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_fec1>;
 };
 };
 
 &nfc {
-       assigned-clocks = <&clks VF610_CLK_NFC>;
-       assigned-clock-rates = <33000000>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_nfc>;
        status = "okay";
 
 &pwm0 {
        pinctrl-names = "default";
-       pinctrl-0 = <&pinctrl_pwm0>;
+       pinctrl-0 = <&pinctrl_pwm0_a &pinctrl_pwm0_c>;
 };
 
 &pwm1 {
        pinctrl-names = "default";
-       pinctrl-0 = <&pinctrl_pwm1>;
+       pinctrl-0 = <&pinctrl_pwm1_b &pinctrl_pwm1_d>;
 };
 
 &uart0 {
 
 &usbdev0 {
        disable-over-current;
+       dr_mode = "otg";
        status = "okay";
 };
 
 };
 
 &iomuxc {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_hog_0 &pinctrl_hog_1>;
+
        vf610-colibri {
-               pinctrl_gpio_ext: gpio_ext {
+               pinctrl_flexcan0: can0grp {
+                       fsl,pins = <
+                               VF610_PAD_PTB14__CAN0_RX        0x31F1
+                               VF610_PAD_PTB15__CAN0_TX        0x31F2
+                       >;
+               };
+
+               pinctrl_flexcan1: can1grp {
                        fsl,pins = <
-                               VF610_PAD_PTD10__GPIO_89        0x22ed /* EXT_IO_0 */
-                               VF610_PAD_PTD9__GPIO_88         0x22ed /* EXT_IO_1 */
-                               VF610_PAD_PTD26__GPIO_68        0x22ed /* EXT_IO_2 */
+                               VF610_PAD_PTB16__CAN1_RX        0x31F1
+                               VF610_PAD_PTB17__CAN1_TX        0x31F2
+                       >;
+               };
+
+               pinctrl_dcu0_1: dcu0grp_1 {
+                       fsl,pins = <
+                               VF610_PAD_PTE0__DCU0_HSYNC      0x1902
+                               VF610_PAD_PTE1__DCU0_VSYNC      0x1902
+                               VF610_PAD_PTE2__DCU0_PCLK       0x1902
+                               VF610_PAD_PTE4__DCU0_DE         0x1902
+                               VF610_PAD_PTE5__DCU0_R0         0x1902
+                               VF610_PAD_PTE6__DCU0_R1         0x1902
+                               VF610_PAD_PTE7__DCU0_R2         0x1902
+                               VF610_PAD_PTE8__DCU0_R3         0x1902
+                               VF610_PAD_PTE9__DCU0_R4         0x1902
+                               VF610_PAD_PTE10__DCU0_R5        0x1902
+                               VF610_PAD_PTE11__DCU0_R6        0x1902
+                               VF610_PAD_PTE12__DCU0_R7        0x1902
+                               VF610_PAD_PTE13__DCU0_G0        0x1902
+                               VF610_PAD_PTE14__DCU0_G1        0x1902
+                               VF610_PAD_PTE15__DCU0_G2        0x1902
+                               VF610_PAD_PTE16__DCU0_G3        0x1902
+                               VF610_PAD_PTE17__DCU0_G4        0x1902
+                               VF610_PAD_PTE18__DCU0_G5        0x1902
+                               VF610_PAD_PTE19__DCU0_G6        0x1902
+                               VF610_PAD_PTE20__DCU0_G7        0x1902
+                               VF610_PAD_PTE21__DCU0_B0        0x1902
+                               VF610_PAD_PTE22__DCU0_B1        0x1902
+                               VF610_PAD_PTE23__DCU0_B2        0x1902
+                               VF610_PAD_PTE24__DCU0_B3        0x1902
+                               VF610_PAD_PTE25__DCU0_B4        0x1902
+                               VF610_PAD_PTE26__DCU0_B5        0x1902
+                               VF610_PAD_PTE27__DCU0_B6        0x1902
+                               VF610_PAD_PTE28__DCU0_B7        0x1902
                        >;
                };
 
                        >;
                };
 
+               pinctrl_gpio_bl_on: gpio_bl_on {
+                       fsl,pins = <
+                               VF610_PAD_PTC0__GPIO_45         0x22ef
+                       >;
+               };
+
+               pinctrl_hog_0: hoggrp-0 {
+                       fsl,pins = <
+                               VF610_PAD_PTA12__GPIO_5         0x22ed
+                               VF610_PAD_PTA17__GPIO_7         0x22ed
+                               VF610_PAD_PTA20__GPIO_10        0x22ed
+                               VF610_PAD_PTA21__GPIO_11        0x22ed
+                               VF610_PAD_PTA30__GPIO_20        0x22ed
+                               VF610_PAD_PTA31__GPIO_21        0x22ed
+                               VF610_PAD_PTB6__GPIO_28         0x22ed
+                               VF610_PAD_PTB7__GPIO_29         0x22ed
+                               VF610_PAD_PTB16__GPIO_38        0x22ed
+                               VF610_PAD_PTB17__GPIO_39        0x22ed
+                               VF610_PAD_PTB18__GPIO_40        0x22ed
+                               VF610_PAD_PTB21__GPIO_43        0x22ed
+                               VF610_PAD_PTB22__GPIO_44        0x22ed
+                               VF610_PAD_PTC1__GPIO_46         0x22ed
+                               VF610_PAD_PTC2__GPIO_47         0x22ed
+                               VF610_PAD_PTC3__GPIO_48         0x22ed
+                               VF610_PAD_PTC4__GPIO_49         0x22ed
+                               VF610_PAD_PTC5__GPIO_50         0x22ed
+                               VF610_PAD_PTC6__GPIO_51         0x22ed
+                               VF610_PAD_PTC7__GPIO_52         0x22ed
+                               VF610_PAD_PTC8__GPIO_53         0x22ed
+                               VF610_PAD_PTD30__GPIO_64        0x22ed
+                               VF610_PAD_PTD29__GPIO_65        0x22ed
+                               VF610_PAD_PTD28__GPIO_66        0x22ed
+                               VF610_PAD_PTD26__GPIO_68        0x22ed
+                               VF610_PAD_PTD25__GPIO_69        0x22ed
+                               VF610_PAD_PTD24__GPIO_70        0x22ed
+                               VF610_PAD_PTD9__GPIO_88         0x22ed
+                               VF610_PAD_PTD10__GPIO_89        0x22ed
+                               VF610_PAD_PTD11__GPIO_90        0x22ed
+                               VF610_PAD_PTD12__GPIO_91        0x22ed
+                               VF610_PAD_PTD13__GPIO_92        0x22ed
+                               VF610_PAD_PTB26__GPIO_96        0x22ed
+                               VF610_PAD_PTB28__GPIO_98        0x22ed
+                               VF610_PAD_PTC30__GPIO_103       0x22ed
+                               VF610_PAD_PTA7__GPIO_134        0x22ed
+                       >;
+               };
+
+               pinctrl_hog_1: hoggrp-1 { /* ATMEL MXT TOUCH */
+                       fsl,pins = <
+                               VF610_PAD_PTD27__GPIO_67        0x22ed
+                               VF610_PAD_PTD31__GPIO_63        0x22ed
+                       >;
+               };
+
+               pinctrl_hog_2: hoggrp-2 {
+                       fsl,pins = <
+                               VF610_PAD_PTB12__GPIO_34        0x22ed
+                               VF610_PAD_PTB13__GPIO_35        0x22ed
+                       >;
+               };
+
                pinctrl_i2c0: i2c0grp {
                        fsl,pins = <
                                VF610_PAD_PTB14__I2C0_SCL               0x37ff
                        >;
                };
 
-               pinctrl_pwm0: pwm0grp {
+               pinctrl_pwm0_a: pwm0agrp {
                        fsl,pins = <
                                VF610_PAD_PTB0__FTM0_CH0                0x1182
+                       >;
+               };
+
+               pinctrl_pwm0_c: pwm0cgrp {
+                       fsl,pins = <
                                VF610_PAD_PTB1__FTM0_CH1                0x1182
                        >;
                };
 
-               pinctrl_pwm1: pwm1grp {
+               pinctrl_pwm1_b: pwm1bgrp {
                        fsl,pins = <
                                VF610_PAD_PTB8__FTM1_CH0                0x1182
+                       >;
+               };
+
+               pinctrl_pwm1_d: pwm1dgrp {
+                       fsl,pins = <
                                VF610_PAD_PTB9__FTM1_CH1                0x1182
                        >;
                };
                        fsl,pins = <
                                VF610_PAD_PTB10__UART0_TX               0x21a2
                                VF610_PAD_PTB11__UART0_RX               0x21a1
+                               VF610_PAD_PTB12__UART0_RTS              0x21a2
+                               VF610_PAD_PTB13__UART0_CTS              0x21a1
                        >;
                };
 
                        >;
                };
 
+               pinctrl_usbc_det: gpio_usbc_det {
+                       fsl,pins = <
+                               VF610_PAD_PTC29__GPIO_102               0x22cd
+                       >;
+               };
+
                pinctrl_usbh1_reg: gpio_usb_vbus {
                        fsl,pins = <
                                VF610_PAD_PTD4__GPIO_83                 0x22ed
diff --git a/arch/arm/boot/dts/vf500-colibri-aster.dts b/arch/arm/boot/dts/vf500-colibri-aster.dts
new file mode 100644 (file)
index 0000000..e314a7a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/dts-v1/;
+#include "vf500-colibri.dtsi"
+#include "vf-colibri-aster.dtsi"
+
+/ {
+       model = "Toradex Colibri VF50 on Colibri Aster Board";
+       compatible = "toradex,vf500-colibri_vf50-on-aster", "toradex,vf500-colibri_vf50", "fsl,vf500";
+};
+
+&touchscreen {
+       vf50-ts-min-pressure = <200>;
+       status = "okay";
+};
diff --git a/arch/arm/boot/dts/vf500-colibri-dual-eth.dts b/arch/arm/boot/dts/vf500-colibri-dual-eth.dts
new file mode 100644 (file)
index 0000000..24990e2
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/dts-v1/;
+#include "vf500-colibri.dtsi"
+#include "vf-colibri-dual-eth.dtsi"
+
+/ {
+       model = "Toradex Colibri VF50 on Dual Ethernet Board";
+       compatible = "toradex,vf500-colibri_vf50-on-dual-eth-board", "toradex,vf500-colibri_vf50", "fsl,vf500";
+};
index 84f091d1fcf29bb3c24ce7c8f9e8c9163f1169c1..731ff0091c9d8dd1a4996c06bff42b2a52ab30b4 100644 (file)
        };
 };
 
+&nfc {
+       assigned-clocks = <&clks VF610_CLK_NFC>;
+       assigned-clock-rates = <33000000>;
+};
+
 &iomuxc {
        vf610-colibri {
                pinctrl_touchctrl_idle: touchctrl_idle {
index e976d2fa15274239b9ed2b0568cb9bd9ba9b2aa6..f2be079aae512dfa87a7b299c77251bdc3bc96e9 100644 (file)
                                clocks = <&clks VF610_CLK_PLATFORM_BUS>;
                        };
                };
+
+               aips-bus@40080000 {
+
+                       pmu@40089000 {
+                               compatible = "arm,cortex-a5-pmu";
+                               interrupts = <7 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupt-affinity = <&a5_cpu>;
+                       };
+               };
+
        };
 };
 
diff --git a/arch/arm/boot/dts/vf610-colibri-aster.dts b/arch/arm/boot/dts/vf610-colibri-aster.dts
new file mode 100644 (file)
index 0000000..55efc16
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2017 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/dts-v1/;
+#include "vf610-colibri.dtsi"
+#include "vf-colibri-aster.dtsi"
+
+/ {
+       model = "Toradex Colibri VF61 on Colibri Aster Board";
+       compatible = "toradex,vf610-colibri_vf61-on-aster", "toradex,vf610-colibri_vf61", "fsl,vf610";
+};
diff --git a/arch/arm/boot/dts/vf610-colibri-dual-eth.dts b/arch/arm/boot/dts/vf610-colibri-dual-eth.dts
new file mode 100644 (file)
index 0000000..a2eff55
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/dts-v1/;
+#include "vf610-colibri.dtsi"
+#include "vf-colibri-dual-eth.dtsi"
+
+/ {
+       model = "Toradex Colibri VF61 on Dual Ethernet Board";
+       compatible = "toradex,vf610-colibri_vf61-on-dual-eth-board", "toradex,vf610-colibri_vf61", "fsl,vf610";
+};
index 2d7eab7552100225efcf2cb5d52a5443459f6a58..a8d59f7bef06b5f880f7f9f1588b48d2e57c4a0b 100644 (file)
        memory {
                reg = <0x80000000 0x10000000>;
        };
+
+       sound {
+               compatible = "fsl,fsl-sai-audio-wm9712";
+               fsl,ac97-controller = <&sai2>;
+
+               fsl,model = "Colibri VF61 AC97 Audio";
+
+               fsl,audio-routing =
+                       "Headphone", "HPOUTL",
+                       "Headphone", "HPOUTR",
+                       "LINEINL", "LineIn",
+                       "LINEINR", "LineIn",
+                       "MIC1", "Mic";
+       };
+};
+
+&nfc {
+       assigned-clocks = <&clks VF610_CLK_NFC>;
+       assigned-clock-rates = <50000000>;
+};
+
+&sai0 {
+       compatible = "fsl,vf610-sai-clk";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_sai0>;
+       status = "okay";
+};
+
+&sai2 {
+       compatible = "fsl,vf610-sai-ac97";
+       #sound-dai-cells = <0>;
+       pinctrl-names = "default", "sleep", "ac97-running", "ac97-reset",
+                       "ac97-warm-reset";
+       pinctrl-0 = <&pinctrl_sai2_ac97_default>;
+       pinctrl-1 = <&pinctrl_sai2_ac97_sleep>;
+       pinctrl-2 = <&pinctrl_sai2_ac97_running>;
+       pinctrl-3 = <&pinctrl_sai2_ac97_reset>;
+       pinctrl-4 = <&pinctrl_sai2_ac97_warm_reset>;
+       ac97-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH &gpio0 8 GPIO_ACTIVE_HIGH
+                     &gpio0 13 GPIO_ACTIVE_HIGH>;
+       status = "okay";
+};
+
+&iomuxc {
+       vf610-colibri {
+               pinctrl_sai0: sai0grp_1 {
+                       fsl,pins = <
+                               VF610_PAD_PTB23__SAI0_TX_BCLK   0x31C3
+                       >;
+               };
+
+               pinctrl_sai2_ac97_default: sai2grp_1 {
+                       fsl,pins = <
+                               /* Pen-down */
+                               VF610_PAD_PTA11__GPIO_4         0x22ed
+
+                               /* GenIRQ */
+                               VF610_PAD_PTB2__GPIO_24         0x22e1
+                       >;
+               };
+
+               pinctrl_sai2_ac97_sleep: sai2grp_2 {
+                       fsl,pins = <
+                               /* AC97 Reset (cold reset) floating */
+                               VF610_PAD_PTA23__GPIO_13        0x22c1
+                       >;
+               };
+
+               pinctrl_sai2_ac97_running: sai2grp_3 {
+                       fsl,pins = <
+                               /* AC97 Bit clock */
+                               VF610_PAD_PTA16__SAI2_TX_BCLK   0x31C3
+
+                               /* AC97 SData Out */
+                               VF610_PAD_PTA18__SAI2_TX_DATA   0x31C2
+
+                               /* AC97 Sync */
+                               VF610_PAD_PTA19__SAI2_TX_SYNC   0x31C3
+
+                               /* AC97 SData In */
+                               VF610_PAD_PTA22__SAI2_RX_DATA   0x0041
+                       >;
+               };
+
+               pinctrl_sai2_ac97_reset: sai2grp_4 {
+                       fsl,pins = <
+                               VF610_PAD_PTA16__SAI2_TX_BCLK   0x31C1
+
+                               /* AC97 SData Out (test mode selection) */
+                               VF610_PAD_PTA18__GPIO_8         0x22c1
+
+                               /* AC97 Sync (warm reset) */
+                               VF610_PAD_PTA19__GPIO_9         0x22c1
+
+                               /* AC97 Reset (cold reset) */
+                               VF610_PAD_PTA23__GPIO_13        0x22c1
+                       >;
+               };
+
+               pinctrl_sai2_ac97_warm_reset: sai2grp_5 {
+                       fsl,pins = <
+                               /* AC97 SData Out (test mode selection) */
+                               VF610_PAD_PTA18__GPIO_8         0x22c1
+
+                               /* AC97 Sync (warm reset) */
+                               VF610_PAD_PTA19__GPIO_9         0x22c1
+                       >;
+               };
+
+
+       };
 };
index 5438ee4be2ecf6278ac35cbf097a225b38a8a3ec..8419c0607f9b58d2091250948bfbeba361605fae 100644 (file)
 &clks {
        clocks = <&sxosc>, <&fxosc>, <&enet_ext>, <&audio_ext>;
        clock-names = "sxosc", "fxosc", "enet_ext", "audio_ext";
+       assigned-clocks = <&clks VF610_CLK_ENET_SEL>,
+                         <&clks VF610_CLK_ENET_TS_SEL>;
+       assigned-clock-parents = <&clks VF610_CLK_ENET_EXT>,
+                                <&clks VF610_CLK_ENET_EXT>;
 };
 
 &dspi0 {
index 58bc6e448be5601ed8d04106fbcc193793f040f1..f731b598e2c52e29893598a0f153042c788ad024 100644 (file)
                arm,data-latency = <3 3 3>;
                arm,tag-latency = <2 2 2>;
        };
+
+       sema4: sema4@4001D000 {
+               compatible = "fsl,vf610-sema4";
+               reg = <0x4001D000 0x1000>;
+               interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
+       };
+
+       CM4: cortexm4 {
+               compatible = "simple-bus";
+               ranges = <0x1f000000 0x3f000000 0x80000
+                         0x3f000000 0x3f000000 0x80000>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               cortexm4core {
+                       compatible = "fsl,vf610-m4";
+                       reg = <0x1f000000 0x80000>,
+                               <0x3f000000 0x80000>;
+                       reg-names = "pc_ocram", "ps_ocram";
+                       fsl,firmware = "freertos-rpmsg.elf";
+               };
+       };
+
+       rpmsg: rpmsg {
+               compatible = "fsl,vf610-rpmsg";
+               status = "okay";
+       };
 };
index 3cd1b27f269780b99d1dbc4af64644bcaa45cce5..bf4e8048fac8567129c6ba3dd6b49f2da58070fe 100644 (file)
@@ -16,6 +16,8 @@
        aliases {
                can0 = &can0;
                can1 = &can1;
+               ethernet0 = &fec0;
+               ethernet1 = &fec1;
                serial0 = &uart0;
                serial1 = &uart1;
                serial2 = &uart2;
        soc {
                #address-cells = <1>;
                #size-cells = <1>;
-               compatible = "simple-bus";
-               interrupt-parent = <&mscm_ir>;
+               compatible = "fsl,vf610-soc-bus", "simple-bus";
+               interrupt-parent = <&gpc>;
                ranges;
+               fsl,rom-revision = <&ocrom 0x80>;
+               fsl,cpu-count = <&mscm_cpucfg 0x2C>;
+               fsl,l2-size = <&mscm_cpucfg 0x14>;
+               nvmem-cells = <&ocotp_cfg0>, <&ocotp_cfg1>;
+               nvmem-cell-names = "cfg0", "cfg1";
+
+               ocrom: ocrom@00000000 {
+                       compatible = "fsl,vf610-ocrom", "syscon";
+                       reg = <0x00000000 0x18000>;
+               };
+
+               ocram0: sram@3f000000 {
+                       compatible = "mmio-sram";
+                       reg = <0x3f000000 0x40000>;
+
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges = <0 0x3f000000 0x40000>;
+
+                       stbyram1@0 {
+                               reg = <0x0 0x4000>;
+                               label = "stbyram1";
+                               pool;
+                       };
+
+                       stbyram2@4000 {
+                               reg = <0x4000 0xc000>;
+                               label = "stbyram2";
+                               pool;
+                       };
+               };
+
+               ocram1: sram@3f040000 {
+                       compatible = "mmio-sram";
+                       reg = <0x3f040000 0x40000>;
+               };
+
+               gfxram0: sram@3f400000 {
+                       compatible = "mmio-sram";
+                       reg = <0x3f400000 0x80000>;
+               };
+
+               /* used by L2 cache */
+               gfxram1: sram@3f480000 {
+                       compatible = "mmio-sram";
+                       reg = <0x3f480000 0x80000>;
+               };
 
                aips0: aips-bus@40000000 {
                        compatible = "fsl,aips-bus", "simple-bus";
 
                        mscm_ir: interrupt-controller@40001800 {
                                compatible = "fsl,vf610-mscm-ir";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
                                reg = <0x40001800 0x400>;
                                fsl,cpucfg = <&mscm_cpucfg>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
+
+                               cpu2cpu@40001800 {
+                                       reg = <0x40001800 0x40>;
+                                       interrupt-parent = <&mscm_ir>;
+                                       interrupts = <0 IRQ_TYPE_LEVEL_HIGH>,
+                                               <1 IRQ_TYPE_LEVEL_HIGH>,
+                                               <2 IRQ_TYPE_LEVEL_HIGH>,
+                                               <3 IRQ_TYPE_LEVEL_HIGH>;
+                                       interrupt-names = "int0", "int1", "int2", "int3";
+                               };
                        };
 
                        edma0: dma-controller@40018000 {
                                clocks = <&clks VF610_CLK_DSPI0>;
                                clock-names = "dspi";
                                spi-num-chipselects = <6>;
+                               dmas = <&edma1 1 12>,
+                                       <&edma1 1 13>;
+                               dma-names = "rx", "tx";
                                status = "disabled";
                        };
 
                                clocks = <&clks VF610_CLK_DSPI1>;
                                clock-names = "dspi";
                                spi-num-chipselects = <4>;
+                               dmas = <&edma1 1 14>,
+                                       <&edma1 1 15>;
+                               dma-names = "rx", "tx";
+                               status = "disabled";
+                       };
+
+                       sai0: sai@4002f000 {
+                               compatible = "fsl,vf610-sai";
+                               reg = <0x4002f000 0x1000>;
+                               interrupts = <84 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_SAI0>,
+                                       <&clks VF610_CLK_SAI0_DIV>,
+                                       <&clks 0>, <&clks 0>;
+                               clock-names = "bus", "mclk1", "mclk2", "mclk3";
+                               dma-names = "tx", "rx";
+                               dmas = <&edma0 0 17>,
+                                       <&edma0 0 16>;
+                               status = "disabled";
+                       };
+
+                       sai1: sai@40030000 {
+                               compatible = "fsl,vf610-sai";
+                               reg = <0x40030000 0x1000>;
+                               interrupts = <85 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_SAI1>,
+                                       <&clks VF610_CLK_SAI1_DIV>,
+                                       <&clks 0>, <&clks 0>;
+                               clock-names = "bus", "mclk1", "mclk2", "mclk3";
+                               dma-names = "tx", "rx";
+                               dmas = <&edma0 0 19>,
+                                       <&edma0 0 18>;
                                status = "disabled";
                        };
 
                                status = "disabled";
                        };
 
+                       sai3: sai@40032000 {
+                               compatible = "fsl,vf610-sai";
+                               reg = <0x40032000 0x1000>;
+                               interrupts = <87 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_SAI3>,
+                                       <&clks VF610_CLK_SAI3_DIV>,
+                                       <&clks 0>, <&clks 0>;
+                               clock-names = "bus", "mclk1", "mclk2", "mclk3";
+                               dma-names = "tx", "rx";
+                               dmas = <&edma0 1 9>,
+                                       <&edma0 1 8>;
+                               status = "disabled";
+                       };
+
                        pit: pit@40037000 {
                                compatible = "fsl,vf610-pit";
                                reg = <0x40037000 0x1000>;
                                                        <20000000>;
                        };
 
+                       tcon0: timing-controller@4003d000 {
+                               compatible = "fsl,vf610-tcon";
+                               reg = <0x4003d000 0x1000>;
+                               clocks = <&clks VF610_CLK_TCON0>;
+                               clock-names = "ipg";
+                               status = "disabled";
+                       };
+
                        wdoga5: wdog@4003e000 {
                                compatible = "fsl,vf610-wdt", "fsl,imx21-wdt";
                                reg = <0x4003e000 0x1000>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                gpio-ranges = <&iomuxc 0 0 32>;
+                               fsl,gpio-wakeup = <&wakeup 22 0 8>; /* PTB0...PTB7 */
                        };
 
                        gpio1: gpio@4004a000 {
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                gpio-ranges = <&iomuxc 0 32 32>;
+                               fsl,gpio-wakeup = <&wakeup 1 8 2>, /* PTB11, PTB12 (NMI)*/
+                                               <&wakeup 4 10 1>, /* PTB14 */
+                                               <&wakeup 6 11 1>, /* PTB16 */
+                                               <&wakeup 9 12 2>; /* PTB19, PTB20 */
                        };
 
                        gpio2: gpio@4004b000 {
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                gpio-ranges = <&iomuxc 0 96 32>;
+                               fsl,gpio-wakeup = <&wakeup 1 14 1>, /* PTB27 */
+                                               <&wakeup 7 15 1>, /* PTC30 */
+                                               <&wakeup 29 16 1>; /* PTE20 */
                        };
 
                        gpio4: gpio@4004d000 {
                                reg = <0x40050000 0x400>;
                        };
 
+                       scsc: scsc@40052000 {
+                               compatible = "fsl,vf610-scsc";
+                               reg = <0x40052000 0x1000>;
+                       };
+
                        usbphy0: usbphy@40050800 {
                                compatible = "fsl,vf610-usbphy";
                                reg = <0x40050800 0x400>;
                                status = "disabled";
                        };
 
+                       dcu0: dcu@40058000 {
+                               compatible = "fsl,vf610-dcu";
+                               reg = <0x40058000 0x1200>;
+                               interrupts = <30 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_DCU0>,
+                                       <&clks VF610_CLK_DCU0_DIV>;
+                               clock-names = "dcu", "pix";
+                               fsl,tcon = <&tcon0>;
+                               status = "disabled";
+                       };
+
                        i2c0: i2c@40066000 {
                                #address-cells = <1>;
                                #size-cells = <0>;
                                status = "disabled";
                        };
 
+                       wakeup: wkpu@4006a000 {
+                               compatible = "fsl,vf610-wkpu";
+                               reg = <0x4006a000 0x1000>;
+                               interrupts = <92 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_WKPU>;
+                       };
+
                        clks: ccm@4006b000 {
-                               compatible = "fsl,vf610-ccm";
+                               compatible = "fsl,vf610-ccm", "syscon";
                                reg = <0x4006b000 0x1000>;
                                clocks = <&sxosc>, <&fxosc>;
                                clock-names = "sxosc", "fxosc";
                        };
 
                        usbdev0: usb@40034000 {
-                               compatible = "fsl,vf610-usb", "fsl,imx27-usb";
+                               compatible = "fsl,vf610-usb";
                                reg = <0x40034000 0x800>;
                                interrupts = <75 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks VF610_CLK_USBC0>;
                                reg = <0x4006e000 0x1000>;
                                interrupts = <96 IRQ_TYPE_LEVEL_HIGH>;
                        };
+
+                       gpc: gpc@4006c000 {
+                               compatible = "fsl,vf610-gpc";
+                               reg = <0x4006c000 0x1000>;
+                               interrupt-controller;
+                               #interrupt-cells = <2>;
+                               interrupt-parent = <&mscm_ir>;
+                       };
                };
 
                aips1: aips-bus@40080000 {
                                status = "disabled";
                        };
 
+                       ocotp@400a5000 {
+                               compatible = "fsl,vf610-ocotp";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               reg = <0x400a5000 0xCF0>;
+                               clocks = <&clks VF610_CLK_OCOTP>;
+
+                               ocotp_cfg0: cfg0@410 {
+                                       reg = <0x410 0x4>;
+                               };
+
+                               ocotp_cfg1: cfg1@420 {
+                                       reg = <0x420 0x4>;
+                               };
+                       };
+
                        snvs0: snvs@400a7000 {
                            compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";
                                reg = <0x400a7000 0x2000>;
                                status = "disabled";
                        };
 
+                       dspi2: dspi2@400ac000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "fsl,vf610-dspi";
+                               reg = <0x400ac000 0x1000>;
+                               interrupts = <69 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_DSPI2>;
+                               clock-names = "dspi";
+                               spi-num-chipselects = <2>;
+                               dmas = <&edma1 0 10>,
+                                       <&edma1 0 11>;
+                               dma-names = "rx", "tx";
+                               status = "disabled";
+                       };
+
+                       dspi3: dspi3@400ad000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "fsl,vf610-dspi";
+                               reg = <0x400ad000 0x1000>;
+                               interrupts = <70 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_DSPI3>;
+                               clock-names = "dspi";
+                               spi-num-chipselects = <2>;
+                               dmas = <&edma1 0 12>,
+                                       <&edma1 0 13>;
+                               dma-names = "rx", "tx";
+                               status = "disabled";
+                       };
+
+                       ddrmc: ddrmc@400ae000 {
+                               compatible = "fsl,vf610-ddrmc";
+                               reg = <0x400ae000 0x400>;
+                               clocks = <&clks VF610_CLK_DDRMC>;
+                               clock-names = "ddrc";
+                       };
+
                        adc1: adc@400bb000 {
                                compatible = "fsl,vf610-adc";
                                reg = <0x400bb000 0x1000>;
                        };
 
                        usbh1: usb@400b4000 {
-                               compatible = "fsl,vf610-usb", "fsl,imx27-usb";
+                               compatible = "fsl,vf610-usb";
                                reg = <0x400b4000 0x800>;
                                interrupts = <76 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks VF610_CLK_USBC1>;
                                status = "disabled";
                        };
 
+                       dac0: dac@400cc000 {
+                               compatible = "fsl,vf610-dac";
+                               reg = <0x400cc000 1000>;
+                               interrupts = <55 IRQ_TYPE_LEVEL_HIGH>;
+                               clock-names = "dac";
+                               clocks = <&clks VF610_CLK_DAC0>;
+                               status = "disabled";
+                       };
+
+                       dac1: dac@400cd000 {
+                               compatible = "fsl,vf610-dac";
+                               reg = <0x400cd000 1000>;
+                               interrupts = <56 IRQ_TYPE_LEVEL_HIGH>;
+                               clock-names = "dac";
+                               clocks = <&clks VF610_CLK_DAC1>;
+                               status = "disabled";
+                       };
+
                        fec0: ethernet@400d0000 {
                                compatible = "fsl,mvf600-fec";
                                reg = <0x400d0000 0x1000>;
                                status = "disabled";
                        };
 
+                       esw: l2-switch@400e8000 {
+                               compatible = "fsl,eth-switch";
+                               reg = <0x400e8000 0x1000 0x400d0000 0x2000>;
+                               interrupts = <82 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clks VF610_CLK_ESW>,
+                                       <&clks VF610_CLK_ENET>,
+                                       <&clks VF610_CLK_ENET0>,
+                                       <&clks VF610_CLK_ENET1>;
+                               clock-names = "esw", "enet", "enet0", "enet1";
+                               status = "disabled";
+                       };
+
                        i2c2: i2c@400e6000 {
                                #address-cells = <1>;
                                #size-cells = <0>;
                                status = "disabled";
                        };
                };
+
+               adc_hwmon: iio_hwmon {
+                       compatible = "iio-hwmon";
+                       io-channels = <&adc0 16>, <&adc1 16>;
+               };
        };
 };
diff --git a/arch/arm/configs/colibri_vf_defconfig b/arch/arm/configs/colibri_vf_defconfig
new file mode 100644 (file)
index 0000000..37b937a
--- /dev/null
@@ -0,0 +1,331 @@
+CONFIG_KERNEL_LZO=y
+CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CGROUPS=y
+CONFIG_NAMESPACES=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_ARCH_MXC=y
+CONFIG_SOC_VF610=y
+CONFIG_SWP_EMULATE=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_AEABI=y
+CONFIG_CMA=y
+CONFIG_KEXEC=y
+# CONFIG_ATAGS_PROC is not set
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_BINFMT_MISC=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPGRE_DEMUX=m
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6_SIT=m
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_NFACCT=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NFT_CHAIN_NAT_IPV4=y
+CONFIG_NFT_MASQ_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_L2TP=m
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_IGMP_SNOOPING is not set
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_CAN=m
+CONFIG_CAN_FLEXCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_DMA_CMA=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VF610_NFC=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_FASTMAP=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_SCAN_ASYNC=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+CONFIG_FSL_L2_SWITCH=y
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_MICREL_PHY=y
+CONFIG_PPP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_MPPE=m
+CONFIG_PPTP=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_USB_NET_DRIVERS=m
+CONFIG_USB_USBNET=m
+# CONFIG_USB_NET_CDC_NCM is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_WLAN is not set
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_MOUSE_PS2 is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=m
+CONFIG_TOUCHSCREEN_FUSION_F0710A=m
+CONFIG_TOUCHSCREEN_WM97XX=y
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9713 is not set
+CONFIG_TOUCHSCREEN_COLIBRI_VF50=y
+# CONFIG_SERIO is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_VF610_SEMA4=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
+CONFIG_HW_RANDOM=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_IMX=y
+CONFIG_SPI=y
+CONFIG_SPI_FSL_DSPI=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_POWER_RESET_GPIO_RESTART=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_IIO_HWMON=y
+CONFIG_WATCHDOG=y
+CONFIG_IMX2_WDT=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_ANATOP=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_RC_SUPPORT=y
+# CONFIG_RC_MAP is not set
+# CONFIG_RC_DECODERS is not set
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+# CONFIG_USB_GSPCA is not set
+CONFIG_DRM=y
+CONFIG_DRM_FSL_DCU=y
+CONFIG_DRM_PANEL_SIMPLE=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_GPIO=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_USB is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_IMX_SOC=y
+CONFIG_SND_SOC_FSL_SAI_WM9712=y
+CONFIG_SND_SOC_AC97_CODEC=y
+CONFIG_HIDRAW=y
+CONFIG_HID_MULTITOUCH=m
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_ACM=m
+CONFIG_USB_WDM=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CONSOLE=y
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MXS_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_FSL_USB2=y
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1307=y
+CONFIG_RTC_DRV_SNVS=y
+CONFIG_DMADEVICES=y
+CONFIG_FSL_EDMA=y
+# CONFIG_MX3_IPU is not set
+CONFIG_ARM_TIMER_SP804=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_VF610_CM4_RPROC=m
+CONFIG_IMX_RPMSG_PINGPONG=m
+CONFIG_IMX_RPMSG_TTY=m
+CONFIG_VF610_RPMSG=m
+CONFIG_SOC_BUS_VF610=y
+CONFIG_EXTCON_USB_GPIO=y
+CONFIG_IIO=y
+CONFIG_VF610_ADC=y
+CONFIG_VF610_DAC=y
+CONFIG_IIO_SYSFS_TRIGGER=y
+CONFIG_PWM=y
+CONFIG_PWM_FSL_FTM=y
+CONFIG_NVMEM=y
+CONFIG_NVMEM_VF610_OCOTP=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_OVERLAY_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS_FS=y
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_FS=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=10
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_FTRACE is not set
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_USER=y
+CONFIG_SECURITYFS=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC_T10DIF=y
+CONFIG_XZ_DEC=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
index 1d45320ee125d572b108d8e40bb6e0150fea8b9a..ece04a457486c5998d312bce4f3c69b97e0e7b64 100644 (file)
@@ -95,7 +95,7 @@ void __init init_IRQ(void)
                        outer_cache.write_sec = machine_desc->l2c_write_sec;
                ret = l2x0_of_init(machine_desc->l2c_aux_val,
                                   machine_desc->l2c_aux_mask);
-               if (ret)
+               if (ret && ret != -ENODEV)
                        pr_err("L2C: failed to init: %d\n", ret);
        }
 
index fb689d813b098464655d45a0cb52b1fb3d150858..1fffae129a4aa7a33564f942da26fa8f27f63f35 100644 (file)
@@ -90,8 +90,11 @@ ifeq ($(CONFIG_SUSPEND),y)
 AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
 obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
+AFLAGS_suspend-vf610.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SOC_VF610) += suspend-vf610.o
 endif
 obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
+obj-$(CONFIG_SOC_VF610) += pm-vf610.o
 
 obj-$(CONFIG_SOC_IMX50) += mach-imx50.o
 obj-$(CONFIG_SOC_IMX51) += mach-imx51.o
index e2d53839fceb632214a9dbe8deade9a9ed618e08..94d93a3b3b582c34401338fa2cf4a7d3f261fe96 100644 (file)
@@ -75,6 +75,13 @@ enum mxc_cpu_pwr_mode {
        STOP_POWER_OFF,         /* STOP + SRPG */
 };
 
+enum vf610_cpu_pwr_mode {
+       VF610_RUN,
+       VF610_LP_RUN,
+       VF610_STOP,
+       VF610_LP_STOP,
+};
+
 enum mx3_cpu_pwr_mode {
        MX3_RUN,
        MX3_WAIT,
@@ -119,11 +126,13 @@ void v7_cpu_resume(void);
 void imx53_suspend(void __iomem *ocram_vbase);
 extern const u32 imx53_suspend_sz;
 void imx6_suspend(void __iomem *ocram_vbase);
+void vf610_suspend(void __iomem *ocram_vbase);
 #else
 static inline void v7_cpu_resume(void) {}
 static inline void imx53_suspend(void __iomem *ocram_vbase) {}
 static const u32 imx53_suspend_sz;
 static inline void imx6_suspend(void __iomem *ocram_vbase) {}
+static inline void vf610_suspend(void __iomem *ocram_vbase) {}
 #endif
 
 void imx6_pm_ccm_init(const char *ccm_compat);
@@ -132,6 +141,7 @@ void imx6dl_pm_init(void);
 void imx6sl_pm_init(void);
 void imx6sx_pm_init(void);
 void imx6ul_pm_init(void);
+void vf610_pm_init(void);
 
 #ifdef CONFIG_PM
 void imx51_pm_init(void);
index b20f6c14eda527cd71be2a4445901ca96e6fcd4a..5ba668feea6555f8a0d3072540d66c6f6ddffaf5 100644 (file)
 #include <linux/irqchip.h>
 #include <asm/mach/arch.h>
 #include <asm/hardware/cache-l2x0.h>
+#include "common.h"
+
+static void __init vf610_init_machine(void)
+{
+       of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+       vf610_pm_init();
+}
 
 static const char * const vf610_dt_compat[] __initconst = {
        "fsl,vf500",
@@ -24,5 +31,6 @@ static const char * const vf610_dt_compat[] __initconst = {
 DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF5xx/VF6xx (Device Tree)")
        .l2c_aux_val    = 0,
        .l2c_aux_mask   = ~0,
+       .init_machine   = vf610_init_machine,
        .dt_compat      = vf610_dt_compat,
 MACHINE_END
diff --git a/arch/arm/mach-imx/pm-vf610.c b/arch/arm/mach-imx/pm-vf610.c
new file mode 100644 (file)
index 0000000..5a3df0c
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2014 Toradex AG
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifdef DEBUG
+#define pr_pmdebug(fmt, ...) pr_info("PM: VF610: " fmt "\n", ##__VA_ARGS__)
+#else
+#define pr_pmdebug(fmt, ...)
+#endif
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/genalloc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/clk.h>
+#include <asm/cacheflush.h>
+#include <asm/fncpy.h>
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+#include <asm/tlb.h>
+
+#include "common.h"
+
+#define DDRMC_PHY_OFFSET               0x400
+
+#define CCR                            0x0
+#define BM_CCR_FIRC_EN                 (0x1 << 16)
+#define BM_CCR_FXOSC_EN                        (0x1 << 12)
+
+#define CCSR                           0x8
+#define BM_CCSR_DDRC_CLK_SEL           (0x1 << 6)
+#define BM_CCSR_FAST_CLK_SEL           (0x1 << 5)
+#define BM_CCSR_SLOW_CLK_SEL           (0x1 << 4)
+#define BM_CCSR_SYS_CLK_SEL_MASK       (0x7 << 0)
+
+#define CACRR                          0xc
+
+#define CLPCR                          0x2c
+#define BM_CLPCR_ARM_CLK_DIS_ON_LPM    (0x1 << 5)
+#define BM_CLPCR_SBYOS                 (0x1 << 6)
+#define BM_CLPCR_DIS_REF_OSC           (0x1 << 7)
+#define BM_CLPCR_ANADIG_STOP_MODE      (0x1 << 8)
+#define BM_CLPCR_FXOSC_BYPSEN          (0x1 << 10)
+#define BM_CLPCR_FXOSC_PWRDWN          (0x1 << 11)
+#define BM_CLPCR_MASK_CORE0_WFI                (0x1 << 22)
+#define BM_CLPCR_MASK_CORE1_WFI                (0x1 << 23)
+#define BM_CLPCR_MASK_SCU_IDLE         (0x1 << 24)
+#define BM_CLPCR_MASK_L2CC_IDLE                (0x1 << 25)
+
+#define CGPR                           0x64
+#define BM_CGPR_INT_MEM_CLK_LPM                (0x1 << 17)
+
+#define GPC_PGCR                       0x0
+#define BM_PGCR_DS_STOP                        (0x1 << 7)
+#define BM_PGCR_DS_LPSTOP              (0x1 << 6)
+#define BM_PGCR_WB_STOP                        (0x1 << 4)
+#define BM_PGCR_HP_OFF                 (0x1 << 3)
+#define BM_PGCR_PG_PD1                 (0x1 << 0)
+
+#define GPC_LPMR                       0x40
+#define BM_LPMR_RUN                    0x0
+#define BM_LPMR_STOP                   0x2
+
+#define ANATOP_PLL1_CTRL               0x270
+#define ANATOP_PLL2_CTRL               0x30
+#define ANATOP_PLL2_PFD                        0x100
+#define BM_PLL_POWERDOWN       (0x1 << 12)
+#define BM_PLL_ENABLE          (0x1 << 13)
+#define BM_PLL_BYPASS          (0x1 << 16)
+#define BM_PLL_LOCK            (0x1 << 31)
+#define BM_PLL_PFD2_CLKGATE    (0x1 << 15)
+#define BM_PLL_USB_POWER       (0x1 << 12)
+#define BM_PLL_EN_USB_CLKS     (0x1 << 6)
+
+#define VF610_DDRMC_IO_NUM             94
+#define VF610_IOMUX_DDR_IO_NUM         48
+#define VF610_ANATOP_IO_NUM            2
+
+static struct vf610_cpu_pm_info *pm_info;
+static void __iomem *suspend_ocram_base;
+static void (*vf610_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
+static bool mem_suspend_available;
+
+#ifdef DEBUG
+static void __iomem *uart_membase;
+static unsigned long uart_clk;
+#endif
+
+static const u32 vf610_iomuxc_ddr_io_offset[] __initconst = {
+       0x220, 0x224, 0x228, 0x22c, 0x230, 0x234, 0x238, 0x23c,
+       0x240, 0x244, 0x248, 0x24c, 0x250, 0x254, 0x258, 0x25c,
+       0x260, 0x264, 0x268, 0x26c, 0x270, 0x274, 0x278, 0x27c,
+       0x280, 0x284, 0x288, 0x28c, 0x290, 0x294, 0x298, 0x29c,
+       0x2a0, 0x2a4, 0x2a8, 0x2ac, 0x2b0, 0x2b4, 0x2b8, 0x2bc,
+       0x2c0, 0x2c4, 0x2c8, 0x2cc, 0x2d0, 0x2d4, 0x2d8, 0x21c,
+};
+
+
+static const u32 vf610_ddrmc_io_offset[] __initconst = {
+       0x00, 0x08, 0x28, 0x2c, 0x30, 0x34, 0x38,
+       0x40, 0x44, 0x48, 0x50, 0x54, 0x58, 0x5c,
+       0x60, 0x64, 0x68, 0x70, 0x74, 0x78, 0x7c,
+       0x84, 0x88, 0x98, 0x9c, 0xa4, 0xc0,
+       0x108, 0x10c, 0x114, 0x118, 0x120, 0x124,
+       0x128, 0x12c, 0x130, 0x134, 0x138, 0x13c,
+       0x148, 0x15c, 0x160, 0x164, 0x16c, 0x180,
+       0x184, 0x188, 0x18c, 0x198, 0x1a4, 0x1a8,
+       0x1b8, 0x1d4, 0x1d8, 0x1e0, 0x1e4, 0x1e8,
+       0x1ec, 0x1f0, 0x1f8, 0x210, 0x224, 0x228,
+       0x22c, 0x230, 0x23c, 0x240, 0x244, 0x248,
+       0x24c, 0x250, 0x25c, 0x268, 0x26c, 0x278,
+       0x268
+};
+
+static const u32 vf610_ddrmc_phy_io_offset[] __initconst = {
+       0x00, 0x04, 0x08, 0x0c, 0x10,
+       0x40, 0x44, 0x48, 0x4c, 0x50,
+       0x80, 0x84, 0x88, 0x8c, 0x90,
+       0xc4, 0xc8, 0xd0
+};
+
+/*
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ *                              .
+ *                              .
+ *                              .
+ *                              ^
+ *                              ^
+ *                              ^
+ *                      vf610_suspend code
+ *              PM_INFO structure(vf610_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+struct vf610_pm_base {
+       phys_addr_t pbase;
+       void __iomem *vbase;
+};
+
+struct vf610_pm_socdata {
+       const char *anatop_compat;
+       const char *scsc_compat;
+       const char *wkpu_compat;
+       const char *ccm_compat;
+       const char *gpc_compat;
+       const char *src_compat;
+       const char *ddrmc_compat;
+       const char *iomuxc_compat;
+};
+
+static const struct vf610_pm_socdata vf610_pm_data __initconst = {
+       .anatop_compat = "fsl,vf610-anatop",
+       .scsc_compat = "fsl,vf610-scsc",
+       .wkpu_compat = "fsl,vf610-wkpu",
+       .ccm_compat = "fsl,vf610-ccm",
+       .gpc_compat = "fsl,vf610-gpc",
+       .src_compat = "fsl,vf610-src",
+       .ddrmc_compat = "fsl,vf610-ddrmc",
+       .iomuxc_compat = "fsl,vf610-iomuxc",
+};
+
+/*
+ * This structure is for passing necessary data for low level ocram
+ * suspend code(arch/arm/mach-imx/suspend-vf610.S), if this struct
+ * definition is changed, the offset definition in
+ * arch/arm/mach-imx/suspend-vf610.S must be also changed accordingly,
+ * otherwise, the suspend to ocram function will be broken!
+ */
+struct vf610_cpu_pm_info {
+       phys_addr_t pbase; /* The physical address of pm_info. */
+       phys_addr_t resume_addr; /* The physical resume address for asm code */
+       u32 cpu_type; /* Currently not used, leave it for alignment */
+       u32 pm_info_size; /* Size of pm_info. */
+       struct vf610_pm_base anatop_base;
+       struct vf610_pm_base scsc_base;
+       struct vf610_pm_base wkpu_base;
+       struct vf610_pm_base ccm_base;
+       struct vf610_pm_base gpc_base;
+       struct vf610_pm_base src_base;
+       struct vf610_pm_base ddrmc_base;
+       struct vf610_pm_base iomuxc_base;
+       struct vf610_pm_base l2_base;
+       u32 ccm_cacrr;
+       u32 ccm_ccsr;
+       u32 ddrmc_io_num; /* Number of MMDC IOs which need saved/restored. */
+       u32 ddrmc_io_val[VF610_DDRMC_IO_NUM][2]; /* To save offset and value */
+       u32 iomux_ddr_io_num;
+       u32 iomux_ddr_io_val[VF610_IOMUX_DDR_IO_NUM][2];
+} __aligned(8);
+
+#ifdef DEBUG
+static void vf610_uart_reinit(unsigned long int rate, unsigned long int baud)
+{
+       u8 tmp, c2;
+       u16 sbr, brfa;
+
+       /* UART_C2 */
+       c2 = __raw_readb(uart_membase + 0x3);
+       __raw_writeb(0, uart_membase + 0x3);
+
+       sbr = (u16) (rate / (baud * 16));
+       brfa = (rate / baud) - (sbr * 16);
+
+       tmp = ((sbr & 0x1f00) >> 8);
+       __raw_writeb(tmp, uart_membase + 0x0);
+       tmp = sbr & 0x00ff;
+       __raw_writeb(tmp, uart_membase + 0x1);
+
+       /* UART_C4 */
+       __raw_writeb(brfa & 0xf, uart_membase + 0xa);
+
+       __raw_writeb(c2, uart_membase + 0x3);
+}
+#else
+#define vf610_uart_reinit(rate, baud)
+#endif
+
+static void vf610_set(void __iomem *pll_base, u32 mask)
+{
+       writel(readl(pll_base) | mask, pll_base);
+}
+
+static void vf610_clr(void __iomem *pll_base, u32 mask)
+{
+       writel(readl(pll_base) & ~mask, pll_base);
+}
+
+int vf610_set_lpm(enum vf610_cpu_pwr_mode mode)
+{
+       void __iomem *ccm_base = pm_info->ccm_base.vbase;
+       void __iomem *gpc_base = pm_info->gpc_base.vbase;
+       void __iomem *anatop = pm_info->anatop_base.vbase;
+       u32 ccr = readl_relaxed(ccm_base + CCR);
+       u32 ccsr = readl_relaxed(ccm_base + CCSR);
+       u32 cacrr = readl_relaxed(ccm_base + CACRR);
+       u32 cclpcr = 0;
+       u32 gpc_pgcr = 0;
+
+       switch (mode) {
+       case VF610_LP_STOP:
+               /* Store clock settings */
+               pm_info->ccm_ccsr = ccsr;
+               pm_info->ccm_cacrr = cacrr;
+
+               ccr |= BM_CCR_FIRC_EN;
+               writel_relaxed(ccr, ccm_base + CCR);
+
+               cclpcr |= BM_CLPCR_ANADIG_STOP_MODE;
+               cclpcr |= BM_CLPCR_SBYOS;
+
+               cclpcr |= BM_CLPCR_MASK_SCU_IDLE;
+               cclpcr |= BM_CLPCR_MASK_L2CC_IDLE;
+               cclpcr |= BM_CLPCR_MASK_CORE1_WFI;
+               writel_relaxed(cclpcr, ccm_base + CLPCR);
+
+               gpc_pgcr |= BM_PGCR_DS_STOP;
+               gpc_pgcr |= BM_PGCR_DS_LPSTOP;
+               gpc_pgcr |= BM_PGCR_WB_STOP;
+               gpc_pgcr |= BM_PGCR_HP_OFF;
+               gpc_pgcr |= BM_PGCR_PG_PD1;
+               writel_relaxed(gpc_pgcr, gpc_base + GPC_PGCR);
+               break;
+       case VF610_STOP:
+               cclpcr &= ~BM_CLPCR_ANADIG_STOP_MODE;
+               cclpcr |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
+               cclpcr |= BM_CLPCR_SBYOS;
+               writel_relaxed(cclpcr, ccm_base + CLPCR);
+
+               gpc_pgcr |= BM_PGCR_DS_STOP;
+               gpc_pgcr |= BM_PGCR_HP_OFF;
+               writel_relaxed(gpc_pgcr, gpc_base + GPC_PGCR);
+
+               /* fall-through */
+       case VF610_LP_RUN:
+               /* Store clock settings */
+               pm_info->ccm_ccsr = ccsr;
+               pm_info->ccm_cacrr = cacrr;
+
+               ccr |= BM_CCR_FIRC_EN;
+               writel_relaxed(ccr, ccm_base + CCR);
+
+               /* Enable PLL2 for DDR clock */
+               vf610_set(anatop + ANATOP_PLL2_CTRL, BM_PLL_ENABLE);
+               vf610_clr(anatop + ANATOP_PLL2_CTRL, BM_PLL_POWERDOWN);
+               vf610_clr(anatop + ANATOP_PLL2_CTRL, BM_PLL_BYPASS);
+               while (!(readl(anatop + ANATOP_PLL2_CTRL) & BM_PLL_LOCK));
+               vf610_clr(anatop + ANATOP_PLL2_PFD, BM_PLL_PFD2_CLKGATE);
+
+               /* Switch internal OSC's */
+               ccsr &= ~BM_CCSR_FAST_CLK_SEL;
+               ccsr &= ~BM_CCSR_SLOW_CLK_SEL;
+
+               /* Select PLL2 as DDR clock */
+               ccsr &= ~BM_CCSR_DDRC_CLK_SEL;
+               writel_relaxed(ccsr, ccm_base + CCSR);
+
+               ccsr &= ~BM_CCSR_SYS_CLK_SEL_MASK;
+               writel_relaxed(ccsr, ccm_base + CCSR);
+               vf610_uart_reinit(4000000UL, 115200);
+
+               vf610_set(anatop + ANATOP_PLL1_CTRL, BM_PLL_BYPASS);
+               writel_relaxed(BM_LPMR_STOP, gpc_base + GPC_LPMR);
+               break;
+       case VF610_RUN:
+               writel_relaxed(BM_LPMR_RUN, gpc_base + GPC_LPMR);
+
+               vf610_clr(anatop + ANATOP_PLL1_CTRL, BM_PLL_BYPASS);
+               while(!(readl(anatop + ANATOP_PLL1_CTRL) & BM_PLL_LOCK));
+
+               /* Restore clock settings */
+               writel(pm_info->ccm_ccsr, ccm_base + CCSR);
+
+               vf610_uart_reinit(uart_clk, 115200);
+               pr_pmdebug("resuming, uart_reinit done");
+
+               /* Disable PLL2 if not needed */
+               if (pm_info->ccm_ccsr & BM_CCSR_DDRC_CLK_SEL)
+                       vf610_set(anatop + ANATOP_PLL2_CTRL, BM_PLL_POWERDOWN);
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vf610_suspend_finish(unsigned long val)
+{
+       if (!vf610_suspend_in_ocram_fn) {
+               cpu_do_idle();
+       } else {
+               /*
+                * call low level suspend function in ocram,
+                * as we need to float DDR IO.
+                */
+               local_flush_tlb_all();
+               flush_cache_all();
+               outer_flush_all();
+               vf610_suspend_in_ocram_fn(suspend_ocram_base);
+       }
+
+       return 0;
+}
+
+static int vf610_pm_enter(suspend_state_t state)
+{
+       switch (state) {
+       case PM_SUSPEND_STANDBY:
+               vf610_set_lpm(VF610_STOP);
+               flush_cache_all();
+
+               /* zzZZZzzz */
+               cpu_do_idle();
+
+               vf610_set_lpm(VF610_RUN);
+               break;
+       case PM_SUSPEND_MEM:
+               vf610_set_lpm(VF610_LP_STOP);
+
+               cpu_suspend(0, vf610_suspend_finish);
+               outer_resume();
+
+               vf610_set_lpm(VF610_RUN);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vf610_pm_valid(suspend_state_t state)
+{
+       return (state == PM_SUSPEND_STANDBY ||
+               (state == PM_SUSPEND_MEM && mem_suspend_available));
+}
+
+static const struct platform_suspend_ops vf610_pm_ops = {
+       .enter = vf610_pm_enter,
+       .valid = vf610_pm_valid,
+};
+
+static int __init imx_pm_get_base(struct vf610_pm_base *base,
+                               const char *compat)
+{
+       struct device_node *node;
+       struct resource res;
+       int ret = 0;
+
+       node = of_find_compatible_node(NULL, NULL, compat);
+       if (!node) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       ret = of_address_to_resource(node, 0, &res);
+       if (ret)
+               goto put_node;
+
+       base->pbase = res.start;
+       base->vbase = ioremap(res.start, resource_size(&res));
+
+       if (!base->vbase)
+               ret = -ENOMEM;
+
+put_node:
+       of_node_put(node);
+out:
+       return ret;
+}
+
+#ifdef DEBUG
+static int __init vf610_uart_init(void)
+{
+       struct device_node *dn;
+       const char *name;
+       struct clk *clk;
+       int ret;
+
+       name = of_get_property(of_chosen, "stdout-path", NULL);
+       if (name == NULL)
+               return -ENODEV;
+
+       dn = of_find_node_by_path(name);
+       if (!dn)
+               return -ENODEV;
+
+       clk = of_clk_get(dn, 0);
+
+       if (!clk) {
+               ret = PTR_ERR(clk);
+               goto put_node;
+       }
+
+       uart_clk = clk_get_rate(clk);
+
+       uart_membase = of_iomap(dn, 0);
+       if (!clk) {
+               ret = -ENOMEM;
+               goto put_node;
+       }
+
+       ret = 0;
+
+put_node:
+       of_node_put(dn);
+       return ret;
+}
+#endif
+
+static void vf610_power_off(void)
+{
+       void __iomem *gpc_base = pm_info->gpc_base.vbase;
+       u32 gpc_pgcr;
+
+       /*
+        * Power gate Power Domain 1
+        */
+       gpc_pgcr = readl_relaxed(gpc_base + GPC_PGCR);
+       gpc_pgcr |= BM_PGCR_PG_PD1;
+       writel_relaxed(gpc_pgcr, gpc_base + GPC_PGCR);
+
+       /* Set low power mode */
+       vf610_set_lpm(VF610_STOP);
+}
+
+static int __init vf610_suspend_mem_init(const struct vf610_pm_socdata *socdata)
+{
+       struct device_node *node;
+       struct platform_device *pdev;
+       bool has_cke_reset_pulls = false;
+       phys_addr_t ocram_pbase;
+       struct gen_pool *ocram_pool;
+       size_t ocram_size;
+       unsigned long ocram_base;
+       int ret = 0, reg = 0;
+       int i;
+
+       node = of_find_compatible_node(NULL, NULL, socdata->ddrmc_compat);
+       if (node) {
+               has_cke_reset_pulls =
+                       of_property_read_bool(node, "fsl,has-cke-reset-pulls");
+
+               of_node_put(node);
+       }
+
+       if (!has_cke_reset_pulls) {
+               pr_info("PM: No CKE/RESET pulls, disable Suspend-to-RAM\n");
+               return -ENODEV;
+       }
+
+       node = of_find_compatible_node(NULL, NULL, "mmio-sram");
+       if (!node) {
+               pr_warn("%s: failed to find ocram node!\n", __func__);
+               return -ENODEV;
+       }
+
+       pdev = of_find_device_by_node(node);
+       if (!pdev) {
+               pr_warn("%s: failed to find ocram device!\n", __func__);
+               ret = -ENODEV;
+               goto put_node;
+       }
+
+       ocram_pool = gen_pool_get(&pdev->dev, "stbyram1");
+       if (!ocram_pool) {
+               pr_warn("%s: ocram pool unavailable!\n", __func__);
+               ret = -ENODEV;
+               goto put_node;
+       }
+
+       ocram_size = gen_pool_size(ocram_pool);
+       ocram_base = gen_pool_alloc(ocram_pool, ocram_size);
+       if (!ocram_base) {
+               pr_warn("%s: unable to alloc ocram!\n", __func__);
+               ret = -ENOMEM;
+               goto put_node;
+       }
+
+       ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
+
+       suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, ocram_size, false);
+
+       pm_info = suspend_ocram_base;
+       pm_info->pbase = ocram_pbase;
+       pm_info->resume_addr = virt_to_phys(cpu_resume);
+
+       pm_info->ddrmc_io_num = VF610_DDRMC_IO_NUM;
+
+       ret = imx_pm_get_base(&pm_info->ddrmc_base, socdata->ddrmc_compat);
+       if (ret) {
+               pr_warn("%s: failed to get ddrmc base %d!\n", __func__, ret);
+               goto put_node;
+       }
+
+       ret = imx_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat);
+       if (ret) {
+               pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret);
+               goto iomuxc_map_failed;
+       }
+
+       /* Store DDRMC registers */
+       for (i = 0; i < ARRAY_SIZE(vf610_ddrmc_io_offset); i++, reg++) {
+               pm_info->ddrmc_io_val[reg][0] = vf610_ddrmc_io_offset[i];
+               pm_info->ddrmc_io_val[reg][1] =
+                       readl_relaxed(pm_info->ddrmc_base.vbase +
+                       vf610_ddrmc_io_offset[i]);
+       }
+
+       /* Store DDRMC PHY registers */
+       for (i = 0; i < ARRAY_SIZE(vf610_ddrmc_phy_io_offset); i++, reg++) {
+               pm_info->ddrmc_io_val[reg][0] = vf610_ddrmc_phy_io_offset[i] +
+                       DDRMC_PHY_OFFSET;
+               pm_info->ddrmc_io_val[reg][1] =
+                       readl_relaxed(pm_info->ddrmc_base.vbase +
+                       DDRMC_PHY_OFFSET + vf610_ddrmc_phy_io_offset[i]);
+       }
+
+       /* Store IOMUX DDR pad registers */
+       pm_info->iomux_ddr_io_num = VF610_IOMUX_DDR_IO_NUM;
+       for (i = 0; i < ARRAY_SIZE(vf610_iomuxc_ddr_io_offset); i++) {
+               pm_info->iomux_ddr_io_val[i][0] = vf610_iomuxc_ddr_io_offset[i];
+               pm_info->iomux_ddr_io_val[i][1] =
+                       readl_relaxed(pm_info->iomuxc_base.vbase +
+                       vf610_iomuxc_ddr_io_offset[i]);
+       }
+
+       vf610_suspend_in_ocram_fn = fncpy(
+               suspend_ocram_base + sizeof(*pm_info),
+               &vf610_suspend, ocram_size - sizeof(*pm_info));
+
+       pr_info("PM: CKE/RESET pulls available, enable Suspend-to-RAM\n");
+       goto put_node;
+
+iomuxc_map_failed:
+       iounmap(pm_info->ddrmc_base.vbase);
+put_node:
+       of_node_put(node);
+
+       return ret;
+}
+
+static int __init vf610_suspend_init(const struct vf610_pm_socdata *socdata)
+{
+       int ret;
+       struct device_node *soc_node;
+
+#ifdef DEBUG
+       ret = vf610_uart_init();
+       if (ret < 0)
+               return ret;
+#endif
+
+       soc_node = of_find_node_by_path("/soc");
+       if (soc_node == NULL)
+               return -ENODEV;
+
+       if (of_property_read_bool(soc_node, "fsl,use-lpm-poweroff"))
+               pm_power_off = vf610_power_off;
+
+       if (vf610_suspend_mem_init(socdata)) {
+               /*
+                * Suspend to memory for some reason not available, use DDR
+                * for standby mode
+                */
+               pm_info = kzalloc(sizeof(*pm_info), GFP_KERNEL);
+       } else {
+               mem_suspend_available = true;
+       }
+
+       pm_info->pm_info_size = sizeof(*pm_info);
+
+       ret = imx_pm_get_base(&pm_info->anatop_base, socdata->anatop_compat);
+       if (ret) {
+               pr_warn("%s: failed to get anatop base %d!\n", __func__, ret);
+               return ret;
+       }
+
+       ret = imx_pm_get_base(&pm_info->scsc_base, socdata->scsc_compat);
+       if (ret) {
+               pr_warn("%s: failed to get scsc base %d!\n", __func__, ret);
+               goto scsc_map_failed;
+       }
+
+       ret = imx_pm_get_base(&pm_info->ccm_base, socdata->ccm_compat);
+       if (ret) {
+               pr_warn("%s: failed to get ccm base %d!\n", __func__, ret);
+               goto ccm_map_failed;
+       }
+
+       ret = imx_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat);
+       if (ret) {
+               pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
+               goto gpc_map_failed;
+       }
+
+       ret = imx_pm_get_base(&pm_info->src_base, socdata->src_compat);
+       if (ret) {
+               pr_warn("%s: failed to get src base %d!\n", __func__, ret);
+               goto src_map_failed;
+       }
+
+       ret = imx_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
+       if (ret == -ENODEV)
+               ret = 0;
+       if (ret) {
+               pr_warn("%s: failed to get pl310-cache base %d!\n",
+                       __func__, ret);
+               goto pl310_cache_map_failed;
+       }
+
+       suspend_set_ops(&vf610_pm_ops);
+
+       return 0;
+
+pl310_cache_map_failed:
+       iounmap(pm_info->src_base.vbase);
+src_map_failed:
+       iounmap(pm_info->gpc_base.vbase);
+gpc_map_failed:
+       iounmap(pm_info->ccm_base.vbase);
+ccm_map_failed:
+       iounmap(pm_info->scsc_base.vbase);
+scsc_map_failed:
+       iounmap(pm_info->anatop_base.vbase);
+
+       if (mem_suspend_available) {
+               iounmap(pm_info->ddrmc_base.vbase);
+               iounmap(pm_info->iomuxc_base.vbase);
+       }
+
+       return ret;
+}
+
+void __init vf610_pm_init(void)
+{
+       int ret;
+
+       if (IS_ENABLED(CONFIG_SUSPEND)) {
+               ret = vf610_suspend_init(&vf610_pm_data);
+               if (ret)
+                       pr_warn("%s: No DDR LPM support with suspend %d!\n",
+                               __func__, ret);
+       }
+}
+
diff --git a/arch/arm/mach-imx/suspend-vf610.S b/arch/arm/mach-imx/suspend-vf610.S
new file mode 100644 (file)
index 0000000..595dd4e
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2015 Toradex AG
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*
+ * ==================== low level suspend ====================
+ *
+ * Better to follow below rules to use ARM registers:
+ * r0: pm_info structure address;
+ * r1 ~ r4: for saving pm_info members;
+ * r5 ~ r10: free registers;
+ * r11: io base address.
+ *
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ *                              .
+ *                              .
+ *                              .
+ *                              ^
+ *                              ^
+ *                              ^
+ *                      vf610_suspend code
+ *              PM_INFO structure(vf610_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+/*
+ * Below offsets are based on struct vf610_cpu_pm_info
+ * which defined in arch/arm/mach-imx/pm-vf610.c, this
+ * structure contains necessary pm info for low level
+ * suspend related code.
+ */
+#define PM_INFO_PBASE_OFFSET                   0x0
+#define PM_INFO_RESUME_ADDR_OFFSET             0x4
+#define PM_INFO_CPU_TYPE_OFFSET                        0x8
+#define PM_INFO_PM_INFO_SIZE_OFFSET            0xC
+#define PM_INFO_VF610_ANATOP_P_OFFSET          0x10
+#define PM_INFO_VF610_ANATOP_V_OFFSET          0x14
+#define PM_INFO_VF610_SCSC_P_OFFSET            0x18
+#define PM_INFO_VF610_SCSC_V_OFFSET            0x1C
+#define PM_INFO_VF610_WKPU_P_OFFSET            0x20
+#define PM_INFO_VF610_WKPU_V_OFFSET            0x24
+#define PM_INFO_VF610_CCM_P_OFFSET             0x28
+#define PM_INFO_VF610_CCM_V_OFFSET             0x2C
+#define PM_INFO_VF610_GPC_P_OFFSET             0x30
+#define PM_INFO_VF610_GPC_V_OFFSET             0x34
+#define PM_INFO_VF610_SRC_P_OFFSET             0x38
+#define PM_INFO_VF610_SRC_V_OFFSET             0x3C
+#define PM_INFO_VF610_DDRMC_P_OFFSET           0x40
+#define PM_INFO_VF610_DDRMC_V_OFFSET           0x44
+#define PM_INFO_VF610_IOMUXC_P_OFFSET          0x48
+#define PM_INFO_VF610_IOMUXC_V_OFFSET          0x4c
+#define PM_INFO_VF610_L2_P_OFFSET              0x50
+#define PM_INFO_VF610_L2_V_OFFSET              0x54
+#define PM_INFO_CCM_CACRR                      0x58
+#define PM_INFO_CCM_CCSR                       0x5c
+#define PM_INFO_DDRMC_IO_NUM_OFFSET            0x60
+#define PM_INFO_DDRMC_IO_VAL_OFFSET            0x64
+#define PM_INFO_IOMUXC_DDR_IO_NUM_OFFSET       (0x64 + 94 * 2 * 4)
+#define PM_INFO_IOMUXC_DDR_IO_VAL_OFFSET       (0x68 + 94 * 2 * 4)
+
+#define VF610_ANADIG_PLL2_CTRL                 0x30
+
+#define VF610_ANADIG_MISC0                     0x150
+#define VF610_ANADIG_MISC0_CLK_24M_IRC_XTAL_SEL        (0x1 < 13)
+
+#define VF610_ANADIG_PLL1_CTRL                 0x270
+
+#define VF610_ANADIG_POWERDOWN                 (1 << 12)
+#define VF610_ANADIG_ENABLE                    (1 << 13)
+#define VF610_ANADIG_BYPASS                    (1 << 16)
+#define VF610_ANADIG_LOCK                      (1 << 31)
+
+#define VF610_SCSC_SIRC                                0x0
+#define VF610_SCSC_SIRC_SIRC_EN                        (0x1 << 0)
+#define VF610_SCSC_SOSC                                0x4
+#define VF610_SCSC_SOSC_SOSC_EN                        (0x1 << 0)
+
+#define VF610_GPC_PGCR                         0x0
+#define VF610_GPC_LPMR                         0x40
+
+#define VF610_CCM_CCR                          0x00
+#define VF610_CCM_CCR_FXOSC_EN                 (0x1 << 12)
+
+#define VF610_CCM_CCSR                         0x08
+#define VF610_CCM_CCSR_DDRC_CLK_SEL            (0x1 << 6)
+#define VF610_CCM_CCSR_FAST_CLK_SEL            (0x1 << 5)
+
+#define VF610_CCM_CACRR                                0x0C
+
+#define VF610_CCM_CLPCR                                0x2C
+#define VF610_CCM_CLPCR_DIS_REF_OSC            (0x1 << 7)
+#define VF610_CCM_CLPCR_FXOSC_PWRDWN           (0x1 << 11)
+
+#define VF610_CCM_CCGR0                                0x40
+#define VF610_CCM_CCGR2                                0x48
+#define VF610_CCM_CCGR3                                0x4C
+#define VF610_CCM_CCGR4                                0x50
+#define VF610_CCM_CCGR6                                0x58
+
+#define VF610_SRC_GPR0                         0x20
+#define VF610_SRC_GPR1                         0x24
+#define VF610_SRC_MISC2                                0x54
+
+#define VF610_DDRMC_CR00                       0x0
+#define VF610_DDRMC_CR00_START                 (0x1 << 0)
+
+#define VF610_DDRMC_CR33                       0x84
+#define VF610_DDRMC_CR33_PWUP_SREF_EX          (0x1 << 0)
+
+#define VF610_DDRMC_CR34                       0x88
+
+#define VF610_DDRMC_CR35                       0x8C
+#define VF610_DDRMC_CR35_LP_CMD(cmd)           ((cmd) << 8)
+
+#define VF610_DDRMC_CR80                       0x140
+#define VF610_DDRMC_CR80_LP_COMPLETE           (0x1 << 9)
+#define VF610_DDRMC_CR80_INIT_COMPLETE         (0x1 << 8)
+#define VF610_DDRMC_CR81                       0x144
+
+       .align 3
+
+       /*
+        * Take DDR RAM out of Low-Power mode
+        */
+       .macro resume_ddrmc ddrmc_base
+
+       /* Clear low power complete flag... */
+       ldr     r6, =VF610_DDRMC_CR80_LP_COMPLETE
+       str     r6, [\ddrmc_base, #VF610_DDRMC_CR81]
+
+       ldr     r6, [\ddrmc_base, #VF610_DDRMC_CR35]
+       orr     r6, r6, #VF610_DDRMC_CR35_LP_CMD(0x9)
+       str     r6, [\ddrmc_base, #VF610_DDRMC_CR35]
+
+1:
+       ldr     r5, [\ddrmc_base, #VF610_DDRMC_CR80]
+       ands    r5, r5, #VF610_DDRMC_CR80_LP_COMPLETE
+       beq     1b
+
+       .endm
+
+       .macro enable_syspll pll_base
+
+       ldr     r5, [\pll_base]
+       orr     r5, r5, #VF610_ANADIG_ENABLE
+       bic     r5, r5, #VF610_ANADIG_POWERDOWN
+       bic     r5, r5, #VF610_ANADIG_BYPASS
+       str     r5, [\pll_base]
+
+1:
+       ldr     r5, [\pll_base]
+       tst     r5, #VF610_ANADIG_LOCK
+       beq     1b
+
+       .endm
+
+ENTRY(vf610_suspend)
+       ldr     r1, [r0, #PM_INFO_PBASE_OFFSET]
+       ldr     r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+       ldr     r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
+       ldr     r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
+
+       /*
+        * make sure TLB contain the addr we want,
+        * as we will access them after MMDC IO floated.
+        */
+
+       ldr     r11, [r0, #PM_INFO_VF610_DDRMC_V_OFFSET]
+       ldr     r6, [r11, #0x0]
+       ldr     r11, [r0, #PM_INFO_VF610_GPC_V_OFFSET]
+       ldr     r6, [r11, #0x0]
+       ldr     r11, [r0, #PM_INFO_VF610_SRC_V_OFFSET]
+       ldr     r6, [r11, #0x0]
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_V_OFFSET]
+       ldr     r6, [r11, #0x0]
+
+       ldr     r11, [r0, #PM_INFO_VF610_SRC_V_OFFSET]
+
+       /* Disable DDR RESET */
+       ldr     r6, [r11, #VF610_SRC_MISC2]
+       orr     r6, r6, #0x1
+       str     r6, [r11, #VF610_SRC_MISC2]
+
+       /* Set ENTRY/ARGUMENT register */
+       ldr     r6, =vf610_suspend
+       ldr     r7, =resume
+       sub     r7, r7, r6
+       add     r8, r1, r4
+       add     r9, r8, r7
+       str     r9, [r11, #VF610_SRC_GPR0]
+       str     r1, [r11, #VF610_SRC_GPR1]
+
+       /* Put memory in self refresh... */
+       ldr     r11, [r0, #PM_INFO_VF610_DDRMC_V_OFFSET]
+
+       ldr     r6, =VF610_DDRMC_CR80_LP_COMPLETE
+       str     r6, [r11, #VF610_DDRMC_CR81]
+
+       ldr     r6, [r11, #VF610_DDRMC_CR35]
+       orr     r6, r6, #VF610_DDRMC_CR35_LP_CMD(0xA)
+       str     r6, [r11, #VF610_DDRMC_CR35]
+
+ddrmc_cmd_complete:
+       /* A Unfixed module seems to hang at this read.... */
+       ldr     r5, [r11, #VF610_DDRMC_CR80]
+       ands    r5, r5, #VF610_DDRMC_CR80_LP_COMPLETE
+       beq     ddrmc_cmd_complete
+
+       /* switch to internal FIRC */
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_V_OFFSET]
+       ldr     r5, [r11, #VF610_CCM_CCSR]
+       bic     r5, r5, #0x30 /* FAST_/SLOW_CLK_SEL */
+       str     r5, [r11, #VF610_CCM_CCSR]
+       bic     r5, r5, #0x07 /* SYS_CLK_SEL */
+       str     r5, [r11, #VF610_CCM_CCSR]
+
+       /* LP-Mode: STOP */
+       ldr     r11, [r0, #PM_INFO_VF610_GPC_V_OFFSET]
+       ldr     r6, =0x02
+       str     r6, [r11, #VF610_GPC_LPMR]
+
+       /* Zzz, enter stop mode */
+       wfi
+       nop
+       nop
+       nop
+       nop
+
+       /* If we get here, there is already an interrupt pending. Restore... */
+       ldr     r6, =0x00
+       str     r6, [r11, #VF610_GPC_LPMR]
+
+       /* Get previous CCSR/CACRR settings */
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_V_OFFSET]
+       ldr     r5, [r0, #PM_INFO_CCM_CCSR]
+       str     r5, [r11, #VF610_CCM_CCSR]
+
+       ldr     r5, [r0, #PM_INFO_CCM_CACRR]
+       str     r5, [r11, #VF610_CCM_CACRR]
+
+       ldr     r11, [r0, #PM_INFO_VF610_DDRMC_V_OFFSET]
+       resume_ddrmc r11
+
+       ret     lr
+
+/* Resume path if CPU uses the SRC_GPR0 (PERSISTENT_ENTRY0) */
+resume:
+       /* invalidate L1 I-cache first */
+       mov     r6, #0x0
+       mcr     p15, 0, r6, c7, c5, 0
+       mcr     p15, 0, r6, c7, c5, 6
+
+       /* enable the Icache and branch prediction */
+       mov     r6, #0x1800
+       mcr     p15, 0, r6, c1, c0, 0
+       isb
+
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_P_OFFSET]
+
+       ldr     r5, [r11, #VF610_CCM_CCSR]
+       orr     r5, r5, #(1 << 13)
+       str     r5, [r11, #VF610_CCM_CCSR]
+
+       /* enable UART0 */
+       ldr     r5, [r11, #VF610_CCM_CCGR0]
+       orr     r5, r5, #0xC000
+       str     r5, [r11, #VF610_CCM_CCGR0]
+
+       /* enable IOMUX, PORT A-E */
+       ldr     r5, [r11, #VF610_CCM_CCGR2]
+       ldr     r6, =0xFFF0000
+       orr     r5, r5, r6
+       str     r5, [r11, #VF610_CCM_CCGR2]
+
+       /* enable ANADIG and SCSM */
+       ldr     r5, [r11, #VF610_CCM_CCGR3]
+       orr     r5, r5, #0x33
+       str     r5, [r11, #VF610_CCM_CCGR3]
+
+       /* enable GPC, CCM and WKUP */
+       ldr     r5, [r11, #VF610_CCM_CCGR4]
+       orr     r5, r5, #0x3f00000
+       str     r5, [r11, #VF610_CCM_CCGR4]
+
+       /* enable mmdc */
+       ldr     r5, [r11, #VF610_CCM_CCGR6]
+       orr     r5, r5, #0x30000000
+       str     r5, [r11, #VF610_CCM_CCGR6]
+
+       /* Mux UART0 */
+       ldr     r5,=0x1021a2
+       ldr     r6,=0x40048080
+       str     r5, [r6, #0x0]
+       ldr     r5,=0x1021a1
+       ldr     r6,=0x40048084
+       str     r5, [r6, #0x0]
+
+       /* Set IOMUX for DDR pads */
+       ldr     r11, [r0, #PM_INFO_VF610_IOMUXC_P_OFFSET]
+
+       ldr     r6, [r0, #PM_INFO_IOMUXC_DDR_IO_NUM_OFFSET]
+       ldr     r7, =PM_INFO_IOMUXC_DDR_IO_VAL_OFFSET
+       add     r7, r7, r0
+
+loop_iomuxc_ddr_restore:
+       ldr     r8, [r7], #0x4
+       ldr     r9, [r7], #0x4
+       str     r9, [r11, r8]
+       subs    r6, r6, #0x1
+       bne     loop_iomuxc_ddr_restore
+
+
+       /* Enable slow oscilators */
+       ldr     r11, [r0, #PM_INFO_VF610_SCSC_P_OFFSET]
+
+       ldr     r5, [r11, #VF610_SCSC_SOSC]
+       orr     r5, r5, #VF610_SCSC_SOSC_SOSC_EN
+       str     r5, [r11, #VF610_SCSC_SOSC]
+
+       ldr     r5, [r11, #VF610_SCSC_SIRC]
+       orr     r5, r5, #VF610_SCSC_SIRC_SIRC_EN
+       str     r5, [r11, #VF610_SCSC_SIRC]
+
+       /* Enable fast osciallator */
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_P_OFFSET]
+
+       ldr     r5, [r11, #VF610_CCM_CLPCR]
+       bic     r5, r5, #VF610_CCM_CLPCR_DIS_REF_OSC
+       bic     r5, r5, #VF610_CCM_CLPCR_FXOSC_PWRDWN
+       str     r5, [r11, #VF610_CCM_CLPCR]
+
+       ldr     r5, [r11, #VF610_CCM_CCR]
+       orr     r5, r5, #VF610_CCM_CCR_FXOSC_EN
+       str     r5, [r11, #VF610_CCM_CCR]
+
+       ldr     r5, [r11, #VF610_CCM_CCSR]
+       orr     r5, r5, #VF610_CCM_CCSR_FAST_CLK_SEL
+       str     r5, [r11, #VF610_CCM_CCSR]
+
+       ldr     r11, [r0, #PM_INFO_VF610_ANATOP_P_OFFSET]
+
+       /* Select external FXOSC */
+       ldr     r5, [r11, #VF610_ANADIG_MISC0]
+       bic     r5, r5, #VF610_ANADIG_MISC0_CLK_24M_IRC_XTAL_SEL
+       str     r5, [r11, #VF610_ANADIG_MISC0]
+
+       /* pll1 enable */
+       add     r6, r11, #VF610_ANADIG_PLL1_CTRL
+       enable_syspll r6
+
+       /* enable pll2 only if required for DDR */
+       ldr     r5, [r0, #PM_INFO_CCM_CCSR]
+       tst     r5, #VF610_CCM_CCSR_DDRC_CLK_SEL
+       bne     switch_sysclk
+
+       /* pll2 enable */
+       add     r6, r11, #VF610_ANADIG_PLL2_CTRL
+       enable_syspll r6
+
+switch_sysclk:
+
+       /* Enable PFD and switch to fast clock */
+       ldr     r11, [r0, #PM_INFO_VF610_CCM_P_OFFSET]
+
+       /* Get previous CCSR/CACRR settings */
+       ldr     r5, [r0, #PM_INFO_CCM_CCSR]
+       str     r5, [r11, #VF610_CCM_CCSR]
+
+       ldr     r5, [r0, #PM_INFO_CCM_CACRR]
+       str     r5, [r11, #VF610_CCM_CACRR]
+
+       /* Restore memory configuration */
+       ldr     r11, [r0, #PM_INFO_VF610_DDRMC_P_OFFSET]
+
+       ldr     r6, [r0, #PM_INFO_DDRMC_IO_NUM_OFFSET]
+       ldr     r7, =PM_INFO_DDRMC_IO_VAL_OFFSET
+       add     r7, r7, r0
+
+       /* Clear start bit of first memory register, do not start yet... */
+       ldr     r8, [r7], #0x4
+       ldr     r9, [r7], #0x4
+       bic     r9, r9, #VF610_DDRMC_CR00_START
+       str     r9, [r11, r8]
+       subs    r6, r6, #0x1
+
+loop_ddrmc_restore:
+       ldr     r8, [r7], #0x4
+       ldr     r9, [r7], #0x4
+       str     r9, [r11, r8]
+       subs    r6, r6, #0x1
+       bne     loop_ddrmc_restore
+
+       /* Set PWUP_SREF_EX to avoid a full memory initialization */
+       ldr     r6, [r11, #VF610_DDRMC_CR33]
+       orr     r6, r6, #VF610_DDRMC_CR33_PWUP_SREF_EX
+       str     r6, [r11, #VF610_DDRMC_CR33]
+
+       /* Start initialization */
+       ldr     r6, =VF610_DDRMC_CR80_INIT_COMPLETE
+       str     r6, [r11, #VF610_DDRMC_CR81]
+
+       ldr     r6, [r11, #VF610_DDRMC_CR00]
+       orr     r6, r6, #VF610_DDRMC_CR00_START
+       str     r6, [r11, #VF610_DDRMC_CR00]
+
+ddrmc_initializing:
+       ldr     r5, [r11, #VF610_DDRMC_CR80]
+       ands    r5, r5, #VF610_DDRMC_CR80_INIT_COMPLETE
+       beq     ddrmc_initializing
+
+       resume_ddrmc r11
+
+       /* LP-Mode: RUN */
+       ldr     r11, [r0, #PM_INFO_VF610_GPC_P_OFFSET]
+       ldr     r5, =0x0
+       str     r5, [r11, #VF610_GPC_LPMR]
+
+       /* Enable SNVS */
+       ldr     r3, [r0, #PM_INFO_VF610_CCM_P_OFFSET]
+       ldr     r4, [r3, #VF610_CCM_CCGR6]
+       orr     r4, r4, #0x0000C000
+       str     r4, [r3, #VF610_CCM_CCGR6]
+
+       /* Enable SNVS access (RTC) */
+       ldr     r11, =0x400a7000
+       ldr     r4, =0x80000100
+       str     r4, [r11, #0x4]
+
+       /* get physical resume address from pm_info. */
+       ldr     lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+
+       ret     lr
+ENDPROC(vf610_suspend)
+
index 3143db57ce4456a8146b475e7a3b7156e3b3fc9f..b0cf97d01fbbc056b43e48add622a33257bd9a5b 100644 (file)
@@ -24,6 +24,12 @@ config DEVKMEM
          kind of kernel debugging operations.
          When in doubt, say "N".
 
+config VF610_SEMA4
+       bool "VF610 SEMA4 driver"
+       depends on SOC_VF610
+       help
+         Support for VF610 SEMA4 driver, most people should say N here.
+
 config SGI_SNSC
        bool "SGI Altix system controller communication support"
        depends on (IA64_SGI_SN2 || IA64_GENERIC)
index d8a7579300d2df1112446c3a69a29713fd2af163..fef56bfad9dbfe2fdfc66b947be21245f4551c3f 100644 (file)
@@ -59,4 +59,5 @@ obj-$(CONFIG_JS_RTC)          += js-rtc.o
 js-rtc-y = rtc.o
 
 obj-$(CONFIG_TILE_SROM)                += tile-srom.o
+obj-$(CONFIG_VF610_SEMA4)      += vf610_sema4.o
 obj-$(CONFIG_XILLYBUS)         += xillybus/
diff --git a/drivers/char/vf610_sema4.c b/drivers/char/vf610_sema4.c
new file mode 100644 (file)
index 0000000..dff78bd
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2016 Toradex AG.
+ *
+ * Taken from the 4.1.15 kernel release for i.MX7 by Freescale.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vf610_sema4.h>
+
+static struct vf610_sema4_mutex_device *vf610_sema4;
+
+struct vf610_sema4_mutex *
+vf610_sema4_mutex_create(u32 dev_num, u32 mutex_num)
+{
+       struct vf610_sema4_mutex *mutex_ptr = NULL;
+
+       if (!vf610_sema4)
+               return ERR_PTR(-ENODEV);
+
+       if (mutex_num >= SEMA4_NUM_GATES || dev_num >= SEMA4_NUM_DEVICES)
+               return ERR_PTR(-EINVAL);
+
+       if (vf610_sema4->cpine_val & (1 < mutex_num)) {
+               pr_err("Error: requiring a allocated sema4.\n");
+               pr_err("mutex_num %d cpine_val 0x%08x.\n",
+                               mutex_num, vf610_sema4->cpine_val);
+       }
+
+       mutex_ptr = kzalloc(sizeof(*mutex_ptr), GFP_KERNEL);
+       if (!mutex_ptr)
+               return ERR_PTR(-ENOMEM);
+
+       vf610_sema4->mutex_ptr[mutex_num] = mutex_ptr;
+       vf610_sema4->alloced |= 1 < mutex_num;
+       vf610_sema4->cpine_val |= idx_sema4[mutex_num];
+       writew(vf610_sema4->cpine_val, vf610_sema4->ioaddr + SEMA4_CP0INE);
+
+       mutex_ptr->valid = CORE_MUTEX_VALID;
+       mutex_ptr->gate_num = mutex_num;
+       init_waitqueue_head(&mutex_ptr->wait_q);
+
+       return mutex_ptr;
+}
+EXPORT_SYMBOL(vf610_sema4_mutex_create);
+
+int vf610_sema4_mutex_destroy(struct vf610_sema4_mutex *mutex_ptr)
+{
+       u32 mutex_num;
+
+       if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
+               return -EINVAL;
+
+       mutex_num = mutex_ptr->gate_num;
+       if ((vf610_sema4->cpine_val & idx_sema4[mutex_num]) == 0) {
+               pr_err("Error: trying to destroy a un-allocated sema4.\n");
+               pr_err("mutex_num %d cpine_val 0x%08x.\n",
+                               mutex_num, vf610_sema4->cpine_val);
+       }
+       vf610_sema4->mutex_ptr[mutex_num] = NULL;
+       vf610_sema4->alloced &= ~(1 << mutex_num);
+       vf610_sema4->cpine_val &= ~(idx_sema4[mutex_num]);
+       writew(vf610_sema4->cpine_val, vf610_sema4->ioaddr + SEMA4_CP0INE);
+
+       kfree(mutex_ptr);
+
+       return 0;
+}
+EXPORT_SYMBOL(vf610_sema4_mutex_destroy);
+
+int _vf610_sema4_mutex_lock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       int ret = 0, i = 0;
+
+       if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
+               return -EINVAL;
+
+       i = mutex_ptr->gate_num;
+       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+       mutex_ptr->gate_val &= SEMA4_GATE_MASK;
+       /* Check to see if this core already own it */
+       if (mutex_ptr->gate_val == SEMA4_A5_LOCK) {
+               /* return -EBUSY, invoker should be in sleep, and re-lock ag */
+               pr_err("%s -> %s %d already locked, wait! num %d val %d.\n",
+                               __FILE__, __func__, __LINE__,
+                               i, mutex_ptr->gate_val);
+               ret = -EBUSY;
+               goto out;
+       } else {
+               /* try to lock the mutex */
+               mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+               mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
+               mutex_ptr->gate_val |= SEMA4_A5_LOCK;
+               writeb(mutex_ptr->gate_val, vf610_sema4->ioaddr + i);
+               mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+               mutex_ptr->gate_val &= SEMA4_GATE_MASK;
+               /* double check the mutex is locked, otherwise, return -EBUSY */
+               if (mutex_ptr->gate_val != SEMA4_A5_LOCK) {
+                       pr_debug("wait-locked num %d val %d.\n",
+                                       i, mutex_ptr->gate_val);
+                       ret = -EBUSY;
+               }
+       }
+out:
+       return ret;
+}
+
+int vf610_sema4_mutex_trylock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       int ret = 0;
+
+       ret = _vf610_sema4_mutex_lock(mutex_ptr);
+       if (ret == 0)
+               return SEMA4_A5_LOCK;
+       else
+               return ret;
+}
+EXPORT_SYMBOL(vf610_sema4_mutex_trylock);
+
+int vf610_sema4_mutex_lock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vf610_sema4->lock, flags);
+       ret = _vf610_sema4_mutex_lock(mutex_ptr);
+       spin_unlock_irqrestore(&vf610_sema4->lock, flags);
+       while (-EBUSY == ret) {
+               spin_lock_irqsave(&vf610_sema4->lock, flags);
+               ret = _vf610_sema4_mutex_lock(mutex_ptr);
+               spin_unlock_irqrestore(&vf610_sema4->lock, flags);
+               if (ret == 0)
+                       break;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(vf610_sema4_mutex_lock);
+
+int vf610_sema4_mutex_unlock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       int ret = 0, i = 0;
+
+       if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
+               return -EINVAL;
+
+       i = mutex_ptr->gate_num;
+       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+       mutex_ptr->gate_val &= SEMA4_GATE_MASK;
+       /* make sure it is locked by this core */
+       if (mutex_ptr->gate_val != SEMA4_A5_LOCK) {
+               pr_err("%d Trying to unlock an unlock mutex.\n", __LINE__);
+               ret = -EINVAL;
+               goto out;
+       }
+       /* unlock it */
+       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+       mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
+       writeb(mutex_ptr->gate_val | SEMA4_UNLOCK, vf610_sema4->ioaddr + i);
+       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+       mutex_ptr->gate_val &= SEMA4_GATE_MASK;
+       /* make sure it is locked by this core */
+       if (mutex_ptr->gate_val == SEMA4_A5_LOCK)
+               pr_err("%d ERROR, failed to unlock the mutex.\n", __LINE__);
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL(vf610_sema4_mutex_unlock);
+
+static irqreturn_t vf610_sema4_isr(int irq, void *dev_id)
+{
+       int i;
+       struct vf610_sema4_mutex *mutex_ptr;
+       unsigned int mask;
+       struct vf610_sema4_mutex_device *vf610_sema4 = dev_id;
+
+       vf610_sema4->cpntf_val = readw(vf610_sema4->ioaddr + SEMA4_CP0NTF);
+       for (i = 0; i < SEMA4_NUM_GATES; i++) {
+               mask = idx_sema4[i];
+               if ((vf610_sema4->cpntf_val) & mask) {
+                       mutex_ptr = vf610_sema4->mutex_ptr[i];
+                       /*
+                        * An interrupt is pending on this mutex, the only way
+                        * to clear it is to lock it (either by this core or
+                        * another).
+                        */
+                       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+                       mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
+                       mutex_ptr->gate_val |= SEMA4_A5_LOCK;
+                       writeb(mutex_ptr->gate_val, vf610_sema4->ioaddr + i);
+                       mutex_ptr->gate_val = readb(vf610_sema4->ioaddr + i);
+                       mutex_ptr->gate_val &= SEMA4_GATE_MASK;
+                       if (mutex_ptr->gate_val == SEMA4_A5_LOCK) {
+                               /*
+                                * wake up the wait queue, whatever there
+                                * are wait task or not.
+                                * NOTE: check gate is locted or not in
+                                * sema4_lock func by wait task.
+                                */
+                               mutex_ptr->gate_val =
+                                       readb(vf610_sema4->ioaddr + i);
+                               mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
+                               mutex_ptr->gate_val |= SEMA4_UNLOCK;
+
+                               writeb(mutex_ptr->gate_val,
+                                               vf610_sema4->ioaddr + i);
+                               wake_up(&mutex_ptr->wait_q);
+                       } else {
+                               pr_debug("can't lock gate%d %s retry!\n", i,
+                                               mutex_ptr->gate_val ?
+                                               "locked by m4" : "");
+                       }
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static const struct of_device_id vf610_sema4_dt_ids[] = {
+       { .compatible = "fsl,vf610-sema4", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_sema4_dt_ids);
+
+static int vf610_sema4_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+
+       vf610_sema4 = devm_kzalloc(&pdev->dev, sizeof(*vf610_sema4), GFP_KERNEL);
+       if (!vf610_sema4)
+               return -ENOMEM;
+
+       vf610_sema4->dev = &pdev->dev;
+       vf610_sema4->cpine_val = 0;
+       spin_lock_init(&vf610_sema4->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (IS_ERR(res)) {
+               dev_err(&pdev->dev, "unable to get vf610 sema4 resource 0\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       vf610_sema4->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(vf610_sema4->ioaddr)) {
+               ret = PTR_ERR(vf610_sema4->ioaddr);
+               goto err;
+       }
+
+       vf610_sema4->irq = platform_get_irq(pdev, 0);
+       if (!vf610_sema4->irq) {
+               dev_err(&pdev->dev, "failed to get irq\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       ret = devm_request_irq(&pdev->dev, vf610_sema4->irq, vf610_sema4_isr,
+                               IRQF_SHARED, "vf610-sema4", vf610_sema4);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to request vf610 sema4 irq\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, vf610_sema4);
+
+err:
+       return ret;
+}
+
+static int vf610_sema4_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static struct platform_driver vf610_sema4_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "vf610-sema4",
+                  .of_match_table = vf610_sema4_dt_ids,
+                  },
+       .probe = vf610_sema4_probe,
+       .remove = vf610_sema4_remove,
+};
+
+module_platform_driver(vf610_sema4_driver);
+
+MODULE_DESCRIPTION("VF610 SEMA4 driver");
+MODULE_LICENSE("GPL");
index 8935bff99fe7acc083fb631c5ff8b7e97e7544c6..db44a198a0d9999f3e998033a3f8a517035c5e74 100644 (file)
@@ -31,6 +31,7 @@ struct clk_gate2 {
        struct clk_hw hw;
        void __iomem    *reg;
        u8              bit_idx;
+       u8              cgr_val;
        u8              flags;
        spinlock_t      *lock;
        unsigned int    *share_count;
@@ -50,7 +51,8 @@ static int clk_gate2_enable(struct clk_hw *hw)
                goto out;
 
        reg = readl(gate->reg);
-       reg |= 3 << gate->bit_idx;
+       reg &= ~(3 << gate->bit_idx);
+       reg |= gate->cgr_val << gate->bit_idx;
        writel(reg, gate->reg);
 
 out:
@@ -125,7 +127,7 @@ static struct clk_ops clk_gate2_ops = {
 
 struct clk *clk_register_gate2(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
-               void __iomem *reg, u8 bit_idx,
+               void __iomem *reg, u8 bit_idx, u8 cgr_val,
                u8 clk_gate2_flags, spinlock_t *lock,
                unsigned int *share_count)
 {
@@ -140,6 +142,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name,
        /* struct clk_gate2 assignments */
        gate->reg = reg;
        gate->bit_idx = bit_idx;
+       gate->cgr_val = cgr_val;
        gate->flags = clk_gate2_flags;
        gate->lock = lock;
        gate->share_count = share_count;
index 0a94d9661d9123551b5b84cd5764cb9b91a6c176..8323efc46997bd7c1ee5dc408ad27ed862be2bcb 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/of_address.h>
 #include <linux/clk.h>
+#include <linux/syscore_ops.h>
 #include <dt-bindings/clock/vf610-clock.h>
 
 #include "clk.h"
@@ -40,6 +41,7 @@
 #define CCM_CCGR9              (ccm_base + 0x64)
 #define CCM_CCGR10             (ccm_base + 0x68)
 #define CCM_CCGR11             (ccm_base + 0x6c)
+#define CCM_CCGRx(x)           (CCM_CCGR0 + (x) * 4)
 #define CCM_CMEOR0             (ccm_base + 0x70)
 #define CCM_CMEOR1             (ccm_base + 0x74)
 #define CCM_CMEOR2             (ccm_base + 0x78)
@@ -115,10 +117,25 @@ static struct clk_div_table pll4_audio_div_table[] = {
 static struct clk *clk[VF610_CLK_END];
 static struct clk_onecell_data clk_data;
 
+static u32 anadig_pll3_ctrl;
+static u32 anadig_pll4_ctrl;
+static u32 anadig_pll5_ctrl;
+static u32 anadig_pll6_ctrl;
+static u32 anadig_pll7_ctrl;
+static u32 ccpgr0;
+static u32 cscmr1;
+static u32 cscmr2;
+static u32 cscdr1;
+static u32 cscdr2;
+static u32 cscdr3;
+static u32 ccgr[12];
+
 static unsigned int const clks_init_on[] __initconst = {
        VF610_CLK_SYS_BUS,
        VF610_CLK_DDR_SEL,
        VF610_CLK_DAP,
+       VF610_CLK_DDRMC,
+       VF610_CLK_WKPU,
 };
 
 static struct clk * __init vf610_get_fixed_clock(
@@ -132,6 +149,57 @@ static struct clk * __init vf610_get_fixed_clock(
        return clk;
 };
 
+static int vf610_clk_suspend(void)
+{
+       int i;
+
+       anadig_pll3_ctrl = readl_relaxed(PLL3_CTRL);
+       anadig_pll4_ctrl = readl_relaxed(PLL4_CTRL);
+       anadig_pll5_ctrl = readl_relaxed(PLL5_CTRL);
+       anadig_pll6_ctrl = readl_relaxed(PLL6_CTRL);
+       anadig_pll7_ctrl = readl_relaxed(PLL7_CTRL);
+
+       ccpgr0 = readl_relaxed(CCM_CCPGR0);
+       cscmr1 = readl_relaxed(CCM_CSCMR1);
+       cscmr2 = readl_relaxed(CCM_CSCMR2);
+
+       cscdr1 = readl_relaxed(CCM_CSCDR1);
+       cscdr2 = readl_relaxed(CCM_CSCDR2);
+       cscdr3 = readl_relaxed(CCM_CSCDR3);
+
+       for (i = 0; i < 12; i++)
+               ccgr[i] = readl_relaxed(CCM_CCGRx(i));
+
+       return 0;
+}
+
+static void vf610_clk_resume(void)
+{
+       int i;
+
+       writel_relaxed(anadig_pll3_ctrl, PLL3_CTRL);
+       writel_relaxed(anadig_pll4_ctrl, PLL4_CTRL);
+       writel_relaxed(anadig_pll5_ctrl, PLL5_CTRL);
+       writel_relaxed(anadig_pll6_ctrl, PLL6_CTRL);
+       writel_relaxed(anadig_pll7_ctrl, PLL7_CTRL);
+
+       writel_relaxed(ccpgr0, CCM_CCPGR0);
+       writel_relaxed(cscmr1, CCM_CSCMR1);
+       writel_relaxed(cscmr2, CCM_CSCMR2);
+
+       writel_relaxed(cscdr1, CCM_CSCDR1);
+       writel_relaxed(cscdr2, CCM_CSCDR2);
+       writel_relaxed(cscdr3, CCM_CSCDR3);
+
+       for (i = 0; i < 12; i++)
+               writel_relaxed(ccgr[i], CCM_CCGRx(i));
+}
+
+static struct syscore_ops vf610_clk_syscore_ops = {
+       .suspend = vf610_clk_suspend,
+       .resume = vf610_clk_resume,
+};
+
 static void __init vf610_clocks_init(struct device_node *ccm_node)
 {
        struct device_node *np;
@@ -233,6 +301,9 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock);
        clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1);
 
+       clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2);
+       clk[VF610_CLK_WKPU] = imx_clk_gate2_cgr("wkpu", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(10), 0x2);
+
        clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6);
        clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6);
 
@@ -261,15 +332,16 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23);
        clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0));
        clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1));
+       clk[VF610_CLK_ESW] = imx_clk_gate2("esw", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(8));
 
        clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7));
 
-       clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7));
-       clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8));
-       clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9));
-       clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10));
-       clk[VF610_CLK_UART4] = imx_clk_gate2("uart4", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(9));
-       clk[VF610_CLK_UART5] = imx_clk_gate2("uart5", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(10));
+       clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7), 0x2);
+       clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8), 0x2);
+       clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9), 0x2);
+       clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10), 0x2);
+       clk[VF610_CLK_UART4] = imx_clk_gate2_cgr("uart4", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(9), 0x2);
+       clk[VF610_CLK_UART5] = imx_clk_gate2_cgr("uart5", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(10), 0x2);
 
        clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6));
        clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7));
@@ -321,11 +393,14 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2);
        clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19);
        clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3);
-       clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "dcu0_div", CCM_CCGR3, CCM_CCGRx_CGn(8));
+       clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "ipg_bus", CCM_CCGR3, CCM_CCGRx_CGn(8));
        clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2);
        clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23);
        clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3);
-       clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "dcu1_div", CCM_CCGR9, CCM_CCGRx_CGn(8));
+       clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(8));
+
+       clk[VF610_CLK_TCON0] = imx_clk_gate2("tcon0", "platform_bus", CCM_CCGR1, CCM_CCGRx_CGn(13));
+       clk[VF610_CLK_TCON1] = imx_clk_gate2("tcon1", "platform_bus", CCM_CCGR7, CCM_CCGRx_CGn(13));
 
        clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4);
        clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30);
@@ -401,17 +476,21 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk_set_rate(clk[VF610_CLK_QSPI1_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X4_DIV]) / 2);
        clk_set_rate(clk[VF610_CLK_QSPI1_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X2_DIV]) / 2);
 
-       clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_AUDIO_EXT]);
+       clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_PLL4_MAIN_DIV]);
        clk_set_parent(clk[VF610_CLK_SAI1_SEL], clk[VF610_CLK_AUDIO_EXT]);
-       clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_AUDIO_EXT]);
+       clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_PLL4_MAIN_DIV]);
        clk_set_parent(clk[VF610_CLK_SAI3_SEL], clk[VF610_CLK_AUDIO_EXT]);
+       clk_set_rate(clk[VF610_CLK_PLL4_MAIN_DIV], 147456000);
 
        for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
                clk_prepare_enable(clk[clks_init_on[i]]);
 
+       register_syscore_ops(&vf610_clk_syscore_ops);
+
        /* Add the clocks to provider list */
        clk_data.clks = clk;
        clk_data.clk_num = ARRAY_SIZE(clk);
        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
 }
 CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init);
+
index c94ac5c26226df1edadfef4ebf1a0ac829c84c14..9311755da52fdcb71804a01ece3cf4317c0fd1fa 100644 (file)
@@ -41,7 +41,7 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
 
 struct clk *clk_register_gate2(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
-               void __iomem *reg, u8 bit_idx,
+               void __iomem *reg, u8 bit_idx, u8 cgr_val,
                u8 clk_gate_flags, spinlock_t *lock,
                unsigned int *share_count);
 
@@ -55,7 +55,7 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
                void __iomem *reg, u8 shift)
 {
        return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
-                       shift, 0, &imx_ccm_lock, NULL);
+                       shift, 0x3, 0, &imx_ccm_lock, NULL);
 }
 
 static inline struct clk *imx_clk_gate2_shared(const char *name,
@@ -63,7 +63,14 @@ static inline struct clk *imx_clk_gate2_shared(const char *name,
                unsigned int *share_count)
 {
        return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
-                       shift, 0, &imx_ccm_lock, share_count);
+                       shift, 0x3, 0, &imx_ccm_lock, share_count);
+}
+
+static inline struct clk *imx_clk_gate2_cgr(const char *name, const char *parent,
+               void __iomem *reg, u8 shift, u8 cgr_val)
+{
+       return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+                       shift, cgr_val, 0, &imx_ccm_lock, NULL);
 }
 
 struct clk *imx_clk_pfd(const char *name, const char *parent_name,
index a0e6c68536a18d8dbc76eb7f239bea31ca24240f..934fe264e58d1fda59a4d7bb5330d2498922d2f0 100644 (file)
@@ -36,6 +36,7 @@
 static void __iomem *clksrc_base;
 static void __iomem *clkevt_base;
 static unsigned long cycle_per_jiffy;
+static void __iomem *timer_base;
 
 static inline void pit_timer_enable(void)
 {
@@ -57,12 +58,17 @@ static u64 notrace pit_read_sched_clock(void)
        return ~__raw_readl(clksrc_base + PITCVAL);
 }
 
-static int __init pit_clocksource_init(unsigned long rate)
+static void pit_load_and_start_clocksource(int clksrc_ldval)
 {
-       /* set the max load value and start the clock source counter */
        __raw_writel(0, clksrc_base + PITTCTRL);
-       __raw_writel(~0UL, clksrc_base + PITLDVAL);
+       __raw_writel(clksrc_ldval, clksrc_base + PITLDVAL);
        __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL);
+}
+
+static int __init pit_clocksource_init(unsigned long rate)
+{
+       /* set the max load value and start the clock source counter */
+       pit_load_and_start_clocksource(~0UL);
 
        sched_clock_register(pit_read_sched_clock, 32, rate);
        return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate,
@@ -118,6 +124,21 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static unsigned int src_ldval;
+
+static void pit_resume(struct clock_event_device *evt)
+{
+       /* Enable the PIT module on resume */
+       __raw_writel(~PITMCR_MDIS, timer_base + PITMCR);
+
+       pit_load_and_start_clocksource(src_ldval);
+}
+
+static void pit_suspend(struct clock_event_device *evt)
+{
+       src_ldval = __raw_readl(clksrc_base + PITLDVAL);
+}
+
 static struct clock_event_device clockevent_pit = {
        .name           = "VF pit timer",
        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
@@ -125,6 +146,8 @@ static struct clock_event_device clockevent_pit = {
        .set_state_periodic = pit_set_periodic,
        .set_next_event = pit_set_next_event,
        .rating         = 300,
+       .resume         = pit_resume,
+       .suspend        = pit_suspend,
 };
 
 static struct irqaction pit_timer_irq = {
@@ -159,7 +182,6 @@ static int __init pit_clockevent_init(unsigned long rate, int irq)
 static void __init pit_timer_init(struct device_node *np)
 {
        struct clk *pit_clk;
-       void __iomem *timer_base;
        unsigned long clk_rate;
        int irq;
 
index 915eec3cc279c90f3111ad5849d16ba9a469cb35..be2e62b879481f592aa965d4fe165b4ae8771896 100644 (file)
                                BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
                                BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
                                BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
+enum fsl_edma_pm_state {
+       RUNNING = 0,
+       SUSPENDED,
+};
 
 struct fsl_edma_hw_tcd {
        __le32  saddr;
@@ -147,6 +151,9 @@ struct fsl_edma_slave_config {
 struct fsl_edma_chan {
        struct virt_dma_chan            vchan;
        enum dma_status                 status;
+       enum fsl_edma_pm_state          pm_state;
+       bool                            idle;
+       u32                             slave_id;
        struct fsl_edma_engine          *edma;
        struct fsl_edma_desc            *edesc;
        struct fsl_edma_slave_config    fsc;
@@ -298,6 +305,7 @@ static int fsl_edma_terminate_all(struct dma_chan *chan)
        spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
        fsl_edma_disable_request(fsl_chan);
        fsl_chan->edesc = NULL;
+       fsl_chan->idle = true;
        vchan_get_all_descriptors(&fsl_chan->vchan, &head);
        spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
        vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
@@ -313,6 +321,7 @@ static int fsl_edma_pause(struct dma_chan *chan)
        if (fsl_chan->edesc) {
                fsl_edma_disable_request(fsl_chan);
                fsl_chan->status = DMA_PAUSED;
+               fsl_chan->idle = true;
        }
        spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
        return 0;
@@ -327,6 +336,7 @@ static int fsl_edma_resume(struct dma_chan *chan)
        if (fsl_chan->edesc) {
                fsl_edma_enable_request(fsl_chan);
                fsl_chan->status = DMA_IN_PROGRESS;
+               fsl_chan->idle = false;
        }
        spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
        return 0;
@@ -648,6 +658,7 @@ static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
        fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
        fsl_edma_enable_request(fsl_chan);
        fsl_chan->status = DMA_IN_PROGRESS;
+       fsl_chan->idle = false;
 }
 
 static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
@@ -676,6 +687,7 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
                                vchan_cookie_complete(&fsl_chan->edesc->vdesc);
                                fsl_chan->edesc = NULL;
                                fsl_chan->status = DMA_COMPLETE;
+                               fsl_chan->idle = true;
                        } else {
                                vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
                        }
@@ -704,6 +716,7 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
                        edma_writeb(fsl_edma, EDMA_CERR_CERR(ch),
                                fsl_edma->membase + EDMA_CERR);
                        fsl_edma->chans[ch].status = DMA_ERROR;
+                       fsl_edma->chans[ch].idle = true;
                }
        }
        return IRQ_HANDLED;
@@ -724,6 +737,12 @@ static void fsl_edma_issue_pending(struct dma_chan *chan)
 
        spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
 
+       if (unlikely(fsl_chan->pm_state != RUNNING)) {
+               spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+               /* cannot submit due to suspend */
+               return;
+       }
+
        if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
                fsl_edma_xfer_desc(fsl_chan);
 
@@ -735,6 +754,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
 {
        struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
        struct dma_chan *chan, *_chan;
+       struct fsl_edma_chan *fsl_chan;
        unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR;
 
        if (dma_spec->args_count != 2)
@@ -748,8 +768,10 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
                        chan = dma_get_slave_channel(chan);
                        if (chan) {
                                chan->device->privatecnt++;
-                               fsl_edma_chan_mux(to_fsl_edma_chan(chan),
-                                       dma_spec->args[1], true);
+                               fsl_chan = to_fsl_edma_chan(chan);
+                               fsl_chan->slave_id = dma_spec->args[1];
+                               fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id,
+                                               true);
                                mutex_unlock(&fsl_edma->fsl_edma_mutex);
                                return chan;
                        }
@@ -888,7 +910,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
                struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
 
                fsl_chan->edma = fsl_edma;
-
+               fsl_chan->pm_state = RUNNING;
+               fsl_chan->slave_id = 0;
+               fsl_chan->idle = true;
                fsl_chan->vchan.desc_free = fsl_edma_free_desc;
                vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
 
@@ -959,6 +983,60 @@ static int fsl_edma_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int fsl_edma_suspend_late(struct device *dev)
+{
+       struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+       struct fsl_edma_chan *fsl_chan;
+       unsigned long flags;
+       int i;
+
+       for (i = 0; i < fsl_edma->n_chans; i++) {
+               fsl_chan = &fsl_edma->chans[i];
+               spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+               /* Make sure chan is idle or will force disable. */
+               if (unlikely(!fsl_chan->idle)) {
+                       dev_warn(dev, "WARN: There is non-idle channel.");
+                       fsl_edma_disable_request(fsl_chan);
+                       fsl_edma_chan_mux(fsl_chan, 0, false);
+               }
+
+               fsl_chan->pm_state = SUSPENDED;
+               spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+       }
+
+       return 0;
+}
+
+static int fsl_edma_resume_early(struct device *dev)
+{
+       struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+       struct fsl_edma_chan *fsl_chan;
+       int i;
+
+       for (i = 0; i < fsl_edma->n_chans; i++) {
+               fsl_chan = &fsl_edma->chans[i];
+               fsl_chan->pm_state = RUNNING;
+               edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
+               if (fsl_chan->slave_id != 0)
+                       fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true);
+       }
+
+       edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA,
+                       fsl_edma->membase + EDMA_CR);
+
+       return 0;
+}
+
+/*
+ * eDMA provides the service to others, so it should be suspend late
+ * and resume early. When eDMA suspend, all of the clients should stop
+ * the DMA data transmission and let the channel idle.
+ */
+static const struct dev_pm_ops fsl_edma_pm_ops = {
+       .suspend_late   = fsl_edma_suspend_late,
+       .resume_early   = fsl_edma_resume_early,
+};
+
 static const struct of_device_id fsl_edma_dt_ids[] = {
        { .compatible = "fsl,vf610-edma", },
        { /* sentinel */ }
@@ -969,6 +1047,7 @@ static struct platform_driver fsl_edma_driver = {
        .driver         = {
                .name   = "fsl-edma",
                .of_match_table = fsl_edma_dt_ids,
+               .pm     = &fsl_edma_pm_ops,
        },
        .probe          = fsl_edma_probe,
        .remove         = fsl_edma_remove,
index 87b950cec6ec929689a3d52b428f9224acebc623..86a8dfe5dc64615410a1c7b17b0098e38151ce66 100644 (file)
@@ -36,7 +36,10 @@ struct vf610_gpio_port {
        void __iomem *base;
        void __iomem *gpio_base;
        u8 irqc[VF610_GPIO_PER_PORT];
+       struct platform_device *pdev_wkpu;
+       s8 fsl_wakeup[VF610_GPIO_PER_PORT];
        int irq;
+       u32 state;
 };
 
 #define GPIO_PDOR              0x00
@@ -60,6 +63,14 @@ struct vf610_gpio_port {
 #define PORT_INT_EITHER_EDGE   0xb
 #define PORT_INT_LOGIC_ONE     0xc
 
+#define WKPU_WISR              0x14
+#define WKPU_IRER              0x18
+#define WKPU_WRER              0x1c
+#define WKPU_WIREER            0x28
+#define WKPU_WIFEER            0x2c
+#define WKPU_WIFER             0x30
+#define WKPU_WIPUER            0x34
+
 static struct irq_chip vf610_gpio_irq_chip;
 
 static struct vf610_gpio_port *to_vf610_gp(struct gpio_chip *gc)
@@ -72,6 +83,11 @@ static const struct of_device_id vf610_gpio_dt_ids[] = {
        { /* sentinel */ }
 };
 
+static const struct of_device_id vf610_wkpu_dt_ids[] = {
+       { .compatible = "fsl,vf610-wkpu" },
+       { /* sentinel */ }
+};
+
 static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
 {
        writel_relaxed(val, reg);
@@ -147,8 +163,26 @@ static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
 {
        struct vf610_gpio_port *port =
                to_vf610_gp(irq_data_get_irq_chip_data(d));
+       s8 wkpu_gpio = port->fsl_wakeup[d->hwirq];
        u8 irqc;
 
+       if (wkpu_gpio >= 0) {
+               void __iomem *base = platform_get_drvdata(port->pdev_wkpu);
+               u32 wireer, wifeer;
+               u32 mask = 1 << wkpu_gpio;
+
+               wireer = vf610_gpio_readl(base + WKPU_WIREER) & ~mask;
+               wifeer = vf610_gpio_readl(base + WKPU_WIFEER) & ~mask;
+
+               if (type & IRQ_TYPE_EDGE_RISING)
+                       wireer |= mask;
+               if (type & IRQ_TYPE_EDGE_FALLING)
+                       wifeer |= mask;
+
+               vf610_gpio_writel(wireer, base + WKPU_WIREER);
+               vf610_gpio_writel(wifeer, base + WKPU_WIFEER);
+       }
+
        switch (type) {
        case IRQ_TYPE_EDGE_RISING:
                irqc = PORT_INT_RISING_EDGE;
@@ -202,6 +236,29 @@ static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
 {
        struct vf610_gpio_port *port =
                to_vf610_gp(irq_data_get_irq_chip_data(d));
+       s8 wkpu_gpio = port->fsl_wakeup[d->hwirq];
+
+       if (wkpu_gpio >= 0) {
+               void __iomem *base = NULL;
+               u32 wrer, irer;
+
+               base = platform_get_drvdata(port->pdev_wkpu);
+
+               /* WKPU wakeup flag for LPSTOPx modes...  */
+               wrer = vf610_gpio_readl(base + WKPU_WRER);
+               irer = vf610_gpio_readl(base + WKPU_IRER);
+
+               if (enable) {
+                       wrer |= 1 << wkpu_gpio;
+                       irer |= 1 << wkpu_gpio;
+               } else {
+                       wrer &= ~(1 << wkpu_gpio);
+                       irer &= ~(1 << wkpu_gpio);
+               }
+
+               vf610_gpio_writel(wrer, base + WKPU_WRER);
+               vf610_gpio_writel(irer, base + WKPU_IRER);
+       }
 
        if (enable)
                enable_irq_wake(port->irq);
@@ -220,6 +277,77 @@ static struct irq_chip vf610_gpio_irq_chip = {
        .irq_set_wake   = vf610_gpio_irq_set_wake,
 };
 
+static int __maybe_unused vf610_gpio_suspend(struct device *dev)
+{
+       struct vf610_gpio_port *port = dev_get_drvdata(dev);
+
+       port->state = vf610_gpio_readl(port->gpio_base + GPIO_PDOR);
+
+       /*
+        * There is no need to store Port state since we maintain the state
+        * alread in the irqc array
+        */
+
+       return 0;
+}
+
+static int __maybe_unused vf610_gpio_resume(struct device *dev)
+{
+       struct vf610_gpio_port *port = dev_get_drvdata(dev);
+       int i;
+
+       vf610_gpio_writel(port->state, port->gpio_base + GPIO_PDOR);
+
+       for (i = 0; i < port->gc.ngpio; i++) {
+               u32 irqc = port->irqc[i] << PORT_PCR_IRQC_OFFSET;
+
+               vf610_gpio_writel(irqc, port->base + PORT_PCR(i));
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops vf610_gpio_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(vf610_gpio_suspend, vf610_gpio_resume)
+};
+
+static int vf610_gpio_wkpu(struct device_node *np, struct vf610_gpio_port *port)
+{
+       struct platform_device *pdev = NULL;
+       struct of_phandle_args arg;
+       int i, ret;
+
+       for (i = 0; i < VF610_GPIO_PER_PORT; i++)
+               port->fsl_wakeup[i] = -1;
+
+       for (i = 0;;i++) {
+               int gpioid, wakeupid, cnt;
+
+               ret = of_parse_phandle_with_fixed_args(np, "fsl,gpio-wakeup",
+                                                       3, i, &arg);
+
+               if (ret == -ENOENT)
+                       break;
+
+               if (!pdev)
+                       pdev = of_find_device_by_node(arg.np);
+               of_node_put(arg.np);
+               if (!pdev)
+                       return -EPROBE_DEFER;
+
+               gpioid = arg.args[0];
+               wakeupid = arg.args[1];
+               cnt = arg.args[2];
+
+               while (cnt-- && gpioid < VF610_GPIO_PER_PORT)
+                       port->fsl_wakeup[gpioid++] = wakeupid++;
+       }
+
+       port->pdev_wkpu = pdev;
+
+       return 0;
+}
+
 static int vf610_gpio_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -247,6 +375,10 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        if (port->irq < 0)
                return port->irq;
 
+       ret = vf610_gpio_wkpu(np, port);
+       if (ret < 0)
+               return ret;
+
        gc = &port->gc;
        gc->of_node = np;
        gc->dev = dev;
@@ -277,6 +409,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        }
        gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq,
                                     vf610_gpio_irq_handler);
+       platform_set_drvdata(pdev, port);
 
        return 0;
 }
@@ -284,6 +417,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
 static struct platform_driver vf610_gpio_driver = {
        .driver         = {
                .name   = "gpio-vf610",
+               .pm = &vf610_gpio_pm_ops,
                .of_match_table = vf610_gpio_dt_ids,
        },
        .probe          = vf610_gpio_probe,
@@ -295,6 +429,60 @@ static int __init gpio_vf610_init(void)
 }
 device_initcall(gpio_vf610_init);
 
+static irqreturn_t vf610_wkpu_irq(int irq, void *data)
+{
+       void __iomem *base = data;
+       u32 wisr;
+
+       wisr = vf610_gpio_readl(base + WKPU_WISR);
+       vf610_gpio_writel(wisr, base + WKPU_WISR);
+       pr_debug("%s, WKPU interrupt received, flags %08x\n", __func__, wisr);
+
+       return 0;
+}
+
+static int vf610_wkpu_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *iores;
+       void __iomem *base;
+       int irq, err;
+
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, iores);
+
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       err = devm_request_irq(dev, irq, vf610_wkpu_irq, 0, "wkpu-vf610", base);
+       if (err) {
+               dev_err(dev, "Error requesting IRQ!\n");
+               return err;
+       }
+
+       platform_set_drvdata(pdev, base);
+
+       return 0;
+}
+
+static struct platform_driver vf610_wkpu_driver = {
+       .driver         = {
+               .name   = "wkpu-vf610",
+               .of_match_table = vf610_wkpu_dt_ids,
+       },
+       .probe          = vf610_wkpu_probe,
+};
+
+static int __init vf610_wkpu_init(void)
+{
+       return platform_driver_register(&vf610_wkpu_driver);
+}
+device_initcall(vf610_wkpu_init);
+
 MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
 MODULE_DESCRIPTION("Freescale VF610 GPIO");
 MODULE_LICENSE("GPL v2");
index 50d74e5ce41b1134fef290a3b63154100f71ef12..5171af2876f958394359f78f1db6e1638ec51108 100644 (file)
@@ -1192,12 +1192,7 @@ void drm_atomic_legacy_backoff(struct drm_atomic_state *state)
 retry:
        drm_modeset_backoff(state->acquire_ctx);
 
-       ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex,
-                              state->acquire_ctx);
-       if (ret)
-               goto retry;
-       ret = drm_modeset_lock_all_crtcs(state->dev,
-                                        state->acquire_ctx);
+       ret = drm_modeset_lock_all_ctx(state->dev, state->acquire_ctx);
        if (ret)
                goto retry;
 }
index ea443fafb9340001b2ce6c331cd94086c91a5033..ad06c20ea07d9f9dcda40d5c986bfb28e18f2b28 100644 (file)
@@ -1813,6 +1813,161 @@ commit:
        return 0;
 }
 
+/**
+ * drm_atomic_helper_disable_all - disable all currently active outputs
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Loops through all connectors, finding those that aren't turned off and then
+ * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
+ * that they are connected to.
+ *
+ * This is used for example in suspend/resume to disable all currently active
+ * functions when suspending.
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
+ */
+int drm_atomic_helper_disable_all(struct drm_device *dev,
+                                 struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_atomic_state *state;
+       struct drm_connector *conn;
+       int err;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = ctx;
+
+       drm_for_each_connector(conn, dev) {
+               struct drm_crtc *crtc = conn->state->crtc;
+               struct drm_crtc_state *crtc_state;
+
+               if (!crtc || conn->dpms != DRM_MODE_DPMS_ON)
+                       continue;
+
+               crtc_state = drm_atomic_get_crtc_state(state, crtc);
+               if (IS_ERR(crtc_state)) {
+                       err = PTR_ERR(crtc_state);
+                       goto free;
+               }
+
+               crtc_state->active = false;
+       }
+
+       err = drm_atomic_commit(state);
+
+free:
+       if (err < 0)
+               drm_atomic_state_free(state);
+
+       return err;
+}
+EXPORT_SYMBOL(drm_atomic_helper_disable_all);
+
+/**
+ * drm_atomic_helper_suspend - subsystem-level suspend helper
+ * @dev: DRM device
+ *
+ * Duplicates the current atomic state, disables all active outputs and then
+ * returns a pointer to the original atomic state to the caller. Drivers can
+ * pass this pointer to the drm_atomic_helper_resume() helper upon resume to
+ * restore the output configuration that was active at the time the system
+ * entered suspend.
+ *
+ * Note that it is potentially unsafe to use this. The atomic state object
+ * returned by this function is assumed to be persistent. Drivers must ensure
+ * that this holds true. Before calling this function, drivers must make sure
+ * to suspend fbdev emulation so that nothing can be using the device.
+ *
+ * Returns:
+ * A pointer to a copy of the state before suspend on success or an ERR_PTR()-
+ * encoded error code on failure. Drivers should store the returned atomic
+ * state object and pass it to the drm_atomic_helper_resume() helper upon
+ * resume.
+ *
+ * See also:
+ * drm_atomic_helper_duplicate_state(), drm_atomic_helper_disable_all(),
+ * drm_atomic_helper_resume()
+ */
+struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev)
+{
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_atomic_state *state;
+       int err;
+
+       drm_modeset_acquire_init(&ctx, 0);
+
+retry:
+       err = drm_modeset_lock_all_ctx(dev, &ctx);
+       if (err < 0) {
+               state = ERR_PTR(err);
+               goto unlock;
+       }
+
+       state = drm_atomic_helper_duplicate_state(dev, &ctx);
+       if (IS_ERR(state))
+               goto unlock;
+
+       err = drm_atomic_helper_disable_all(dev, &ctx);
+       if (err < 0) {
+               drm_atomic_state_free(state);
+               state = ERR_PTR(err);
+               goto unlock;
+       }
+
+unlock:
+       if (PTR_ERR(state) == -EDEADLK) {
+               drm_modeset_backoff(&ctx);
+               goto retry;
+       }
+
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+       return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_suspend);
+
+/**
+ * drm_atomic_helper_resume - subsystem-level resume helper
+ * @dev: DRM device
+ * @state: atomic state to resume to
+ *
+ * Calls drm_mode_config_reset() to synchronize hardware and software states,
+ * grabs all modeset locks and commits the atomic state object. This can be
+ * used in conjunction with the drm_atomic_helper_suspend() helper to
+ * implement suspend/resume for drivers that support atomic mode-setting.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend()
+ */
+int drm_atomic_helper_resume(struct drm_device *dev,
+                            struct drm_atomic_state *state)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       int err;
+
+       drm_mode_config_reset(dev);
+       drm_modeset_lock_all(dev);
+       state->acquire_ctx = config->acquire_ctx;
+       err = drm_atomic_commit(state);
+       drm_modeset_unlock_all(dev);
+
+       return err;
+}
+EXPORT_SYMBOL(drm_atomic_helper_resume);
+
 /**
  * drm_atomic_helper_crtc_set_property - helper for crtc properties
  * @crtc: DRM crtc
@@ -2426,7 +2581,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
  * @ctx: lock acquisition context
  *
  * Makes a copy of the current atomic state by looping over all objects and
- * duplicating their respective states.
+ * duplicating their respective states. This is used for example by suspend/
+ * resume support code to save the state prior to suspend such that it can
+ * be restored upon resume.
  *
  * Note that this treats atomic state as persistent between save and restore.
  * Drivers must make sure that this is possible and won't result in confusion
@@ -2438,6 +2595,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
  * Returns:
  * A pointer to the copy of the atomic state object on success or an
  * ERR_PTR()-encoded error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
  */
 struct drm_atomic_state *
 drm_atomic_helper_duplicate_state(struct drm_device *dev,
index ef534758a02c6f946061107aaa0262527c8a25fb..08620a25fe06f7a70fdc93dd1036b1b6862a0864 100644 (file)
@@ -855,6 +855,12 @@ EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
  * due to slight differences in allocating shared resources when the
  * configuration is restored in a different order than when userspace set it up)
  * need to use their own restore logic.
+ *
+ * This function is deprecated. New drivers should implement atomic mode-
+ * setting and use the atomic suspend/resume helpers.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
  */
 void drm_helper_resume_force_mode(struct drm_device *dev)
 {
index c19a62561183537046dc896254d09d4ac3a70b1e..a17d74474f1a6aa6d6298c170b2a0b1f26621732 100644 (file)
@@ -348,9 +348,6 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 
        }
 
-       /* disable all the possible outputs/crtcs before entering KMS mode */
-       drm_helper_disable_unused_functions(dev);
-
        ret = drm_fb_helper_initial_config(helper, preferred_bpp);
        if (ret < 0) {
                dev_err(dev->dev, "Failed to set initial hw configuration.\n");
@@ -368,6 +365,18 @@ err_free:
 }
 EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
 
+/**
+ * drm_fbdev_cma_get_helper() - Get drm_fb_helper struct of a CMA framebuffer
+ * @fbdev_cma: drm_fbdev_cma struct
+ *
+ * Returns the assigned drm_fb_helper struct.
+ */
+struct drm_fb_helper *drm_fbdev_cma_get_helper(struct drm_fbdev_cma *fbdev_cma)
+{
+       return &fbdev_cma->fb_helper;
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_get_helper);
+
 /**
  * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
  * @fbdev_cma: The drm_fbdev_cma struct
index c257de351cfa5b4e570bd7734ab21b41f0e339d6..220eee1c1ef7f1815f7aef5c9fc49a5893ad6e36 100644 (file)
 
 /**
  * drm_modeset_lock_all - take all modeset locks
- * @dev: drm device
+ * @dev: DRM device
  *
  * This function takes all modeset locks, suitable where a more fine-grained
- * scheme isn't (yet) implemented. Locks must be dropped with
- * drm_modeset_unlock_all.
+ * scheme isn't (yet) implemented. Locks must be dropped by calling the
+ * drm_modeset_unlock_all() function.
+ *
+ * This function is deprecated. It allocates a lock acquisition context and
+ * stores it in the DRM device's ->mode_config. This facilitate conversion of
+ * existing code because it removes the need to manually deal with the
+ * acquisition context, but it is also brittle because the context is global
+ * and care must be taken not to nest calls. New code should use the
+ * drm_modeset_lock_all_ctx() function and pass in the context explicitly.
  */
 void drm_modeset_lock_all(struct drm_device *dev)
 {
@@ -78,39 +85,43 @@ void drm_modeset_lock_all(struct drm_device *dev)
        drm_modeset_acquire_init(ctx, 0);
 
 retry:
-       ret = drm_modeset_lock(&config->connection_mutex, ctx);
-       if (ret)
-               goto fail;
-       ret = drm_modeset_lock_all_crtcs(dev, ctx);
-       if (ret)
-               goto fail;
+       ret = drm_modeset_lock_all_ctx(dev, ctx);
+       if (ret < 0) {
+               if (ret == -EDEADLK) {
+                       drm_modeset_backoff(ctx);
+                       goto retry;
+               }
+
+               drm_modeset_acquire_fini(ctx);
+               kfree(ctx);
+               return;
+       }
 
        WARN_ON(config->acquire_ctx);
 
-       /* now we hold the locks, so now that it is safe, stash the
-        * ctx for drm_modeset_unlock_all():
+       /*
+        * We hold the locks now, so it is safe to stash the acquisition
+        * context for drm_modeset_unlock_all().
         */
        config->acquire_ctx = ctx;
 
        drm_warn_on_modeset_not_all_locked(dev);
-
-       return;
-
-fail:
-       if (ret == -EDEADLK) {
-               drm_modeset_backoff(ctx);
-               goto retry;
-       }
-
-       kfree(ctx);
 }
 EXPORT_SYMBOL(drm_modeset_lock_all);
 
 /**
  * drm_modeset_unlock_all - drop all modeset locks
- * @dev: device
+ * @dev: DRM device
  *
- * This function drop all modeset locks taken by drm_modeset_lock_all.
+ * This function drops all modeset locks taken by a previous call to the
+ * drm_modeset_lock_all() function.
+ *
+ * This function is deprecated. It uses the lock acquisition context stored
+ * in the DRM device's ->mode_config. This facilitates conversion of existing
+ * code because it removes the need to manually deal with the acquisition
+ * context, but it is also brittle because the context is global and care must
+ * be taken not to nest calls. New code should pass the acquisition context
+ * directly to the drm_modeset_drop_locks() function.
  */
 void drm_modeset_unlock_all(struct drm_device *dev)
 {
@@ -431,14 +442,34 @@ void drm_modeset_unlock(struct drm_modeset_lock *lock)
 }
 EXPORT_SYMBOL(drm_modeset_unlock);
 
-/* In some legacy codepaths it's convenient to just grab all the crtc and plane
- * related locks. */
-int drm_modeset_lock_all_crtcs(struct drm_device *dev,
-               struct drm_modeset_acquire_ctx *ctx)
+/**
+ * drm_modeset_lock_all_ctx - take all modeset locks
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * This function takes all modeset locks, suitable where a more fine-grained
+ * scheme isn't (yet) implemented.
+ *
+ * Unlike drm_modeset_lock_all(), it doesn't take the dev->mode_config.mutex
+ * since that lock isn't required for modeset state changes. Callers which
+ * need to grab that lock too need to do so outside of the acquire context
+ * @ctx.
+ *
+ * Locks acquired with this function should be released by calling the
+ * drm_modeset_drop_locks() function on @ctx.
+ *
+ * Returns: 0 on success or a negative error-code on failure.
+ */
+int drm_modeset_lock_all_ctx(struct drm_device *dev,
+                            struct drm_modeset_acquire_ctx *ctx)
 {
        struct drm_crtc *crtc;
        struct drm_plane *plane;
-       int ret = 0;
+       int ret;
+
+       ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx);
+       if (ret)
+               return ret;
 
        drm_for_each_crtc(crtc, dev) {
                ret = drm_modeset_lock(&crtc->mutex, ctx);
@@ -454,4 +485,4 @@ int drm_modeset_lock_all_crtcs(struct drm_device *dev,
 
        return 0;
 }
-EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
+EXPORT_SYMBOL(drm_modeset_lock_all_ctx);
index 6ea1523ae6ec39affef7831997d660763b68b1d0..b35a292287f3476a591e8c6647ecca7920f94518 100644 (file)
@@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
                 fsl_dcu_drm_rgb.o \
                 fsl_dcu_drm_plane.o \
                 fsl_dcu_drm_crtc.o \
-                fsl_dcu_drm_fbdev.o
+                fsl_dcu_drm_fbdev.o \
+                fsl_tcon.o
 obj-$(CONFIG_DRM_FSL_DCU)      += fsl-dcu-drm.o
index 82a3d311e164f53f4431f2b659a890610f099b90..9ceeb1a5b47dad7a4ec41339e0d79424aac0b8ea 100644 (file)
@@ -17,6 +17,9 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
+
+#include <video/display_timing.h>
 
 #include "fsl_dcu_drm_crtc.h"
 #include "fsl_dcu_drm_drv.h"
 static void fsl_dcu_drm_crtc_atomic_begin(struct drm_crtc *crtc,
                                          struct drm_crtc_state *old_crtc_state)
 {
+       struct fsl_dcu_drm_crtc *dcu_crtc = to_fsl_dcu_crtc(crtc);
+       if (crtc->state->event) {
+               crtc->state->event->pipe = drm_crtc_index(crtc);
+
+               WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+               dcu_crtc->event = crtc->state->event;
+               crtc->state->event = NULL;
+       }
 }
 
 static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc,
                                         struct drm_crtc_state *state)
 {
+       struct fsl_dcu_drm_crtc *dcu_crtc = to_fsl_dcu_crtc(crtc);
+
+       if (dcu_crtc->event != NULL && state->event != NULL)
+               return -EINVAL;
+
        return 0;
 }
 
@@ -38,38 +54,99 @@ static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 {
 }
 
+void fsl_dcu_crtc_finish_page_flip(struct drm_device *dev)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       struct fsl_dcu_drm_crtc *dcu_crtc = &fsl_dev->crtc;
+       struct drm_crtc *crtc = &dcu_crtc->base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
+       if (dcu_crtc->event) {
+               drm_send_vblank_event(crtc->dev,
+                                     drm_crtc_index(crtc),
+                                     dcu_crtc->event);
+               drm_vblank_put(dev, drm_crtc_index(crtc));
+               dcu_crtc->event = NULL;
+       }
+       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+
+void fsl_dcu_crtc_cancel_page_flip(struct drm_device *dev, struct drm_file *f)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       struct fsl_dcu_drm_crtc *dcu_crtc = &fsl_dev->crtc;
+       struct drm_crtc *crtc = &dcu_crtc->base;
+       struct drm_pending_vblank_event *event;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = dcu_crtc->event;
+
+       if (event && event->base.file_priv == f) {
+               event->base.destroy(&event->base);
+               drm_vblank_put(dev, drm_crtc_index(crtc));
+               dcu_crtc->event = NULL;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
-       int ret;
-
-       ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
-                                DCU_MODE_DCU_MODE_MASK,
-                                DCU_MODE_DCU_MODE(DCU_MODE_OFF));
-       if (ret)
-               dev_err(fsl_dev->dev, "Disable CRTC failed\n");
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               dev_err(fsl_dev->dev, "Enable CRTC failed\n");
+       int i;
+       unsigned int value;
+       unsigned int mode;
+
+       /* Disable automatic transfer mode */
+       regmap_update_bits(fsl_dev->regmap, DCU_UPDATE_MODE,
+                          DCU_UPDATE_MODE_MODE, 0);
+
+       /* Disable all planes */
+       for (i = 0; i < fsl_dev->soc->total_layer; i++) {
+               regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(i, 4), &value);
+               value &= ~DCU_LAYER_EN;
+               regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, 4), value);
+       }
+       regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+                    DCU_UPDATE_MODE_READREG);
+       while (!regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode) &&
+               mode & DCU_UPDATE_MODE_READREG);
+
+       regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
+                          DCU_MODE_DCU_MODE_MASK,
+                          DCU_MODE_DCU_MODE(DCU_MODE_OFF));
+       regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+                    DCU_UPDATE_MODE_READREG);
+       clk_disable_unprepare(fsl_dev->pix_clk);
 }
 
 static void fsl_dcu_drm_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
-       int ret;
-
-       ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
-                                DCU_MODE_DCU_MODE_MASK,
-                                DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
-       if (ret)
-               dev_err(fsl_dev->dev, "Enable CRTC failed\n");
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               dev_err(fsl_dev->dev, "Enable CRTC failed\n");
+       unsigned int mode;
+
+       if (clk_prepare_enable(fsl_dev->pix_clk) < 0)
+               dev_err(fsl_dev->dev, "failed to enable pix clk\n");
+
+       regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
+                          DCU_MODE_DCU_MODE_MASK,
+                          DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
+       regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+                    DCU_UPDATE_MODE_READREG);
+
+       /*
+        * Wait until transfer is complete and switch to automatic update
+        * mode. Automatic updates avoids flickers when changing layer
+        * parameters.
+        */
+       while (!regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode) &&
+               mode & DCU_UPDATE_MODE_READREG);
+       regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+                    DCU_UPDATE_MODE_MODE);
+       regmap_read(fsl_dev->regmap, DCU_UPDATE_MODE, &mode);
 }
 
 static bool fsl_dcu_drm_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -83,14 +160,12 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       struct drm_connector *con = &fsl_dev->connector.base;
        struct drm_display_mode *mode = &crtc->state->mode;
-       unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index;
-       unsigned long dcuclk;
-       int ret;
+       unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
 
        index = drm_crtc_index(crtc);
-       dcuclk = clk_get_rate(fsl_dev->clk);
-       div = dcuclk / mode->clock / 1000;
+       clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
 
        /* Configure timings: */
        hbp = mode->htotal - mode->hsync_end;
@@ -100,51 +175,37 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
        vfp = mode->vsync_start - mode->vdisplay;
        vsw = mode->vsync_end - mode->vsync_start;
 
-       ret = regmap_write(fsl_dev->regmap, DCU_HSYN_PARA,
-                          DCU_HSYN_PARA_BP(hbp) |
-                          DCU_HSYN_PARA_PW(hsw) |
-                          DCU_HSYN_PARA_FP(hfp));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_VSYN_PARA,
-                          DCU_VSYN_PARA_BP(vbp) |
-                          DCU_VSYN_PARA_PW(vsw) |
-                          DCU_VSYN_PARA_FP(vfp));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
-                          DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
-                          DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div);
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_SYN_POL,
-                          DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW);
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
-                          DCU_BGND_G(0) | DCU_BGND_B(0));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_DCU_MODE,
-                          DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN);
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_THRESHOLD,
-                          DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) |
-                          DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) |
-                          DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               goto set_failed;
+       /* INV_PXCK as default (most display sample data on rising edge) */
+       if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE))
+               pol |= DCU_SYN_POL_INV_PXCK;
+
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               pol |= DCU_SYN_POL_INV_HS_LOW;
+
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               pol |= DCU_SYN_POL_INV_VS_LOW;
+
+       regmap_write(fsl_dev->regmap, DCU_HSYN_PARA,
+                    DCU_HSYN_PARA_BP(hbp) |
+                    DCU_HSYN_PARA_PW(hsw) |
+                    DCU_HSYN_PARA_FP(hfp));
+       regmap_write(fsl_dev->regmap, DCU_VSYN_PARA,
+                    DCU_VSYN_PARA_BP(vbp) |
+                    DCU_VSYN_PARA_PW(vsw) |
+                    DCU_VSYN_PARA_FP(vfp));
+       regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
+                    DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
+                    DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
+       regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
+       regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
+                    DCU_BGND_G(0) | DCU_BGND_B(0));
+       regmap_write(fsl_dev->regmap, DCU_DCU_MODE,
+                    DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN);
+       regmap_write(fsl_dev->regmap, DCU_THRESHOLD,
+                    DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) |
+                    DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) |
+                    DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL));
        return;
-set_failed:
-       dev_err(dev->dev, "set DCU register failed\n");
 }
 
 static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = {
@@ -168,43 +229,24 @@ static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = {
 
 int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev)
 {
-       struct drm_plane *primary;
-       struct drm_crtc *crtc = &fsl_dev->crtc;
-       unsigned int i, j, reg_num;
+       struct drm_plane *primary, *cursor;
+       struct fsl_dcu_drm_crtc *crtc = &fsl_dev->crtc;
        int ret;
 
-       primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm);
-       ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL,
-                                       &fsl_dcu_drm_crtc_funcs);
-       if (ret < 0)
+       fsl_dcu_drm_init_planes(fsl_dev->drm);
+
+       ret = fsl_dcu_drm_create_planes(fsl_dev->drm, &primary, &cursor);
+       if (ret)
                return ret;
 
-       drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs);
-
-       if (!strcmp(fsl_dev->soc->name, "ls1021a"))
-               reg_num = LS1021A_LAYER_REG_NUM;
-       else
-               reg_num = VF610_LAYER_REG_NUM;
-       for (i = 0; i <= fsl_dev->soc->total_layer; i++) {
-               for (j = 0; j < reg_num; j++) {
-                       ret = regmap_write(fsl_dev->regmap,
-                                          DCU_CTRLDESCLN(i, j), 0);
-                       if (ret)
-                               goto init_failed;
-               }
+       ret = drm_crtc_init_with_planes(fsl_dev->drm, &crtc->base, primary,
+                                       cursor, &fsl_dcu_drm_crtc_funcs);
+       if (ret) {
+               primary->funcs->destroy(primary);
+               return ret;
        }
-       ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
-                                DCU_MODE_DCU_MODE_MASK,
-                                DCU_MODE_DCU_MODE(DCU_MODE_OFF));
-       if (ret)
-               goto init_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               goto init_failed;
+
+       drm_crtc_helper_add(&crtc->base, &fsl_dcu_drm_crtc_helper_funcs);
 
        return 0;
-init_failed:
-       dev_err(fsl_dev->dev, "init DCU register failed\n");
-       return ret;
 }
index 43d4da2c5fe51e13f934ad3358dd49e7d9f081c0..23a33023d324ed620245e69659c451297be236e6 100644 (file)
 
 struct fsl_dcu_drm_device;
 
+struct fsl_dcu_drm_crtc {
+       struct drm_crtc base;
+       struct drm_pending_vblank_event *event;
+};
+
+static inline struct fsl_dcu_drm_crtc *to_fsl_dcu_crtc(struct drm_crtc *crtc)
+{
+       return crtc ? container_of(crtc, struct fsl_dcu_drm_crtc, base)
+                    : NULL;
+}
+
 int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev);
+void fsl_dcu_crtc_finish_page_flip(struct drm_device *dev);
+void fsl_dcu_crtc_cancel_page_flip(struct drm_device *dev, struct drm_file *f);
 
 #endif /* __FSL_DCU_DRM_CRTC_H__ */
index 1930234ba5f13596f3b998d737c88e987b3370e7..f4e318d62d22c45d4e229b9716c5f9f45b720238 100644 (file)
 #include <linux/regmap.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "fsl_dcu_drm_crtc.h"
 #include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
+
+static int legacyfb_depth = 24;
+module_param(legacyfb_depth, int, 0444);
+
+static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
+               return true;
+
+       return false;
+}
 
 static const struct regmap_config fsl_dcu_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
-       .cache_type = REGCACHE_RBTREE,
+
+       .volatile_reg = fsl_dcu_drm_is_volatile_reg,
 };
 
 static int fsl_dcu_drm_irq_init(struct drm_device *dev)
 {
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
-       unsigned int value;
        int ret;
 
        ret = drm_irq_install(dev, fsl_dev->irq);
        if (ret < 0)
                dev_err(dev->dev, "failed to install IRQ handler\n");
 
-       ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_STATUS failed\n");
-       ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
-       if (ret)
-               dev_err(dev->dev, "read DCU_INT_MASK failed\n");
-       value &= DCU_INT_MASK_VBLANK;
-       ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_MASK failed\n");
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n");
+       regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0);
+       regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
 
        return ret;
 }
 
+static void fsl_dcu_unref_worker(struct drm_flip_work *work, void *val)
+{
+       struct drm_atomic_state *state = val;
+       struct drm_device *dev = state->dev;
+
+       fsl_dcu_cleanup_atomic_state(dev, state);
+}
+
 static int fsl_dcu_load(struct drm_device *drm, unsigned long flags)
 {
        struct device *dev = drm->dev;
@@ -82,12 +93,25 @@ static int fsl_dcu_load(struct drm_device *drm, unsigned long flags)
        }
        drm->vblank_disable_allowed = true;
 
+       drm_flip_work_init(&fsl_dev->unref_work, "unref", fsl_dcu_unref_worker);
+       fsl_dev->unref_wq = alloc_ordered_workqueue("fsl-dcu-drm", 0);
+
        ret = fsl_dcu_drm_irq_init(drm);
        if (ret < 0)
                goto done;
        drm->irq_enabled = true;
 
-       fsl_dcu_fbdev_init(drm);
+       if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
+           legacyfb_depth != 32) {
+               dev_warn(dev, "Invalid legacyfb_depth.  Defaulting to 24bpp\n");
+               legacyfb_depth = 24;
+       }
+       fsl_dev->fbdev = drm_fbdev_cma_init(drm, legacyfb_depth, 1, 1);
+       if (IS_ERR(fsl_dev->fbdev)) {
+               ret = PTR_ERR(fsl_dev->fbdev);
+               fsl_dev->fbdev = NULL;
+               goto done;
+       }
 
        return 0;
 done:
@@ -103,9 +127,12 @@ done:
 
 static int fsl_dcu_unload(struct drm_device *dev)
 {
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
        drm_mode_config_cleanup(dev);
        drm_vblank_cleanup(dev);
        drm_irq_uninstall(dev);
+       drm_flip_work_cleanup(&fsl_dev->unref_work);
 
        dev->dev_private = NULL;
 
@@ -114,6 +141,7 @@ static int fsl_dcu_unload(struct drm_device *dev)
 
 static void fsl_dcu_drm_preclose(struct drm_device *dev, struct drm_file *file)
 {
+       fsl_dcu_crtc_cancel_page_flip(dev, file);
 }
 
 static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
@@ -124,18 +152,27 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
        int ret;
 
        ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_STATUS failed\n");
-       if (int_status & DCU_INT_STATUS_VBLANK)
+       if (ret) {
+               dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
+               return IRQ_NONE;
+       }
+
+       if (int_status & DCU_INT_STATUS_VBLANK) {
                drm_handle_vblank(dev, 0);
 
-       ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0xffffffff);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_STATUS failed\n");
-       ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
-                          DCU_UPDATE_MODE_READREG);
-       if (ret)
-               dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n");
+               fsl_dcu_crtc_finish_page_flip(dev);
+
+               if (fsl_dev->cleanup_state) {
+                       drm_flip_work_queue(&fsl_dev->unref_work,
+                                           fsl_dev->cleanup_state);
+                       fsl_dev->cleanup_state = NULL;
+
+                       drm_flip_work_commit(&fsl_dev->unref_work,
+                                            fsl_dev->unref_wq);
+               }
+       }
+
+       regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
 
        return IRQ_HANDLED;
 }
@@ -144,15 +181,11 @@ static int fsl_dcu_drm_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
        unsigned int value;
-       int ret;
 
-       ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
-       if (ret)
-               dev_err(dev->dev, "read DCU_INT_MASK failed\n");
+       regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
        value &= ~DCU_INT_MASK_VBLANK;
-       ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_MASK failed\n");
+       regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
+
        return 0;
 }
 
@@ -161,15 +194,17 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
 {
        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
        unsigned int value;
-       int ret;
 
-       ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
-       if (ret)
-               dev_err(dev->dev, "read DCU_INT_MASK failed\n");
+       regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
        value |= DCU_INT_MASK_VBLANK;
-       ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
-       if (ret)
-               dev_err(dev->dev, "set DCU_INT_MASK failed\n");
+       regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
+}
+
+static void fsl_dcu_drm_lastclose(struct drm_device *dev)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+       drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
 }
 
 static const struct file_operations fsl_dcu_drm_fops = {
@@ -189,6 +224,7 @@ static const struct file_operations fsl_dcu_drm_fops = {
 static struct drm_driver fsl_dcu_drm_driver = {
        .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
                                | DRIVER_PRIME | DRIVER_ATOMIC,
+       .lastclose              = fsl_dcu_drm_lastclose,
        .load                   = fsl_dcu_load,
        .unload                 = fsl_dcu_unload,
        .preclose               = fsl_dcu_drm_preclose,
@@ -226,11 +262,18 @@ static int fsl_dcu_drm_pm_suspend(struct device *dev)
        if (!fsl_dev)
                return 0;
 
+       disable_irq(fsl_dev->irq);
        drm_kms_helper_poll_disable(fsl_dev->drm);
-       regcache_cache_only(fsl_dev->regmap, true);
-       regcache_mark_dirty(fsl_dev->regmap);
-       clk_disable(fsl_dev->clk);
-       clk_unprepare(fsl_dev->clk);
+       fsl_dcu_fbdev_suspend(fsl_dev->drm);
+
+       fsl_dev->state = drm_atomic_helper_suspend(fsl_dev->drm);
+       if (IS_ERR(fsl_dev->state)) {
+               fsl_dcu_fbdev_resume(fsl_dev->drm);
+               enable_irq(fsl_dev->irq);
+               return PTR_ERR(fsl_dev->state);
+       }
+
+       clk_disable_unprepare(fsl_dev->clk);
 
        return 0;
 }
@@ -243,21 +286,22 @@ static int fsl_dcu_drm_pm_resume(struct device *dev)
        if (!fsl_dev)
                return 0;
 
-       ret = clk_enable(fsl_dev->clk);
+       ret = clk_prepare_enable(fsl_dev->clk);
        if (ret < 0) {
                dev_err(dev, "failed to enable dcu clk\n");
-               clk_unprepare(fsl_dev->clk);
-               return ret;
-       }
-       ret = clk_prepare(fsl_dev->clk);
-       if (ret < 0) {
-               dev_err(dev, "failed to prepare dcu clk\n");
                return ret;
        }
 
+       fsl_tcon_bypass_enable(fsl_dev->tcon);
+       fsl_dcu_drm_init_planes(fsl_dev->drm);
+
+       drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state);
+
+       regmap_write(fsl_dev->regmap, DCU_INT_MASK, fsl_dev->irq_state);
+
+       fsl_dcu_fbdev_resume(fsl_dev->drm);
        drm_kms_helper_poll_enable(fsl_dev->drm);
-       regcache_cache_only(fsl_dev->regmap, false);
-       regcache_sync(fsl_dev->regmap);
+       enable_irq(fsl_dev->irq);
 
        return 0;
 }
@@ -271,12 +315,14 @@ static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
        .name = "ls1021a",
        .total_layer = 16,
        .max_layer = 4,
+       .layer_regs = LS1021A_LAYER_REG_NUM,
 };
 
 static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
        .name = "vf610",
        .total_layer = 64,
        .max_layer = 6,
+       .layer_regs = VF610_LAYER_REG_NUM,
 };
 
 static const struct of_device_id fsl_dcu_of_match[] = {
@@ -299,6 +345,9 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *base;
        struct drm_driver *driver = &fsl_dcu_drm_driver;
+       struct clk *pix_clk_in;
+       char pix_clk_name[32];
+       const char *pix_clk_in_name;
        const struct of_device_id *id;
        int ret;
 
@@ -306,6 +355,11 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
        if (!fsl_dev)
                return -ENOMEM;
 
+       id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
+       if (!id)
+               return -ENODEV;
+       fsl_dev->soc = id->data;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "could not get memory IO resource\n");
@@ -324,40 +378,48 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
+       fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
+                       &fsl_dcu_regmap_config);
+       if (IS_ERR(fsl_dev->regmap)) {
+               dev_err(dev, "regmap init failed\n");
+               return PTR_ERR(fsl_dev->regmap);
+       }
+
        fsl_dev->clk = devm_clk_get(dev, "dcu");
        if (IS_ERR(fsl_dev->clk)) {
-               ret = PTR_ERR(fsl_dev->clk);
                dev_err(dev, "failed to get dcu clock\n");
-               return ret;
-       }
-       ret = clk_prepare(fsl_dev->clk);
-       if (ret < 0) {
-               dev_err(dev, "failed to prepare dcu clk\n");
-               return ret;
+               return PTR_ERR(fsl_dev->clk);
        }
-       ret = clk_enable(fsl_dev->clk);
+       ret = clk_prepare_enable(fsl_dev->clk);
        if (ret < 0) {
                dev_err(dev, "failed to enable dcu clk\n");
-               clk_unprepare(fsl_dev->clk);
                return ret;
        }
 
-       fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
-                       &fsl_dcu_regmap_config);
-       if (IS_ERR(fsl_dev->regmap)) {
-               dev_err(dev, "regmap init failed\n");
-               return PTR_ERR(fsl_dev->regmap);
+       pix_clk_in = devm_clk_get(dev, "pix");
+       if (IS_ERR(pix_clk_in)) {
+               /* legancy binding, use dcu clock as pixel clock input */
+               pix_clk_in = fsl_dev->clk;
        }
 
-       id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
-       if (!id)
-               return -ENODEV;
-       fsl_dev->soc = id->data;
+       pix_clk_in_name = __clk_get_name(pix_clk_in);
+       snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
+       fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
+                       pix_clk_in_name, 0, base + DCU_DIV_RATIO,
+                       0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
+       if (IS_ERR(fsl_dev->pix_clk)) {
+               dev_err(dev, "failed to register pix clk\n");
+               ret = PTR_ERR(fsl_dev->pix_clk);
+               goto disable_clk;
+       }
 
        drm = drm_dev_alloc(driver, dev);
-       if (!drm)
-               return -ENOMEM;
+       if (!drm) {
+               ret = -ENOMEM;
+               goto unregister_pix_clk;
+       }
 
+       fsl_dev->tcon = fsl_tcon_init(dev);
        fsl_dev->dev = dev;
        fsl_dev->drm = drm;
        fsl_dev->np = dev->of_node;
@@ -377,6 +439,10 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
 
 unref:
        drm_dev_unref(drm);
+unregister_pix_clk:
+       clk_unregister(fsl_dev->pix_clk);
+disable_clk:
+       clk_disable_unprepare(fsl_dev->clk);
        return ret;
 }
 
@@ -384,6 +450,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev)
 {
        struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
 
+       clk_disable_unprepare(fsl_dev->clk);
+       clk_disable_unprepare(fsl_dev->pix_clk);
+       clk_unregister(fsl_dev->pix_clk);
        drm_put_dev(fsl_dev->drm);
 
        return 0;
index 579b9e44e7645f7e28aa3894dde708cc88bdc900..22210766e77d04a00db7bdeb5dfd558d7c2cf8f3 100644 (file)
@@ -47,8 +47,8 @@
 #define DCU_VSYN_PARA_FP(x)            (x)
 
 #define DCU_SYN_POL                    0x0024
-#define DCU_SYN_POL_INV_PXCK_FALL      (0 << 6)
-#define DCU_SYN_POL_NEG_REMAIN         (0 << 5)
+#define DCU_SYN_POL_INV_PXCK           BIT(6)
+#define DCU_SYN_POL_NEG                        BIT(5)
 #define DCU_SYN_POL_INV_VS_LOW         BIT(1)
 #define DCU_SYN_POL_INV_HS_LOW         BIT(0)
 
 
 #define DCU_CTRLDESCLN(layer, reg)     (0x200 + (reg - 1) * 4 + (layer) * 0x40)
 
-#define DCU_LAYER_HEIGHT(x)            ((x) << 16)
-#define DCU_LAYER_WIDTH(x)             (x)
+#define DCU_LAYER_HEIGHT(x)            (((x) & 0x7ff) << 16)
+#define DCU_LAYER_WIDTH(x)             ((x) & 0x7ff)
 
-#define DCU_LAYER_POSY(x)              ((x) << 16)
-#define DCU_LAYER_POSX(x)              (x)
+#define DCU_LAYER_POSY(x)              (((x) & 0xfff) << 16)
+#define DCU_LAYER_POSX(x)              ((x) & 0xfff)
 
 #define DCU_LAYER_EN                   BIT(31)
 #define DCU_LAYER_TILE_EN              BIT(30)
 #define DCU_LAYER_RLE_EN               BIT(15)
 #define DCU_LAYER_LUOFFS(x)            ((x) << 4)
 #define DCU_LAYER_BB_ON                        BIT(2)
-#define DCU_LAYER_AB(x)                        (x)
+#define DCU_LAYER_AB_NONE              0
+#define DCU_LAYER_AB_CHROMA_KEYING     1
+#define DCU_LAYER_AB_WHOLE_FRAME       2
 
 #define DCU_LAYER_CKMAX_R(x)           ((x) << 16)
 #define DCU_LAYER_CKMAX_G(x)           ((x) << 8)
 struct clk;
 struct device;
 struct drm_device;
+struct drm_flip_work;
 
 struct fsl_dcu_soc_data {
        const char *name;
@@ -173,6 +176,7 @@ struct fsl_dcu_soc_data {
        unsigned int total_layer;
        /*max layer number DCU supported*/
        unsigned int max_layer;
+       unsigned int layer_regs;
 };
 
 struct fsl_dcu_drm_device {
@@ -181,17 +185,27 @@ struct fsl_dcu_drm_device {
        struct regmap *regmap;
        int irq;
        struct clk *clk;
+       struct clk *pix_clk;
+       struct fsl_tcon *tcon;
        /*protects hardware register*/
        spinlock_t irq_lock;
        struct drm_device *drm;
        struct drm_fbdev_cma *fbdev;
-       struct drm_crtc crtc;
+       struct fsl_dcu_drm_crtc crtc;
        struct drm_encoder encoder;
        struct fsl_dcu_drm_connector connector;
        const struct fsl_dcu_soc_data *soc;
+       struct drm_atomic_state *cleanup_state;
+       struct workqueue_struct *unref_wq;
+       struct drm_flip_work unref_work;
+       struct drm_atomic_state *state;
+       unsigned int irq_state;
 };
 
-void fsl_dcu_fbdev_init(struct drm_device *dev);
+void fsl_dcu_fbdev_suspend(struct drm_device *dev);
+void fsl_dcu_fbdev_resume(struct drm_device *dev);
 int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev);
+void fsl_dcu_cleanup_atomic_state(struct drm_device *dev,
+                                 struct drm_atomic_state *state);
 
 #endif /* __FSL_DCU_DRM_DRV_H__ */
index 8b8b819ea704b1ce0714318686ce9c1f64b142ad..eaa447e26423965da0b0effcf8931798122006c0 100644 (file)
@@ -9,15 +9,29 @@
  * (at your option) any later version.
  */
 
+#include <linux/console.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_flip_work.h>
 
 #include "fsl_dcu_drm_drv.h"
 
-/* initialize fbdev helper */
-void fsl_dcu_fbdev_init(struct drm_device *dev)
+void fsl_dcu_fbdev_suspend(struct drm_device *dev)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev);
+
+       console_lock();
+       drm_fb_helper_set_suspend(drm_fbdev_cma_get_helper(fsl_dev->fbdev), 1);
+       console_unlock();
+}
+
+void fsl_dcu_fbdev_resume(struct drm_device *dev)
 {
        struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev);
 
-       fsl_dev->fbdev = drm_fbdev_cma_init(dev, 24, 1, 1);
+       console_lock();
+       drm_fb_helper_set_suspend(drm_fbdev_cma_get_helper(fsl_dev->fbdev), 0);
+       console_unlock();
 }
index 0ef5959710e733aa8c856ebc053e2866db8e1a97..710d1ca7a9b4df7870db209e28317e6bf22d79a7 100644 (file)
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
 #include <drm/drm_fb_cma_helper.h>
 
 #include "fsl_dcu_drm_crtc.h"
 #include "fsl_dcu_drm_drv.h"
 
+void fsl_dcu_cleanup_atomic_state(struct drm_device *dev,
+                                 struct drm_atomic_state *state)
+{
+       drm_atomic_helper_cleanup_planes(dev, state);
+       drm_atomic_state_free(state);
+}
+
+static int fsl_dcu_drm_atomic_commit(struct drm_device *dev,
+                                    struct drm_atomic_state *state,
+                                    bool async)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       int ret;
+
+       ret = drm_atomic_helper_prepare_planes(dev, state);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * This is the point of no return - everything below never fails except
+        * when the hw goes bonghits. Which means we can commit the new state on
+        * the software side now.
+        */
+       drm_atomic_helper_swap_state(dev, state);
+
+       drm_atomic_helper_commit_modeset_disables(dev, state);
+       drm_atomic_helper_commit_planes(dev, state, false);
+       drm_atomic_helper_commit_modeset_enables(dev, state);
+
+       if (async) {
+               fsl_dev->cleanup_state = state;
+       } else {
+               drm_atomic_helper_wait_for_vblanks(dev, state);
+               fsl_dcu_cleanup_atomic_state(dev, state);
+       }
+
+       return 0;
+}
+
 static const struct drm_mode_config_funcs fsl_dcu_drm_mode_config_funcs = {
        .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
+       .atomic_commit = fsl_dcu_drm_atomic_commit,
        .fb_create = drm_fb_cma_create,
 };
 
 int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev)
 {
+       int ret;
+
        drm_mode_config_init(fsl_dev->drm);
 
        fsl_dev->drm->mode_config.min_width = 0;
@@ -33,11 +76,25 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev)
        fsl_dev->drm->mode_config.max_height = 2047;
        fsl_dev->drm->mode_config.funcs = &fsl_dcu_drm_mode_config_funcs;
 
-       drm_kms_helper_poll_init(fsl_dev->drm);
-       fsl_dcu_drm_crtc_create(fsl_dev);
-       fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc);
-       fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder);
+       ret = fsl_dcu_drm_crtc_create(fsl_dev);
+       if (ret)
+               return ret;
+
+       ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc.base);
+       if (ret)
+               goto fail_encoder;
+
+       ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder);
+       if (ret)
+               goto fail_connector;
+
        drm_mode_config_reset(fsl_dev->drm);
+       drm_kms_helper_poll_init(fsl_dev->drm);
 
        return 0;
+fail_encoder:
+       fsl_dev->crtc.base.funcs->destroy(&fsl_dev->crtc.base);
+fail_connector:
+       fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder);
+       return ret;
 }
index 51daaea40b4d882c714609d38a5ced2be8c94a0c..3ad0debacadf6177fd68721ce2bc6db6ceb03eb9 100644 (file)
@@ -15,6 +15,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
@@ -41,11 +42,17 @@ static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane,
 {
        struct drm_framebuffer *fb = state->fb;
 
+       if (!state->fb || !state->crtc)
+               return 0;
+
        switch (fb->pixel_format) {
        case DRM_FORMAT_RGB565:
        case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_XRGB8888:
        case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_BGRA4444:
+       case DRM_FORMAT_XRGB4444:
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_XRGB1555:
        case DRM_FORMAT_ARGB1555:
        case DRM_FORMAT_YUV422:
                return 0;
@@ -59,19 +66,15 @@ static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane,
 {
        struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private;
        unsigned int value;
-       int index, ret;
+       int index;
 
        index = fsl_dcu_drm_plane_index(plane);
        if (index < 0)
                return;
 
-       ret = regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value);
-       if (ret)
-               dev_err(fsl_dev->dev, "read DCU_INT_MASK failed\n");
+       regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value);
        value &= ~DCU_LAYER_EN;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value);
-       if (ret)
-               dev_err(fsl_dev->dev, "set DCU register failed\n");
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value);
 }
 
 static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane,
@@ -82,8 +85,8 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane,
        struct drm_plane_state *state = plane->state;
        struct drm_framebuffer *fb = plane->state->fb;
        struct drm_gem_cma_object *gem;
-       unsigned int alpha, bpp;
-       int index, ret;
+       unsigned int alpha = DCU_LAYER_AB_NONE, bpp;
+       int index;
 
        if (!fb)
                return;
@@ -97,96 +100,69 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane,
        switch (fb->pixel_format) {
        case DRM_FORMAT_RGB565:
                bpp = FSL_DCU_RGB565;
-               alpha = 0xff;
                break;
        case DRM_FORMAT_RGB888:
                bpp = FSL_DCU_RGB888;
-               alpha = 0xff;
                break;
        case DRM_FORMAT_ARGB8888:
+               alpha = DCU_LAYER_AB_WHOLE_FRAME;
+               /* fall-through */
+       case DRM_FORMAT_XRGB8888:
                bpp = FSL_DCU_ARGB8888;
-               alpha = 0xff;
                break;
-       case DRM_FORMAT_BGRA4444:
+       case DRM_FORMAT_ARGB4444:
+               alpha = DCU_LAYER_AB_WHOLE_FRAME;
+               /* fall-through */
+       case DRM_FORMAT_XRGB4444:
                bpp = FSL_DCU_ARGB4444;
-               alpha = 0xff;
                break;
        case DRM_FORMAT_ARGB1555:
+               alpha = DCU_LAYER_AB_WHOLE_FRAME;
+               /* fall-through */
+       case DRM_FORMAT_XRGB1555:
                bpp = FSL_DCU_ARGB1555;
-               alpha = 0xff;
                break;
        case DRM_FORMAT_YUV422:
                bpp = FSL_DCU_YUV422;
-               alpha = 0xff;
                break;
        default:
                return;
        }
 
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1),
-                          DCU_LAYER_HEIGHT(state->crtc_h) |
-                          DCU_LAYER_WIDTH(state->crtc_w));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2),
-                          DCU_LAYER_POSY(state->crtc_y) |
-                          DCU_LAYER_POSX(state->crtc_x));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap,
-                          DCU_CTRLDESCLN(index, 3), gem->paddr);
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4),
-                          DCU_LAYER_EN |
-                          DCU_LAYER_TRANS(alpha) |
-                          DCU_LAYER_BPP(bpp) |
-                          DCU_LAYER_AB(0));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5),
-                          DCU_LAYER_CKMAX_R(0xFF) |
-                          DCU_LAYER_CKMAX_G(0xFF) |
-                          DCU_LAYER_CKMAX_B(0xFF));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6),
-                          DCU_LAYER_CKMIN_R(0) |
-                          DCU_LAYER_CKMIN_G(0) |
-                          DCU_LAYER_CKMIN_B(0));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0);
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8),
-                          DCU_LAYER_FG_FCOLOR(0));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9),
-                          DCU_LAYER_BG_BCOLOR(0));
-       if (ret)
-               goto set_failed;
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1),
+                    DCU_LAYER_HEIGHT(state->crtc_h) |
+                    DCU_LAYER_WIDTH(state->crtc_w));
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2),
+                    DCU_LAYER_POSY(state->crtc_y) |
+                    DCU_LAYER_POSX(state->crtc_x));
+       regmap_write(fsl_dev->regmap,
+                    DCU_CTRLDESCLN(index, 3), gem->paddr);
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4),
+                    DCU_LAYER_EN |
+                    DCU_LAYER_TRANS(0xff) |
+                    DCU_LAYER_BPP(bpp) |
+                    alpha);
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5),
+                    DCU_LAYER_CKMAX_R(0xFF) |
+                    DCU_LAYER_CKMAX_G(0xFF) |
+                    DCU_LAYER_CKMAX_B(0xFF));
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6),
+                    DCU_LAYER_CKMIN_R(0) |
+                    DCU_LAYER_CKMIN_G(0) |
+                    DCU_LAYER_CKMIN_B(0));
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0);
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8),
+                    DCU_LAYER_FG_FCOLOR(0));
+       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9),
+                    DCU_LAYER_BG_BCOLOR(0));
+
        if (!strcmp(fsl_dev->soc->name, "ls1021a")) {
-               ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10),
-                                  DCU_LAYER_POST_SKIP(0) |
-                                  DCU_LAYER_PRE_SKIP(0));
-               if (ret)
-                       goto set_failed;
+               regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10),
+                            DCU_LAYER_POST_SKIP(0) |
+                            DCU_LAYER_PRE_SKIP(0));
        }
-       ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
-                                DCU_MODE_DCU_MODE_MASK,
-                                DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
-       if (ret)
-               goto set_failed;
-       ret = regmap_write(fsl_dev->regmap,
-                          DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
-       if (ret)
-               goto set_failed;
-       return;
 
-set_failed:
-       dev_err(fsl_dev->dev, "set DCU register failed\n");
+       return;
 }
 
 static void
@@ -213,6 +189,7 @@ static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = {
 static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane)
 {
        drm_plane_cleanup(plane);
+       kfree(plane);
 }
 
 static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = {
@@ -227,34 +204,69 @@ static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = {
 static const u32 fsl_dcu_drm_plane_formats[] = {
        DRM_FORMAT_RGB565,
        DRM_FORMAT_RGB888,
+       DRM_FORMAT_XRGB8888,
        DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB4444,
        DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB1555,
        DRM_FORMAT_ARGB1555,
        DRM_FORMAT_YUV422,
 };
 
-struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev)
+void fsl_dcu_drm_init_planes(struct drm_device *dev)
 {
-       struct drm_plane *primary;
-       int ret;
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       int i, j;
 
-       primary = kzalloc(sizeof(*primary), GFP_KERNEL);
-       if (!primary) {
-               DRM_DEBUG_KMS("Failed to allocate primary plane\n");
-               return NULL;
+       for (i = 0; i < fsl_dev->soc->total_layer; i++) {
+               for (j = 1; j <= fsl_dev->soc->layer_regs; j++)
+                       regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, j), 0);
        }
+}
+
+int fsl_dcu_drm_create_planes(struct drm_device *dev, struct drm_plane **primary,
+                             struct drm_plane **cursor)
+{
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+       struct drm_plane *planes, *plane;
+       int total_layer = fsl_dev->soc->total_layer;
+       int ret, i;
 
-       /* possible_crtc's will be filled in later by crtc_init */
-       ret = drm_universal_plane_init(dev, primary, 0,
+       planes = devm_kzalloc(dev->dev, sizeof(struct drm_plane) * total_layer,
+                             GFP_KERNEL);
+       if (!planes) {
+               DRM_DEBUG_KMS("Failed to allocate planes\n");
+               return -ENOMEM;
+       }
+
+       plane = planes;
+
+       for (i = 0; i < total_layer; i++) {
+               enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
+               if (i == 0) {
+                       type = DRM_PLANE_TYPE_PRIMARY;
+                       *primary = plane;
+               } else if (i == total_layer - 1) {
+                       type = DRM_PLANE_TYPE_CURSOR;
+                       *cursor = plane;
+               }
+
+               ret = drm_universal_plane_init(dev, plane, 1,
                                       &fsl_dcu_drm_plane_funcs,
                                       fsl_dcu_drm_plane_formats,
                                       ARRAY_SIZE(fsl_dcu_drm_plane_formats),
-                                      DRM_PLANE_TYPE_PRIMARY);
-       if (ret) {
-               kfree(primary);
-               primary = NULL;
+                                      type);
+               if (ret)
+                       goto err_cleanup_planes;
+
+               drm_plane_helper_add(plane, &fsl_dcu_drm_plane_helper_funcs);
+               plane++;
        }
-       drm_plane_helper_add(primary, &fsl_dcu_drm_plane_helper_funcs);
 
-       return primary;
+       return 0;
+
+err_cleanup_planes:
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               drm_plane_cleanup(plane);
+       return ret;
 }
index d657f088d859369fddc797e7c7b166210f32dc25..be3604fb43ce6affa08c007ffe53854b316a753e 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __FSL_DCU_DRM_PLANE_H__
 #define __FSL_DCU_DRM_PLANE_H__
 
-struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev);
+void fsl_dcu_drm_init_planes(struct drm_device *dev);
+int fsl_dcu_drm_create_planes(struct drm_device *dev, struct drm_plane **primary,
+                             struct drm_plane **cursor);
 
 #endif /* __FSL_DCU_DRM_PLANE_H__ */
index fe8ab5da04fb0eabff083b70dac6e9ee09640496..4246129972b6ce096b067505245f7263ae56950d 100644 (file)
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
 #include <drm/drm_panel.h>
 
 #include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
 
 static int
 fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
@@ -56,6 +58,10 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
        int ret;
 
        encoder->possible_crtcs = 1;
+
+       /* Set TCON to bypass for parallel RGB/LVDS */
+       fsl_tcon_bypass_enable(fsl_dev->tcon);
+
        ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs,
                               DRM_MODE_ENCODER_LVDS);
        if (ret < 0)
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
new file mode 100644 (file)
index 0000000..e5001a1
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "fsl_tcon.h"
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
+{
+       regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+                          FSL_TCON_CTRL1_TCON_BYPASS, 0);
+}
+
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
+{
+       regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+                          FSL_TCON_CTRL1_TCON_BYPASS,
+                          FSL_TCON_CTRL1_TCON_BYPASS);
+}
+
+static struct regmap_config fsl_tcon_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .name = "tcon",
+};
+
+static int fsl_tcon_init_regmap(struct device *dev,
+                               struct fsl_tcon *tcon,
+                               struct device_node *np)
+{
+       struct resource res;
+       void __iomem *regs;
+
+       if (of_address_to_resource(np, 0, &res))
+               return -EINVAL;
+
+       regs = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       tcon->regs = devm_regmap_init_mmio(dev, regs,
+                                          &fsl_tcon_regmap_config);
+       if (IS_ERR(tcon->regs))
+               return PTR_ERR(tcon->regs);
+
+       return 0;
+}
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev)
+{
+       struct fsl_tcon *tcon;
+       struct device_node *np;
+       int ret;
+
+       tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
+       if (!tcon)
+               return NULL;
+
+       np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
+       if (!np) {
+               dev_warn(dev, "Couldn't find the tcon node\n");
+               return NULL;
+       }
+
+       ret = fsl_tcon_init_regmap(dev, tcon, np);
+       if (ret) {
+               dev_err(dev, "Couldn't create the TCON regmap\n");
+               goto err_node_put;
+       }
+
+       tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
+       if (IS_ERR(tcon->ipg_clk)) {
+               dev_err(dev, "Couldn't get the TCON bus clock\n");
+               goto err_node_put;
+       }
+
+       clk_prepare_enable(tcon->ipg_clk);
+
+       return tcon;
+
+err_node_put:
+       of_node_put(np);
+       return NULL;
+}
+
+void fsl_tcon_free(struct fsl_tcon *tcon)
+{
+       clk_disable_unprepare(tcon->ipg_clk);
+       clk_put(tcon->ipg_clk);
+}
+
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
new file mode 100644 (file)
index 0000000..80a7617
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_TCON_H__
+#define __FSL_TCON_H__
+
+#include <linux/bitops.h>
+
+#define FSL_TCON_CTRL1                 0x0
+#define FSL_TCON_CTRL1_TCON_BYPASS     BIT(29)
+
+struct fsl_tcon {
+       struct regmap           *regs;
+       struct clk              *ipg_clk;
+};
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev);
+void fsl_tcon_free(struct fsl_tcon *tcon);
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon);
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon);
+
+#endif /* __FSL_TCON_H__ */
index 5378bdc3bbf9fcb8432409cc7d18d2d2a6a4ff4b..5f936223e67b9cd73c81c4a57c388177d2b11263 100644 (file)
@@ -313,6 +313,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
                dev_warn(drm->dev, "Invalid legacyfb_depth.  Defaulting to 16bpp\n");
                legacyfb_depth = 16;
        }
+       drm_helper_disable_unused_functions(drm);
        imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
                                drm->mode_config.num_crtc, MAX_CRTC);
        if (IS_ERR(imxdrm->fbhelper)) {
index f418c002d3235bd94dc32e4a348d57deb3f73c8a..5101e2544f969988a4a02150b846f0374fe9ecfb 100644 (file)
@@ -68,6 +68,7 @@ struct panel_desc {
        } delay;
 
        u32 bus_format;
+       u32 bus_flags;
 };
 
 struct panel_simple {
@@ -140,6 +141,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
        if (panel->desc->bus_format)
                drm_display_info_set_bus_formats(&connector->display_info,
                                                 &panel->desc->bus_format, 1);
+       connector->display_info.bus_flags = panel->desc->bus_flags;
 
        return num;
 }
@@ -951,6 +953,29 @@ static const struct panel_desc lg_lp129qe = {
        },
 };
 
+static const struct drm_display_mode logic_lt161010_2nhc_mode = {
+       .clock = 33300,
+       .hdisplay = 800,
+       .hsync_start = 800 + 40,
+       .hsync_end = 800 + 40 + 128,
+       .htotal = 800 + 40 + 128 + 88,
+       .vdisplay = 480,
+       .vsync_start = 480 + 10,
+       .vsync_end = 480 + 2 + 10,
+       .vtotal = 480 + 2 + 10 + 33,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc logic_lt161010_2nhc = {
+       .modes = &logic_lt161010_2nhc_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 165,
+               .height = 100,
+       },
+       .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+};
+
 static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
        .clock = 10870,
        .hdisplay = 480,
@@ -962,6 +987,7 @@ static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
        .vsync_end = 272 + 2 + 4,
        .vtotal = 272 + 2 + 4 + 2,
        .vrefresh = 74,
+       .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
 };
 
 static const struct panel_desc nec_nl4827hc19_05b = {
@@ -972,7 +998,8 @@ static const struct panel_desc nec_nl4827hc19_05b = {
                .width = 95,
                .height = 54,
        },
-       .bus_format = MEDIA_BUS_FMT_RGB888_1X24
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+       .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
 };
 
 static const struct display_timing okaya_rs800480t_7x0gp_timing = {
@@ -1098,6 +1125,51 @@ static const struct panel_desc shelly_sca07010_bfn_lnn = {
        .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
 };
 
+static const struct drm_display_mode tpk_f07a_0102_mode = {
+       .clock = 33260,
+       .hdisplay = 800,
+       .hsync_start = 800 + 40,
+       .hsync_end = 800 + 40 + 128,
+       .htotal = 800 + 40 + 128 + 88,
+       .vdisplay = 480,
+       .vsync_start = 480 + 10,
+       .vsync_end = 480 + 10 + 2,
+       .vtotal = 480 + 10 + 2 + 33,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc tpk_f07a_0102 = {
+       .modes = &tpk_f07a_0102_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 152,
+               .height = 91,
+       },
+       .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+};
+
+static const struct drm_display_mode tpk_f10a_0102_mode = {
+       .clock = 45000,
+       .hdisplay = 1024,
+       .hsync_start = 1024 + 176,
+       .hsync_end = 1024 + 176 + 5,
+       .htotal = 1024 + 176 + 5 + 88,
+       .vdisplay = 600,
+       .vsync_start = 600 + 20,
+       .vsync_end = 600 + 20 + 5,
+       .vtotal = 600 + 20 + 5 + 25,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc tpk_f10a_0102 = {
+       .modes = &tpk_f10a_0102_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 223,
+               .height = 125,
+       },
+};
+
 static const struct of_device_id platform_of_match[] = {
        {
                .compatible = "ampire,am800480r3tmqwa1h",
@@ -1174,6 +1246,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "lg,lp129qe",
                .data = &lg_lp129qe,
+       }, {
+               .compatible = "logic,lt161010-2nhc",
+               .data = &logic_lt161010_2nhc,
        }, {
                .compatible = "nec,nl4827hc19-05b",
                .data = &nec_nl4827hc19_05b,
@@ -1192,6 +1267,12 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "shelly,sca07010-bfn-lnn",
                .data = &shelly_sca07010_bfn_lnn,
+       }, {
+               .compatible = "tpk,f07a-0102",
+               .data = &tpk_f07a_0102,
+       }, {
+               .compatible = "tpk,f10a-0102",
+               .data = &tpk_f10a_0102,
        }, {
                /* sentinel */
        }
index 1469987949d88f8b43cfdd97990e925156a149ec..506b5626f3ed2ed3a188ff9bc1e51144e40b8932 100644 (file)
@@ -160,6 +160,7 @@ static int sti_load(struct drm_device *dev, unsigned long flags)
 
        drm_mode_config_reset(dev);
 
+       drm_helper_disable_unused_functions(dev);
        drm_fbdev_cma_init(dev, 32,
                           dev->mode_config.num_crtc,
                           dev->mode_config.num_connector);
index 876cad58b1f9ee1863d8de1bccf737b75b597ec6..24be31d697014266ba6ea06eb68506b9593f8a60 100644 (file)
@@ -294,6 +294,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
                        break;
        }
 
+       drm_helper_disable_unused_functions(dev);
        priv->fbdev = drm_fbdev_cma_init(dev, bpp,
                        dev->mode_config.num_crtc,
                        dev->mode_config.num_connector);
index d4d853680ae47881151effb56269e4e660d522e1..ee4d7b7d4aa25d14e0b717fd7bca262d7ea540a6 100644 (file)
@@ -988,11 +988,24 @@ static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap)
        pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_default);
 }
 
-static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
+/*
+ * We switch SCL and SDA to their GPIO function and do some bitbanging
+ * for bus recovery. These alternative pinmux settings can be
+ * described in the device tree by a separate pinctrl state "gpio". If
+ * this is missing this is not a big problem, the only implication is
+ * that we can't do bus recovery.
+ */
+static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
                struct platform_device *pdev)
 {
        struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo;
 
+       i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
+       if (!i2c_imx->pinctrl || IS_ERR(i2c_imx->pinctrl)) {
+               dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n");
+               return PTR_ERR(i2c_imx->pinctrl);
+       }
+
        i2c_imx->pinctrl_pins_default = pinctrl_lookup_state(i2c_imx->pinctrl,
                        PINCTRL_STATE_DEFAULT);
        i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
@@ -1002,12 +1015,15 @@ static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
        rinfo->scl_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
                        "scl-gpios", 0, NULL);
 
-       if (!gpio_is_valid(rinfo->sda_gpio) ||
-           !gpio_is_valid(rinfo->scl_gpio) ||
-           IS_ERR(i2c_imx->pinctrl_pins_default) ||
-           IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
+       if (rinfo->sda_gpio == -EPROBE_DEFER ||
+           rinfo->scl_gpio == -EPROBE_DEFER) {
+               return -EPROBE_DEFER;
+       } else if (!gpio_is_valid(rinfo->sda_gpio) ||
+                  !gpio_is_valid(rinfo->scl_gpio) ||
+                  IS_ERR(i2c_imx->pinctrl_pins_default) ||
+                  IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
                dev_dbg(&pdev->dev, "recovery information incomplete\n");
-               return;
+               return 0;
        }
 
        dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n",
@@ -1017,6 +1033,8 @@ static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
        rinfo->unprepare_recovery = i2c_imx_unprepare_recovery;
        rinfo->recover_bus = i2c_generic_gpio_recovery;
        i2c_imx->adapter.bus_recovery_info = rinfo;
+
+       return 0;
 }
 
 static u32 i2c_imx_func(struct i2c_adapter *adapter)
@@ -1087,12 +1105,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
                return ret;
        }
 
-       i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
-       if (IS_ERR(i2c_imx->pinctrl)) {
-               ret = PTR_ERR(i2c_imx->pinctrl);
-               goto clk_disable;
-       }
-
        /* Request IRQ */
        ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
                                pdev->name, i2c_imx);
@@ -1119,7 +1131,11 @@ static int i2c_imx_probe(struct platform_device *pdev)
                        i2c_imx, IMX_I2C_I2CR);
        imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
 
-       i2c_imx_init_recovery_info(i2c_imx, pdev);
+       /* Init optional bus recovery function */
+       ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
+       /* Give it another chance if pinctrl used is not ready yet */
+       if (ret == -EPROBE_DEFER)
+               goto clk_disable;
 
        /* Add I2C adapter */
        ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
index e701e28fb1cd6009212bae9a19c4ac81ae8cb90f..75e97081994fbc68f2203a35b5268fde9d2b1fdb 100644 (file)
@@ -196,4 +196,14 @@ config MCP4922
          To compile this driver as a module, choose M here: the module
          will be called mcp4922.
 
+config VF610_DAC
+       tristate "Vybrid vf610 DAC driver"
+       depends on OF
+       depends on HAS_IOMEM
+       help
+         Say yes here to support Vybrid board digital-to-analog converter.
+
+         This driver can also be built as a module. If so, the module will
+         be called vf610_dac.
+
 endmenu
index 63ae05633e0c956ed96f833a5afffd00fe500812..93feb2717671c5ed7590d544c597ec75265e0579 100644 (file)
@@ -21,3 +21,4 @@ obj-$(CONFIG_MAX517) += max517.o
 obj-$(CONFIG_MAX5821) += max5821.o
 obj-$(CONFIG_MCP4725) += mcp4725.o
 obj-$(CONFIG_MCP4922) += mcp4922.o
+obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
new file mode 100644 (file)
index 0000000..c4ec777
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Freescale Vybrid vf610 DAC driver
+ *
+ * Copyright 2016 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VF610_DACx_STATCTRL            0x20
+
+#define VF610_DAC_DACEN                        BIT(15)
+#define VF610_DAC_DACRFS               BIT(14)
+#define VF610_DAC_LPEN                 BIT(11)
+
+#define VF610_DAC_DAT0(x)              ((x) & 0xFFF)
+
+enum vf610_conversion_mode_sel {
+       VF610_DAC_CONV_HIGH_POWER,
+       VF610_DAC_CONV_LOW_POWER,
+};
+
+struct vf610_dac {
+       struct clk *clk;
+       struct device *dev;
+       enum vf610_conversion_mode_sel conv_mode;
+       void __iomem *regs;
+};
+
+static void vf610_dac_init(struct vf610_dac *info)
+{
+       int val;
+
+       info->conv_mode = VF610_DAC_CONV_LOW_POWER;
+       val = VF610_DAC_DACEN | VF610_DAC_DACRFS |
+               VF610_DAC_LPEN;
+       writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static void vf610_dac_exit(struct vf610_dac *info)
+{
+       int val;
+
+       val = readl(info->regs + VF610_DACx_STATCTRL);
+       val &= ~VF610_DAC_DACEN;
+       writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+                               const struct iio_chan_spec *chan,
+                               unsigned int mode)
+{
+       struct vf610_dac *info = iio_priv(indio_dev);
+       int val;
+
+       mutex_lock(&indio_dev->mlock);
+       info->conv_mode = mode;
+       val = readl(info->regs + VF610_DACx_STATCTRL);
+       if (mode)
+               val |= VF610_DAC_LPEN;
+       else
+               val &= ~VF610_DAC_LPEN;
+       writel(val, info->regs + VF610_DACx_STATCTRL);
+       mutex_unlock(&indio_dev->mlock);
+
+       return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+                               const struct iio_chan_spec *chan)
+{
+       struct vf610_dac *info = iio_priv(indio_dev);
+
+       return info->conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "high-power", "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+       .items = vf610_conv_modes,
+       .num_items = ARRAY_SIZE(vf610_conv_modes),
+       .get = vf610_get_conversion_mode,
+       .set = vf610_set_conversion_mode,
+};
+
+static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
+       IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR,
+               &vf610_conversion_mode),
+       {},
+};
+
+#define VF610_DAC_CHAN(_chan_type) { \
+       .type = (_chan_type), \
+       .output = 1, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+       .ext_info = vf610_ext_info, \
+}
+
+static const struct iio_chan_spec vf610_dac_iio_channels[] = {
+       VF610_DAC_CHAN(IIO_VOLTAGE),
+};
+
+static int vf610_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *chan,
+                       int *val, int *val2,
+                       long mask)
+{
+       struct vf610_dac *info = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               *val = VF610_DAC_DAT0(readl(info->regs));
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               /*
+                * DACRFS is always 1 for valid reference and typical
+                * reference voltage as per Vybrid datasheet is 3.3V
+                * from section 9.1.2.1 of Vybrid datasheet
+                */
+               *val = 3300 /* mV */;
+               *val2 = 12;
+               return IIO_VAL_FRACTIONAL_LOG2;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int vf610_write_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *chan,
+                       int val, int val2,
+                       long mask)
+{
+       struct vf610_dac *info = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               writel(VF610_DAC_DAT0(val), info->regs);
+               mutex_unlock(&indio_dev->mlock);
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info vf610_dac_iio_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = &vf610_read_raw,
+       .write_raw = &vf610_write_raw,
+};
+
+static const struct of_device_id vf610_dac_match[] = {
+       { .compatible = "fsl,vf610-dac", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_dac_match);
+
+static int vf610_dac_probe(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev;
+       struct vf610_dac *info;
+       struct resource *mem;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&pdev->dev,
+                                       sizeof(struct vf610_dac));
+       if (!indio_dev) {
+               dev_err(&pdev->dev, "Failed allocating iio device\n");
+               return -ENOMEM;
+       }
+
+       info = iio_priv(indio_dev);
+       info->dev = &pdev->dev;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       info->regs = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(info->regs))
+               return PTR_ERR(info->regs);
+
+       info->clk = devm_clk_get(&pdev->dev, "dac");
+       if (IS_ERR(info->clk)) {
+               dev_err(&pdev->dev, "Failed getting clock, err = %ld\n",
+                       PTR_ERR(info->clk));
+               return PTR_ERR(info->clk);
+       }
+
+       platform_set_drvdata(pdev, indio_dev);
+
+       indio_dev->name = dev_name(&pdev->dev);
+       indio_dev->dev.parent = &pdev->dev;
+       indio_dev->dev.of_node = pdev->dev.of_node;
+       indio_dev->info = &vf610_dac_iio_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = vf610_dac_iio_channels;
+       indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels);
+
+       ret = clk_prepare_enable(info->clk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Could not prepare or enable the clock\n");
+               return ret;
+       }
+
+       vf610_dac_init(info);
+
+       ret = iio_device_register(indio_dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Couldn't register the device\n");
+               goto error_iio_device_register;
+       }
+
+       return 0;
+
+error_iio_device_register:
+       clk_disable_unprepare(info->clk);
+
+       return ret;
+}
+
+static int vf610_dac_remove(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct vf610_dac *info = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       vf610_dac_exit(info);
+       clk_disable_unprepare(info->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vf610_dac_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct vf610_dac *info = iio_priv(indio_dev);
+
+       vf610_dac_exit(info);
+       clk_disable_unprepare(info->clk);
+
+       return 0;
+}
+
+static int vf610_dac_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct vf610_dac *info = iio_priv(indio_dev);
+       int ret;
+
+       ret = clk_prepare_enable(info->clk);
+       if (ret)
+               return ret;
+
+       vf610_dac_init(info);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume);
+
+static struct platform_driver vf610_dac_driver = {
+       .probe          = vf610_dac_probe,
+       .remove         = vf610_dac_remove,
+       .driver         = {
+               .name   = "vf610-dac",
+               .of_match_table = vf610_dac_match,
+               .pm     = &vf610_dac_pm_ops,
+       },
+};
+module_platform_driver(vf610_dac_driver);
+
+MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
+MODULE_DESCRIPTION("Freescale VF610 DAC driver");
+MODULE_LICENSE("GPL v2");
index ae33da7ab51ff7f5f84464366be1c4b9f8030527..36bcc1417420778f238863d53ee79ae940653579 100644 (file)
@@ -619,6 +619,13 @@ config TOUCHSCREEN_MIGOR
          To compile this driver as a module, choose M here: the
          module will be called migor_ts.
 
+config TOUCHSCREEN_FUSION_F0710A
+       tristate "TouchRevolution Fusion F0710A Touchscreens"
+       depends on I2C
+       help
+         Say Y here if you want to support the multi-touch input driver for
+         the TouchRevolution Fusion 7 and 10 panels.
+
 config TOUCHSCREEN_TOUCHRIGHT
        tristate "Touchright serial touchscreen"
        select SERIO
index cbaa6abb08dae6c58ea1125b0fc0baa335dbc444..4ae9f5c8f6f2d798c9714800f1a396dbcfbca638 100644 (file)
@@ -91,3 +91,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X)    += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)       += zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_FUSION_F0710A)        += fusion_F0710A.o
index 5d4903a402cc6a5f183010ded6e7af17c1136a49..51e86f80cdba3c61e6151a76fda26c32dc9d58e5 100644 (file)
@@ -100,6 +100,8 @@ static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts)
 
        /* Wait for the pull-up to be stable on high */
        usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US);
+
+       enable_irq(vf50_ts->pen_irq);
 }
 
 /*
@@ -112,6 +114,8 @@ static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
        int val_x, val_y, val_z1, val_z2, val_p = 0;
        bool discard_val_on_start = true;
 
+       disable_irq_nosync(vf50_ts->pen_irq);
+
        /* Disable the touch detection plates */
        gpiod_set_value(vf50_ts->gpio_ym, 0);
 
@@ -202,7 +206,8 @@ static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
        input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0);
        input_sync(vf50_ts->ts_input);
 
-       vf50_ts_enable_touch_detection(vf50_ts);
+       if (!vf50_ts->stop_touchscreen)
+               vf50_ts_enable_touch_detection(vf50_ts);
 
        return IRQ_HANDLED;
 }
@@ -353,6 +358,7 @@ static int vf50_ts_probe(struct platform_device *pdev)
        if (touchdev->pen_irq < 0)
                return touchdev->pen_irq;
 
+       touchdev->stop_touchscreen = true;
        error = devm_request_threaded_irq(dev, touchdev->pen_irq,
                                          NULL, vf50_ts_irq_bh, IRQF_ONESHOT,
                                          "vf50 touch", touchdev);
diff --git a/drivers/input/touchscreen/fusion_F0710A.c b/drivers/input/touchscreen/fusion_F0710A.c
new file mode 100644 (file)
index 0000000..862725b
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ *  "fusion_F0710A"  touchscreen 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <linux/gpio.h>
+#include <linux/input/fusion_F0710A.h>
+#include <linux/input/mt.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+
+
+#include "fusion_F0710A.h"
+
+#define DRV_NAME               "fusion_F0710A"
+#define MAX_TOUCHES 2
+
+static struct fusion_F0710A_data fusion_F0710A;
+
+static unsigned short normal_i2c[] = { fusion_F0710A_I2C_SLAVE_ADDR, I2C_CLIENT_END };
+
+static int fusion_F0710A_write_u8(u8 addr, u8 data)
+{
+       return i2c_smbus_write_byte_data(fusion_F0710A.client, addr, data);
+}
+
+static int fusion_F0710A_read_u8(u8 addr)
+{
+       return i2c_smbus_read_byte_data(fusion_F0710A.client, addr);
+}
+
+static int fusion_F0710A_read_block(u8 addr, u8 len, u8 *data)
+{
+       u8 msgbuf0[1] = { addr };
+       u16 slave = fusion_F0710A.client->addr;
+       u16 flags = fusion_F0710A.client->flags;
+       struct i2c_msg msg[2] = { { slave, flags, 1, msgbuf0 },
+                                 { slave, flags | I2C_M_RD, len, data }
+       };
+
+       return i2c_transfer(fusion_F0710A.client->adapter, msg, ARRAY_SIZE(msg));
+}
+
+static int fusion_F0710A_register_input(void)
+{
+       int ret;
+       struct input_dev *dev;
+
+       dev = fusion_F0710A.input = input_allocate_device();
+       if (dev == NULL)
+               return -ENOMEM;
+
+       dev->name = "fusion_F0710A";
+
+       set_bit(EV_KEY, dev->evbit);
+       set_bit(EV_ABS, dev->evbit);
+       set_bit(EV_SYN, dev->evbit);
+       set_bit(BTN_TOUCH, dev->keybit);
+
+       input_mt_init_slots(dev, MAX_TOUCHES, 0);
+       input_set_abs_params(dev, ABS_MT_POSITION_X, 0, fusion_F0710A.info.xres-1, 0, 0);
+       input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, fusion_F0710A.info.yres-1, 0, 0);
+       input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+       input_set_abs_params(dev, ABS_X, 0, fusion_F0710A.info.xres-1, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 0, fusion_F0710A.info.yres-1, 0, 0);
+       input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+       ret = input_register_device(dev);
+       if (ret < 0)
+               goto bail1;
+
+       return 0;
+
+bail1:
+       input_free_device(dev);
+       return ret;
+}
+
+static void fusion_F0710A_reset(void)
+{
+       /* Generate a 0 => 1 edge explicitly, and wait for startup... */
+       gpio_set_value(fusion_F0710A.gpio_reset, 0);
+       msleep(10);
+       gpio_set_value(fusion_F0710A.gpio_reset, 1);
+       /* Wait for startup (up to 125ms according to datasheet) */
+       msleep(125);
+}
+
+#define WC_RETRY_COUNT         3
+static int fusion_F0710A_write_complete(void)
+{
+       int ret, i;
+
+       for (i = 0; i < WC_RETRY_COUNT; i++)
+       {
+               ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0);
+               if (!ret)
+                       break;
+
+               dev_warn(&fusion_F0710A.client->dev,
+                       "Write complete failed(%d): %d. Resetting controller...\n", i, ret);
+               fusion_F0710A_reset();
+       }
+
+       return ret;
+}
+
+#define DATA_START     fusion_F0710A_DATA_INFO
+#define        DATA_END        fusion_F0710A_SEC_TIDTS
+#define DATA_LEN       (DATA_END - DATA_START + 1)
+#define DATA_OFF(x)    ((x) - DATA_START)
+
+static int fusion_F0710A_read_sensor(void)
+{
+       int ret;
+       u8 data[DATA_LEN];
+
+#define DATA(x) (data[DATA_OFF(x)])
+       /* To ensure data coherency, read the sensor with a single transaction. */
+       ret = fusion_F0710A_read_block(DATA_START, DATA_LEN, data);
+       if (ret < 0) {
+               dev_err(&fusion_F0710A.client->dev,
+                       "Read block failed: %d\n", ret);
+
+               return ret;
+       }
+
+       fusion_F0710A.f_num = DATA(fusion_F0710A_DATA_INFO)&0x03;
+
+       fusion_F0710A.y1 = DATA(fusion_F0710A_POS_X1_HI) << 8;
+       fusion_F0710A.y1 |= DATA(fusion_F0710A_POS_X1_LO);
+       fusion_F0710A.x1 = DATA(fusion_F0710A_POS_Y1_HI) << 8;
+       fusion_F0710A.x1 |= DATA(fusion_F0710A_POS_Y1_LO);
+       fusion_F0710A.z1 = DATA(fusion_F0710A_FIR_PRESS);
+       fusion_F0710A.tip1 = DATA(fusion_F0710A_FIR_TIDTS)&0x0f;
+       fusion_F0710A.tid1 = (DATA(fusion_F0710A_FIR_TIDTS)&0xf0)>>4;
+
+       fusion_F0710A.y2 = DATA(fusion_F0710A_POS_X2_HI) << 8;
+       fusion_F0710A.y2 |= DATA(fusion_F0710A_POS_X2_LO);
+       fusion_F0710A.x2 = DATA(fusion_F0710A_POS_Y2_HI) << 8;
+       fusion_F0710A.x2 |= DATA(fusion_F0710A_POS_Y2_LO);
+       fusion_F0710A.z2 = DATA(fusion_F0710A_SEC_PRESS);
+       fusion_F0710A.tip2 = DATA(fusion_F0710A_SEC_TIDTS)&0x0f;
+       fusion_F0710A.tid2 =(DATA(fusion_F0710A_SEC_TIDTS)&0xf0)>>4;
+#undef DATA
+
+       return 0;
+}
+
+#define val_cut_max(x, max, reverse)   \
+do                                     \
+{                                      \
+       if(x > max)                     \
+               x = max;                \
+       if(reverse)                     \
+               x = (max) - (x);        \
+}                                      \
+while(0)
+
+static void fusion_F0710A_wq(struct work_struct *work)
+{
+       struct input_dev *dev = fusion_F0710A.input;
+
+       if (fusion_F0710A_read_sensor() < 0)
+               goto restore_irq;
+
+#ifdef DEBUG
+       printk(KERN_DEBUG "tip1, tid1, x1, y1, z1 (%x,%x,%d,%d,%d); tip2, tid2, x2, y2, z2 (%x,%x,%d,%d,%d)\n",
+               fusion_F0710A.tip1, fusion_F0710A.tid1, fusion_F0710A.x1, fusion_F0710A.y1, fusion_F0710A.z1,
+               fusion_F0710A.tip2, fusion_F0710A.tid2, fusion_F0710A.x2, fusion_F0710A.y2, fusion_F0710A.z2);
+#endif /* DEBUG */
+
+       val_cut_max(fusion_F0710A.x1, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse);
+       val_cut_max(fusion_F0710A.y1, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse);
+       val_cut_max(fusion_F0710A.x2, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse);
+       val_cut_max(fusion_F0710A.y2, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse);
+
+       if (fusion_F0710A.tid1) {
+               input_mt_slot(dev, fusion_F0710A.tid1 - 1);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, fusion_F0710A.tip1);
+               if (fusion_F0710A.tip1) {
+                       input_report_abs(dev, ABS_MT_POSITION_X, fusion_F0710A.x1);
+                       input_report_abs(dev, ABS_MT_POSITION_Y, fusion_F0710A.y1);
+                       input_report_abs(dev, ABS_MT_PRESSURE, fusion_F0710A.z1);
+               }
+       }
+
+       if (fusion_F0710A.tid2) {
+               input_mt_slot(dev, fusion_F0710A.tid2 - 1);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, fusion_F0710A.tip2);
+               if (fusion_F0710A.tip2) {
+                       input_report_abs(dev, ABS_MT_POSITION_X, fusion_F0710A.x2);
+                       input_report_abs(dev, ABS_MT_POSITION_Y, fusion_F0710A.y2);
+                       input_report_abs(dev, ABS_MT_PRESSURE, fusion_F0710A.z2);
+               }
+       }
+
+       input_mt_report_pointer_emulation(dev, false);
+       input_sync(dev);
+
+restore_irq:
+       enable_irq(fusion_F0710A.client->irq);
+
+       /* Clear fusion_F0710A interrupt */
+       fusion_F0710A_write_complete();
+}
+
+static DECLARE_WORK(fusion_F0710A_work, fusion_F0710A_wq);
+
+static irqreturn_t fusion_F0710A_interrupt(int irq, void *dev_id)
+{
+       disable_irq_nosync(fusion_F0710A.client->irq);
+
+       queue_work(fusion_F0710A.workq, &fusion_F0710A_work);
+
+       return IRQ_HANDLED;
+}
+
+const static u8* g_ver_product[4] = {
+       "10Z8", "70Z7", "43Z6", ""
+};
+
+static int of_fusion_F0710A_get_pins(struct device_node *np,
+                               unsigned int *int_pin, unsigned int *reset_pin)
+{
+       if (of_gpio_count(np) < 2)
+               return -ENODEV;
+
+       *int_pin = of_get_gpio(np, 0);
+       *reset_pin = of_get_gpio(np, 1);
+
+       if (!gpio_is_valid(*int_pin) || !gpio_is_valid(*reset_pin)) {
+               pr_err("%s: invalid GPIO pins, int=%d/reset=%d\n",
+                      np->full_name, *int_pin, *reset_pin);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int fusion_F0710A_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+       struct device_node *np = i2c->dev.of_node;
+       struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data;
+       int ret;
+       u8 ver_product, ver_id;
+       u32 version;
+
+       if (np != NULL) {
+               pdata = i2c->dev.platform_data =
+                       devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
+               if (pdata == NULL) {
+                       dev_err(&i2c->dev, "No platform data for Fusion driver\n");
+                       return -ENODEV;
+               }
+               /* the dtb did the pinmuxing for us */
+               pdata->pinmux_fusion_pins = NULL;
+               ret = of_fusion_F0710A_get_pins(i2c->dev.of_node,
+                               &pdata->gpio_int, &pdata->gpio_reset);
+               if (ret)
+                       return ret;
+       } else if (pdata == NULL) {
+               dev_err(&i2c->dev, "No platform data for Fusion driver\n");
+               return -ENODEV;
+       }
+
+       /* Request pinmuxing, if necessary */
+       if (pdata->pinmux_fusion_pins != NULL) {
+               ret = pdata->pinmux_fusion_pins();
+               if (ret < 0) {
+                       dev_err(&i2c->dev, "muxing GPIOs failed\n");
+                       return -ENODEV;
+               }
+       }
+
+       if ((gpio_request(pdata->gpio_int, "Fusion pen down interrupt") == 0) &&
+           (gpio_direction_input(pdata->gpio_int) == 0)) {
+               gpio_export(pdata->gpio_int, 0);
+       } else {
+               dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion pen down\n");
+               return -ENODEV;
+       }
+
+       if ((gpio_request(pdata->gpio_reset, "Fusion reset") == 0) &&
+           (gpio_direction_output(pdata->gpio_reset, 1) == 0)) {
+               fusion_F0710A.gpio_reset = pdata->gpio_reset;
+               fusion_F0710A_reset();
+               gpio_export(pdata->gpio_reset, 0);
+       } else {
+               dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion reset\n");
+               ret = -ENODEV;
+               goto bail0;
+       }
+
+       /* Use Pen Down GPIO as sampling interrupt */
+       i2c->irq = gpio_to_irq(pdata->gpio_int);
+       irq_set_irq_type(i2c->irq, IRQ_TYPE_LEVEL_HIGH);
+
+       if (!i2c->irq) {
+               dev_err(&i2c->dev, "fusion_F0710A irq < 0 \n");
+               ret = -ENOMEM;
+               goto bail1;
+       }
+
+       /* Attach the I2C client */
+       fusion_F0710A.client =  i2c;
+       i2c_set_clientdata(i2c, &fusion_F0710A);
+
+       dev_info(&i2c->dev, "Touchscreen registered with bus id (%d) with slave address 0x%x\n",
+                       i2c_adapter_id(fusion_F0710A.client->adapter), fusion_F0710A.client->addr);
+
+       /* Read out a lot of registers */
+       ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO_LO);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "query failed: %d\n", ret);
+               goto bail1;
+       }
+
+       ver_product = (((u8)ret) & 0xc0) >> 6;
+       version = (10 + ((((u32)ret)&0x30) >> 4)) * 100000;
+       version += (((u32)ret)&0xf) * 1000;
+       /* Read out a lot of registers */
+       ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "query failed: %d\n", ret);
+               goto bail1;
+       }
+
+       ver_id = ((u8)(ret) & 0x6) >> 1;
+       version += ((((u32)ret) & 0xf8) >> 3) * 10;
+       version += (((u32)ret) & 0x1) + 1; /* 0 is build 1, 1 is build 2 */
+       dev_info(&i2c->dev, "version product %s(%d)\n", g_ver_product[ver_product], ver_product);
+       dev_info(&i2c->dev, "version id %s(%d)\n", ver_id ? "1.4" : "1.0", ver_id);
+       dev_info(&i2c->dev, "version series (%d)\n", version);
+
+       switch(ver_product)
+       {
+       case fusion_F0710A_VIESION_07: /* 7 inch */
+               fusion_F0710A.info.xres = fusion_F0710A07_XMAX;
+               fusion_F0710A.info.yres = fusion_F0710A07_YMAX;
+               fusion_F0710A.info.xy_reverse = fusion_F0710A07_REV;
+               break;
+       case fusion_F0710A_VIESION_43: /* 4.3 inch */
+               fusion_F0710A.info.xres = fusion_F0710A43_XMAX;
+               fusion_F0710A.info.yres = fusion_F0710A43_YMAX;
+               fusion_F0710A.info.xy_reverse = fusion_F0710A43_REV;
+               break;
+       default: /* fusion_F0710A_VIESION_10 10 inch */
+               fusion_F0710A.info.xres = fusion_F0710A10_XMAX;
+               fusion_F0710A.info.yres = fusion_F0710A10_YMAX;
+               fusion_F0710A.info.xy_reverse = fusion_F0710A10_REV;
+               break;
+       }
+
+       /* Register the input device. */
+       ret = fusion_F0710A_register_input();
+       if (ret < 0) {
+               dev_err(&i2c->dev, "can't register input: %d\n", ret);
+               goto bail1;
+       }
+
+       /* Create a worker thread */
+       fusion_F0710A.workq = create_singlethread_workqueue(DRV_NAME);
+       if (fusion_F0710A.workq == NULL) {
+               dev_err(&i2c->dev, "can't create work queue\n");
+               ret = -ENOMEM;
+               goto bail2;
+       }
+
+       /* Register for the interrupt and enable it. Our handler will
+        *  start getting invoked after this call.
+        */
+       ret = request_irq(i2c->irq, fusion_F0710A_interrupt, IRQF_TRIGGER_RISING,
+       i2c->name, &fusion_F0710A);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "can't get irq %d: %d\n", i2c->irq, ret);
+               goto bail3;
+       }
+
+       /* clear the irq first */
+       ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "Clear irq failed: %d\n", ret);
+               goto bail4;
+       }
+
+       return 0;
+
+bail4:
+       free_irq(i2c->irq, &fusion_F0710A);
+
+bail3:
+       destroy_workqueue(fusion_F0710A.workq);
+       fusion_F0710A.workq = NULL;
+
+bail2:
+       input_unregister_device(fusion_F0710A.input);
+bail1:
+       gpio_free(pdata->gpio_reset);
+bail0:
+       gpio_free(pdata->gpio_int);
+
+       return ret;
+}
+
+static int __maybe_unused fusion_F0710A_suspend(struct device *dev)
+{
+       struct i2c_client *i2c = to_i2c_client(dev);
+       disable_irq(i2c->irq);
+       flush_workqueue(fusion_F0710A.workq);
+
+       return 0;
+}
+
+static int __maybe_unused fusion_F0710A_resume(struct device *dev)
+{
+       struct i2c_client *i2c = to_i2c_client(dev);
+       enable_irq(i2c->irq);
+
+       return 0;
+}
+
+static int fusion_F0710A_remove(struct i2c_client *i2c)
+{
+       struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data;
+
+       gpio_free(pdata->gpio_int);
+       gpio_free(pdata->gpio_reset);
+       destroy_workqueue(fusion_F0710A.workq);
+       free_irq(i2c->irq, &fusion_F0710A);
+       input_unregister_device(fusion_F0710A.input);
+       i2c_set_clientdata(i2c, NULL);
+
+       dev_info(&i2c->dev, "driver removed\n");
+
+       return 0;
+}
+
+static struct i2c_device_id fusion_F0710A_id[] = {
+       {"fusion_F0710A", 0},
+       {},
+};
+
+static const struct of_device_id fusion_F0710A_dt_ids[] = {
+       {
+               .compatible = "touchrevolution,fusion-f0710a",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, fusion_F0710A_dt_ids);
+
+static const struct dev_pm_ops fusion_F0710A_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fusion_F0710A_suspend, fusion_F0710A_resume)
+};
+
+static struct i2c_driver fusion_F0710A_i2c_drv = {
+       .driver = {
+               .owner          = THIS_MODULE,
+               .name           = DRV_NAME,
+               .pm             = &fusion_F0710A_pm_ops,
+               .of_match_table = fusion_F0710A_dt_ids,
+       },
+       .probe          = fusion_F0710A_probe,
+       .remove         = fusion_F0710A_remove,
+       .id_table       = fusion_F0710A_id,
+       .address_list   = normal_i2c,
+};
+
+static int __init fusion_F0710A_init( void )
+{
+       int ret;
+
+       memset(&fusion_F0710A, 0, sizeof(fusion_F0710A));
+
+       /* Probe for fusion_F0710A on I2C. */
+       ret = i2c_add_driver(&fusion_F0710A_i2c_drv);
+       if (ret < 0) {
+               printk(KERN_WARNING DRV_NAME " can't add i2c driver: %d\n", ret);
+       }
+
+       return ret;
+}
+
+static void __exit fusion_F0710A_exit( void )
+{
+       i2c_del_driver(&fusion_F0710A_i2c_drv);
+}
+
+module_init(fusion_F0710A_init);
+module_exit(fusion_F0710A_exit);
+
+MODULE_DESCRIPTION("fusion_F0710A Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/fusion_F0710A.h b/drivers/input/touchscreen/fusion_F0710A.h
new file mode 100644 (file)
index 0000000..6805332
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  "fusion_F0710A" touchscreen 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.
+ */
+
+/* I2C slave address */
+#define fusion_F0710A_I2C_SLAVE_ADDR           0x10
+
+/* I2C registers */
+#define fusion_F0710A_DATA_INFO                        0x00
+
+/* First Point*/
+#define fusion_F0710A_POS_X1_HI                        0x01    /* 16-bit register, MSB */
+#define fusion_F0710A_POS_X1_LO                        0x02    /* 16-bit register, LSB */
+#define fusion_F0710A_POS_Y1_HI                        0x03    /* 16-bit register, MSB */
+#define fusion_F0710A_POS_Y1_LO                        0x04    /* 16-bit register, LSB */
+#define fusion_F0710A_FIR_PRESS                        0X05
+#define fusion_F0710A_FIR_TIDTS                        0X06
+
+/* Second Point */
+#define fusion_F0710A_POS_X2_HI                        0x07    /* 16-bit register, MSB */
+#define fusion_F0710A_POS_X2_LO                        0x08    /* 16-bit register, LSB */
+#define fusion_F0710A_POS_Y2_HI                        0x09    /* 16-bit register, MSB */
+#define fusion_F0710A_POS_Y2_LO                        0x0A    /* 16-bit register, LSB */
+#define fusion_F0710A_SEC_PRESS                        0x0B
+#define fusion_F0710A_SEC_TIDTS                        0x0C
+
+#define fusion_F0710A_VIESION_INFO_LO          0X0E
+#define fusion_F0710A_VIESION_INFO                     0X0F
+
+#define fusion_F0710A_RESET                            0x10
+#define fusion_F0710A_SCAN_COMPLETE            0x11
+
+
+#define fusion_F0710A_VIESION_10                       0
+#define fusion_F0710A_VIESION_07                       1
+#define fusion_F0710A_VIESION_43                       2
+
+/* fusion_F0710A 10 inch panel */
+#define fusion_F0710A10_XMAX                           2275
+#define fusion_F0710A10_YMAX                           1275
+#define fusion_F0710A10_REV                            1
+
+/* fusion_F0710A 7 inch panel */
+#define fusion_F0710A07_XMAX                           1500
+#define fusion_F0710A07_YMAX                           900
+#define fusion_F0710A07_REV                            0
+
+/* fusion_F0710A 4.3 inch panel */
+#define fusion_F0710A43_XMAX                           900
+#define fusion_F0710A43_YMAX                           500
+#define fusion_F0710A43_REV                            0
+
+#define        fusion_F0710A_SAVE_PT1                          0x1
+#define        fusion_F0710A_SAVE_PT2                          0x2
+
+
+
+/* fusion_F0710A touch screen information */
+struct fusion_F0710A_info {
+       int xres; /* x resolution */
+       int yres; /* y resolution */
+       int xy_reverse; /* if need reverse in the x,y value x=xres-1-x, y=yres-1-y*/
+};
+
+struct fusion_F0710A_data {
+       struct fusion_F0710A_info               info;
+       struct i2c_client               *client;
+       struct workqueue_struct *workq;
+       struct input_dev                *input;
+       int                             gpio_reset;
+       u16                                             x1;
+       u16                                             y1;
+       u8                                              z1;
+       u8                                              tip1;
+       u8                                              tid1;
+       u16                                             x2;
+       u16                                             y2;
+       u8                                              z2;
+       u8                                              tip2;
+       u8                                              tid2;
+       u8                                              f_num;
+       u8                                              save_points;
+};
index 177f78f6e6d6313fd9fd16595fe36089296ce25f..b0a94c3fc75ea45d824894a30f29819c9acb9280 100644 (file)
@@ -42,6 +42,7 @@ obj-$(CONFIG_TB10X_IRQC)              += irq-tb10x.o
 obj-$(CONFIG_XTENSA)                   += irq-xtensa-pic.o
 obj-$(CONFIG_XTENSA_MX)                        += irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)             += irq-crossbar.o
+obj-$(CONFIG_SOC_VF610)                        += irq-vf610-gpc.o
 obj-$(CONFIG_SOC_VF610)                        += irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM7038_L1_IRQ)           += irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)           += irq-bcm7120-l2.o
diff --git a/drivers/irqchip/irq-vf610-gpc.c b/drivers/irqchip/irq-vf610-gpc.c
new file mode 100644 (file)
index 0000000..105a660
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 Toradex AG
+ * Author: Stefan Agner <stefan@agner.ch>
+ *
+ * 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.
+ *
+ *
+ * The GPC (General Power Controller) irqchip driver takes care of the
+ * interrupt wakeup functionality.
+ *
+ * o All peripheral interrupts of the Vybrid SoC can be used as wakeup
+ *   source from STOP mode. In LPSTOP mode however, the GPC is unpowered
+ *   too and cannot be used to as a wakeup source. The WKPU (Wakeup Unit)
+ *   is responsible for wakeups from LPSTOP modes.
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#define IMR_NUM                        4
+#define VF610_GPC_IMR1         0x044
+#define VF610_GPC_MAX_IRQS     (IMR_NUM * 32)
+
+static void __iomem *gpc_base;
+
+static int vf610_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+       unsigned int idx = d->hwirq / 32;
+       void __iomem *reg_imr = gpc_base + VF610_GPC_IMR1 + (idx * 4);
+       u32 mask = 1 << d->hwirq % 32;
+
+       if (on)
+               writel_relaxed(readl_relaxed(reg_imr) & ~mask, reg_imr);
+       else
+               writel_relaxed(readl_relaxed(reg_imr) | mask, reg_imr);
+
+       /*
+        * Do *not* call into the parent, as the GIC doesn't have any
+        * wake-up facility...
+        */
+       return 0;
+}
+
+static struct irq_chip vf610_gpc_chip = {
+       .name                   = "vf610-gpc",
+       .irq_mask               = irq_chip_mask_parent,
+       .irq_unmask             = irq_chip_unmask_parent,
+       .irq_enable             = irq_chip_enable_parent,
+       .irq_disable            = irq_chip_disable_parent,
+       .irq_eoi                = irq_chip_eoi_parent,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
+       .irq_set_wake           = vf610_gpc_irq_set_wake,
+};
+
+static int vf610_gpc_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *arg)
+{
+       int i;
+       irq_hw_number_t hwirq;
+       struct irq_fwspec *fwspec = arg;
+       struct irq_fwspec parent_fwspec;
+
+       if (!irq_domain_get_of_node(domain->parent))
+               return -EINVAL;
+
+       if (fwspec->param_count != 2)
+               return -EINVAL;
+
+       hwirq = fwspec->param[0];
+       for (i = 0; i < nr_irqs; i++)
+               irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+                                             &vf610_gpc_chip, NULL);
+
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
+}
+
+static int vf610_gpc_domain_translate(struct irq_domain *d,
+                                         struct irq_fwspec *fwspec,
+                                         unsigned long *hwirq,
+                                         unsigned int *type)
+{
+       if (WARN_ON(fwspec->param_count < 2))
+               return -EINVAL;
+       *hwirq = fwspec->param[0];
+       *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+       return 0;
+}
+
+static const struct irq_domain_ops gpc_irq_domain_ops = {
+       .translate = vf610_gpc_domain_translate,
+       .alloc = vf610_gpc_domain_alloc,
+       .free = irq_domain_free_irqs_common,
+};
+
+static int __init vf610_gpc_of_init(struct device_node *node,
+                              struct device_node *parent)
+{
+       struct irq_domain *domain, *domain_parent;
+       int i;
+
+       domain_parent = irq_find_host(parent);
+       if (!domain_parent) {
+               pr_err("vf610_gpc: interrupt-parent not found\n");
+               return -EINVAL;
+       }
+
+       gpc_base = of_io_request_and_map(node, 0, "gpc");
+       if (WARN_ON(!gpc_base))
+               return -ENOMEM;
+
+       domain = irq_domain_add_hierarchy(domain_parent, 0, VF610_GPC_MAX_IRQS,
+                                         node, &gpc_irq_domain_ops, NULL);
+       if (!domain) {
+               iounmap(gpc_base);
+               return -ENOMEM;
+       }
+
+       /* Initially mask all interrupts for wakeup */
+       for (i = 0; i < IMR_NUM; i++)
+               writel_relaxed(~0, gpc_base + VF610_GPC_IMR1 + i * 4);
+
+       return 0;
+}
+IRQCHIP_DECLARE(vf610_gpc, "fsl,vf610-gpc", vf610_gpc_of_init);
index 56b5e3cb9de2c4f2c050fcad42b9f91419e92b81..a36f2ea74185e4999ecf9495c5bad7d25e437f18 100644 (file)
  *   variants of Vybrid.
  */
 
+#include <linux/bitops.h>
 #include <linux/cpu_pm.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
 
 #define MSCM_CPxNUM            0x4
 
+#define MSCM_IRCP0IR           0x0
+#define MSCM_IRCP1IR           0x4
+#define MSCM_IRCPnIR(n)                ((n) * 0x4 + MSCM_IRCP0IR)
+#define MSCM_IRCPnIR_INT(n)    (0x1 << (n))
+#define MSCM_IRCPGIR           0x20
+
+#define MSCM_INTID_MASK                0x3
+#define MSCM_INTID(n)          ((n) & MSCM_INTID_MASK)
+#define MSCM_CPUTL(n)          (((n) == 0 ? 1 : 2) << 16)
+
 #define MSCM_IRSPRC(n)         (0x80 + 2 * (n))
 #define MSCM_IRSPRC_CPEN_MASK  0x3
 
 #define MSCM_IRSPRC_NUM                112
 
+#define MSCM_CPU2CPU_NUM       4
+
 struct vf610_mscm_ir_chip_data {
        void __iomem *mscm_ir_base;
-       u16 cpu_mask;
+       u16 cpu_id;
        u16 saved_irsprc[MSCM_IRSPRC_NUM];
        bool is_nvic;
+       struct device_node *cpu2cpu_node;
+};
+
+struct mscm_cpu2cpu_irq_data {
+       int intid;
+       int irq;
+       irq_handler_t handler;
+       void *priv;
 };
 
 static struct vf610_mscm_ir_chip_data *mscm_ir_data;
 
+static struct mscm_cpu2cpu_irq_data cpu2cpu_irq_data[MSCM_CPU2CPU_NUM];
+
 static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data)
 {
        int i;
@@ -94,11 +119,8 @@ static void vf610_mscm_ir_enable(struct irq_data *data)
        u16 irsprc;
 
        irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
-       irsprc &= MSCM_IRSPRC_CPEN_MASK;
 
-       WARN_ON(irsprc & ~chip_data->cpu_mask);
-
-       writew_relaxed(chip_data->cpu_mask,
+       writew_relaxed(irsprc | BIT(chip_data->cpu_id),
                       chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
 
        irq_chip_enable_parent(data);
@@ -108,8 +130,11 @@ static void vf610_mscm_ir_disable(struct irq_data *data)
 {
        irq_hw_number_t hwirq = data->hwirq;
        struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
+       u16 irsprc;
 
-       writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
+       irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
+       writew_relaxed(irsprc & ~BIT(chip_data->cpu_id),
+                       chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
 
        irq_chip_disable_parent(data);
 }
@@ -179,6 +204,90 @@ static const struct irq_domain_ops mscm_irq_domain_ops = {
        .free = irq_domain_free_irqs_common,
 };
 
+static irqreturn_t mscm_cpu2cpu_irq_handler(int irq, void *dev_id)
+{
+       irqreturn_t ret;
+       struct mscm_cpu2cpu_irq_data *data = dev_id;
+       void __iomem *mscm_base = mscm_ir_data->mscm_ir_base;
+       int cpu_id = mscm_ir_data->cpu_id;
+
+
+       ret = data->handler(data->intid, data->priv);
+       if (ret == IRQ_HANDLED)
+               writel(MSCM_IRCPnIR_INT(data->intid), mscm_base + MSCM_IRCPnIR(cpu_id));
+
+       return ret;
+}
+
+int mscm_request_cpu2cpu_irq(unsigned int intid, irq_handler_t handler,
+                            const char *name, void *priv)
+{
+       int irq;
+       struct mscm_cpu2cpu_irq_data *data;
+
+       if (intid >= MSCM_CPU2CPU_NUM)
+               return -EINVAL;
+
+       irq = of_irq_get(mscm_ir_data->cpu2cpu_node, intid);
+       if (irq < 0)
+               return irq;
+
+       data = &cpu2cpu_irq_data[intid];
+       data->intid = intid;
+       data->irq = irq;
+       data->handler = handler;
+       data->priv = priv;
+
+       return request_irq(irq, mscm_cpu2cpu_irq_handler, 0, name, data);
+}
+EXPORT_SYMBOL(mscm_request_cpu2cpu_irq);
+
+void mscm_free_cpu2cpu_irq(unsigned int intid, void *priv)
+{
+       struct mscm_cpu2cpu_irq_data *data;
+
+       if (intid >= MSCM_CPU2CPU_NUM)
+               return;
+
+       data = &cpu2cpu_irq_data[intid];
+
+       if (data->irq < 0)
+               return;
+
+       free_irq(data->irq, data);
+}
+EXPORT_SYMBOL(mscm_free_cpu2cpu_irq);
+
+void mscm_trigger_cpu2cpu_irq(unsigned int intid, int cpuid)
+{
+       void __iomem *mscm_base = mscm_ir_data->mscm_ir_base;
+
+       writel(MSCM_INTID(intid) | MSCM_CPUTL(cpuid), mscm_base + MSCM_IRCPGIR);
+}
+EXPORT_SYMBOL(mscm_trigger_cpu2cpu_irq);
+
+void mscm_enable_cpu2cpu_irq(unsigned int intid)
+{
+       struct mscm_cpu2cpu_irq_data *data = &cpu2cpu_irq_data[intid];
+
+       if (intid >= MSCM_CPU2CPU_NUM)
+               return;
+
+       enable_irq(data->irq);
+}
+EXPORT_SYMBOL(mscm_enable_cpu2cpu_irq);
+
+void mscm_disable_cpu2cpu_irq(unsigned int intid)
+{
+       struct mscm_cpu2cpu_irq_data *data = &cpu2cpu_irq_data[intid];
+
+       if (intid >= MSCM_CPU2CPU_NUM)
+               return;
+
+       disable_irq(data->irq);
+}
+EXPORT_SYMBOL(mscm_disable_cpu2cpu_irq);
+
 static int __init vf610_mscm_ir_of_init(struct device_node *node,
                               struct device_node *parent)
 {
@@ -210,8 +319,7 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
                goto out_unmap;
        }
 
-       regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
-       mscm_ir_data->cpu_mask = 0x1 << cpuid;
+       mscm_ir_data->cpu_id = regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
 
        domain = irq_domain_add_hierarchy(domain_parent, 0,
                                          MSCM_IRSPRC_NUM, node,
@@ -227,6 +335,8 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
 
        cpu_pm_register_notifier(&mscm_ir_notifier_block);
 
+       mscm_ir_data->cpu2cpu_node = of_get_child_by_name(node, "cpu2cpu");
+
        return 0;
 
 out_unmap:
index 176bf0fa2685e70749f42bad44931b8489e0d681..34d71de1bc1c65f161ece980eee00d0a4385b040 100644 (file)
@@ -116,6 +116,36 @@ struct regmap *syscon_node_to_regmap(struct device_node *np)
 }
 EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
 
+int syscon_regmap_read_from_offset(struct device_node *np,
+                               const char *s, unsigned int *val)
+{
+       struct of_phandle_args pargs;
+       struct regmap *regmap;
+       int offset;
+       int ret;
+
+       if (!np)
+               return -ENODEV;
+
+       ret = of_parse_phandle_with_fixed_args(np, s, 1, 0, &pargs);
+       if (ret)
+               return ret;
+
+       regmap = syscon_node_to_regmap(pargs.np);
+       if (IS_ERR(regmap)) {
+               of_node_put(pargs.np);
+               return PTR_ERR(regmap);
+       }
+
+       offset = pargs.args[0];
+       of_node_put(pargs.np);
+
+       ret = regmap_read(regmap, offset, val);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(syscon_regmap_read_from_offset);
+
 struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
 {
        struct device_node *syscon_np;
index 8d838779fd1bcd856a7cf5169f673ccc4224379f..31e95f9c1c933f4a2c409035ea6d2cc1cd57e37b 100644 (file)
@@ -1004,6 +1004,10 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
                host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
        }
 
+       if (!of_property_read_u32(np, "bus-width", &boarddata->max_bus_width)
+               && boarddata->max_bus_width == 1)
+               host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+
        /* call to generic mmc_of_parse to support additional capabilities */
        ret = mmc_of_parse(host->mmc);
        if (ret)
@@ -1293,7 +1297,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
 #endif
 
 static const struct dev_pm_ops sdhci_esdhc_pmops = {
-       SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_rpm_suspend, sdhci_pltfm_rpm_resume)
        SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
                                sdhci_esdhc_runtime_resume, NULL)
 };
index 87fb5ea8ebe7cae575b8281f07c630ce03ce2223..714ffb5a692984863cdc2df1dfe81ecd68f1a475 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/pm_runtime.h>
 #ifdef CONFIG_PPC
 #include <asm/machdep.h>
 #endif
@@ -252,6 +253,41 @@ const struct dev_pm_ops sdhci_pltfm_pmops = {
        .resume         = sdhci_pltfm_resume,
 };
 EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
+
+int sdhci_pltfm_rpm_suspend(struct device *dev)
+{
+       int ret;
+       struct sdhci_host *host = dev_get_drvdata(dev);
+
+       pm_runtime_get_sync(dev);
+       ret = sdhci_suspend_host(host);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+       if (ret)
+               return ret;
+
+       return pm_runtime_force_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_suspend);
+
+int sdhci_pltfm_rpm_resume(struct device *dev)
+{
+       int ret;
+       struct sdhci_host *host = dev_get_drvdata(dev);
+
+       ret = pm_runtime_force_resume(dev);
+
+       if (ret)
+               return ret;
+
+       pm_runtime_get_sync(dev);
+       ret = sdhci_resume_host(host);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_resume);
 #endif /* CONFIG_PM */
 
 static int __init sdhci_pltfm_drv_init(void)
index 04bc2481e5c32cdd5c68242815655994f724d3e7..ac5f6ea9b55f56c697e2bf33164e3e1414d70495 100644 (file)
@@ -114,6 +114,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
 extern int sdhci_pltfm_suspend(struct device *dev);
 extern int sdhci_pltfm_resume(struct device *dev);
 extern const struct dev_pm_ops sdhci_pltfm_pmops;
+extern int sdhci_pltfm_rpm_suspend(struct device *dev);
+extern int sdhci_pltfm_rpm_resume(struct device *dev);
 #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
 #else
 #define SDHCI_PLTFM_PMOPS NULL
index bee32a9d9876f02719a5c46be9b65ff145f76c3b..c7df0aebb2b8bb9350a8fa4791b28273fe41d41e 100644 (file)
@@ -30,6 +30,15 @@ config FEC
          Say Y here if you want to use the built-in 10/100 Fast ethernet
          controller on some Motorola ColdFire and Freescale i.MX processors.
 
+config FSL_L2_SWITCH
+       tristate "Ethernet switch controller (Freescale Vybrid platform)"
+       depends on (SOC_VF610)
+       help
+         Say Y here if you want to use the built-in ethernet switch
+         controller on Vybrid processors.
+         The Integrated Ethernet switch engine is compatible with
+         10/100 MAC-NET core.
+
 config FEC_MPC52xx
        tristate "FEC MPC52xx driver"
        depends on PPC_MPC52xx && PPC_BESTCOMM
index 71debd1c18c9923a895f313c71f3b4617974433f..d5d24d182919dd6f2ca81637e90f2230d9a79ef0 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FSL_L2_SWITCH) += fsl_l2_switch.o
 fec-objs :=fec_main.o fec_ptp.o
 obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
 ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
index 458e2d97d096f4542b03e4cc235561f43935091c..17fd4a83eefe752be439986ab7776f9d5e0d4154 100644 (file)
@@ -103,7 +103,9 @@ static struct platform_device_id fec_devtype[] = {
                                FEC_QUIRK_HAS_RACC,
        }, {
                .name = "mvf600-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
+               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC |
+                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_VLAN |
+                               FEC_QUIRK_HAS_CSUM,
        }, {
                .name = "imx6sx-fec",
                .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
@@ -170,6 +172,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 /* 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)
 
 /*
@@ -975,9 +978,11 @@ fec_restart(struct net_device *ndev)
 
 #if !defined(CONFIG_M5272)
        if (fep->quirks & FEC_QUIRK_HAS_RACC) {
-               /* set RX checksum */
                val = readl(fep->hwp + FEC_RACC);
+               /* align IP header */
+               val |= FEC_RACC_SHIFT16;
                if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
+                       /* set RX checksum */
                        val |= FEC_RACC_OPTIONS;
                else
                        val &= ~FEC_RACC_OPTIONS;
@@ -1463,6 +1468,12 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
                prefetch(skb->data - NET_IP_ALIGN);
                skb_put(skb, pkt_len - 4);
                data = skb->data;
+
+#if !defined(CONFIG_M5272)
+               if (fep->quirks & FEC_QUIRK_HAS_RACC)
+                       data = skb_pull_inline(skb, 2);
+#endif
+
                if (!is_copybreak && need_swap)
                        swap_buffer(data, pkt_len);
 
diff --git a/drivers/net/ethernet/freescale/fsl_l2_switch.c b/drivers/net/ethernet/freescale/fsl_l2_switch.c
new file mode 100644 (file)
index 0000000..af38259
--- /dev/null
@@ -0,0 +1,912 @@
+/*
+ *  L2 switch Controller (Ethernet switch) driver
+ *  for Freescale M5441x and Vybrid.
+ *
+ *  Copyright 2010-2012 Freescale Semiconductor, Inc.
+ *    Alison Wang (b18965@freescale.com)
+ *    Jason Jin (Jason.jin@freescale.com)
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/phy.h>
+#include <linux/syscalls.h>
+#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_net.h>
+
+#include "fsl_l2_switch.h"
+
+/* switch ports status */
+struct port_status ports_link_status;
+
+static unsigned char macaddr[ETH_ALEN];
+module_param_array(macaddr, byte, NULL, 0);
+MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
+
+static void switch_hw_init(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+
+       /* Initialize MAC 0/1 */
+       writel(FSL_FEC_RCR_MAX_FL(1522) | FSL_FEC_RCR_RMII_MODE
+               | FSL_FEC_RCR_PROM | FSL_FEC_RCR_MII_MODE
+               | FSL_FEC_RCR_MII_MODE, fep->enetbase + FSL_FEC_RCR0);
+       writel(FSL_FEC_RCR_MAX_FL(1522) | FSL_FEC_RCR_RMII_MODE
+               | FSL_FEC_RCR_PROM | FSL_FEC_RCR_MII_MODE
+               | FSL_FEC_RCR_MII_MODE, fep->enetbase + FSL_FEC_RCR1);
+
+       writel(FSL_FEC_TCR_FDEN, fep->enetbase + FSL_FEC_TCR0);
+       writel(FSL_FEC_TCR_FDEN, fep->enetbase + FSL_FEC_TCR1);
+
+       /* Set the station address for the ENET Adapter */
+       writel(dev->dev_addr[3] |
+               dev->dev_addr[2] << 8 |
+               dev->dev_addr[1] << 16 |
+               dev->dev_addr[0] << 24, fep->enetbase + FSL_FEC_PALR0);
+       writel((dev->dev_addr[5] << 16) |
+               (dev->dev_addr[4] << 24),
+               fep->enetbase + FSL_FEC_PAUR0);
+       writel(dev->dev_addr[3] |
+               dev->dev_addr[2] << 8 |
+               dev->dev_addr[1] << 16 |
+               dev->dev_addr[0] << 24, fep->enetbase + FSL_FEC_PALR1);
+       writel((dev->dev_addr[5] << 16) |
+               (dev->dev_addr[4] << 24),
+               fep->enetbase + FSL_FEC_PAUR1);
+
+       writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->enetbase + FSL_FEC_EIMR0);
+       writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->enetbase + FSL_FEC_EIMR1);
+
+       writel(FSL_FEC_ECR_ETHER_EN |
+               (0x1 << 8), fep->enetbase + FSL_FEC_ECR0);
+       writel(FSL_FEC_ECR_ETHER_EN |
+               (0x1 << 8), fep->enetbase + FSL_FEC_ECR1);
+       udelay(20);
+}
+
+/* Set a MAC change in hardware.*/
+static void switch_get_mac_address(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+       unsigned char *iap, tmpaddr[ETH_ALEN];
+
+       iap = macaddr;
+
+       if (!is_valid_ether_addr(iap)) {
+               struct device_node *np = fep->pdev->dev.of_node;
+
+               if (np) {
+                       const char *mac = of_get_mac_address(np);
+
+                       if (mac)
+                               iap = (unsigned char *) mac;
+               }
+       }
+
+       if (!is_valid_ether_addr(iap)) {
+               *((__be32 *)&tmpaddr[0]) =
+                       cpu_to_be32(readl(fep->enetbase + FSL_FEC_PALR0));
+               *((__be16 *)&tmpaddr[4]) =
+                       cpu_to_be16(readl(fep->enetbase + FSL_FEC_PAUR0) >> 16);
+               iap = &tmpaddr[0];
+       }
+
+       if (!is_valid_ether_addr(iap)) {
+               /* Report it and use a random ethernet address instead */
+               netdev_err(dev, "Invalid MAC address: %pM\n", iap);
+               eth_hw_addr_random(dev);
+               netdev_info(dev, "Using random MAC address: %pM\n",
+                           dev->dev_addr);
+               return;
+       }
+
+       memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+       /* Adjust MAC if using macaddr */
+       if (iap == macaddr)
+               dev->dev_addr[ETH_ALEN - 1] =
+                       macaddr[ETH_ALEN - 1] + fep->dev_id;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change.  This only happens when switching between half and full
+ * duplex.
+ */
+static void switch_restart(struct net_device *dev, int duplex)
+{
+       struct switch_enet_private *fep;
+       int i;
+
+       fep = netdev_priv(dev);
+
+       /* Whack a reset.  We should wait for this.*/
+       writel(1, fep->enetbase + FSL_FEC_ECR0);
+       writel(1, fep->enetbase + FSL_FEC_ECR1);
+       udelay(10);
+
+       writel(FSL_ESW_MODE_SW_RST, fep->membase + FEC_ESW_MODE);
+       udelay(10);
+       writel(FSL_ESW_MODE_STATRST, fep->membase + FEC_ESW_MODE);
+       writel(FSL_ESW_MODE_SW_EN, fep->membase + FEC_ESW_MODE);
+
+       /* Enable transmit/receive on all ports */
+       writel(0xffffffff, fep->membase + FEC_ESW_PER);
+
+       /* Management port configuration,
+        * make port 0 as management port
+        */
+       writel(0, fep->membase + FEC_ESW_BMPC);
+
+       /* Clear any outstanding interrupt.*/
+       writel(0xffffffff, fep->membase + FEC_ESW_ISR);
+
+       switch_hw_init(dev);
+
+       /* Set station address.*/
+       switch_get_mac_address(dev);
+
+       writel(0, fep->membase + FEC_ESW_IMR);
+       udelay(10);
+
+       /* Set maximum receive buffer size. */
+       writel(PKT_MAXBLR_SIZE, fep->membase + FEC_ESW_MRBR);
+
+       /* Set receive and transmit descriptor base. */
+       writel(fep->bd_dma, fep->membase + FEC_ESW_RDSR);
+       writel((unsigned long)fep->bd_dma +
+                       sizeof(struct bufdesc) * RX_RING_SIZE,
+                       fep->membase + FEC_ESW_TDSR);
+
+       fep->cur_tx = fep->tx_bd_base;
+       fep->dirty_tx = fep->cur_tx;
+       fep->cur_rx = fep->rx_bd_base;
+
+       /* Reset SKB transmit buffers. */
+       fep->skb_cur = 0;
+       fep->skb_dirty = 0;
+       for (i = 0; i <= TX_RING_MOD_MASK; i++) {
+               if (fep->tx_skbuff[i] != NULL) {
+                       dev_kfree_skb_any(fep->tx_skbuff[i]);
+                       fep->tx_skbuff[i] = NULL;
+               }
+       }
+
+       /* Clear any outstanding interrupt.*/
+       writel(0xffffffff, fep->membase + FEC_ESW_ISR);
+       writel(FSL_ESW_IMR_RXF | FSL_ESW_IMR_TXF, fep->membase + FEC_ESW_IMR);
+}
+
+static int switch_enet_clk_enable(struct net_device *ndev, bool enable)
+{
+       struct switch_enet_private *fep = netdev_priv(ndev);
+       int ret;
+
+       if (enable) {
+               ret = clk_prepare_enable(fep->clk_esw);
+               if (ret)
+                       goto failed_clk_esw;
+
+               ret = clk_prepare_enable(fep->clk_enet);
+               if (ret)
+                       goto failed_clk_enet;
+
+               ret = clk_prepare_enable(fep->clk_enet0);
+               if (ret)
+                       goto failed_clk_enet0;
+
+               ret = clk_prepare_enable(fep->clk_enet1);
+               if (ret)
+                       goto failed_clk_enet1;
+       } else {
+               clk_disable_unprepare(fep->clk_esw);
+               clk_disable_unprepare(fep->clk_enet);
+               clk_disable_unprepare(fep->clk_enet0);
+               clk_disable_unprepare(fep->clk_enet1);
+       }
+
+       return 0;
+
+failed_clk_esw:
+               clk_disable_unprepare(fep->clk_esw);
+failed_clk_enet:
+               clk_disable_unprepare(fep->clk_enet);
+failed_clk_enet0:
+               clk_disable_unprepare(fep->clk_enet0);
+failed_clk_enet1:
+               clk_disable_unprepare(fep->clk_enet1);
+
+       return ret;
+}
+
+static void switch_enet_free_buffers(struct net_device *ndev)
+{
+       struct switch_enet_private *fep = netdev_priv(ndev);
+       int i;
+       struct sk_buff *skb;
+       cbd_t   *bdp;
+
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = fep->rx_skbuff[i];
+
+               if (bdp->cbd_bufaddr)
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               SWITCH_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               if (skb)
+                       dev_kfree_skb(skb);
+               bdp++;
+       }
+
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++)
+               kfree(fep->tx_bounce[i]);
+}
+
+static int switch_alloc_buffers(struct net_device *ndev)
+{
+       struct switch_enet_private *fep = netdev_priv(ndev);
+       int i;
+       struct sk_buff *skb;
+       cbd_t   *bdp;
+
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = dev_alloc_skb(SWITCH_ENET_RX_FRSIZE);
+               if (!skb) {
+                       switch_enet_free_buffers(ndev);
+                       return -ENOMEM;
+               }
+               fep->rx_skbuff[i] = skb;
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
+                               SWITCH_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               bdp->cbd_sc = BD_ENET_RX_EMPTY;
+
+               bdp++;
+       }
+
+       /* Set the last buffer to wrap. */
+       bdp--;
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               fep->tx_bounce[i] = kmalloc(SWITCH_ENET_TX_FRSIZE, GFP_KERNEL);
+
+               bdp->cbd_sc = 0;
+               bdp->cbd_bufaddr = 0;
+               bdp++;
+       }
+
+       /* Set the last buffer to wrap. */
+       bdp--;
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       return 0;
+}
+
+static int switch_enet_open(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+       unsigned long tmp = 0;
+
+       /* no phy,  go full duplex,  it's most likely a hub chip */
+       switch_restart(dev, 1);
+
+       /* if the fec open firstly, we need to do nothing
+        * otherwise, we need to restart the FEC
+        */
+       if (fep->sequence_done == 0)
+               switch_restart(dev, 1);
+       else
+               fep->sequence_done = 0;
+
+       writel(0x70007, fep->membase + FEC_ESW_PER);
+       writel(FSL_ESW_DBCR_P0 | FSL_ESW_DBCR_P1 | FSL_ESW_DBCR_P2,
+                       fep->membase + FEC_ESW_DBCR);
+       writel(FSL_ESW_DMCR_P0 | FSL_ESW_DMCR_P1 | FSL_ESW_DMCR_P2,
+                       fep->membase + FEC_ESW_DMCR);
+
+       /* Disable port learning */
+       tmp = readl(fep->membase + FEC_ESW_BKLR);
+       tmp &= ~FSL_ESW_BKLR_LDX;
+       writel(tmp, fep->membase + FEC_ESW_BKLR);
+
+       netif_start_queue(dev);
+
+       /* And last, enable the receive processing. */
+       writel(FSL_ESW_RDAR_R_DES_ACTIVE, fep->membase + FEC_ESW_RDAR);
+
+       fep->opened = 1;
+
+       return 0;
+}
+
+static int switch_enet_close(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+
+       /* Don't know what to do yet. */
+       fep->opened = 0;
+       netif_stop_queue(dev);
+
+       return 0;
+}
+
+static netdev_tx_t switch_enet_start_xmit(struct sk_buff *skb,
+                               struct net_device *dev)
+{
+       struct switch_enet_private *fep;
+       cbd_t   *bdp;
+       unsigned short  status;
+       unsigned long flags;
+       void *bufaddr;
+
+       fep = netdev_priv(dev);
+
+       spin_lock_irqsave(&fep->hw_lock, flags);
+       /* Fill in a Tx ring entry */
+       bdp = fep->cur_tx;
+
+       status = bdp->cbd_sc;
+
+       /* 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;
+
+       /*      On some FEC implementations data must be aligned on
+        *      4-byte boundaries. Use bounce buffers to copy data
+        *      and get it aligned. Ugh.
+        */
+       if (((unsigned long)bufaddr) & 0xf) {
+               unsigned int index;
+               index = bdp - fep->tx_bd_base;
+
+               memcpy(fep->tx_bounce[index],
+                      (void *)skb->data, bdp->cbd_datlen);
+               bufaddr = fep->tx_bounce[index];
+       }
+
+       /* Save skb pointer. */
+       fep->tx_skbuff[fep->skb_cur] = skb;
+
+       dev->stats.tx_bytes += skb->len;
+       fep->skb_cur = (fep->skb_cur + 1) & TX_RING_MOD_MASK;
+
+       /* Push the data cache so the CPM does not get stale memory
+        * data.
+        */
+       bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+                       SWITCH_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+
+       /* 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;
+
+       dev->trans_start = jiffies;
+
+       /* Trigger transmission start */
+       writel(FSL_ESW_TDAR_X_DES_ACTIVE, fep->membase + FEC_ESW_TDAR);
+
+       /* If this was the last BD in the ring,
+        * start at the beginning again.
+        */
+       if (status & BD_ENET_TX_WRAP)
+               bdp = fep->tx_bd_base;
+       else
+               bdp++;
+
+       if (bdp == fep->dirty_tx) {
+               fep->tx_full = 1;
+               netif_stop_queue(dev);
+               netdev_err(dev, "%s:  net stop\n", __func__);
+       }
+
+       fep->cur_tx = (cbd_t *)bdp;
+
+       spin_unlock_irqrestore(&fep->hw_lock, flags);
+
+       return NETDEV_TX_OK;
+}
+
+static void switch_timeout(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+
+       netdev_err(dev, "%s: transmit timed out.\n", dev->name);
+       dev->stats.tx_errors++;
+       switch_restart(dev, fep->full_duplex);
+       netif_wake_queue(dev);
+}
+
+static const struct net_device_ops switch_netdev_ops = {
+       .ndo_open               = switch_enet_open,
+       .ndo_stop               = switch_enet_close,
+       .ndo_start_xmit         = switch_enet_start_xmit,
+       .ndo_tx_timeout         = switch_timeout,
+};
+
+/* Initialize the FEC Ethernet. */
+static int switch_enet_init(struct net_device *dev)
+{
+       struct switch_enet_private *fep = netdev_priv(dev);
+       cbd_t *cbd_base = NULL;
+       int ret = 0;
+
+       /* Allocate memory for buffer descriptors. */
+       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+                       GFP_KERNEL);
+       if (!cbd_base) {
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&fep->hw_lock);
+
+       writel(FSL_ESW_MODE_SW_RST, fep->membase + FEC_ESW_MODE);
+       udelay(10);
+       writel(FSL_ESW_MODE_STATRST, fep->membase + FEC_ESW_MODE);
+       writel(FSL_ESW_MODE_SW_EN, fep->membase + FEC_ESW_MODE);
+
+       /* Enable transmit/receive on all ports */
+       writel(0xffffffff, fep->membase + FEC_ESW_PER);
+
+       /* Management port configuration,
+        * make port 0 as management port
+        */
+       writel(0, fep->membase + FEC_ESW_BMPC);
+
+       /* Clear any outstanding interrupt.*/
+       writel(0xffffffff, fep->membase + FEC_ESW_ISR);
+       writel(0, fep->membase + FEC_ESW_IMR);
+       udelay(100);
+
+       /* Get the Ethernet address */
+       switch_get_mac_address(dev);
+
+       /* Set receive and transmit descriptor base. */
+       fep->rx_bd_base = cbd_base;
+       fep->tx_bd_base = cbd_base + RX_RING_SIZE;
+
+       dev->base_addr = (unsigned long)fep->membase;
+
+       /* The FEC Ethernet specific entries in the device structure. */
+       dev->watchdog_timeo = TX_TIMEOUT;
+       dev->netdev_ops = &switch_netdev_ops;
+
+       fep->skb_cur = fep->skb_dirty = 0;
+
+       ret = switch_alloc_buffers(dev);
+       if (ret)
+               goto err_enet_alloc;
+
+       /* Set receive and transmit descriptor base */
+       writel(fep->bd_dma, fep->membase + FEC_ESW_RDSR);
+       writel((unsigned long)fep->bd_dma +
+                       sizeof(struct bufdesc) * RX_RING_SIZE,
+                       fep->membase + FEC_ESW_TDSR);
+
+       /* set mii */
+       switch_hw_init(dev);
+
+       /* Clear any outstanding interrupt. */
+       writel(0xffffffff, fep->membase + FEC_ESW_ISR);
+       writel(FSL_ESW_IMR_RXF | FSL_ESW_IMR_TXF, fep->membase + FEC_ESW_IMR);
+
+       fep->sequence_done = 1;
+
+       return ret;
+
+err_enet_alloc:
+       switch_enet_clk_enable(dev, false);
+
+       return ret;
+}
+
+static void switch_enet_tx(struct net_device *dev)
+{
+       struct  switch_enet_private *fep;
+       struct bufdesc *bdp;
+       unsigned short status;
+       struct  sk_buff *skb;
+       unsigned long flags;
+
+       fep = netdev_priv(dev);
+       spin_lock_irqsave(&fep->hw_lock, flags);
+       bdp = fep->dirty_tx;
+
+       while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
+               if (bdp == fep->cur_tx && fep->tx_full == 0)
+                       break;
+
+               if (bdp->cbd_bufaddr)
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               SWITCH_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+               bdp->cbd_bufaddr = 0;
+
+               skb = fep->tx_skbuff[fep->skb_dirty];
+               /* 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)) {
+                       dev->stats.tx_errors++;
+                       if (status & BD_ENET_TX_HB)  /* No heartbeat */
+                               dev->stats.tx_heartbeat_errors++;
+                       if (status & BD_ENET_TX_LC)  /* Late collision */
+                               dev->stats.tx_window_errors++;
+                       if (status & BD_ENET_TX_RL)  /* Retrans limit */
+                               dev->stats.tx_aborted_errors++;
+                       if (status & BD_ENET_TX_UN)  /* Underrun */
+                               dev->stats.tx_fifo_errors++;
+                       if (status & BD_ENET_TX_CSL) /* Carrier lost */
+                               dev->stats.tx_carrier_errors++;
+               } else {
+                       dev->stats.tx_packets++;
+               }
+
+               /* Deferred means some collisions occurred during transmit,
+                * but we eventually sent the packet OK.
+                */
+               if (status & BD_ENET_TX_DEF)
+                       dev->stats.collisions++;
+
+               /* Free the sk buffer associated with this last transmit.
+                */
+               dev_kfree_skb_any(skb);
+               fep->tx_skbuff[fep->skb_dirty] = NULL;
+               fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK;
+
+               /* Update pointer to next buffer descriptor to be transmitted.
+                */
+               if (status & BD_ENET_TX_WRAP)
+                       bdp = fep->tx_bd_base;
+               else
+                       bdp++;
+
+               /* Since we have freed up a buffer, the ring is no longer
+                * full.
+                */
+               if (fep->tx_full) {
+                       fep->tx_full = 0;
+                       netdev_err(dev, "%s: tx full is zero\n", __func__);
+                       if (netif_queue_stopped(dev))
+                               netif_wake_queue(dev);
+               }
+       }
+       fep->dirty_tx = (cbd_t *)bdp;
+       spin_unlock_irqrestore(&fep->hw_lock, flags);
+}
+
+/* During a receive, the cur_rx 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.
+ */
+static void switch_enet_rx(struct net_device *dev)
+{
+       struct  switch_enet_private *fep;
+       cbd_t *bdp;
+       unsigned short status;
+       struct  sk_buff *skb;
+       ushort  pkt_len;
+       __u8 *data;
+       unsigned long flags;
+
+       fep = netdev_priv(dev);
+
+       spin_lock_irqsave(&fep->hw_lock, flags);
+       /* 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;
+
+       while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
+               /* Since we have allocated space to hold a complete frame,
+                * the last indicator should be set.
+                */
+               if ((status & BD_ENET_RX_LAST) == 0)
+                       netdev_err(dev, "SWITCH ENET: rcv is not +last\n");
+
+               if (!fep->opened)
+                       goto rx_processing_done;
+
+               /* 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)) {
+                       dev->stats.rx_errors++;
+                       if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+                               /* Frame too long or too short. */
+                               dev->stats.rx_length_errors++;
+                       }
+                       if (status & BD_ENET_RX_NO)     /* Frame alignment */
+                               dev->stats.rx_frame_errors++;
+                       if (status & BD_ENET_RX_CR)     /* CRC Error */
+                               dev->stats.rx_crc_errors++;
+                       if (status & BD_ENET_RX_OV)     /* FIFO overrun */
+                               dev->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) {
+                       dev->stats.rx_errors++;
+                       dev->stats.rx_frame_errors++;
+                       goto rx_processing_done;
+               }
+               /* Process the incoming frame */
+               dev->stats.rx_packets++;
+               pkt_len = bdp->cbd_datlen;
+               dev->stats.rx_bytes += pkt_len;
+               data = (__u8 *)__va(bdp->cbd_bufaddr);
+
+               if (bdp->cbd_bufaddr)
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               SWITCH_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+
+               /* 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.
+                */
+               skb = dev_alloc_skb(pkt_len - 4 + NET_IP_ALIGN);
+
+               if (!skb)
+                       dev->stats.rx_dropped++;
+
+               if (unlikely(!skb)) {
+                       netdev_err(dev,
+                               "%s:Memory squeeze, dropping packet.\n",
+                               dev->name);
+                       dev->stats.rx_dropped++;
+               } else {
+                       skb_reserve(skb, NET_IP_ALIGN);
+                       skb_put(skb, pkt_len - 4);      /* Make room */
+                       skb_copy_to_linear_data(skb, data, pkt_len - 4);
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);
+               }
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
+                       SWITCH_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+rx_processing_done:
+
+               /* 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;
+
+               /* Update BD pointer to next entry */
+               if (status & BD_ENET_RX_WRAP)
+                       bdp = fep->rx_bd_base;
+               else
+                       bdp++;
+
+               /* 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(FSL_ESW_RDAR_R_DES_ACTIVE, fep->membase + FEC_ESW_RDAR);
+       }
+       fep->cur_rx = (cbd_t *)bdp;
+
+       spin_unlock_irqrestore(&fep->hw_lock, flags);
+}
+
+/* The interrupt handler */
+static irqreturn_t switch_enet_interrupt(int irq, void *dev_id)
+{
+       struct  net_device *dev = dev_id;
+       struct switch_enet_private *fep = netdev_priv(dev);
+       uint    int_events;
+       irqreturn_t ret = IRQ_NONE;
+
+       /* Get the interrupt events that caused us to be here. */
+       do {
+               int_events = readl(fep->membase + FEC_ESW_ISR);
+               writel(int_events, fep->membase + FEC_ESW_ISR);
+
+               /* Handle receive event in its own function. */
+
+               if (int_events & FSL_ESW_ISR_RXF) {
+                       ret = IRQ_HANDLED;
+                       switch_enet_rx(dev);
+               }
+
+               if (int_events & FSL_ESW_ISR_TXF) {
+                       ret = IRQ_HANDLED;
+                       switch_enet_tx(dev);
+               }
+
+       } while (int_events);
+
+       return ret;
+}
+
+static int eth_switch_remove(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct switch_enet_private *fep;
+
+       fep = netdev_priv(dev);
+
+       unregister_netdev(dev);
+
+       writel(0, fep->enetbase + FSL_FEC_ECR0);
+       writel(0, fep->enetbase + FSL_FEC_ECR1);
+       udelay(10);
+
+       writel(FSL_ESW_MODE_SW_RST, fep->membase + FEC_ESW_MODE);
+       udelay(10);
+       writel(FSL_ESW_MODE_STATRST, fep->membase + FEC_ESW_MODE);
+       writel(0, fep->membase + FEC_ESW_MODE);
+
+       /* Disable transmit/receive on all ports */
+       writel(0, fep->membase + FEC_ESW_PER);
+
+       clk_disable_unprepare(fep->clk_enet1);
+       clk_disable_unprepare(fep->clk_enet0);
+       clk_disable_unprepare(fep->clk_enet);
+       clk_disable_unprepare(fep->clk_esw);
+
+       free_netdev(dev);
+
+       return 0;
+}
+
+static const struct of_device_id of_eth_switch_match[] = {
+       { .compatible = "fsl,eth-switch", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, of_eth_switch_match);
+
+/* TODO: suspend/resume related code */
+
+static int eth_switch_probe(struct platform_device *pdev)
+{
+       struct net_device *ndev = NULL;
+       struct switch_enet_private *fep = NULL;
+       int irq = 0, ret = 0, err = 0;
+       struct resource *res = NULL;
+       const struct of_device_id *match;
+       static int dev_id;
+
+       match = of_match_device(of_eth_switch_match, &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       pdev->id_entry = match->data;
+
+       /* Initialize network device */
+       ndev = alloc_etherdev(sizeof(struct switch_enet_private));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       fep = netdev_priv(ndev);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       fep->membase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(fep->membase)) {
+               ret = PTR_ERR(fep->membase);
+               goto failed_ioremap;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       fep->enetbase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(fep->enetbase)) {
+               ret = PTR_ERR(fep->enetbase);
+               goto failed_ioremap;
+       }
+
+       fep->pdev = pdev;
+       fep->dev_id = dev_id++;
+
+       platform_set_drvdata(pdev, ndev);
+
+       irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(&pdev->dev, irq, switch_enet_interrupt,
+                              0, pdev->name, ndev);
+       if (ret)
+               return ret;
+
+       fep->clk_esw = devm_clk_get(&pdev->dev, "esw");
+       if (IS_ERR(fep->clk_esw)) {
+               ret = PTR_ERR(fep->clk_esw);
+               goto failed_clk;
+       }
+
+       fep->clk_enet = devm_clk_get(&pdev->dev, "enet");
+       if (IS_ERR(fep->clk_enet)) {
+               ret = PTR_ERR(fep->clk_enet);
+               goto failed_clk;
+       }
+
+       fep->clk_enet0 = devm_clk_get(&pdev->dev, "enet0");
+       if (IS_ERR(fep->clk_enet0)) {
+               ret = PTR_ERR(fep->clk_enet0);
+               goto failed_clk;
+       }
+
+       fep->clk_enet1 = devm_clk_get(&pdev->dev, "enet1");
+       if (IS_ERR(fep->clk_enet1)) {
+               ret = PTR_ERR(fep->clk_enet1);
+               goto failed_clk;
+       }
+
+       switch_enet_clk_enable(ndev, true);
+
+       err = switch_enet_init(ndev);
+       if (err) {
+               free_netdev(ndev);
+               platform_set_drvdata(pdev, NULL);
+               return -EIO;
+       }
+
+       /* register network device */
+       ret = register_netdev(ndev);
+       if (ret)
+               goto failed_register;
+
+       netdev_info(ndev, "%s: Ethernet switch %pM\n",
+                       ndev->name, ndev->dev_addr);
+
+       return 0;
+
+failed_register:
+failed_clk:
+failed_ioremap:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static struct platform_driver eth_switch_driver = {
+       .probe          = eth_switch_probe,
+       .remove         = (eth_switch_remove),
+       .driver         = {
+               .name   = "eth-switch",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_eth_switch_match,
+       },
+};
+
+static int __init fsl_l2_switch_init(void)
+{
+       return platform_driver_register(&eth_switch_driver);
+}
+
+static void __exit fsl_l2_switch_exit(void)
+{
+       platform_driver_unregister(&eth_switch_driver);
+}
+
+module_init(fsl_l2_switch_init);
+module_exit(fsl_l2_switch_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/freescale/fsl_l2_switch.h b/drivers/net/ethernet/freescale/fsl_l2_switch.h
new file mode 100644 (file)
index 0000000..62b5f12
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ *   Copyright 2010-2012 Freescale Semiconductor, Inc
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or (at
+ *   your option) any later version.
+ *
+ */
+
+#ifndef FSL_L2_SWITCH_H
+#define        FSL_L2_SWITCH_H
+
+/* Interrupt events/masks */
+#define FEC_ENET_HBERR BIT(31) /* Heartbeat error */
+#define FEC_ENET_BABR  BIT(30) /* Babbling receiver */
+#define FEC_ENET_BABT  BIT(29) /* Babbling transmitter */
+#define FEC_ENET_GRA   BIT(28) /* Graceful stop complete */
+#define FEC_ENET_TXF   BIT(27) /* Full frame transmitted */
+#define FEC_ENET_TXB   BIT(26) /* A buffer was transmitted */
+#define FEC_ENET_RXF   BIT(25) /* Full frame received */
+#define FEC_ENET_RXB   BIT(24) /* A buffer was received */
+#define FEC_ENET_MII   BIT(23) /* MII interrupt */
+#define FEC_ENET_EBERR BIT(22) /* SDMA bus error */
+
+#define                NMII    20
+
+/* Make MII read/write commands for the FEC */
+#define mk_mii_read(REG)       (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
+                                               (VAL & 0xffff))
+/* MII MMFR bits definition */
+#define ESW_MMFR_ST            BIT(30)
+#define ESW_MMFR_OP_READ       (2 << 28)
+#define ESW_MMFR_OP_WRITE      BIT(28)
+#define ESW_MMFR_PA(v)         ((v & 0x1f) << 23)
+#define ESW_MMFR_RA(v)         ((v & 0x1f) << 18)
+#define ESW_MMFR_TA            (2 << 16)
+#define ESW_MMFR_DATA(v)       (v & 0xffff)
+
+#define ESW_MII_TIMEOUT                30 /* ms */
+
+/* Transmitter timeout.*/
+#define TX_TIMEOUT (2 * HZ)
+
+/* The Switch stores dest/src/type, data,
+ * and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE         1518
+#define PKT_MINBUF_SIZE         64
+#define PKT_MAXBLR_SIZE         1520
+
+/* The 5441x RX control register also contains maximum frame
+ * size bits.
+ */
+#define OPT_FRAME_SIZE  (PKT_MAXBUF_SIZE << 16)
+
+/* The number of Tx and Rx buffers. These are allocated from the page
+ * pool. The code may assume these are power of two, so it it best
+ * to keep them that size.
+ * We don't need to allocate pages for the transmitter.  We just use
+ * the skbuffer directly.
+ */
+#ifdef CONFIG_SWITCH_DMA_USE_SRAM
+#define SWITCH_ENET_RX_PAGES       6
+#else
+#define SWITCH_ENET_RX_PAGES       8
+#endif
+
+#define SWITCH_ENET_RX_FRSIZE  2048
+#define SWITCH_ENET_RX_FRPPG   (PAGE_SIZE / SWITCH_ENET_RX_FRSIZE)
+#define RX_RING_SIZE           (SWITCH_ENET_RX_FRPPG * SWITCH_ENET_RX_PAGES)
+#define SWITCH_ENET_TX_FRSIZE  2048
+#define SWITCH_ENET_TX_FRPPG   (PAGE_SIZE / SWITCH_ENET_TX_FRSIZE)
+
+#ifdef CONFIG_SWITCH_DMA_USE_SRAM
+#define TX_RING_SIZE           8      /* Must be power of two */
+#define TX_RING_MOD_MASK       7      /*   for this to work */
+#else
+#define TX_RING_SIZE           16      /* Must be power of two */
+#define TX_RING_MOD_MASK       15      /*   for this to work */
+#endif
+
+#define SWITCH_EPORT_NUMBER    2
+
+#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE)
+#error "L2SWITCH: descriptor ring size constants too large"
+#endif
+
+/* memory-mapped register offset */
+#define        FEC_ESW_REVISION        0x00
+#define        FEC_ESW_SCRATCH         0x04
+#define        FEC_ESW_PER             0x08
+
+#define        FEC_ESW_VLANV   0x10
+#define        FEC_ESW_DBCR    0x14
+#define        FEC_ESW_DMCR    0x18
+#define        FEC_ESW_BKLR    0x1C
+#define        FEC_ESW_BMPC    0x20
+#define        FEC_ESW_MODE    0x24
+#define        FEC_ESW_VIMSEL  0x28
+#define        FEC_ESW_VOMSEL  0x2C
+#define        FEC_ESW_VIMEN   0x30
+#define        FEC_ESW_VID     0x34
+
+#define        FEC_ESW_MCR     0x40
+#define        FEC_ESW_EGMAP   0x44
+#define        FEC_ESW_INGMAP  0x48
+#define        FEC_ESW_INGSAL  0x4C
+#define        FEC_ESW_INGSAH  0x50
+#define        FEC_ESW_INGDAL  0x54
+#define        FEC_ESW_INGDAH  0x58
+#define        FEC_ESW_ENGSAL  0x5C
+#define        FEC_ESW_ENGSAH  0x60
+#define        FEC_ESW_ENGDAL  0x64
+#define        FEC_ESW_ENGDAH  0x68
+#define        FEC_ESW_MCVAL   0x6C
+
+#define        FEC_ESW_MMSR    0x80
+#define        FEC_ESW_LMT     0x84
+#define        FEC_ESW_LFC     0x88
+#define        FEC_ESW_PCSR    0x8C
+#define        FEC_ESW_IOSR    0x90
+#define        FEC_ESW_QWT     0x94
+
+#define        FEC_ESW_P0BCT   0x9C
+
+#define        FEC_ESW_P0FFEN          0xBC
+#define        FEC_ESW_PSNP(n)         (0xC0 + 4 * n)
+#define        FEC_ESW_IPSNP(n)        (0xE0 + 4 * n)
+#define        FEC_ESW_PVRES(n)        (0x100 + 4 * n)
+
+#define        FEC_ESW_IPRES   0x140
+
+/* port0-port2 Priority Configuration  0xFC0D_C180-C188 */
+#define        FEC_ESW_PRES(n) (0x180 + n * 4)
+
+/* port0-port2 VLAN ID 0xFC0D_C200-C208 */
+#define        FEC_ESW_PID(n)  (0x200 + 4 * n)
+
+/* port0-port2 VLAN domain resolution entry 0xFC0D_C280-C2FC */
+#define        FEC_ESW_VRES(n) (0x280 + n * 4)
+
+#define        FEC_ESW_DISCN   0x300
+#define        FEC_ESW_DISCB   0x304
+#define        FEC_ESW_NDISCN  0x308
+#define        FEC_ESW_NDISCB  0x30C
+/* per port statistics 0xFC0DC310_C33C */
+
+#define FEC_ESW_POQC(n)                (0x310 + n * 16)
+#define FEC_ESW_PMVID(n)       (0x310 + n * 16 + 0x04)
+#define FEC_ESW_PMVTAG(n)      (0x310 + n * 16 + 0x08)
+#define FEC_ESW_PBL(n)         (0x310 + n * 16 + 0x0C)
+
+#define        FEC_ESW_ISR     0x400 /* Interrupt event reg */
+#define        FEC_ESW_IMR     0x404 /* Interrupt mask reg */
+#define        FEC_ESW_RDSR    0x408 /* Receive descriptor ring */
+#define        FEC_ESW_TDSR    0x40C /* Transmit descriptor ring */
+#define        FEC_ESW_MRBR    0x410 /* Maximum receive buff size */
+#define        FEC_ESW_RDAR    0x414 /* Receive descriptor active */
+#define        FEC_ESW_TDAR    0x418 /* Transmit descriptor active */
+
+#define        FEC_ESW_LREC0   0x500
+#define        FEC_ESW_LREC1   0x504
+#define        FEC_ESW_LSR     0x508
+
+#include <linux/phy.h>
+struct switch_platform_data {
+       phy_interface_t phy;
+       unsigned char mac[ETH_ALEN];
+};
+
+#define FSL_FEC_MMFR0  (0x40)
+#define FSL_FEC_MSCR0  (0x44)
+#define FSL_FEC_MSCR1  (0x1044)
+#define FSL_FEC_RCR0   (0x84)
+#define FSL_FEC_RCR1   (0x1084)
+#define FSL_FEC_TCR0   (0xc4)
+#define FSL_FEC_TCR1   (0x10c4)
+#define FSL_FEC_ECR0   (0x24)
+#define FSL_FEC_ECR1   (0x1024)
+#define FSL_FEC_EIR0   (0x04)
+#define FSL_FEC_EIR1   (0x1004)
+#define FSL_FEC_EIMR0   (0x08)
+#define FSL_FEC_EIMR1   (0x1008)
+#define FSL_FEC_PALR0  (0x0e4)
+#define FSL_FEC_PAUR0  (0x0e8)
+#define FSL_FEC_PALR1  (0x10e4)
+#define FSL_FEC_PAUR1  (0x10e8)
+#define FSL_FEC_X_WMRK0        (0x0144)
+#define FSL_FEC_X_WMRK1        (0x1144)
+
+#define FSL_FEC_RCR_MII_MODE                 (0x00000004)
+#define FSL_FEC_RCR_PROM                     (0x00000008)
+#define FSL_FEC_RCR_RMII_MODE                (0x00000100)
+#define FSL_FEC_RCR_MAX_FL(x)                (((x) & 0x00003FFF) << 16)
+#define FSL_FEC_RCR_CRC_FWD                  (0x00004000)
+
+#define FSL_FEC_TCR_FDEN                     (0x00000004)
+
+#define FSL_FEC_ECR_ETHER_EN                 (0x00000002)
+#define FSL_FEC_ECR_ENA_1588                 (0x00000010)
+
+#define LEARNING_AGING_TIMER (10 * HZ)
+
+/* Define the buffer descriptor structure. */
+typedef struct bufdesc {
+       unsigned short  cbd_datlen;             /* Data length */
+       unsigned short  cbd_sc;                 /* Control and status info */
+       unsigned long   cbd_bufaddr;            /* Buffer address */
+} cbd_t;
+
+typedef struct bufdesc_rx {
+       unsigned short  cbd_datlen;             /* Data length */
+       unsigned short  cbd_sc;                 /* Control and status info */
+       unsigned long   cbd_bufaddr;            /* Buffer address */
+} cbd_t_r;
+
+/* Forward declarations of some structures to support different PHYs */
+typedef struct _phy_cmd_t {
+       uint mii_data;
+       void (*funct)(uint mii_reg, struct net_device *dev);
+} phy_cmd_t;
+
+typedef struct _phy_info_t {
+       uint id;
+       char *name;
+
+       const phy_cmd_t *config;
+       const phy_cmd_t *startup;
+       const phy_cmd_t *ack_int;
+       const phy_cmd_t *shutdown;
+} phy_info_t;
+
+struct port_status {
+       /* 1: link is up, 0: link is down */
+       int port1_link_status;
+       int port2_link_status;
+       /* 1: blocking, 0: unblocking */
+       int port0_block_status;
+       int port1_block_status;
+       int port2_block_status;
+};
+
+/* The switch 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.
+ * The dirty_tx tracks the current buffer that is being sent by the
+ * controller.  The cur_tx and dirty_tx are equal under both completely
+ * empty and completely full conditions.  The empty/ready indicator in
+ * the buffer descriptor determines the actual condition.
+ */
+struct switch_enet_private {
+       /* Hardware registers of the switch device */
+       void __iomem *membase;
+       void __iomem *macbase; /* MAC address lookup table */
+       void __iomem *enetbase;
+
+       struct clk *clk_esw;
+       struct clk *clk_enet;
+       struct clk *clk_enet0;
+       struct clk *clk_enet1;
+
+       struct net_device *netdev;
+       struct platform_device *pdev;
+
+       int     dev_id;
+
+       /* 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];
+       ushort  skb_cur;
+       ushort  skb_dirty;
+
+       /* CPM dual port RAM relative addresses */
+       dma_addr_t      bd_dma;
+
+       cbd_t   *rx_bd_base;            /* Address of Rx and Tx buffers. */
+       cbd_t   *tx_bd_base;
+       cbd_t   *cur_rx, *cur_tx;       /* The next free ring entry */
+       cbd_t   *dirty_tx;              /* The ring entries to be free()ed. */
+       uint    tx_full;
+       /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
+       spinlock_t hw_lock;
+
+       uint    sequence_done;
+
+       int     index;
+       int     opened;
+       int     full_duplex;
+       int     msg_enable;
+
+       /* timer related */
+       /* current time (for timestamping) */
+       int curr_time;
+       /* flag set by timer when curr_time changed
+        * and cleared by serving function
+        */
+       int time_changed;
+
+       /* Timer for Aging */
+       struct timer_list       timer_aging;
+       int learning_irqhandle_enable;
+};
+
+struct switch_platform_private {
+       unsigned long           quirks;
+       int                     num_slots;      /* Slots on controller */
+       struct switch_enet_private *fep_host[0];        /* Pointers to hosts */
+};
+
+/* Receive is empty */
+#define BD_SC_EMPTY     ((unsigned short)0x8000)
+/* Transmit is ready */
+#define BD_SC_READY     ((unsigned short)0x8000)
+/* Last buffer descriptor */
+#define BD_SC_WRAP      ((unsigned short)0x2000)
+/* Interrupt on change */
+#define BD_SC_INTRPT    ((unsigned short)0x1000)
+/* Continuous mode */
+#define BD_SC_CM        ((unsigned short)0x0200)
+/* Rec'd too many idles */
+#define BD_SC_ID        ((unsigned short)0x0100)
+/* xmt preamble */
+#define BD_SC_P         ((unsigned short)0x0100)
+/* Break received */
+#define BD_SC_BR        ((unsigned short)0x0020)
+/* Framing error */
+#define BD_SC_FR        ((unsigned short)0x0010)
+/* Parity error */
+#define BD_SC_PR        ((unsigned short)0x0008)
+/* Overrun */
+#define BD_SC_OV        ((unsigned short)0x0002)
+#define BD_SC_CD        ((unsigned short)0x0001)
+
+/* Buffer descriptor control/status used by Ethernet receive.*/
+#define BD_ENET_RX_EMPTY        ((unsigned short)0x8000)
+#define BD_ENET_RX_WRAP         ((unsigned short)0x2000)
+#define BD_ENET_RX_INTR         ((unsigned short)0x1000)
+#define BD_ENET_RX_LAST         ((unsigned short)0x0800)
+#define BD_ENET_RX_FIRST        ((unsigned short)0x0400)
+#define BD_ENET_RX_MISS         ((unsigned short)0x0100)
+#define BD_ENET_RX_LG           ((unsigned short)0x0020)
+#define BD_ENET_RX_NO           ((unsigned short)0x0010)
+#define BD_ENET_RX_SH           ((unsigned short)0x0008)
+#define BD_ENET_RX_CR           ((unsigned short)0x0004)
+#define BD_ENET_RX_OV           ((unsigned short)0x0002)
+#define BD_ENET_RX_CL           ((unsigned short)0x0001)
+
+/* All status bits */
+#define BD_ENET_RX_STATS        ((unsigned short)0x013f)
+
+/* Buffer descriptor control/status used by Ethernet transmit. */
+#define BD_ENET_TX_READY        ((unsigned short)0x8000)
+#define BD_ENET_TX_PAD          ((unsigned short)0x4000)
+#define BD_ENET_TX_WRAP         ((unsigned short)0x2000)
+#define BD_ENET_TX_INTR         ((unsigned short)0x1000)
+#define BD_ENET_TX_LAST         ((unsigned short)0x0800)
+#define BD_ENET_TX_TC           ((unsigned short)0x0400)
+#define BD_ENET_TX_DEF          ((unsigned short)0x0200)
+#define BD_ENET_TX_HB           ((unsigned short)0x0100)
+#define BD_ENET_TX_LC           ((unsigned short)0x0080)
+#define BD_ENET_TX_RL           ((unsigned short)0x0040)
+#define BD_ENET_TX_RCMASK       ((unsigned short)0x003c)
+#define BD_ENET_TX_UN           ((unsigned short)0x0002)
+#define BD_ENET_TX_CSL          ((unsigned short)0x0001)
+
+/* All status bits */
+#define BD_ENET_TX_STATS        ((unsigned short)0x03ff)
+
+/* Copy from validation code */
+#define RX_BUFFER_SIZE 1520
+#define TX_BUFFER_SIZE 1520
+#define NUM_RXBDS 20
+#define NUM_TXBDS 20
+
+#define TX_BD_R                 0x8000
+#define TX_BD_TO1               0x4000
+#define TX_BD_W                 0x2000
+#define TX_BD_TO2               0x1000
+#define TX_BD_L                 0x0800
+#define TX_BD_TC                0x0400
+
+#define TX_BD_INT              0x40000000
+#define TX_BD_TS               0x20000000
+#define TX_BD_PINS             0x10000000
+#define TX_BD_IINS             0x08000000
+#define TX_BD_TXE              0x00008000
+#define TX_BD_UE               0x00002000
+#define TX_BD_EE               0x00001000
+#define TX_BD_FE               0x00000800
+#define TX_BD_LCE              0x00000400
+#define TX_BD_OE               0x00000200
+#define TX_BD_TSE              0x00000100
+#define TX_BD_BDU              0x80000000
+
+#define RX_BD_E                 0x8000
+#define RX_BD_R01               0x4000
+#define RX_BD_W                 0x2000
+#define RX_BD_R02               0x1000
+#define RX_BD_L                 0x0800
+#define RX_BD_M                 0x0100
+#define RX_BD_BC                0x0080
+#define RX_BD_MC                0x0040
+#define RX_BD_LG                0x0020
+#define RX_BD_NO                0x0010
+#define RX_BD_CR                0x0004
+#define RX_BD_OV                0x0002
+#define RX_BD_TR                0x0001
+
+#define RX_BD_ME               0x80000000
+#define RX_BD_PE               0x04000000
+#define RX_BD_CE               0x02000000
+#define RX_BD_UC               0x01000000
+#define RX_BD_INT              0x00800000
+#define RX_BD_ICE              0x00000020
+#define RX_BD_PCR              0x00000010
+#define RX_BD_VLAN             0x00000004
+#define RX_BD_IPV6             0x00000002
+#define RX_BD_FRAG             0x00000001
+#define RX_BD_BDU              0x80000000
+
+/* Address Table size in bytes(2048 64bit entry ) */
+#define ESW_ATABLE_MEM_SIZE         (2048 * 8)
+/* How many 64-bit elements fit in the address table */
+#define ESW_ATABLE_MEM_NUM_ENTRIES  (2048)
+/* Address Table Maximum number of entries in each Slot */
+#define ATABLE_ENTRY_PER_SLOT 8
+/* log2(ATABLE_ENTRY_PER_SLOT) */
+#define ATABLE_ENTRY_PER_SLOT_bits 3
+/* entry size in byte */
+#define ATABLE_ENTRY_SIZE     8
+/*  slot size in byte */
+#define ATABLE_SLOT_SIZE    (ATABLE_ENTRY_PER_SLOT * ATABLE_ENTRY_SIZE)
+/* width of timestamp variable (bits) within address table entry */
+#define AT_DENTRY_TIMESTAMP_WIDTH    10
+/* number of bits for port number storage */
+#define AT_DENTRY_PORT_WIDTH     4
+/* number of bits for port bitmask number storage */
+#define AT_SENTRY_PORT_WIDTH     7
+/* address table static entry port bitmask start address bit */
+#define AT_SENTRY_PORTMASK_shift     21
+/* number of bits for port priority storage */
+#define AT_SENTRY_PRIO_WIDTH   7
+/* address table static entry priority start address bit */
+#define AT_SENTRY_PRIO_shift     18
+/* address table dynamic entry port start address bit */
+#define AT_DENTRY_PORT_shift     28
+/* address table dynamic entry timestamp start address bit */
+#define AT_DENTRY_TIME_shift     18
+/* address table entry record type start address bit */
+#define AT_ENTRY_TYPE_shift     17
+/* address table entry record type bit: 1 static, 0 dynamic */
+#define AT_ENTRY_TYPE_STATIC      1
+#define AT_ENTRY_TYPE_DYNAMIC     0
+/* address table entry record valid start address bit */
+#define AT_ENTRY_VALID_shift     16
+#define AT_ENTRY_RECORD_VALID     1
+
+#define AT_EXTRACT_VALID(x)   \
+       ((x >> AT_ENTRY_VALID_shift) & AT_ENTRY_RECORD_VALID)
+
+#define AT_EXTRACT_PORTMASK(x)  \
+       ((x >> AT_SENTRY_PORTMASK_shift) & AT_SENTRY_PORT_WIDTH)
+
+#define AT_EXTRACT_PRIO(x)  \
+       ((x >> AT_SENTRY_PRIO_shift) & AT_SENTRY_PRIO_WIDTH)
+
+/* return block corresponding to the 8 bit hash value calculated */
+#define GET_BLOCK_PTR(hash)  (hash << 3)
+#define AT_EXTRACT_TIMESTAMP(x) \
+       ((x >> AT_DENTRY_TIME_shift) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1))
+#define AT_EXTRACT_PORT(x)   \
+       ((x >> AT_DENTRY_PORT_shift) & ((1 << AT_DENTRY_PORT_WIDTH) - 1))
+#define AT_SEXTRACT_PORT(x)  \
+       ((~((x >> AT_SENTRY_PORTMASK_shift) &  \
+          ((1 << AT_DENTRY_PORT_WIDTH) - 1))) >> 1)
+#define TIMEDELTA(newtime, oldtime) \
+        ((newtime - oldtime) & \
+         ((1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1))
+
+#define AT_EXTRACT_IP_PROTOCOL(x) ((x >> 8) & 0xff)
+#define AT_EXTRACT_TCP_UDP_PORT(x) ((x >> 16) & 0xffff)
+
+/* increment time value respecting modulo. */
+#define TIMEINCREMENT(time) \
+       ((time) = ((time)+1) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH)-1))
+
+/* Bit definitions and macros for FSL_ESW_REVISION */
+#define FSL_ESW_REVISION_CORE_REVISION(x)      (((x)&0x0000FFFF) << 0)
+#define FSL_ESW_REVISION_CUSTOMER_REVISION(x)  (((x)&0x0000FFFF) << 16)
+
+/* Bit definitions and macros for FSL_ESW_PER */
+#define FSL_ESW_PER_TE0                        (0x00000001)
+#define FSL_ESW_PER_TE1                        (0x00000002)
+#define FSL_ESW_PER_TE2                        (0x00000004)
+#define FSL_ESW_PER_RE0                        (0x00010000)
+#define FSL_ESW_PER_RE1                        (0x00020000)
+#define FSL_ESW_PER_RE2                        (0x00040000)
+
+/* Bit definitions and macros for FSL_ESW_VLANV */
+#define FSL_ESW_VLANV_VV0                      (0x00000001)
+#define FSL_ESW_VLANV_VV1                      (0x00000002)
+#define FSL_ESW_VLANV_VV2                      (0x00000004)
+#define FSL_ESW_VLANV_DU0                      (0x00010000)
+#define FSL_ESW_VLANV_DU1                      (0x00020000)
+#define FSL_ESW_VLANV_DU2                      (0x00040000)
+
+/* Bit definitions and macros for FSL_ESW_DBCR */
+#define FSL_ESW_DBCR_P0                        (0x00000001)
+#define FSL_ESW_DBCR_P1                        (0x00000002)
+#define FSL_ESW_DBCR_P2                        (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_DMCR */
+#define FSL_ESW_DMCR_P0                        (0x00000001)
+#define FSL_ESW_DMCR_P1                        (0x00000002)
+#define FSL_ESW_DMCR_P2                        (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_BKLR */
+#define FSL_ESW_BKLR_BE0                       (0x00000001)
+#define FSL_ESW_BKLR_BE1                       (0x00000002)
+#define FSL_ESW_BKLR_BE2                       (0x00000004)
+#define FSL_ESW_BKLR_LD0                       (0x00010000)
+#define FSL_ESW_BKLR_LD1                       (0x00020000)
+#define FSL_ESW_BKLR_LD2                       (0x00040000)
+#define        FSL_ESW_BKLR_LDX                        (0x00070007)
+
+/* Bit definitions and macros for FSL_ESW_BMPC */
+#define FSL_ESW_BMPC_PORT(x)                   (((x) & 0x0000000F) << 0)
+#define FSL_ESW_BMPC_MSG_TX                    (0x00000020)
+#define FSL_ESW_BMPC_EN                        (0x00000040)
+#define FSL_ESW_BMPC_DIS                       (0x00000080)
+#define FSL_ESW_BMPC_PRIORITY(x)               (((x) & 0x00000007) << 13)
+#define FSL_ESW_BMPC_PORTMASK(x)               (((x) & 0x00000007) << 16)
+
+/* Bit definitions and macros for FSL_ESW_MODE */
+#define FSL_ESW_MODE_SW_RST                    (0x00000001)
+#define FSL_ESW_MODE_SW_EN                     (0x00000002)
+#define FSL_ESW_MODE_STOP                      (0x00000080)
+#define FSL_ESW_MODE_CRC_TRAN                  (0x00000100)
+#define FSL_ESW_MODE_P0CT                      (0x00000200)
+#define FSL_ESW_MODE_STATRST                   (0x80000000)
+
+/* Bit definitions and macros for FSL_ESW_VIMSEL */
+#define FSL_ESW_VIMSEL_IM0(x)                  (((x) & 0x00000003) << 0)
+#define FSL_ESW_VIMSEL_IM1(x)                  (((x) & 0x00000003) << 2)
+#define FSL_ESW_VIMSEL_IM2(x)                  (((x) & 0x00000003) << 4)
+
+/* Bit definitions and macros for FSL_ESW_VOMSEL */
+#define FSL_ESW_VOMSEL_OM0(x)                  (((x) & 0x00000003) << 0)
+#define FSL_ESW_VOMSEL_OM1(x)                  (((x) & 0x00000003) << 2)
+#define FSL_ESW_VOMSEL_OM2(x)                  (((x) & 0x00000003) << 4)
+
+/* Bit definitions and macros for FSL_ESW_VIMEN */
+#define FSL_ESW_VIMEN_EN0                      (0x00000001)
+#define FSL_ESW_VIMEN_EN1                      (0x00000002)
+#define FSL_ESW_VIMEN_EN2                      (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_VID */
+#define FSL_ESW_VID_TAG(x)                     (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_MCR */
+#define FSL_ESW_MCR_PORT(x)                    (((x) & 0x0000000F) << 0)
+#define FSL_ESW_MCR_MEN                        (0x00000010)
+#define FSL_ESW_MCR_INGMAP                     (0x00000020)
+#define FSL_ESW_MCR_EGMAP                      (0x00000040)
+#define FSL_ESW_MCR_INGSA                      (0x00000080)
+#define FSL_ESW_MCR_INGDA                      (0x00000100)
+#define FSL_ESW_MCR_EGSA                       (0x00000200)
+#define FSL_ESW_MCR_EGDA                       (0x00000400)
+
+/* Bit definitions and macros for FSL_ESW_EGMAP */
+#define FSL_ESW_EGMAP_EG0                      (0x00000001)
+#define FSL_ESW_EGMAP_EG1                      (0x00000002)
+#define FSL_ESW_EGMAP_EG2                      (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_INGMAP */
+#define FSL_ESW_INGMAP_ING0                    (0x00000001)
+#define FSL_ESW_INGMAP_ING1                    (0x00000002)
+#define FSL_ESW_INGMAP_ING2                    (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_INGSAL */
+#define FSL_ESW_INGSAL_ADDLOW(x)               (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_INGSAH */
+#define FSL_ESW_INGSAH_ADDHIGH(x)              (((x) & 0x0000FFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_INGDAL */
+#define FSL_ESW_INGDAL_ADDLOW(x)               (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_INGDAH */
+#define FSL_ESW_INGDAH_ADDHIGH(x)              (((x) & 0x0000FFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_ENGSAL */
+#define FSL_ESW_ENGSAL_ADDLOW(x)               (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_ENGSAH */
+#define FSL_ESW_ENGSAH_ADDHIGH(x)              (((x) & 0x0000FFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_ENGDAL */
+#define FSL_ESW_ENGDAL_ADDLOW(x)               (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_ENGDAH */
+#define FSL_ESW_ENGDAH_ADDHIGH(x)              (((x) & 0x0000FFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_MCVAL */
+#define FSL_ESW_MCVAL_COUNT(x)                 (((x) & 0x000000FF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_MMSR */
+#define FSL_ESW_MMSR_BUSY                      (0x00000001)
+#define FSL_ESW_MMSR_NOCELL                    (0x00000002)
+#define FSL_ESW_MMSR_MEMFULL                   (0x00000004)
+#define FSL_ESW_MMSR_MFLATCH                   (0x00000008)
+#define FSL_ESW_MMSR_DQ_GRNT                   (0x00000040)
+#define FSL_ESW_MMSR_CELLS_AVAIL(x)            (((x) & 0x000000FF) << 16)
+
+/* Bit definitions and macros for FSL_ESW_LMT */
+#define FSL_ESW_LMT_THRESH(x)                  (((x) & 0x000000FF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_LFC */
+#define FSL_ESW_LFC_COUNT(x)                   (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_PCSR */
+#define FSL_ESW_PCSR_PC0                       (0x00000001)
+#define FSL_ESW_PCSR_PC1                       (0x00000002)
+#define FSL_ESW_PCSR_PC2                       (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_IOSR */
+#define FSL_ESW_IOSR_OR0                       (0x00000001)
+#define FSL_ESW_IOSR_OR1                       (0x00000002)
+#define FSL_ESW_IOSR_OR2                       (0x00000004)
+
+/* Bit definitions and macros for FSL_ESW_QWT */
+#define FSL_ESW_QWT_Q0WT(x)                    (((x) & 0x0000001F) << 0)
+#define FSL_ESW_QWT_Q1WT(x)                    (((x) & 0x0000001F) << 8)
+#define FSL_ESW_QWT_Q2WT(x)                    (((x) & 0x0000001F) << 16)
+#define FSL_ESW_QWT_Q3WT(x)                    (((x) & 0x0000001F) << 24)
+
+/* Bit definitions and macros for FSL_ESW_P0BCT */
+#define FSL_ESW_P0BCT_THRESH(x)                (((x) & 0x000000FF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_P0FFEN */
+#define FSL_ESW_P0FFEN_FEN                     (0x00000001)
+#define FSL_ESW_P0FFEN_FD(x)                   (((x) & 0x00000003) << 2)
+
+/* Bit definitions and macros for FSL_ESW_PSNP */
+#define FSL_ESW_PSNP_EN                        (0x00000001)
+#define FSL_ESW_PSNP_MODE(x)                   (((x) & 0x00000003) << 1)
+#define FSL_ESW_PSNP_CD                        (0x00000008)
+#define FSL_ESW_PSNP_CS                        (0x00000010)
+#define FSL_ESW_PSNP_PORT_COMPARE(x)           (((x) & 0x0000FFFF) << 16)
+
+/* Bit definitions and macros for FSL_ESW_IPSNP */
+#define FSL_ESW_IPSNP_EN                       (0x00000001)
+#define FSL_ESW_IPSNP_MODE(x)                  (((x) & 0x00000003) << 1)
+#define FSL_ESW_IPSNP_PROTOCOL(x)              (((x) & 0x000000FF) << 8)
+
+/* Bit definitions and macros for FSL_ESW_PVRES */
+#define FSL_ESW_PVRES_PRI0(x)                  (((x) & 0x00000007) << 0)
+#define FSL_ESW_PVRES_PRI1(x)                  (((x) & 0x00000007) << 3)
+#define FSL_ESW_PVRES_PRI2(x)                  (((x) & 0x00000007) << 6)
+#define FSL_ESW_PVRES_PRI3(x)                  (((x) & 0x00000007) << 9)
+#define FSL_ESW_PVRES_PRI4(x)                  (((x) & 0x00000007) << 12)
+#define FSL_ESW_PVRES_PRI5(x)                  (((x) & 0x00000007) << 15)
+#define FSL_ESW_PVRES_PRI6(x)                  (((x) & 0x00000007) << 18)
+#define FSL_ESW_PVRES_PRI7(x)                  (((x) & 0x00000007) << 21)
+
+/* Bit definitions and macros for FSL_ESW_IPRES */
+#define FSL_ESW_IPRES_ADDRESS(x)               (((x) & 0x000000FF) << 0)
+#define FSL_ESW_IPRES_IPV4SEL                  (0x00000100)
+#define FSL_ESW_IPRES_PRI0(x)                  (((x) & 0x00000003) << 9)
+#define FSL_ESW_IPRES_PRI1(x)                  (((x) & 0x00000003) << 11)
+#define FSL_ESW_IPRES_PRI2(x)                   (((x) & 0x00000003) << 13)
+#define FSL_ESW_IPRES_READ                     (0x80000000)
+
+/* Bit definitions and macros for FSL_ESW_PRES */
+#define FSL_ESW_PRES_VLAN                      (0x00000001)
+#define FSL_ESW_PRES_IP                        (0x00000002)
+#define FSL_ESW_PRES_MAC                       (0x00000004)
+#define FSL_ESW_PRES_DFLT_PRI(x)               (((x) & 0x00000007) << 4)
+
+/* Bit definitions and macros for FSL_ESW_PID */
+#define FSL_ESW_PID_VLANID(x)                  (((x) & 0x0000FFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_VRES */
+#define FSL_ESW_VRES_P0                        (0x00000001)
+#define FSL_ESW_VRES_P1                        (0x00000002)
+#define FSL_ESW_VRES_P2                        (0x00000004)
+#define FSL_ESW_VRES_VLANID(x)                 (((x) & 0x00000FFF) << 3)
+
+/* Bit definitions and macros for FSL_ESW_DISCN */
+#define FSL_ESW_DISCN_COUNT(x)                 (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_DISCB */
+#define FSL_ESW_DISCB_COUNT(x)                 (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_NDISCN */
+#define FSL_ESW_NDISCN_COUNT(x)                (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_NDISCB */
+#define FSL_ESW_NDISCB_COUNT(x)                (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_POQC */
+#define FSL_ESW_POQC_COUNT(x)                  (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_PMVID */
+#define FSL_ESW_PMVID_COUNT(x)                 (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_PMVTAG */
+#define FSL_ESW_PMVTAG_COUNT(x)                (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_PBL */
+#define FSL_ESW_PBL_COUNT(x)                   (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_ISR */
+#define FSL_ESW_ISR_EBERR                      (0x00000001)
+#define FSL_ESW_ISR_RXB                        (0x00000002)
+#define FSL_ESW_ISR_RXF                        (0x00000004)
+#define FSL_ESW_ISR_TXB                        (0x00000008)
+#define FSL_ESW_ISR_TXF                        (0x00000010)
+#define FSL_ESW_ISR_QM                         (0x00000020)
+#define FSL_ESW_ISR_OD0                        (0x00000040)
+#define FSL_ESW_ISR_OD1                        (0x00000080)
+#define FSL_ESW_ISR_OD2                        (0x00000100)
+#define FSL_ESW_ISR_LRN                        (0x00000200)
+
+/* Bit definitions and macros for FSL_ESW_IMR */
+#define FSL_ESW_IMR_EBERR                      (0x00000001)
+#define FSL_ESW_IMR_RXB                        (0x00000002)
+#define FSL_ESW_IMR_RXF                        (0x00000004)
+#define FSL_ESW_IMR_TXB                        (0x00000008)
+#define FSL_ESW_IMR_TXF                        (0x00000010)
+#define FSL_ESW_IMR_QM                         (0x00000020)
+#define FSL_ESW_IMR_OD0                        (0x00000040)
+#define FSL_ESW_IMR_OD1                        (0x00000080)
+#define FSL_ESW_IMR_OD2                        (0x00000100)
+#define FSL_ESW_IMR_LRN                        (0x00000200)
+
+/* Bit definitions and macros for FSL_ESW_RDSR */
+#define FSL_ESW_RDSR_ADDRESS(x)                (((x) & 0x3FFFFFFF) << 2)
+
+/* Bit definitions and macros for FSL_ESW_TDSR */
+#define FSL_ESW_TDSR_ADDRESS(x)                (((x) & 0x3FFFFFFF) << 2)
+
+/* Bit definitions and macros for FSL_ESW_MRBR */
+#define FSL_ESW_MRBR_SIZE(x)                   (((x) & 0x000003FF) << 4)
+
+/* Bit definitions and macros for FSL_ESW_RDAR */
+#define FSL_ESW_RDAR_R_DES_ACTIVE              (0x01000000)
+
+/* Bit definitions and macros for FSL_ESW_TDAR */
+#define FSL_ESW_TDAR_X_DES_ACTIVE              (0x01000000)
+
+/* Bit definitions and macros for FSL_ESW_LREC0 */
+#define FSL_ESW_LREC0_MACADDR0(x)              (((x) & 0xFFFFFFFF) << 0)
+
+/* Bit definitions and macros for FSL_ESW_LREC1 */
+#define FSL_ESW_LREC1_MACADDR1(x)              (((x) & 0x0000FFFF) << 0)
+#define FSL_ESW_LREC1_HASH(x)                  (((x) & 0x000000FF) << 16)
+#define FSL_ESW_LREC1_SWPORT(x)                (((x) & 0x00000003) << 24)
+
+/* Bit definitions and macros for FSL_ESW_LSR */
+#define FSL_ESW_LSR_DA                         (0x00000001)
+
+/* port mirroring port number match */
+#define MIRROR_EGRESS_PORT_MATCH               1
+#define MIRROR_INGRESS_PORT_MATCH              2
+
+/* port mirroring mac address match */
+#define MIRROR_EGRESS_SOURCE_MATCH             1
+#define MIRROR_INGRESS_SOURCE_MATCH            2
+#define MIRROR_EGRESS_DESTINATION_MATCH                3
+#define MIRROR_INGRESS_DESTINATION_MATCH       4
+
+#endif /* FSL_L2_SWITCH_H */
index 920391165f18297652e1a525f753a57449cb3d4f..ab5d75249bcf0f2afc36446929473edb81fa8de7 100644 (file)
@@ -696,8 +696,6 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
-       .suspend        = genphy_suspend,
-       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8041RNLI,
index 398ec45aadef9fcf335b9604c0ce641dff9fa3c7..3035d33f595751a899db844b29432981466dc7c7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/pinctrl/pinmux.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
 
 #include "../core.h"
 #include "pinctrl-imx.h"
@@ -42,6 +43,8 @@ struct imx_pinctrl {
        void __iomem *base;
        void __iomem *input_sel_base;
        const struct imx_pinctrl_soc_info *info;
+       u32 *mux_regs;
+       u32 *input_regs;
 };
 
 static const inline struct imx_pin_group *imx_pinctrl_find_group_by_name(
@@ -341,6 +344,36 @@ mux_pin:
        return 0;
 }
 
+static void imx_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
+                       struct pinctrl_gpio_range *range, unsigned offset)
+{
+       struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+       const struct imx_pinctrl_soc_info *info = ipctl->info;
+       const struct imx_pin_reg *pin_reg;
+       u32 reg;
+
+       /*
+        * Only Vybrid has the input/output buffer enable flags (IBE/OBE)
+        * They are part of the shared mux/conf register.
+        */
+       if (!(info->flags & SHARE_MUX_CONF_REG))
+               return;
+
+       pin_reg = &info->pin_regs[offset];
+       if (pin_reg->mux_reg == -1)
+               return;
+
+       reg = readl(ipctl->base + pin_reg->mux_reg);
+
+       /* Only change pad configuration if pad is still a GPIO */
+       if (reg & (0x7 << 20))
+               return;
+
+       /* Clear IBE/OBE/PUE to disable the pin (Hi-Z) */
+       reg &= ~0x7;
+       writel(reg, ipctl->base + pin_reg->mux_reg);
+}
+
 static int imx_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
           struct pinctrl_gpio_range *range, unsigned offset, bool input)
 {
@@ -377,6 +410,7 @@ static const struct pinmux_ops imx_pmx_ops = {
        .get_function_groups = imx_pmx_get_groups,
        .set_mux = imx_pmx_set,
        .gpio_request_enable = imx_pmx_gpio_request_enable,
+       .gpio_disable_free = imx_pmx_gpio_disable_free,
        .gpio_set_direction = imx_pmx_gpio_set_direction,
 };
 
@@ -689,6 +723,53 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev,
        return 0;
 }
 
+static int __maybe_unused imx_pinctrl_suspend(struct device *dev)
+{
+       struct imx_pinctrl *ipctl = dev_get_drvdata(dev);
+       const struct imx_pinctrl_soc_info *info = ipctl->info;
+       int i;
+
+       for (i = 0; i < info->npins; i++) {
+               const struct imx_pin_reg *pin_reg = &info->pin_regs[i];
+               if (pin_reg->mux_reg == -1)
+                       continue;
+
+               ipctl->mux_regs[i] = readl(ipctl->base + pin_reg->mux_reg);
+       }
+
+       for (i = 0; i < info->ninput_regs; i++)
+               ipctl->input_regs[i] = readl(ipctl->base +
+                               info->input_regs_offset + i * sizeof(u32 *));
+
+       return 0;
+}
+
+static int __maybe_unused imx_pinctrl_resume(struct device *dev)
+{
+       struct imx_pinctrl *ipctl = dev_get_drvdata(dev);
+       const struct imx_pinctrl_soc_info *info = ipctl->info;
+       const struct imx_pin_reg *pin_reg;
+       int i;
+
+       for (i = 0; i < info->npins; i++) {
+               pin_reg = &info->pin_regs[i];
+               if (pin_reg->mux_reg == -1)
+                       continue;
+
+               writel(ipctl->mux_regs[i], ipctl->base + pin_reg->mux_reg);
+       }
+
+       for (i = 0; i < info->ninput_regs; i++)
+               writel(ipctl->input_regs[i], ipctl->base +
+                               info->input_regs_offset + i * sizeof(u32 *));
+
+       return 0;
+}
+
+const struct dev_pm_ops imx_pinctrl_dev_pm_ops = {
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(imx_pinctrl_suspend, imx_pinctrl_resume)
+};
+
 int imx_pinctrl_probe(struct platform_device *pdev,
                      struct imx_pinctrl_soc_info *info)
 {
@@ -719,6 +800,18 @@ int imx_pinctrl_probe(struct platform_device *pdev,
                info->pin_regs[i].conf_reg = -1;
        }
 
+#ifdef CONFIG_PM_SLEEP
+       ipctl->mux_regs = devm_kzalloc(&pdev->dev, sizeof(u32 *) *
+                                      info->npins, GFP_KERNEL);
+       if (!ipctl->mux_regs)
+               return -ENOMEM;
+
+       ipctl->input_regs = devm_kzalloc(&pdev->dev, sizeof(u32 *) *
+                                        info->ninput_regs, GFP_KERNEL);
+       if (!ipctl->input_regs)
+               return -ENOMEM;
+#endif
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ipctl->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(ipctl->base))
index 2a592f657c184918ab1e7fbb6d9a4d862f0a2ab5..56851a66c27b8733150f13e3d17e61a53022d4d3 100644 (file)
@@ -81,6 +81,8 @@ struct imx_pinctrl_soc_info {
        unsigned int group_index;
        struct imx_pmx_func *functions;
        unsigned int nfunctions;
+       unsigned int input_regs_offset;
+       unsigned int ninput_regs;
        unsigned int flags;
 };
 
@@ -99,4 +101,5 @@ struct imx_pinctrl_soc_info {
 int imx_pinctrl_probe(struct platform_device *pdev,
                        struct imx_pinctrl_soc_info *info);
 int imx_pinctrl_remove(struct platform_device *pdev);
+extern const struct dev_pm_ops imx_pinctrl_dev_pm_ops;
 #endif /* __DRIVERS_PINCTRL_IMX_H */
index 587d1ff6210efb62341b792d0318b412c0a4d046..b6280a8ef9582d09018b46c1f6f07c6dfc99efc6 100644 (file)
@@ -19,6 +19,9 @@
 
 #include "pinctrl-imx.h"
 
+#define VF610_INPUT_REG_CNT            49
+#define VF610_INPUT_REG_BASE           0x2ec
+
 enum vf610_pads {
        VF610_PAD_PTA6 = 0,
        VF610_PAD_PTA8 = 1,
@@ -299,6 +302,8 @@ static const struct pinctrl_pin_desc vf610_pinctrl_pads[] = {
 static struct imx_pinctrl_soc_info vf610_pinctrl_info = {
        .pins = vf610_pinctrl_pads,
        .npins = ARRAY_SIZE(vf610_pinctrl_pads),
+       .input_regs_offset = VF610_INPUT_REG_BASE,
+       .ninput_regs = VF610_INPUT_REG_CNT,
        .flags = SHARE_MUX_CONF_REG | ZERO_OFFSET_VALID,
 };
 
@@ -316,6 +321,7 @@ static struct platform_driver vf610_pinctrl_driver = {
        .driver = {
                .name = "vf610-pinctrl",
                .of_match_table = vf610_pinctrl_of_match,
+               .pm = &imx_pinctrl_dev_pm_ops,
        },
        .probe = vf610_pinctrl_probe,
        .remove = imx_pinctrl_remove,
index 28c711f0ac6bd5c3d77c32018c64abad77c4bdfa..1dd53b1ebf6b918dd1296b732deb2040324b1272 100644 (file)
@@ -41,6 +41,15 @@ config STE_MODEM_RPROC
          This can be either built-in or a loadable module.
          If unsure say N.
 
+config VF610_CM4_RPROC
+       tristate "Freescale Vybrid Cortex-M4 remoteproc support"
+       depends on SOC_VF610
+       select REMOTEPROC
+       select RPMSG
+       help
+        Say y here to support Freescale Vybrid's secondary core via the
+        remote processor framework.
+
 config WKUP_M3_RPROC
        tristate "AMx3xx Wakeup M3 remoteproc support"
        depends on SOC_AM33XX || SOC_AM43XX
index 81b04d1e2e5888b8ebdaca78a551045e3aa0db40..df1814ff283c44c3687ecf37ce4470211f1b7b40 100644 (file)
@@ -9,5 +9,6 @@ remoteproc-y                            += remoteproc_virtio.o
 remoteproc-y                           += remoteproc_elf_loader.o
 obj-$(CONFIG_OMAP_REMOTEPROC)          += omap_remoteproc.o
 obj-$(CONFIG_STE_MODEM_RPROC)          += ste_modem_rproc.o
+obj-$(CONFIG_VF610_CM4_RPROC)          += vf610_cm4_rproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)            += wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)         += da8xx_remoteproc.o
index 4f7ce0097191d5ae0c4b351ddb25c179b19231ee..44d0f5995f547372538f2e77cf11645a7a9efdeb 100644 (file)
@@ -799,12 +799,11 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
        struct resource_table *table, *loaded_table;
        int ret, tablesz;
 
-       if (!rproc->table_ptr)
-               return -ENOMEM;
-
        ret = rproc_fw_sanity_check(rproc, fw);
-       if (ret)
+       if (ret) {
+               dev_err(dev, "rproc_fw_sanity_check returned %d\n", ret);
                return ret;
+       }
 
        dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
 
@@ -823,20 +822,22 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
 
        /* look for the resource table */
        table = rproc_find_rsc_table(rproc, fw, &tablesz);
-       if (!table)
-               goto clean_up;
-
-       /* Verify that resource table in loaded fw is unchanged */
-       if (rproc->table_csum != crc32(0, table, tablesz)) {
-               dev_err(dev, "resource checksum failed, fw changed?\n");
-               goto clean_up;
-       }
+       if (!table) {
+               dev_info(dev, "No resource table found, continuing...\n");
+       } else {
+               /* Verify that resource table in loaded fw is unchanged */
+               if (rproc->table_csum != crc32(0, table, tablesz)) {
+                       dev_err(dev, "resource checksum failed, fw changed?\n");
+                       goto clean_up;
+               }
 
-       /* handle fw resources which are required to boot rproc */
-       ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
-       if (ret) {
-               dev_err(dev, "Failed to process resources: %d\n", ret);
-               goto clean_up;
+               /* handle fw resources which are required to boot rproc */
+               ret = rproc_handle_resources(rproc, tablesz,
+                               rproc_loading_handlers);
+               if (ret) {
+                       dev_err(dev, "Failed to process resources: %d\n", ret);
+                       goto clean_up;
+               }
        }
 
        /* load the ELF segments to memory */
@@ -853,14 +854,15 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
         * In order to pass this information to the remote device we must
         * copy this information to device memory.
         */
-       loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
-       if (!loaded_table) {
-               ret = -EINVAL;
-               goto clean_up;
+       if (table) {
+               loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
+               if (!loaded_table) {
+                       ret = -EINVAL;
+                       goto clean_up;
+               }
+               memcpy(loaded_table, rproc->cached_table, tablesz);
        }
 
-       memcpy(loaded_table, rproc->cached_table, tablesz);
-
        /* power up the remote processor */
        ret = rproc->ops->start(rproc);
        if (ret) {
diff --git a/drivers/remoteproc/vf610_cm4_rproc.c b/drivers/remoteproc/vf610_cm4_rproc.c
new file mode 100644 (file)
index 0000000..da45162
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Freescale VF610 Cortex-M4 Remote Processor driver
+ *
+ * Copyright (C) 2016 Toradex AG
+ *
+ * Based on wkup_m3_rproc.c by:
+ * Copyright (C) 2014-2015 Texas Instruments, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+#define CM4_MEM_MAX    2
+
+/*
+ * struct vf610_m4_mem - Cm4 internal memory structure
+ * @cpu_addr: MPU virtual address of the memory region
+ * @bus_addr: Bus address used to access the memory region
+ * @dev_addr: Device address from Wakeup M4 view
+ * @size: Size of the memory region
+ */
+struct vf610_m4_mem {
+       void __iomem *cpu_addr;
+       phys_addr_t bus_addr;
+       u32 dev_addr;
+       size_t size;
+};
+
+/*
+ * struct vf610_m4_rproc - Cm4 remote processor state
+ * @rproc: rproc handle
+ * @pdev: pointer to platform device
+ * @mem: Cm4 memory information
+ */
+struct vf610_m4_rproc {
+       struct rproc *rproc;
+       struct platform_device *pdev;
+       struct vf610_m4_mem mem[CM4_MEM_MAX];
+       struct regmap *ccm;
+       struct regmap *src;
+};
+
+static int vf610_m4_rproc_start(struct rproc *rproc)
+{
+       struct vf610_m4_rproc *cm4 = rproc->priv;
+
+       regmap_write(cm4->src, 0x28, rproc->bootaddr);
+       regmap_write(cm4->ccm, 0x8c, 0x00015a5a);
+
+       return 0;
+}
+
+static int vf610_m4_rproc_stop(struct rproc *rproc)
+{
+       return -EINVAL;
+}
+
+static void *vf610_m4_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+       struct vf610_m4_rproc *cm4 = rproc->priv;
+       void *va = NULL;
+       u32 offset;
+       int i;
+
+       if (len <= 0)
+               return NULL;
+
+       for (i = 0; i < CM4_MEM_MAX; i++) {
+               if (da >= cm4->mem[i].dev_addr && da + len <=
+                   cm4->mem[i].dev_addr +  cm4->mem[i].size) {
+                       offset = da -  cm4->mem[i].dev_addr;
+                       /* __force to make sparse happy with type conversion */
+                       va = (__force void *)(cm4->mem[i].cpu_addr + offset);
+                       break;
+               }
+       }
+
+       return va;
+}
+
+static void vf610_m4_rproc_kick(struct rproc *rproc, int vqid)
+{
+       /*
+        * We provide this just to prevent Linux from complaining
+        * and causing a kernel panic.
+        */
+}
+
+static struct rproc_ops vf610_m4_rproc_ops = {
+       .start          = vf610_m4_rproc_start,
+       .stop           = vf610_m4_rproc_stop,
+       .da_to_va       = vf610_m4_rproc_da_to_va,
+       .kick           = vf610_m4_rproc_kick,
+};
+
+static const struct of_device_id vf610_m4_rproc_of_match[] = {
+       { .compatible = "fsl,vf610-m4", },
+       {},
+};
+
+static int vf610_m4_rproc_probe(struct platform_device *pdev)
+{
+       const char *mem_names[CM4_MEM_MAX] = { "pc_ocram", "ps_ocram" };
+       struct device *dev = &pdev->dev;
+       struct vf610_m4_rproc *cm4;
+       const char *fw_name;
+       struct rproc *rproc;
+       struct resource *res;
+       const __be32 *addrp;
+       u32 l4_offset = 0;
+       u64 size;
+       int ret;
+       int i, j;
+
+       ret = of_property_read_string(dev->of_node, "fsl,firmware",
+                               &fw_name);
+       if (ret) {
+               dev_err(dev, "No firmware filename given\n");
+               return -ENODEV;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       ret = pm_runtime_get_sync(&pdev->dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+               goto err;
+       }
+
+       rproc = rproc_alloc(dev, "vf610_m4", &vf610_m4_rproc_ops,
+                       fw_name, sizeof(*cm4));
+       if (!rproc) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       cm4 = rproc->priv;
+       cm4->rproc = rproc;
+       cm4->pdev = pdev;
+
+       cm4->src = syscon_regmap_lookup_by_compatible("fsl,vf610-src");
+       cm4->ccm = syscon_regmap_lookup_by_compatible("fsl,vf610-ccm");
+
+       for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                       mem_names[i]);
+
+               /*
+                 * The Cortex-M4 has address aliases, which means we
+                 * have two device addresses which map to the same
+                 * bus address. From Linux side, just use the already
+                 * mapped region for those cases.
+                 */
+               for (j = i - 1; j >= 0; j--) {
+                       if (res->start == cm4->mem[j].bus_addr) {
+                               cm4->mem[i].cpu_addr =
+                                       cm4->mem[j].cpu_addr;
+                               break;
+                       }
+               }
+
+               /* No previous mapping found, create a new mapping */
+               if (j < 0) {
+                       cm4->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
+                       if (IS_ERR(cm4->mem[i].cpu_addr)) {
+                               dev_err(&pdev->dev,
+                                       "devm_ioremap_resource failed for resource %d\n", i);
+                               ret = PTR_ERR(cm4->mem[i].cpu_addr);
+                               goto err;
+                       }
+               }
+
+               cm4->mem[i].bus_addr = res->start;
+               cm4->mem[i].size = resource_size(res);
+               addrp = of_get_address(dev->of_node, i, &size, NULL);
+               cm4->mem[i].dev_addr = be32_to_cpu(*addrp) - l4_offset;
+       }
+
+       dev_set_drvdata(dev, rproc);
+
+       ret = rproc_add(rproc);
+       if (ret) {
+               dev_err(dev, "rproc_add failed\n");
+               goto err_put_rproc;
+       }
+
+       rproc_boot(rproc);
+
+       return 0;
+
+err_put_rproc:
+       rproc_put(rproc);
+err:
+       pm_runtime_put_noidle(dev);
+       pm_runtime_disable(dev);
+       return ret;
+}
+
+static int vf610_m4_rproc_remove(struct platform_device *pdev)
+{
+       struct rproc *rproc = platform_get_drvdata(pdev);
+
+       rproc_del(rproc);
+       rproc_put(rproc);
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int vf610_m4_rpm_suspend(struct device *dev)
+{
+       return -EBUSY;
+}
+
+static int vf610_m4_rpm_resume(struct device *dev)
+{
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops vf610_m4_rproc_pm_ops = {
+       SET_RUNTIME_PM_OPS(vf610_m4_rpm_suspend, vf610_m4_rpm_resume, NULL)
+};
+
+static struct platform_driver vf610_m4_rproc_driver = {
+       .probe = vf610_m4_rproc_probe,
+       .remove = vf610_m4_rproc_remove,
+       .driver = {
+               .name = "vf610_m4_rproc",
+               .of_match_table = vf610_m4_rproc_of_match,
+               .pm = &vf610_m4_rproc_pm_ops,
+       },
+};
+module_platform_driver(vf610_m4_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Freescale Vybrid Cortex-M4 Remote Processor driver");
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
index 69a219387582bf3391f6ecd7075e037769d0de9c..4f42214bf2d6eca7c0de78b8d999835b556b0630 100644 (file)
@@ -6,4 +6,16 @@ config RPMSG
        select VIRTIO
        select VIRTUALIZATION
 
+config IMX_RPMSG_PINGPONG
+       tristate "IMX RPMSG pingpong driver -- loadable modules only"
+       depends on RPMSG && m
+
+config IMX_RPMSG_TTY
+       tristate "IMX RPMSG tty driver -- loadable modules only"
+       depends on RPMSG && m
+
+config VF610_RPMSG
+       tristate "VF610 RPMSG driver -- loadable modules only"
+       depends on RPMSG && m
+
 endmenu
index 7617fcb8259f4836d1383fab3c4798c62c0d2e16..c19b2f7b41c436a8bdb61e0c44843d2750db5b23 100644 (file)
@@ -1 +1,4 @@
 obj-$(CONFIG_RPMSG)    += virtio_rpmsg_bus.o
+obj-$(CONFIG_IMX_RPMSG_PINGPONG)       += imx_rpmsg_pingpong.o
+obj-$(CONFIG_IMX_RPMSG_TTY)    += imx_rpmsg_tty.o
+obj-$(CONFIG_VF610_RPMSG)      += vf610_rpmsg.o
diff --git a/drivers/rpmsg/imx_rpmsg_pingpong.c b/drivers/rpmsg/imx_rpmsg_pingpong.c
new file mode 100644 (file)
index 0000000..a6843b3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * derived from the omap-rpmsg implementation.
+ * Remote processor messaging transport - pingpong driver
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/rpmsg.h>
+
+#define MSG            "hello world!"
+#define MSG_LIMIT      1000
+static unsigned int rpmsg_pingpong;
+static int rx_count;
+
+static void rpmsg_pingpong_cb(struct rpmsg_channel *rpdev, void *data, int len,
+                                               void *priv, u32 src)
+{
+       int err;
+
+       /* reply */
+       rpmsg_pingpong = *(unsigned int *)data;
+       pr_info("get %d (src: 0x%x)\n",
+                       rpmsg_pingpong, src);
+       rx_count++;
+
+       /* pingpongs should not live forever */
+       if (rx_count >= MSG_LIMIT) {
+               dev_info(&rpdev->dev, "goodbye!\n");
+               return;
+       }
+       rpmsg_pingpong++;
+       err = rpmsg_sendto(rpdev, (void *)(&rpmsg_pingpong), 4, src);
+
+       if (err)
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", err);
+}
+
+static int rpmsg_pingpong_probe(struct rpmsg_channel *rpdev)
+{
+       int err;
+
+       dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+                       rpdev->src, rpdev->dst);
+
+       /*
+        * send a message to our remote processor, and tell remote
+        * processor about this channel
+        */
+       err = rpmsg_send(rpdev, MSG, strlen(MSG));
+       if (err) {
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", err);
+               return err;
+       }
+
+       rpmsg_pingpong = 0;
+       rx_count = 0;
+       err = rpmsg_sendto(rpdev, (void *)(&rpmsg_pingpong), 4, rpdev->dst);
+       if (err) {
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static void rpmsg_pingpong_remove(struct rpmsg_channel *rpdev)
+{
+       dev_info(&rpdev->dev, "rpmsg pingpong driver is removed\n");
+}
+
+static struct rpmsg_device_id rpmsg_driver_pingpong_id_table[] = {
+       { .name = "rpmsg-openamp-demo-channel" },
+       { },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pingpong_id_table);
+
+static struct rpmsg_driver rpmsg_pingpong_driver = {
+       .drv.name       = KBUILD_MODNAME,
+       .drv.owner      = THIS_MODULE,
+       .id_table       = rpmsg_driver_pingpong_id_table,
+       .probe          = rpmsg_pingpong_probe,
+       .callback       = rpmsg_pingpong_cb,
+       .remove         = rpmsg_pingpong_remove,
+};
+
+static int __init init(void)
+{
+       return register_rpmsg_driver(&rpmsg_pingpong_driver);
+}
+
+static void __exit fini(void)
+{
+       unregister_rpmsg_driver(&rpmsg_pingpong_driver);
+}
+module_init(init);
+module_exit(fini);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("iMX virtio remote processor messaging pingpong driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/imx_rpmsg_tty.c b/drivers/rpmsg/imx_rpmsg_tty.c
new file mode 100644 (file)
index 0000000..14904c7
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * derived from the omap-rpmsg implementation.
+ * Remote processor messaging transport - tty driver
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/virtio.h>
+
+/*
+ * struct rpmsgtty_port - Wrapper struct for imx rpmsg tty port.
+ * @port:              TTY port data
+ */
+struct rpmsgtty_port {
+       struct tty_port         port;
+       spinlock_t              rx_lock;
+       struct rpmsg_channel    *rpdev;
+};
+
+static struct rpmsgtty_port rpmsg_tty_port;
+
+#define RPMSG_MAX_SIZE         (512 - sizeof(struct rpmsg_hdr))
+#define MSG            "hello world!"
+
+static void rpmsg_tty_cb(struct rpmsg_channel *rpdev, void *data, int len,
+                                               void *priv, u32 src)
+{
+       int space;
+       unsigned char *cbuf;
+       struct rpmsgtty_port *cport = &rpmsg_tty_port;
+
+       /* flush the recv-ed none-zero data to tty node */
+       if (len == 0)
+               return;
+
+       dev_dbg(&rpdev->dev, "msg(<- src 0x%x) len %d\n", src, len);
+
+       print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
+                       data, len,  true);
+
+       spin_lock_bh(&cport->rx_lock);
+       space = tty_prepare_flip_string(&cport->port, &cbuf, len);
+       if (space <= 0) {
+               dev_err(&rpdev->dev, "No memory for tty_prepare_flip_string\n");
+               spin_unlock_bh(&cport->rx_lock);
+               return;
+       }
+
+       memcpy(cbuf, data, len);
+       tty_flip_buffer_push(&cport->port);
+       spin_unlock_bh(&cport->rx_lock);
+}
+
+static struct tty_port_operations  rpmsgtty_port_ops = { };
+
+static int rpmsgtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+       return tty_port_install(&rpmsg_tty_port.port, driver, tty);
+}
+
+static int rpmsgtty_open(struct tty_struct *tty, struct file *filp)
+{
+       return tty_port_open(tty->port, tty, filp);
+}
+
+static void rpmsgtty_close(struct tty_struct *tty, struct file *filp)
+{
+       return tty_port_close(tty->port, tty, filp);
+}
+
+static int rpmsgtty_write(struct tty_struct *tty, const unsigned char *buf,
+                        int total)
+{
+       int count, ret = 0;
+       const unsigned char *tbuf;
+       struct rpmsgtty_port *rptty_port = container_of(tty->port,
+                       struct rpmsgtty_port, port);
+       struct rpmsg_channel *rpdev = rptty_port->rpdev;
+
+       if (NULL == buf) {
+               pr_err("buf shouldn't be null.\n");
+               return -ENOMEM;
+       }
+
+       count = total;
+       tbuf = buf;
+       do {
+               /* send a message to our remote processor */
+               ret = rpmsg_send(rpdev, (void *)tbuf,
+                       count > RPMSG_MAX_SIZE ? RPMSG_MAX_SIZE : count);
+               if (ret) {
+                       dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+                       return ret;
+               }
+
+               if (count > RPMSG_MAX_SIZE) {
+                       count -= RPMSG_MAX_SIZE;
+                       tbuf += RPMSG_MAX_SIZE;
+               } else {
+                       count = 0;
+               }
+       } while (count > 0);
+
+       return total;
+}
+
+static int rpmsgtty_write_room(struct tty_struct *tty)
+{
+       /* report the space in the rpmsg buffer */
+       return RPMSG_MAX_SIZE;
+}
+
+static const struct tty_operations imxrpmsgtty_ops = {
+       .install                = rpmsgtty_install,
+       .open                   = rpmsgtty_open,
+       .close                  = rpmsgtty_close,
+       .write                  = rpmsgtty_write,
+       .write_room             = rpmsgtty_write_room,
+};
+
+static struct tty_driver *rpmsgtty_driver;
+
+static int rpmsg_tty_probe(struct rpmsg_channel *rpdev)
+{
+       int err;
+       struct rpmsgtty_port *cport = &rpmsg_tty_port;
+
+       dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+                       rpdev->src, rpdev->dst);
+
+       /*
+        * send a message to our remote processor, and tell remote
+        * processor about this channel
+        */
+       err = rpmsg_send(rpdev, MSG, strlen(MSG));
+       if (err) {
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", err);
+               return err;
+       }
+
+       rpmsgtty_driver = tty_alloc_driver(1, TTY_DRIVER_UNNUMBERED_NODE);
+       if (IS_ERR(rpmsgtty_driver))
+               return PTR_ERR(rpmsgtty_driver);
+
+       rpmsgtty_driver->driver_name = "rpmsg_tty";
+       rpmsgtty_driver->name = "ttyRPMSG";
+       rpmsgtty_driver->major = TTYAUX_MAJOR;
+       rpmsgtty_driver->minor_start = 3;
+       rpmsgtty_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+       rpmsgtty_driver->init_termios = tty_std_termios;
+
+       tty_set_operations(rpmsgtty_driver, &imxrpmsgtty_ops);
+
+       tty_port_init(&cport->port);
+       cport->port.ops = &rpmsgtty_port_ops;
+       spin_lock_init(&cport->rx_lock);
+       cport->port.low_latency = cport->port.flags | ASYNC_LOW_LATENCY;
+
+       err = tty_register_driver(rpmsgtty_driver);
+       if (err < 0) {
+               pr_err("Couldn't install rpmsg tty driver: err %d\n", err);
+               goto error;
+       } else
+               pr_info("Install rpmsg tty driver!\n");
+       cport->rpdev = rpdev;
+
+       return 0;
+
+error:
+       tty_unregister_driver(rpmsgtty_driver);
+       put_tty_driver(rpmsgtty_driver);
+       tty_port_destroy(&cport->port);
+       rpmsgtty_driver = NULL;
+
+       return err;
+}
+
+static void rpmsg_tty_remove(struct rpmsg_channel *rpdev)
+{
+       struct rpmsgtty_port *cport = &rpmsg_tty_port;
+
+       dev_info(&rpdev->dev, "rpmsg tty driver is removed\n");
+
+       tty_unregister_driver(rpmsgtty_driver);
+       put_tty_driver(rpmsgtty_driver);
+       tty_port_destroy(&cport->port);
+       rpmsgtty_driver = NULL;
+}
+
+static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = {
+       { .name = "rpmsg-openamp-demo-channel" },
+       { },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table);
+
+static struct rpmsg_driver rpmsg_tty_driver = {
+       .drv.name       = KBUILD_MODNAME,
+       .drv.owner      = THIS_MODULE,
+       .id_table       = rpmsg_driver_tty_id_table,
+       .probe          = rpmsg_tty_probe,
+       .callback       = rpmsg_tty_cb,
+       .remove         = rpmsg_tty_remove,
+};
+
+static int __init init(void)
+{
+       return register_rpmsg_driver(&rpmsg_tty_driver);
+}
+
+static void __exit fini(void)
+{
+       unregister_rpmsg_driver(&rpmsg_tty_driver);
+}
+module_init(init);
+module_exit(fini);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("iMX virtio remote processor messaging tty driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/vf610_rpmsg.c b/drivers/rpmsg/vf610_rpmsg.c
new file mode 100644 (file)
index 0000000..72ce28c
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2016 Toradex AG
+ *
+ * Derived from the downstream iMX7 rpmsg implementation by Freescale.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/vf610_mscm.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <linux/vf610_sema4.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+
+struct vf610_rpmsg_vproc {
+       struct virtio_device vdev;
+       unsigned int vring[2];
+       char *rproc_name;
+       struct mutex lock;
+       struct delayed_work rpmsg_work;
+       struct virtqueue *vq[2];
+       int base_vq_id;
+       int num_of_vqs;
+};
+
+#define MSCM_CPU_M4            1
+
+/*
+ * For now, allocate 256 buffers of 512 bytes for each side. each buffer
+ * will then have 16B for the msg header and 496B for the payload.
+ * This will require a total space of 256KB for the buffers themselves, and
+ * 3 pages for every vring (the size of the vring depends on the number of
+ * buffers it supports).
+ */
+#define RPMSG_NUM_BUFS         (32)
+#define RPMSG_BUF_SIZE         (512)
+#define RPMSG_BUFS_SPACE       (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE)
+
+/*
+ * The alignment between the consumer and producer parts of the vring.
+ * Note: this is part of the "wire" protocol. If you change this, you need
+ * to update your BIOS image as well
+ */
+#define RPMSG_VRING_ALIGN      (4096)
+
+/* With 256 buffers, our vring will occupy 3 pages */
+#define RPMSG_RING_SIZE        ((DIV_ROUND_UP(vring_size(RPMSG_NUM_BUFS / 2, \
+                               RPMSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE)
+
+#define to_vf610_rpdev(vd) container_of(vd, struct vf610_rpmsg_vproc, vdev)
+
+struct vf610_rpmsg_vq_info {
+       __u16 num;      /* number of entries in the virtio_ring */
+       __u16 vq_id;    /* a globaly unique index of this virtqueue */
+       void *addr;     /* address where we mapped the virtio ring */
+       struct vf610_rpmsg_vproc *rpdev;
+};
+
+static struct vf610_sema4_mutex *rpmsg_mutex;
+
+static u64 vf610_rpmsg_get_features(struct virtio_device *vdev)
+{
+       return 1 << VIRTIO_RPMSG_F_NS;
+}
+
+static int vf610_rpmsg_finalize_features(struct virtio_device *vdev)
+{
+       /* Give virtio_ring a chance to accept features */
+       vring_transport_features(vdev);
+
+       return 0;
+}
+
+/* kick the remote processor */
+static bool vf610_rpmsg_notify(struct virtqueue *vq)
+{
+       struct vf610_rpmsg_vq_info *rpvq = vq->priv;
+
+       mutex_lock(&rpvq->rpdev->lock);
+
+       mscm_trigger_cpu2cpu_irq(rpvq->vq_id, MSCM_CPU_M4);
+
+       mutex_unlock(&rpvq->rpdev->lock);
+
+       return true;
+}
+
+static void rpmsg_work_handler(struct work_struct *work)
+{
+       struct vf610_rpmsg_vproc *rpdev = container_of(work,
+                               struct vf610_rpmsg_vproc, rpmsg_work.work);
+
+       /* Process incoming buffers on all our vrings */
+       vring_interrupt(0, rpdev->vq[0]);
+       vring_interrupt(1, rpdev->vq[1]);
+}
+
+static irqreturn_t cpu_to_cpu_irq_handler(int irq, void *p)
+{
+       struct vf610_rpmsg_vproc *rpdev = (struct vf610_rpmsg_vproc *)p;
+
+       schedule_delayed_work(&rpdev->rpmsg_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
+                                   unsigned index,
+                                   void (*callback)(struct virtqueue *vq),
+                                   const char *name)
+{
+       struct vf610_rpmsg_vproc *rpdev = to_vf610_rpdev(vdev);
+       struct vf610_rpmsg_vq_info *rpvq;
+       struct virtqueue *vq;
+       int err;
+
+       rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
+       if (!rpvq)
+               return ERR_PTR(-ENOMEM);
+
+       /* ioremap'ing normal memory, so we cast away sparse's complaints */
+       rpvq->addr = (__force void *) ioremap_nocache(rpdev->vring[index],
+                                                       RPMSG_RING_SIZE);
+       if (!rpvq->addr) {
+               err = -ENOMEM;
+               goto free_rpvq;
+       }
+
+       memset(rpvq->addr, 0, RPMSG_RING_SIZE);
+
+       pr_debug("vring%d: phys 0x%x, virt 0x%x\n", index, rpdev->vring[index],
+                                       (unsigned int) rpvq->addr);
+
+       vq = vring_new_virtqueue(index, RPMSG_NUM_BUFS, RPMSG_VRING_ALIGN,
+                       vdev, true, rpvq->addr, vf610_rpmsg_notify, callback,
+                       name);
+       if (!vq) {
+               pr_err("vring_new_virtqueue failed\n");
+               err = -ENOMEM;
+               goto unmap_vring;
+       }
+
+       rpdev->vq[index] = vq;
+       vq->priv = rpvq;
+       /* system-wide unique id for this virtqueue */
+       rpvq->vq_id = rpdev->base_vq_id + index;
+       rpvq->rpdev = rpdev;
+       mutex_init(&rpdev->lock);
+
+       return vq;
+
+unmap_vring:
+       /* iounmap normal memory, so make sparse happy */
+       iounmap((__force void __iomem *) rpvq->addr);
+free_rpvq:
+       kfree(rpvq);
+       return ERR_PTR(err);
+}
+
+static void vf610_rpmsg_del_vqs(struct virtio_device *vdev)
+{
+       struct virtqueue *vq, *n;
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+               struct vf610_rpmsg_vq_info *rpvq = vq->priv;
+               iounmap(rpvq->addr);
+               vring_del_virtqueue(vq);
+               kfree(rpvq);
+       }
+}
+
+static int vf610_rpmsg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                      struct virtqueue *vqs[],
+                      vq_callback_t *callbacks[],
+                      const char *names[])
+{
+       struct vf610_rpmsg_vproc *rpdev = to_vf610_rpdev(vdev);
+       int i, err;
+
+       /* we maintain two virtqueues per remote processor (for RX and TX) */
+       if (nvqs != 2)
+               return -EINVAL;
+
+       for (i = 0; i < nvqs; ++i) {
+               vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i])) {
+                       err = PTR_ERR(vqs[i]);
+                       goto error;
+               }
+       }
+
+       rpdev->num_of_vqs = nvqs;
+
+       return 0;
+
+error:
+       vf610_rpmsg_del_vqs(vdev);
+       return err;
+}
+
+static void vf610_rpmsg_reset(struct virtio_device *vdev)
+{
+       dev_dbg(&vdev->dev, "reset !\n");
+}
+
+static u8 vf610_rpmsg_get_status(struct virtio_device *vdev)
+{
+       return 0;
+}
+
+static void vf610_rpmsg_set_status(struct virtio_device *vdev, u8 status)
+{
+       dev_dbg(&vdev->dev, "%s new status: %d\n", __func__, status);
+}
+
+static void vf610_rpmsg_vproc_release(struct device *dev)
+{
+       /* this handler is provided so driver core doesn't yell at us */
+}
+
+static struct virtio_config_ops vf610_rpmsg_config_ops = {
+       .get_features   = vf610_rpmsg_get_features,
+       .finalize_features = vf610_rpmsg_finalize_features,
+       .find_vqs       = vf610_rpmsg_find_vqs,
+       .del_vqs        = vf610_rpmsg_del_vqs,
+       .reset          = vf610_rpmsg_reset,
+       .set_status     = vf610_rpmsg_set_status,
+       .get_status     = vf610_rpmsg_get_status,
+};
+
+static struct vf610_rpmsg_vproc vf610_rpmsg_vprocs = {
+       .vdev.id.device = VIRTIO_ID_RPMSG,
+       .vdev.config    = &vf610_rpmsg_config_ops,
+       .rproc_name     = "vf610_m4",
+       .base_vq_id     = 0,
+};
+
+static const struct of_device_id vf610_rpmsg_dt_ids[] = {
+       { .compatible = "fsl,vf610-rpmsg", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_rpmsg_dt_ids);
+
+static int vf610_rpmsg_probe(struct platform_device *pdev)
+{
+       int i;
+       int ret = 0;
+       struct vf610_rpmsg_vproc *rpdev = &vf610_rpmsg_vprocs;
+
+       INIT_DELAYED_WORK(&rpdev->rpmsg_work, rpmsg_work_handler);
+
+       rpdev->vring[0] = 0x3f070000;
+       rpdev->vring[1] = 0x3f074000;
+
+       rpdev->vdev.dev.parent = &pdev->dev;
+       rpdev->vdev.dev.release = vf610_rpmsg_vproc_release;
+
+       ret = register_virtio_device(&rpdev->vdev);
+       if (ret) {
+               pr_err("%s failed to register rpdev: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       for (i = 0; i < 2; i++) {
+               ret = mscm_request_cpu2cpu_irq(i, cpu_to_cpu_irq_handler,
+                               (const char *)rpdev->rproc_name, rpdev);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to register CPU2CPU interrupt\n");
+                       unregister_virtio_device(&rpdev->vdev);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int vf610_rpmsg_remove(struct platform_device *pdev)
+{
+       struct vf610_rpmsg_vproc *rpdev = &vf610_rpmsg_vprocs;
+       int i;
+
+       for (i = 0; i < 2; i++)
+               mscm_free_cpu2cpu_irq(i, NULL);
+
+       unregister_virtio_device(&rpdev->vdev);
+
+       return 0;
+}
+
+static struct platform_driver vf610_rpmsg_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "vf610-rpmsg",
+                  .of_match_table = vf610_rpmsg_dt_ids,
+                  },
+       .probe = vf610_rpmsg_probe,
+       .remove = vf610_rpmsg_remove,
+};
+
+static int __init vf610_rpmsg_init(void)
+{
+       int ret;
+
+       rpmsg_mutex = vf610_sema4_mutex_create(0, 0);
+       if (IS_ERR(rpmsg_mutex)) {
+               pr_err("vf610 rpmsg unable to create mutex\n");
+               return PTR_ERR(rpmsg_mutex);
+       }
+
+       vf610_sema4_mutex_lock(rpmsg_mutex);
+
+       ret = platform_driver_register(&vf610_rpmsg_driver);
+       if (ret)
+               pr_err("Unable to initialize rpmsg driver\n");
+       else
+               pr_info("vf610 rpmsg driver is registered.\n");
+
+       vf610_sema4_mutex_unlock(rpmsg_mutex);
+
+       return ret;
+}
+
+static void __exit vf610_rpmsg_exit(void)
+{
+       if (rpmsg_mutex)
+               vf610_sema4_mutex_destroy(rpmsg_mutex);
+
+       pr_info("vf610 rpmsg driver is unregistered.\n");
+       platform_driver_unregister(&vf610_rpmsg_driver);
+}
+module_exit(vf610_rpmsg_exit);
+module_init(vf610_rpmsg_init);
+
+MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
+MODULE_DESCRIPTION("vf610 remote processor messaging virtio device");
+MODULE_LICENSE("GPL v2");
index aa705bb4748c08ac302d45775ef698cf26c69d5b..097ca2a8565f7758780e85f916df421c200f6eba 100644 (file)
@@ -34,6 +34,7 @@ enum ds_type {
        ds_1340,
        ds_1388,
        ds_3231,
+       m41t0,
        m41t00,
        mcp794xx,
        rx_8025,
@@ -48,6 +49,7 @@ enum ds_type {
 #      define DS1340_BIT_nEOSC         0x80
 #      define MCP794XX_BIT_ST          0x80
 #define DS1307_REG_MIN         0x01    /* 00-59 */
+#      define M41T0_BIT_OF             0x80
 #define DS1307_REG_HOUR                0x02    /* 00-23, or 1-12{am,pm} */
 #      define DS1307_BIT_12HR          0x40    /* in REG_HOUR */
 #      define DS1307_BIT_PM            0x20    /* in REG_HOUR */
@@ -174,6 +176,7 @@ static const struct i2c_device_id ds1307_id[] = {
        { "ds1388", ds_1388 },
        { "ds1340", ds_1340 },
        { "ds3231", ds_3231 },
+       { "m41t0", m41t0 },
        { "m41t00", m41t00 },
        { "mcp7940x", mcp794xx },
        { "mcp7941x", mcp794xx },
@@ -363,6 +366,13 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
 
        dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
 
+       /* if oscillator fail bit is set, no data can be trusted */
+       if (ds1307->type == m41t0 &&
+           ds1307->regs[DS1307_REG_MIN] & M41T0_BIT_OF) {
+               dev_warn_once(dev, "oscillator failed, set time!\n");
+               return -EINVAL;
+       }
+
        t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);
        t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f);
        tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
@@ -1044,6 +1054,7 @@ read_rtc:
        tmp = ds1307->regs[DS1307_REG_SECS];
        switch (ds1307->type) {
        case ds_1307:
+       case m41t0:
        case m41t00:
                /* clock halted?  turn it on, so clock can tick. */
                if (tmp & DS1307_BIT_CH) {
@@ -1108,6 +1119,7 @@ read_rtc:
        tmp = ds1307->regs[DS1307_REG_HOUR];
        switch (ds1307->type) {
        case ds_1340:
+       case m41t0:
        case m41t00:
                /*
                 * NOTE: ignores century bits; fix before deploying
index 4e853ed2c82b937ebd8fb6cf4c22288a122fca3a..9864fd55a0ab1b75137051ecdf21e45eb0a3d21b 100644 (file)
@@ -1,6 +1,7 @@
 menu "SOC (System On Chip) specific Drivers"
 
 source "drivers/soc/brcmstb/Kconfig"
+source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
 source "drivers/soc/rockchip/Kconfig"
index f2ba2e932ae10c5d2cda1de269b826b9875a4a5c..21c9c3a0c98f52d36bf9a364b8dad63057c565c5 100644 (file)
@@ -10,4 +10,5 @@ obj-$(CONFIG_ARCH_ROCKCHIP)           += rockchip/
 obj-$(CONFIG_ARCH_SUNXI)       += sunxi/
 obj-$(CONFIG_ARCH_TEGRA)       += tegra/
 obj-$(CONFIG_SOC_TI)           += ti/
+obj-$(CONFIG_SOC_VF610)                += fsl/
 obj-$(CONFIG_PLAT_VERSATILE)   += versatile/
diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig
new file mode 100644 (file)
index 0000000..5568c34
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Freescale SoC drivers
+
+config SOC_BUS_VF610
+          tristate "SoC bus device for the Freescale Vybrid platform"
+          depends on SOC_VF610 && NVMEM && NVMEM_VF610_OCOTP
+          select SOC_BUS
+          help
+            Include support for the SoC bus on the Freescale Vybrid platform
+            providing some sysfs information about the module variant.
diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile
new file mode 100644 (file)
index 0000000..41ee22f
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_BUS_VF610)    += soc-vf610.o
diff --git a/drivers/soc/fsl/soc-vf610.c b/drivers/soc/fsl/soc-vf610.c
new file mode 100644 (file)
index 0000000..864bf56
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 Toradex AG.
+ *
+ * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+struct vf610_soc {
+       struct device *dev;
+       struct soc_device_attribute *soc_dev_attr;
+       struct soc_device *soc_dev;
+       struct nvmem_cell *ocotp_cfg0;
+       struct nvmem_cell *ocotp_cfg1;
+};
+
+static int vf610_soc_probe(struct platform_device *pdev)
+{
+       struct vf610_soc *info;
+       struct device *dev = &pdev->dev;
+       struct device_node *soc_node;
+       char soc_type[] = "xx0";
+       size_t id1_len;
+       size_t id2_len;
+       u32 cpucount;
+       u32 l2size;
+       u32 rom_rev;
+       u8 *socid1;
+       u8 *socid2;
+       int ret;
+
+       info = devm_kzalloc(dev, sizeof(struct vf610_soc), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+       info->dev = dev;
+
+       info->ocotp_cfg0 = devm_nvmem_cell_get(dev, "cfg0");
+       if (IS_ERR(info->ocotp_cfg0))
+               return -EPROBE_DEFER;
+
+       info->ocotp_cfg1 = devm_nvmem_cell_get(dev, "cfg1");
+       if (IS_ERR(info->ocotp_cfg1))
+               return -EPROBE_DEFER;
+
+       socid1 = nvmem_cell_read(info->ocotp_cfg0, &id1_len);
+       if (IS_ERR(socid1)) {
+               dev_err(dev, "Could not read nvmem cell %ld\n",
+                       PTR_ERR(socid1));
+               return PTR_ERR(socid1);
+       }
+
+       socid2 = nvmem_cell_read(info->ocotp_cfg1, &id2_len);
+       if (IS_ERR(socid2)) {
+               dev_err(dev, "Could not read nvmem cell %ld\n",
+                       PTR_ERR(socid2));
+               return PTR_ERR(socid2);
+       }
+       add_device_randomness(socid1, id1_len);
+       add_device_randomness(socid2, id2_len);
+
+       soc_node = of_find_node_by_path("/soc");
+       if (soc_node == NULL)
+               return -ENODEV;
+
+       ret = syscon_regmap_read_from_offset(soc_node,
+                                       "fsl,rom-revision", &rom_rev);
+       if (ret) {
+               of_node_put(soc_node);
+               return ret;
+       }
+
+       ret = syscon_regmap_read_from_offset(soc_node,
+                                       "fsl,cpu-count", &cpucount);
+       if (ret) {
+               of_node_put(soc_node);
+               return ret;
+       }
+
+       ret = syscon_regmap_read_from_offset(soc_node,
+                                       "fsl,l2-size", &l2size);
+       if (ret) {
+               of_node_put(soc_node);
+               return ret;
+       }
+
+       of_node_put(soc_node);
+
+       soc_type[0] = cpucount ? '6' : '5'; /* Dual Core => VF6x0 */
+       soc_type[1] = l2size ? '1' : '0'; /* L2 Cache => VFx10 */
+
+       info->soc_dev_attr = devm_kzalloc(dev,
+                               sizeof(info->soc_dev_attr), GFP_KERNEL);
+       if (!info->soc_dev_attr)
+               return -ENOMEM;
+
+       info->soc_dev_attr->machine = devm_kasprintf(dev,
+                                       GFP_KERNEL, "Freescale Vybrid");
+       info->soc_dev_attr->soc_id = devm_kasprintf(dev,
+                                       GFP_KERNEL,
+                                       "%02x%02x%02x%02x%02x%02x%02x%02x",
+                                       socid1[3], socid1[2], socid1[1],
+                                       socid1[0], socid2[3], socid2[2],
+                                       socid2[1], socid2[0]);
+       info->soc_dev_attr->family = devm_kasprintf(&pdev->dev,
+                                       GFP_KERNEL, "Freescale Vybrid VF%s",
+                                       soc_type);
+       info->soc_dev_attr->revision = devm_kasprintf(dev,
+                                       GFP_KERNEL, "%08x", rom_rev);
+
+       platform_set_drvdata(pdev, info);
+
+       info->soc_dev = soc_device_register(info->soc_dev_attr);
+       if (IS_ERR(info->soc_dev))
+               return -ENODEV;
+
+       return 0;
+}
+
+static int vf610_soc_remove(struct platform_device *pdev)
+{
+       struct vf610_soc *info = platform_get_drvdata(pdev);
+
+       if (info->soc_dev)
+               soc_device_unregister(info->soc_dev);
+
+       return 0;
+}
+
+static const struct of_device_id vf610_soc_bus_match[] = {
+       { .compatible = "fsl,vf610-soc-bus", },
+       { /* */ }
+};
+
+static struct platform_driver vf610_soc_driver = {
+       .probe          = vf610_soc_probe,
+       .remove         = vf610_soc_remove,
+       .driver         = {
+               .name = "vf610-soc-bus",
+               .of_match_table = vf610_soc_bus_match,
+       },
+};
+module_platform_driver(vf610_soc_driver);
index a3965cac1b3447ce6cf6c715e46269bbc301508f..e280c6179e579e2b3d6517c9897ecbe7d379e891 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
@@ -40,6 +42,7 @@
 #define TRAN_STATE_WORD_ODD_NUM        0x04
 
 #define DSPI_FIFO_SIZE                 4
+#define DSPI_DMA_BUFSIZE               (DSPI_FIFO_SIZE * 1024)
 
 #define SPI_MCR                0x00
 #define SPI_MCR_MASTER         (1 << 31)
 #define SPI_SR_EOQF            0x10000000
 #define SPI_SR_TCFQF           0x80000000
 
+#define SPI_RSER_TFFFE         BIT(25)
+#define SPI_RSER_TFFFD         BIT(24)
+#define SPI_RSER_RFDFE         BIT(17)
+#define SPI_RSER_RFDFD         BIT(16)
+
 #define SPI_RSER               0x30
 #define SPI_RSER_EOQFE         0x10000000
 #define SPI_RSER_TCFQE         0x80000000
 
 #define SPI_TCR_TCNT_MAX       0x10000
 
+#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
+
 struct chip_data {
        u32 mcr_val;
        u32 ctar_val;
@@ -117,22 +127,43 @@ struct chip_data {
 enum dspi_trans_mode {
        DSPI_EOQ_MODE = 0,
        DSPI_TCFQ_MODE,
+       DSPI_DMA_MODE,
 };
 
 struct fsl_dspi_devtype_data {
        enum dspi_trans_mode trans_mode;
+       u8 max_clock_factor;
 };
 
 static const struct fsl_dspi_devtype_data vf610_data = {
        .trans_mode = DSPI_EOQ_MODE,
+       .max_clock_factor = 2,
 };
 
 static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
        .trans_mode = DSPI_TCFQ_MODE,
+       .max_clock_factor = 8,
 };
 
 static const struct fsl_dspi_devtype_data ls2085a_data = {
        .trans_mode = DSPI_TCFQ_MODE,
+       .max_clock_factor = 8,
+};
+
+struct fsl_dspi_dma {
+       u32 curr_xfer_len;
+
+       u32 *tx_dma_buf;
+       struct dma_chan *chan_tx;
+       dma_addr_t tx_dma_phys;
+       struct completion cmd_tx_complete;
+       struct dma_async_tx_descriptor *tx_desc;
+
+       u32 *rx_dma_buf;
+       struct dma_chan *chan_rx;
+       dma_addr_t rx_dma_phys;
+       struct completion cmd_rx_complete;
+       struct dma_async_tx_descriptor *rx_desc;
 };
 
 struct fsl_dspi {
@@ -161,8 +192,11 @@ struct fsl_dspi {
        u32                     waitflags;
 
        u32                     spi_tcnt;
+       struct fsl_dspi_dma     *dma;
 };
 
+static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word);
+
 static inline int is_double_byte_mode(struct fsl_dspi *dspi)
 {
        unsigned int val;
@@ -364,6 +398,256 @@ static void dspi_tcfq_read(struct fsl_dspi *dspi)
        dspi_data_from_popr(dspi, rx_word);
 }
 
+static void dspi_tx_dma_callback(void *arg)
+{
+       struct fsl_dspi *dspi = arg;
+       struct fsl_dspi_dma *dma = dspi->dma;
+
+       complete(&dma->cmd_tx_complete);
+}
+
+static void dspi_rx_dma_callback(void *arg)
+{
+       struct fsl_dspi *dspi = arg;
+       struct fsl_dspi_dma *dma = dspi->dma;
+       int rx_word;
+       int i;
+       u16 d;
+
+       rx_word = is_double_byte_mode(dspi);
+
+       if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) {
+               for (i = 0; i < dma->curr_xfer_len; i++) {
+                       d = dspi->dma->rx_dma_buf[i];
+                       rx_word ? (*(u16 *)dspi->rx = d) :
+                                               (*(u8 *)dspi->rx = d);
+                       dspi->rx += rx_word + 1;
+               }
+       }
+
+       complete(&dma->cmd_rx_complete);
+}
+
+static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
+{
+       struct fsl_dspi_dma *dma = dspi->dma;
+       struct device *dev = &dspi->pdev->dev;
+       int time_left;
+       int tx_word;
+       int i;
+
+       tx_word = is_double_byte_mode(dspi);
+
+       for (i = 0; i < dma->curr_xfer_len; i++) {
+               dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word);
+               if ((dspi->cs_change) && (!dspi->len))
+                       dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT;
+       }
+
+       dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,
+                                       dma->tx_dma_phys,
+                                       dma->curr_xfer_len *
+                                       DMA_SLAVE_BUSWIDTH_4_BYTES,
+                                       DMA_MEM_TO_DEV,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!dma->tx_desc) {
+               dev_err(dev, "Not able to get desc for DMA xfer\n");
+               return -EIO;
+       }
+
+       dma->tx_desc->callback = dspi_tx_dma_callback;
+       dma->tx_desc->callback_param = dspi;
+       if (dma_submit_error(dmaengine_submit(dma->tx_desc))) {
+               dev_err(dev, "DMA submit failed\n");
+               return -EINVAL;
+       }
+
+       dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx,
+                                       dma->rx_dma_phys,
+                                       dma->curr_xfer_len *
+                                       DMA_SLAVE_BUSWIDTH_4_BYTES,
+                                       DMA_DEV_TO_MEM,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!dma->rx_desc) {
+               dev_err(dev, "Not able to get desc for DMA xfer\n");
+               return -EIO;
+       }
+
+       dma->rx_desc->callback = dspi_rx_dma_callback;
+       dma->rx_desc->callback_param = dspi;
+       if (dma_submit_error(dmaengine_submit(dma->rx_desc))) {
+               dev_err(dev, "DMA submit failed\n");
+               return -EINVAL;
+       }
+
+       reinit_completion(&dspi->dma->cmd_rx_complete);
+       reinit_completion(&dspi->dma->cmd_tx_complete);
+
+       dma_async_issue_pending(dma->chan_rx);
+       dma_async_issue_pending(dma->chan_tx);
+
+       time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete,
+                                       DMA_COMPLETION_TIMEOUT);
+       if (time_left == 0) {
+               dev_err(dev, "DMA tx timeout\n");
+               dmaengine_terminate_all(dma->chan_tx);
+               dmaengine_terminate_all(dma->chan_rx);
+               return -ETIMEDOUT;
+       }
+
+       time_left = wait_for_completion_timeout(&dspi->dma->cmd_rx_complete,
+                                       DMA_COMPLETION_TIMEOUT);
+       if (time_left == 0) {
+               dev_err(dev, "DMA rx timeout\n");
+               dmaengine_terminate_all(dma->chan_tx);
+               dmaengine_terminate_all(dma->chan_rx);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int dspi_dma_xfer(struct fsl_dspi *dspi)
+{
+       struct fsl_dspi_dma *dma = dspi->dma;
+       struct device *dev = &dspi->pdev->dev;
+       int curr_remaining_bytes;
+       int bytes_per_buffer;
+       int word = 1;
+       int ret = 0;
+
+       if(is_double_byte_mode(dspi))
+               word = 2;
+       curr_remaining_bytes = dspi->len;
+       bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE;
+       while (curr_remaining_bytes) {
+               /* Check if current transfer fits the DMA buffer */
+               dma->curr_xfer_len = curr_remaining_bytes / word;
+               if (dma->curr_xfer_len > bytes_per_buffer)
+                       dma->curr_xfer_len = bytes_per_buffer;
+
+               ret = dspi_next_xfer_dma_submit(dspi);
+               if (ret) {
+                       dev_err(dev, "DMA transfer failed\n");
+                       goto exit;
+
+               } else {
+                       curr_remaining_bytes -= dma->curr_xfer_len * word;
+                       if (curr_remaining_bytes < 0)
+                               curr_remaining_bytes = 0;
+               }
+       }
+
+exit:
+       return ret;
+}
+
+static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
+{
+       struct fsl_dspi_dma *dma;
+       struct dma_slave_config cfg;
+       struct device *dev = &dspi->pdev->dev;
+       int ret;
+
+       dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       dma->chan_rx = dma_request_slave_channel(dev, "rx");
+       if (!dma->chan_rx) {
+               dev_err(dev, "rx dma channel not available\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       dma->chan_tx = dma_request_slave_channel(dev, "tx");
+       if (!dma->chan_tx) {
+               dev_err(dev, "tx dma channel not available\n");
+               ret = -ENODEV;
+               goto err_tx_channel;
+       }
+
+       dma->tx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
+                                       &dma->tx_dma_phys, GFP_KERNEL);
+       if (!dma->tx_dma_buf) {
+               ret = -ENOMEM;
+               goto err_tx_dma_buf;
+       }
+
+       dma->rx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
+                                       &dma->rx_dma_phys, GFP_KERNEL);
+       if (!dma->rx_dma_buf) {
+               ret = -ENOMEM;
+               goto err_rx_dma_buf;
+       }
+
+       cfg.src_addr = phy_addr + SPI_POPR;
+       cfg.dst_addr = phy_addr + SPI_PUSHR;
+       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.src_maxburst = 1;
+       cfg.dst_maxburst = 1;
+
+       cfg.direction = DMA_DEV_TO_MEM;
+       ret = dmaengine_slave_config(dma->chan_rx, &cfg);
+       if (ret) {
+               dev_err(dev, "can't configure rx dma channel\n");
+               ret = -EINVAL;
+               goto err_slave_config;
+       }
+
+       cfg.direction = DMA_MEM_TO_DEV;
+       ret = dmaengine_slave_config(dma->chan_tx, &cfg);
+       if (ret) {
+               dev_err(dev, "can't configure tx dma channel\n");
+               ret = -EINVAL;
+               goto err_slave_config;
+       }
+
+       dspi->dma = dma;
+       dspi->devtype_data->trans_mode = DSPI_DMA_MODE;
+       init_completion(&dma->cmd_tx_complete);
+       init_completion(&dma->cmd_rx_complete);
+
+       return 0;
+
+err_slave_config:
+       dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
+                       dma->rx_dma_buf, dma->rx_dma_phys);
+err_rx_dma_buf:
+       dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
+                       dma->tx_dma_buf, dma->rx_dma_phys);
+err_tx_dma_buf:
+       dma_release_channel(dma->chan_tx);
+err_tx_channel:
+       dma_release_channel(dma->chan_rx);
+
+       devm_kfree(dev, dma);
+       dspi->dma = NULL;
+
+       return ret;
+}
+
+static void dspi_release_dma(struct fsl_dspi *dspi)
+{
+       struct fsl_dspi_dma *dma = dspi->dma;
+       struct device *dev = &dspi->pdev->dev;
+
+       if (dma) {
+               if (dma->chan_tx) {
+                       dma_unmap_single(dev, dma->tx_dma_phys,
+                                       DSPI_DMA_BUFSIZE, DMA_TO_DEVICE);
+                       dma_release_channel(dma->chan_tx);
+               }
+
+               if (dma->chan_rx) {
+                       dma_unmap_single(dev, dma->rx_dma_phys,
+                                       DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE);
+                       dma_release_channel(dma->chan_rx);
+               }
+       }
+}
+
 static int dspi_transfer_one_message(struct spi_master *master,
                struct spi_message *message)
 {
@@ -420,6 +704,12 @@ static int dspi_transfer_one_message(struct spi_master *master,
                        regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
                        dspi_tcfq_write(dspi);
                        break;
+               case DSPI_DMA_MODE:
+                       regmap_write(dspi->regmap, SPI_RSER,
+                               SPI_RSER_TFFFE | SPI_RSER_TFFFD |
+                               SPI_RSER_RFDFE | SPI_RSER_RFDFD);
+                       status = dspi_dma_xfer(dspi);
+                       break;
                default:
                        dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
                                trans_mode);
@@ -427,9 +717,13 @@ static int dspi_transfer_one_message(struct spi_master *master,
                        goto out;
                }
 
-               if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
-                       dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
-               dspi->waitflags = 0;
+               if (dspi->devtype_data->trans_mode != DSPI_DMA_MODE) {
+                       if (wait_event_interruptible(dspi->waitq,
+                                               dspi->waitflags))
+                               dev_err(&dspi->pdev->dev,
+                                       "wait transfer complete fail!\n");
+                       dspi->waitflags = 0;
+               }
 
                if (transfer->delay_usecs)
                        udelay(transfer->delay_usecs);
@@ -726,6 +1020,12 @@ static int dspi_probe(struct platform_device *pdev)
        }
        clk_prepare_enable(dspi->clk);
 
+       if (dspi_request_dma(dspi, res->start))
+               dev_warn(&pdev->dev, "can't get dma channels\n");
+
+       master->max_speed_hz =
+               clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor;
+
        init_waitqueue_head(&dspi->waitq);
        platform_set_drvdata(pdev, master);
 
@@ -751,6 +1051,7 @@ static int dspi_remove(struct platform_device *pdev)
        struct fsl_dspi *dspi = spi_master_get_devdata(master);
 
        /* Disconnect from the SPI framework */
+       dspi_release_dma(dspi);
        clk_disable_unprepare(dspi->clk);
        spi_unregister_master(dspi->master);
 
index d0e7dfc647cf21a729696cbe499f7fc6dbf53b02..a6786fb526e51e64f05d8911dfa238662cbf04d9 100644 (file)
@@ -695,6 +695,7 @@ static struct class *spidev_class;
 static const struct of_device_id spidev_dt_ids[] = {
        { .compatible = "rohm,dh2228fv" },
        { .compatible = "lineartechnology,ltc2488" },
+       { .compatible = "toradex,evalspi" },
        {},
 };
 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
index 3d790033744efd060bf42dad1f494a5ee4da0155..b94a4d7b4849fbae3a986ac80152fd34557609b5 100644 (file)
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE    64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT         (10)
 
 #define DRIVER_NAME    "fsl-lpuart"
 #define DEV_NAME       "ttyLP"
 #define UART_NR                6
 
+static bool nodma = false;
+module_param(nodma, bool, S_IRUGO);
+
 struct lpuart_port {
        struct uart_port        port;
        struct clk              *clk;
@@ -243,18 +247,18 @@ struct lpuart_port {
        struct dma_chan         *dma_rx_chan;
        struct dma_async_tx_descriptor  *dma_tx_desc;
        struct dma_async_tx_descriptor  *dma_rx_desc;
-       dma_addr_t              dma_tx_buf_bus;
-       dma_addr_t              dma_rx_buf_bus;
        dma_cookie_t            dma_tx_cookie;
        dma_cookie_t            dma_rx_cookie;
-       unsigned char           *dma_tx_buf_virt;
-       unsigned char           *dma_rx_buf_virt;
        unsigned int            dma_tx_bytes;
        unsigned int            dma_rx_bytes;
-       int                     dma_tx_in_progress;
-       int                     dma_rx_in_progress;
+       bool                    dma_tx_in_progress;
        unsigned int            dma_rx_timeout;
        struct timer_list       lpuart_timer;
+       struct scatterlist      rx_sgl, tx_sgl[2];
+       struct circ_buf         rx_ring;
+       int                     rx_dma_rng_buf_len;
+       unsigned int            dma_tx_nents;
+       wait_queue_head_t       dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +274,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,141 +319,102 @@ static void lpuart32_stop_rx(struct uart_port *port)
        lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-               struct tty_port *tty, int count)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
-       int copied;
-
-       sport->port.icount.rx += count;
+       struct circ_buf *xmit = &sport->port.state->xmit;
+       struct scatterlist *sgl = sport->tx_sgl;
+       struct device *dev = sport->port.dev;
+       int ret;
 
-       if (!tty) {
-               dev_err(sport->port.dev, "No tty port\n");
+       if (sport->dma_tx_in_progress)
                return;
-       }
 
-       dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-                       FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-       copied = tty_insert_flip_string(tty,
-                       ((unsigned char *)(sport->dma_rx_buf_virt)), count);
+       sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-       if (copied != count) {
-               WARN_ON(1);
-               dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+       if (xmit->tail < xmit->head || xmit->head == 0) {
+               sport->dma_tx_nents = 1;
+               sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+       } else {
+               sport->dma_tx_nents = 2;
+               sg_init_table(sgl, 2);
+               sg_set_buf(sgl, xmit->buf + xmit->tail,
+                               UART_XMIT_SIZE - xmit->tail);
+               sg_set_buf(sgl + 1, xmit->buf, xmit->head);
        }
 
-       dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-                       FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
-static void lpuart_pio_tx(struct lpuart_port *sport)
-{
-       struct circ_buf *xmit = &sport->port.state->xmit;
-       unsigned long flags;
-
-       spin_lock_irqsave(&sport->port.lock, flags);
-
-       while (!uart_circ_empty(xmit) &&
-               readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-               writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-               sport->port.icount.tx++;
+       ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+       if (!ret) {
+               dev_err(dev, "DMA mapping error for TX.\n");
+               return;
        }
 
-       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-               uart_write_wakeup(&sport->port);
-
-       if (uart_circ_empty(xmit))
-               writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-                       sport->port.membase + UARTCR5);
-
-       spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-       struct circ_buf *xmit = &sport->port.state->xmit;
-       dma_addr_t tx_bus_addr;
-
-       dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-                               UART_XMIT_SIZE, DMA_TO_DEVICE);
-       sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-       tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-       sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-                                       tx_bus_addr, sport->dma_tx_bytes,
+       sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+                                       sport->dma_tx_nents,
                                        DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
        if (!sport->dma_tx_desc) {
-               dev_err(sport->port.dev, "Not able to get desc for tx\n");
-               return -EIO;
+               dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+               dev_err(dev, "Cannot prepare TX slave DMA!\n");
+               return;
        }
 
        sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
        sport->dma_tx_desc->callback_param = sport;
-       sport->dma_tx_in_progress = 1;
+       sport->dma_tx_in_progress = true;
        sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
        dma_async_issue_pending(sport->dma_tx_chan);
-
-       return 0;
-}
-
-static void lpuart_prepare_tx(struct lpuart_port *sport)
-{
-       struct circ_buf *xmit = &sport->port.state->xmit;
-       unsigned long count =  CIRC_CNT_TO_END(xmit->head,
-                                       xmit->tail, UART_XMIT_SIZE);
-
-       if (!count)
-               return;
-
-       if (count < sport->txfifo_size)
-               writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
-                               sport->port.membase + UARTCR5);
-       else {
-               writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-                               sport->port.membase + UARTCR5);
-               lpuart_dma_tx(sport, count);
-       }
 }
 
 static void lpuart_dma_tx_complete(void *arg)
 {
        struct lpuart_port *sport = arg;
+       struct scatterlist *sgl = &sport->tx_sgl[0];
        struct circ_buf *xmit = &sport->port.state->xmit;
        unsigned long flags;
 
-       async_tx_ack(sport->dma_tx_desc);
-
        spin_lock_irqsave(&sport->port.lock, flags);
 
+       dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
        xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
-       sport->dma_tx_in_progress = 0;
+
+       sport->port.icount.tx += sport->dma_tx_bytes;
+       sport->dma_tx_in_progress = false;
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&sport->port);
 
-       lpuart_prepare_tx(sport);
+       if (waitqueue_active(&sport->dma_wait)) {
+               wake_up(&sport->dma_wait);
+               return;
+       }
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+               lpuart_dma_tx(sport);
 
        spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
+static int lpuart_dma_tx_request(struct uart_port *port)
 {
-       dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-                       FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-       sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-                       sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-                       DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+       struct lpuart_port *sport = container_of(port,
+                                       struct lpuart_port, port);
+       struct dma_slave_config dma_tx_sconfig = {};
+       int ret;
 
-       if (!sport->dma_rx_desc) {
-               dev_err(sport->port.dev, "Not able to get desc for rx\n");
-               return -EIO;
-       }
+       dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+       dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma_tx_sconfig.dst_maxburst = 1;
+       dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+       ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
 
-       sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-       sport->dma_rx_desc->callback_param = sport;
-       sport->dma_rx_in_progress = 1;
-       sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-       dma_async_issue_pending(sport->dma_rx_chan);
+       if (ret) {
+               dev_err(sport->port.dev,
+                               "DMA slave config failed, err = %d\n", ret);
+               return ret;
+       }
 
        return 0;
 }
@@ -458,75 +422,76 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
 static void lpuart_flush_buffer(struct uart_port *port)
 {
        struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+
        if (sport->lpuart_dma_tx_use) {
+               if (sport->dma_tx_in_progress) {
+                       dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0],
+                               sport->dma_tx_nents, DMA_TO_DEVICE);
+                       sport->dma_tx_in_progress = false;
+               }
                dmaengine_terminate_all(sport->dma_tx_chan);
-               sport->dma_tx_in_progress = 0;
        }
 }
 
-static void lpuart_dma_rx_complete(void *arg)
+#if defined(CONFIG_CONSOLE_POLL)
+
+static int lpuart_poll_init(struct uart_port *port)
 {
-       struct lpuart_port *sport = arg;
-       struct tty_port *port = &sport->port.state->port;
+       struct lpuart_port *sport = container_of(port,
+                                       struct lpuart_port, port);
        unsigned long flags;
+       unsigned char temp;
 
-       async_tx_ack(sport->dma_rx_desc);
-       mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
+       sport->port.fifosize = 0;
 
        spin_lock_irqsave(&sport->port.lock, flags);
+       /* Disable Rx & Tx */
+       writeb(0, sport->port.membase + UARTCR2);
 
-       sport->dma_rx_in_progress = 0;
-       lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
-       tty_flip_buffer_push(port);
-       lpuart_dma_rx(sport);
-
-       spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
-static void lpuart_timer_func(unsigned long data)
-{
-       struct lpuart_port *sport = (struct lpuart_port *)data;
-       struct tty_port *port = &sport->port.state->port;
-       struct dma_tx_state state;
-       unsigned long flags;
-       unsigned char temp;
-       int count;
+       temp = readb(sport->port.membase + UARTPFIFO);
+       /* Enable Rx and Tx FIFO */
+       writeb(temp | UARTPFIFO_RXFE | UARTPFIFO_TXFE,
+                       sport->port.membase + UARTPFIFO);
 
-       del_timer(&sport->lpuart_timer);
-       dmaengine_pause(sport->dma_rx_chan);
-       dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
-       dmaengine_terminate_all(sport->dma_rx_chan);
-       count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
-       async_tx_ack(sport->dma_rx_desc);
+       /* flush Tx and Rx FIFO */
+       writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
+                       sport->port.membase + UARTCFIFO);
 
-       spin_lock_irqsave(&sport->port.lock, flags);
+       /* explicitly clear RDRF */
+       if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+               readb(sport->port.membase + UARTDR);
+               writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+       }
 
-       sport->dma_rx_in_progress = 0;
-       lpuart_copy_rx_to_tty(sport, port, count);
-       tty_flip_buffer_push(port);
-       temp = readb(sport->port.membase + UARTCR5);
-       writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+       writeb(0, sport->port.membase + UARTTWFIFO);
+       writeb(1, sport->port.membase + UARTRWFIFO);
 
+       /* Enable Rx and Tx */
+       writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2);
        spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       return 0;
 }
 
-static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+static void lpuart_poll_put_char(struct uart_port *port, unsigned char c)
 {
-       unsigned long flags;
-       unsigned char temp;
-
-       spin_lock_irqsave(&sport->port.lock, flags);
+       /* drain */
+       while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
+               barrier();
 
-       sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
-       add_timer(&sport->lpuart_timer);
+       writeb(c, port->membase + UARTDR);
+}
 
-       lpuart_dma_rx(sport);
-       temp = readb(sport->port.membase + UARTCR5);
-       writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+static int lpuart_poll_get_char(struct uart_port *port)
+{
+       if (!(readb(port->membase + UARTSR1) & UARTSR1_RDRF))
+               return NO_POLL_CHAR;
 
-       spin_unlock_irqrestore(&sport->port.lock, flags);
+       return readb(port->membase + UARTDR);
 }
 
+#endif
+
 static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 {
        struct circ_buf *xmit = &sport->port.state->xmit;
@@ -580,8 +545,8 @@ static void lpuart_start_tx(struct uart_port *port)
        writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
        if (sport->lpuart_dma_tx_use) {
-               if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
-                       lpuart_prepare_tx(sport);
+               if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
+                       lpuart_dma_tx(sport);
        } else {
                if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
                        lpuart_transmit_buffer(sport);
@@ -600,6 +565,29 @@ static void lpuart32_start_tx(struct uart_port *port)
                lpuart32_transmit_buffer(sport);
 }
 
+/* return TIOCSER_TEMT when transmitter is not busy */
+static unsigned int lpuart_tx_empty(struct uart_port *port)
+{
+       struct lpuart_port *sport = container_of(port,
+                       struct lpuart_port, port);
+       unsigned char sr1 = readb(port->membase + UARTSR1);
+       unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+       if (sport->dma_tx_in_progress)
+               return 0;
+
+       if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+               return TIOCSER_TEMT;
+
+       return 0;
+}
+
+static unsigned int lpuart32_tx_empty(struct uart_port *port)
+{
+       return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ?
+               TIOCSER_TEMT : 0;
+}
+
 static irqreturn_t lpuart_txint(int irq, void *dev_id)
 {
        struct lpuart_port *sport = dev_id;
@@ -644,6 +632,9 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
        unsigned long flags;
        unsigned char rx, sr;
 
+       if (sport->port.irq_wake)
+               pm_wakeup_event(port->tty->dev, 0);
+
        spin_lock_irqsave(&sport->port.lock, flags);
 
        while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) {
@@ -766,23 +757,15 @@ out:
 static irqreturn_t lpuart_int(int irq, void *dev_id)
 {
        struct lpuart_port *sport = dev_id;
-       unsigned char sts, crdma;
+       unsigned char sts;
 
        sts = readb(sport->port.membase + UARTSR1);
-       crdma = readb(sport->port.membase + UARTCR5);
 
-       if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
-               if (sport->lpuart_dma_rx_use)
-                       lpuart_prepare_rx(sport);
-               else
-                       lpuart_rxint(irq, dev_id);
-       }
-       if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
-               if (sport->lpuart_dma_tx_use)
-                       lpuart_pio_tx(sport);
-               else
-                       lpuart_txint(irq, dev_id);
-       }
+       if (sts & UARTSR1_RDRF)
+               lpuart_rxint(irq, dev_id);
+
+       if (sts & UARTSR1_TDRE)
+               lpuart_txint(irq, dev_id);
 
        return IRQ_HANDLED;
 }
@@ -807,17 +790,241 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int lpuart_tx_empty(struct uart_port *port)
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
 {
-       return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-               TIOCSER_TEMT : 0;
+       struct tty_port *port = &sport->port.state->port;
+       struct dma_tx_state state;
+       enum dma_status dmastat;
+       struct circ_buf *ring = &sport->rx_ring;
+       unsigned long flags;
+       int count = 0;
+       unsigned char sr;
+
+       sr = readb(sport->port.membase + UARTSR1);
+
+       if (sr & (UARTSR1_PE | UARTSR1_FE)) {
+               /* Read DR to clear the error flags */
+               readb(sport->port.membase + UARTDR);
+
+               if (sr & UARTSR1_PE)
+                   sport->port.icount.parity++;
+               else if (sr & UARTSR1_FE)
+                   sport->port.icount.frame++;
+       }
+
+       async_tx_ack(sport->dma_rx_desc);
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       dmastat = dmaengine_tx_status(sport->dma_rx_chan,
+                               sport->dma_rx_cookie,
+                               &state);
+
+       if (dmastat == DMA_ERROR) {
+               dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
+               spin_unlock_irqrestore(&sport->port.lock, flags);
+               return;
+       }
+
+       /* CPU claims ownership of RX DMA buffer */
+       dma_sync_sg_for_cpu(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+
+       /*
+        * ring->head points to the end of data already written by the DMA.
+        * ring->tail points to the beginning of data to be read by the
+        * framework.
+        * The current transfer size should not be larger than the dma buffer
+        * length.
+        */
+       ring->head = sport->rx_sgl.length - state.residue;
+       BUG_ON(ring->head > sport->rx_sgl.length);
+       /*
+        * At this point ring->head may point to the first byte right after the
+        * last byte of the dma buffer:
+        * 0 <= ring->head <= sport->rx_sgl.length
+        *
+        * However ring->tail must always points inside the dma buffer:
+        * 0 <= ring->tail <= sport->rx_sgl.length - 1
+        *
+        * Since we use a ring buffer, we have to handle the case
+        * where head is lower than tail. In such a case, we first read from
+        * tail to the end of the buffer then reset tail.
+        */
+       if (ring->head < ring->tail) {
+               count = sport->rx_sgl.length - ring->tail;
+
+               tty_insert_flip_string(port, ring->buf + ring->tail, count);
+               ring->tail = 0;
+               sport->port.icount.rx += count;
+       }
+
+       /* Finally we read data from tail to head */
+       if (ring->tail < ring->head) {
+               count = ring->head - ring->tail;
+               tty_insert_flip_string(port, ring->buf + ring->tail, count);
+               /* Wrap ring->head if needed */
+               if (ring->head >= sport->rx_sgl.length)
+                       ring->head = 0;
+               ring->tail = ring->head;
+               sport->port.icount.rx += count;
+       }
+
+       dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1,
+                              DMA_FROM_DEVICE);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       tty_flip_buffer_push(port);
+       mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
 }
 
-static unsigned int lpuart32_tx_empty(struct uart_port *port)
+static void lpuart_dma_rx_complete(void *arg)
 {
-       return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ?
-               TIOCSER_TEMT : 0;
+       struct lpuart_port *sport = arg;
+
+       lpuart_copy_rx_to_tty(sport);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+       struct lpuart_port *sport = (struct lpuart_port *)data;
+
+       lpuart_copy_rx_to_tty(sport);
+}
+
+static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
+{
+       struct dma_slave_config dma_rx_sconfig = {};
+       struct circ_buf *ring = &sport->rx_ring;
+       int ret, nent;
+       int bits, baud;
+       struct tty_struct *tty = tty_port_tty_get(&sport->port.state->port);
+       struct ktermios *termios = &tty->termios;
+
+       baud = tty_get_baud_rate(tty);
+
+       bits = (termios->c_cflag & CSIZE) == CS7 ? 9 : 10;
+       if (termios->c_cflag & PARENB)
+               bits++;
+
+       /*
+        * Calculate length of one DMA buffer size to keep latency below
+        * 10ms at any baud rate.
+        */
+       sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud /  bits / 1000) * 2;
+       sport->rx_dma_rng_buf_len = (1 << (fls(sport->rx_dma_rng_buf_len) - 1));
+       if (sport->rx_dma_rng_buf_len < 16)
+               sport->rx_dma_rng_buf_len = 16;
+
+       ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
+       if (!ring->buf) {
+               dev_err(sport->port.dev, "Ring buf alloc failed\n");
+               return -ENOMEM;
+       }
+
+       sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
+       sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
+       nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+
+       if (!nent) {
+               dev_err(sport->port.dev, "DMA Rx mapping error\n");
+               return -EINVAL;
+       }
+
+       dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+       dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma_rx_sconfig.src_maxburst = 1;
+       dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+       ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
+
+       if (ret < 0) {
+               dev_err(sport->port.dev,
+                               "DMA Rx slave config failed, err = %d\n", ret);
+               return ret;
+       }
+
+       sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan,
+                                sg_dma_address(&sport->rx_sgl),
+                                sport->rx_sgl.length,
+                                sport->rx_sgl.length / 2,
+                                DMA_DEV_TO_MEM,
+                                DMA_PREP_INTERRUPT);
+       if (!sport->dma_rx_desc) {
+               dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
+               return -EFAULT;
+       }
+
+       sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+       sport->dma_rx_desc->callback_param = sport;
+       sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+       dma_async_issue_pending(sport->dma_rx_chan);
+
+       writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
+                               sport->port.membase + UARTCR5);
+
+       return 0;
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+       struct lpuart_port *sport = container_of(port,
+                                       struct lpuart_port, port);
+
+       if (sport->dma_rx_chan)
+               dmaengine_terminate_all(sport->dma_rx_chan);
+
+       dma_unmap_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+       kfree(sport->rx_ring.buf);
+       sport->rx_ring.tail = 0;
+       sport->rx_ring.head = 0;
+       sport->dma_rx_desc = NULL;
+       sport->dma_rx_cookie = -EINVAL;
+}
+
+static int lpuart_config_rs485(struct uart_port *port,
+                       struct serial_rs485 *rs485)
+{
+       struct lpuart_port *sport = container_of(port,
+                       struct lpuart_port, port);
+
+       u8 modem = readb(sport->port.membase + UARTMODEM) &
+               ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+       writeb(modem, sport->port.membase + UARTMODEM);
+
+       if (rs485->flags & SER_RS485_ENABLED) {
+               /* Enable auto RS-485 RTS mode */
+               modem |= UARTMODEM_TXRTSE;
+
+               /*
+                * RTS needs to be logic HIGH either during transer _or_ after
+                * transfer, other variants are not supported by the hardware.
+                */
+
+               if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+                               SER_RS485_RTS_AFTER_SEND)))
+                       rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+               if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+                               rs485->flags & SER_RS485_RTS_AFTER_SEND)
+                       rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+               /*
+                * The hardware defaults to RTS logic HIGH while transfer.
+                * Switch polarity in case RTS shall be logic HIGH
+                * after transfer.
+                * Note: UART is assumed to be active high.
+                */
+               if (rs485->flags & SER_RS485_RTS_ON_SEND)
+                       modem &= ~UARTMODEM_TXRTSPOL;
+               else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+                       modem |= UARTMODEM_TXRTSPOL;
+       }
+
+       /* Store the new configuration */
+       sport->port.rs485 = *rs485;
+
+       writeb(modem, sport->port.membase + UARTMODEM);
+       return 0;
 }
 
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
@@ -853,17 +1060,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
        unsigned char temp;
+       struct lpuart_port *sport = container_of(port,
+                               struct lpuart_port, port);
 
-       temp = readb(port->membase + UARTMODEM) &
+       /* Make sure RXRTSE bit is not set when RS485 is enabled */
+       if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+               temp = readb(sport->port.membase + UARTMODEM) &
                        ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-       if (mctrl & TIOCM_RTS)
-               temp |= UARTMODEM_RXRTSE;
+               if (mctrl & TIOCM_RTS)
+                       temp |= UARTMODEM_RXRTSE;
 
-       if (mctrl & TIOCM_CTS)
-               temp |= UARTMODEM_TXCTSE;
+               if (mctrl & TIOCM_CTS)
+                       temp |= UARTMODEM_TXCTSE;
 
-       writeb(temp, port->membase + UARTMODEM);
+               writeb(temp, port->membase + UARTMODEM);
+       }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -921,13 +1133,16 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
        writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
                        sport->port.membase + UARTPFIFO);
 
-       /* explicitly clear RDRF */
-       readb(sport->port.membase + UARTSR1);
-
        /* flush Tx and Rx FIFO */
        writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
                        sport->port.membase + UARTCFIFO);
 
+       /* explicitly clear RDRF */
+       if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+               readb(sport->port.membase + UARTDR);
+               writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+       }
+
        writeb(0, sport->port.membase + UARTTWFIFO);
        writeb(1, sport->port.membase + UARTRWFIFO);
 
@@ -960,110 +1175,12 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
        lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL);
 }
 
-static int lpuart_dma_tx_request(struct uart_port *port)
-{
-       struct lpuart_port *sport = container_of(port,
-                                       struct lpuart_port, port);
-       struct dma_slave_config dma_tx_sconfig;
-       dma_addr_t dma_bus;
-       unsigned char *dma_buf;
-       int ret;
-
-       dma_bus = dma_map_single(sport->dma_tx_chan->device->dev,
-                               sport->port.state->xmit.buf,
-                               UART_XMIT_SIZE, DMA_TO_DEVICE);
-
-       if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) {
-               dev_err(sport->port.dev, "dma_map_single tx failed\n");
-               return -ENOMEM;
-       }
-
-       dma_buf = sport->port.state->xmit.buf;
-       dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
-       dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
-       dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
-       dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
-       ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
-
-       if (ret < 0) {
-               dev_err(sport->port.dev,
-                               "Dma slave config failed, err = %d\n", ret);
-               return ret;
-       }
-
-       sport->dma_tx_buf_virt = dma_buf;
-       sport->dma_tx_buf_bus = dma_bus;
-       sport->dma_tx_in_progress = 0;
-
-       return 0;
-}
-
-static int lpuart_dma_rx_request(struct uart_port *port)
+static void rx_dma_timer_init(struct lpuart_port *sport)
 {
-       struct lpuart_port *sport = container_of(port,
-                                       struct lpuart_port, port);
-       struct dma_slave_config dma_rx_sconfig;
-       dma_addr_t dma_bus;
-       unsigned char *dma_buf;
-       int ret;
-
-       dma_buf = devm_kzalloc(sport->port.dev,
-                               FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
-
-       if (!dma_buf) {
-               dev_err(sport->port.dev, "Dma rx alloc failed\n");
-               return -ENOMEM;
-       }
-
-       dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf,
-                               FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-
-       if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) {
-               dev_err(sport->port.dev, "dma_map_single rx failed\n");
-               return -ENOMEM;
-       }
-
-       dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
-       dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
-       dma_rx_sconfig.src_maxburst = 1;
-       dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
-       ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
-
-       if (ret < 0) {
-               dev_err(sport->port.dev,
-                               "Dma slave config failed, err = %d\n", ret);
-               return ret;
-       }
-
-       sport->dma_rx_buf_virt = dma_buf;
-       sport->dma_rx_buf_bus = dma_bus;
-       sport->dma_rx_in_progress = 0;
-
-       return 0;
-}
-
-static void lpuart_dma_tx_free(struct uart_port *port)
-{
-       struct lpuart_port *sport = container_of(port,
-                                       struct lpuart_port, port);
-
-       dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
-                       UART_XMIT_SIZE, DMA_TO_DEVICE);
-
-       sport->dma_tx_buf_bus = 0;
-       sport->dma_tx_buf_virt = NULL;
-}
-
-static void lpuart_dma_rx_free(struct uart_port *port)
-{
-       struct lpuart_port *sport = container_of(port,
-                                       struct lpuart_port, port);
-
-       dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
-                       FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-
-       sport->dma_rx_buf_bus = 0;
-       sport->dma_rx_buf_virt = NULL;
+               setup_timer(&sport->lpuart_timer, lpuart_timer_func,
+                               (unsigned long)sport);
+               sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+               add_timer(&sport->lpuart_timer);
 }
 
 static int lpuart_startup(struct uart_port *port)
@@ -1084,22 +1201,6 @@ static int lpuart_startup(struct uart_port *port)
        sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
                UARTPFIFO_FIFOSIZE_MASK) + 1);
 
-       if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) {
-               sport->lpuart_dma_rx_use = true;
-               setup_timer(&sport->lpuart_timer, lpuart_timer_func,
-                           (unsigned long)sport);
-       } else
-               sport->lpuart_dma_rx_use = false;
-
-
-       if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
-               sport->lpuart_dma_tx_use = true;
-               temp = readb(port->membase + UARTCR5);
-               temp &= ~UARTCR5_RDMAS;
-               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
-       } else
-               sport->lpuart_dma_tx_use = false;
-
        ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
                                DRIVER_NAME, sport);
        if (ret)
@@ -1113,7 +1214,29 @@ static int lpuart_startup(struct uart_port *port)
        temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
        writeb(temp, sport->port.membase + UARTCR2);
 
+       if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
+               /* set Rx DMA timeout */
+               sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+               if (!sport->dma_rx_timeout)
+                    sport->dma_rx_timeout = 1;
+
+               sport->lpuart_dma_rx_use = true;
+               rx_dma_timer_init(sport);
+       } else {
+               sport->lpuart_dma_rx_use = false;
+       }
+
+       if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
+               init_waitqueue_head(&sport->dma_wait);
+               sport->lpuart_dma_tx_use = true;
+               temp = readb(port->membase + UARTCR5);
+               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+       } else {
+               sport->lpuart_dma_tx_use = false;
+       }
+
        spin_unlock_irqrestore(&sport->port.lock, flags);
+
        return 0;
 }
 
@@ -1170,12 +1293,19 @@ static void lpuart_shutdown(struct uart_port *port)
        devm_free_irq(port->dev, port->irq, sport);
 
        if (sport->lpuart_dma_rx_use) {
-               lpuart_dma_rx_free(&sport->port);
                del_timer_sync(&sport->lpuart_timer);
+               lpuart_dma_rx_free(&sport->port);
        }
 
-       if (sport->lpuart_dma_tx_use)
-               lpuart_dma_tx_free(&sport->port);
+       if (sport->lpuart_dma_tx_use) {
+               if (wait_event_interruptible(sport->dma_wait,
+                       !sport->dma_tx_in_progress) != false) {
+                       sport->dma_tx_in_progress = false;
+                       dmaengine_terminate_all(sport->dma_tx_chan);
+               }
+
+               lpuart_stop_tx(port);
+       }
 }
 
 static void lpuart32_shutdown(struct uart_port *port)
@@ -1203,13 +1333,14 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 {
        struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
        unsigned long flags;
-       unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+       unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
        unsigned int  baud;
        unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
        unsigned int sbr, brfa;
 
        cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
        old_cr2 = readb(sport->port.membase + UARTCR2);
+       cr3 = readb(sport->port.membase + UARTCR3);
        cr4 = readb(sport->port.membase + UARTCR4);
        bdh = readb(sport->port.membase + UARTBDH);
        modem = readb(sport->port.membase + UARTMODEM);
@@ -1240,6 +1371,13 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
                cr1 |= UARTCR1_M;
        }
 
+       /*
+        * When auto RS-485 RTS mode is enabled,
+        * hardware flow control need to be disabled.
+        */
+       if (sport->port.rs485.flags & SER_RS485_ENABLED)
+               termios->c_cflag &= ~CRTSCTS;
+
        if (termios->c_cflag & CRTSCTS) {
                modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
        } else {
@@ -1257,7 +1395,10 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
        if ((termios->c_cflag & PARENB)) {
                if (termios->c_cflag & CMSPAR) {
                        cr1 &= ~UARTCR1_PE;
-                       cr1 |= UARTCR1_M;
+                       if (termios->c_cflag & PARODD)
+                               cr3 |= UARTCR3_T8;
+                       else
+                               cr3 &= ~UARTCR3_T8;
                } else {
                        cr1 |= UARTCR1_PE;
                        if ((termios->c_cflag & CSIZE) == CS8)
@@ -1272,6 +1413,18 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
        /* ask the core to calculate the divisor */
        baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
 
+       /*
+        * Need to update the Ring buffer length according to the selected
+        * baud rate and restart Rx DMA path.
+        *
+        * Since timer function acqures sport->port.lock, need to stop before
+        * acquring same lock because otherwise del_timer_sync() can deadlock.
+        */
+       if (old && sport->lpuart_dma_rx_use) {
+               del_timer_sync(&sport->lpuart_timer);
+               lpuart_dma_rx_free(&sport->port);
+       }
+
        spin_lock_irqsave(&sport->port.lock, flags);
 
        sport->port.read_status_mask = 0;
@@ -1297,17 +1450,6 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
        /* update the per-port timeout */
        uart_update_timeout(port, termios->c_cflag, baud);
 
-       if (sport->lpuart_dma_rx_use) {
-               /* Calculate delay for 1.5 DMA buffers */
-               sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
-                                       FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
-                                       sport->rxfifo_size / 2;
-               dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n",
-                       sport->dma_rx_timeout * 1000 / HZ, sport->port.timeout);
-               if (sport->dma_rx_timeout < msecs_to_jiffies(20))
-                       sport->dma_rx_timeout = msecs_to_jiffies(20);
-       }
-
        /* wait transmit engin complete */
        while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
                barrier();
@@ -1325,12 +1467,20 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
        writeb(cr4 | brfa, sport->port.membase + UARTCR4);
        writeb(bdh, sport->port.membase + UARTBDH);
        writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+       writeb(cr3, sport->port.membase + UARTCR3);
        writeb(cr1, sport->port.membase + UARTCR1);
        writeb(modem, sport->port.membase + UARTMODEM);
 
        /* restore control register */
        writeb(old_cr2, sport->port.membase + UARTCR2);
 
+       if (old && sport->lpuart_dma_rx_use) {
+               if (!lpuart_start_rx_dma(sport))
+                       rx_dma_timer_init(sport);
+               else
+                       sport->lpuart_dma_rx_use = false;
+       }
+
        spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
@@ -1402,6 +1552,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
                        else
                                ctrl &= ~UARTCTRL_PT;
                }
+       } else {
+               ctrl &= ~UARTCTRL_PE;
        }
 
        /* ask the core to calculate the divisor */
@@ -1494,7 +1646,7 @@ static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser)
        return ret;
 }
 
-static struct uart_ops lpuart_pops = {
+static const struct uart_ops lpuart_pops = {
        .tx_empty       = lpuart_tx_empty,
        .set_mctrl      = lpuart_set_mctrl,
        .get_mctrl      = lpuart_get_mctrl,
@@ -1511,9 +1663,14 @@ static struct uart_ops lpuart_pops = {
        .config_port    = lpuart_config_port,
        .verify_port    = lpuart_verify_port,
        .flush_buffer   = lpuart_flush_buffer,
+#if defined(CONFIG_CONSOLE_POLL)
+       .poll_init      = lpuart_poll_init,
+       .poll_get_char  = lpuart_poll_get_char,
+       .poll_put_char  = lpuart_poll_put_char,
+#endif
 };
 
-static struct uart_ops lpuart32_pops = {
+static const struct uart_ops lpuart32_pops = {
        .tx_empty       = lpuart32_tx_empty,
        .set_mctrl      = lpuart32_set_mctrl,
        .get_mctrl      = lpuart32_get_mctrl,
@@ -1556,6 +1713,13 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct lpuart_port *sport = lpuart_ports[co->index];
        unsigned char  old_cr2, cr2;
+       unsigned long flags;
+       int locked = 1;
+
+       if (sport->port.sysrq || oops_in_progress)
+               locked = spin_trylock_irqsave(&sport->port.lock, flags);
+       else
+               spin_lock_irqsave(&sport->port.lock, flags);
 
        /* first save CR2 and then disable interrupts */
        cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
@@ -1570,6 +1734,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count)
                barrier();
 
        writeb(old_cr2, sport->port.membase + UARTCR2);
+
+       if (locked)
+               spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void
@@ -1577,6 +1744,13 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct lpuart_port *sport = lpuart_ports[co->index];
        unsigned long  old_cr, cr;
+       unsigned long flags;
+       int locked = 1;
+
+       if (sport->port.sysrq || oops_in_progress)
+               locked = spin_trylock_irqsave(&sport->port.lock, flags);
+       else
+               spin_lock_irqsave(&sport->port.lock, flags);
 
        /* first save CR2 and then disable interrupts */
        cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL);
@@ -1591,6 +1765,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
                barrier();
 
        lpuart32_write(old_cr, sport->port.membase + UARTCTRL);
+
+       if (locked)
+               spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
@@ -1800,6 +1977,18 @@ static struct uart_driver lpuart_reg = {
        .cons           = LPUART_CONSOLE,
 };
 
+static struct dma_chan *lpuart_request_dma_chan(struct lpuart_port *sport,
+                                               const char *name)
+{
+       struct dma_chan *chan;
+
+       chan = dma_request_slave_channel(sport->port.dev, name);
+       if (!chan)
+               dev_info(sport->port.dev, "DMA %s channel request failed, "
+                               "operating without %s DMA\n", name, name);
+       return chan;
+}
+
 static int lpuart_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -1830,13 +2019,21 @@ static int lpuart_probe(struct platform_device *pdev)
        sport->port.dev = &pdev->dev;
        sport->port.type = PORT_LPUART;
        sport->port.iotype = UPIO_MEM;
-       sport->port.irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "cannot obtain irq\n");
+               return ret;
+       }
+       sport->port.irq = ret;
+
        if (sport->lpuart32)
                sport->port.ops = &lpuart32_pops;
        else
                sport->port.ops = &lpuart_pops;
        sport->port.flags = UPF_BOOT_AUTOCONF;
 
+       sport->port.rs485_config = lpuart_config_rs485;
+
        sport->clk = devm_clk_get(&pdev->dev, "ipg");
        if (IS_ERR(sport->clk)) {
                ret = PTR_ERR(sport->clk);
@@ -1867,15 +2064,16 @@ static int lpuart_probe(struct platform_device *pdev)
                return ret;
        }
 
-       sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
-       if (!sport->dma_tx_chan)
-               dev_info(sport->port.dev, "DMA tx channel request failed, "
-                               "operating without tx DMA\n");
+       if (!nodma) {
+               sport->dma_tx_chan = lpuart_request_dma_chan(sport, "tx");
+               sport->dma_rx_chan = lpuart_request_dma_chan(sport, "rx");
+       }
 
-       sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
-       if (!sport->dma_rx_chan)
-               dev_info(sport->port.dev, "DMA rx channel request failed, "
-                               "operating without rx DMA\n");
+       if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) {
+               sport->port.rs485.flags |= SER_RS485_ENABLED;
+               sport->port.rs485.flags |= SER_RS485_RTS_ON_SEND;
+               writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM);
+       }
 
        return 0;
 }
@@ -1917,6 +2115,32 @@ static int lpuart_suspend(struct device *dev)
 
        uart_suspend_port(&lpuart_reg, &sport->port);
 
+       if (sport->lpuart_dma_rx_use) {
+               /*
+                * EDMA driver during suspend will forcefully release any
+                * non-idle DMA channels. If port wakeup is enabled or if port
+                * is console port or 'no_console_suspend' is set the Rx DMA
+                * cannot resume as as expected, hence gracefully release the
+                * Rx DMA path before suspend and start Rx DMA path on resume.
+                */
+               if (sport->port.irq_wake) {
+                       del_timer_sync(&sport->lpuart_timer);
+                       lpuart_dma_rx_free(&sport->port);
+               }
+
+               /* Disable Rx DMA to use UART port as wakeup source */
+               writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+                                       sport->port.membase + UARTCR5);
+       }
+
+       if (sport->lpuart_dma_tx_use) {
+               sport->dma_tx_in_progress = false;
+               dmaengine_terminate_all(sport->dma_tx_chan);
+       }
+
+       if (sport->port.suspended && !sport->port.irq_wake)
+               clk_disable_unprepare(sport->clk);
+
        return 0;
 }
 
@@ -1925,6 +2149,9 @@ static int lpuart_resume(struct device *dev)
        struct lpuart_port *sport = dev_get_drvdata(dev);
        unsigned long temp;
 
+       if (sport->port.suspended && !sport->port.irq_wake)
+               clk_prepare_enable(sport->clk);
+
        if (sport->lpuart32) {
                lpuart32_setup_watermark(sport);
                temp = lpuart32_read(sport->port.membase + UARTCTRL);
@@ -1938,6 +2165,24 @@ static int lpuart_resume(struct device *dev)
                writeb(temp, sport->port.membase + UARTCR2);
        }
 
+       if (sport->lpuart_dma_rx_use) {
+               if (sport->port.irq_wake) {
+                       if (!lpuart_start_rx_dma(sport))
+                               rx_dma_timer_init(sport);
+                       else
+                               sport->lpuart_dma_rx_use = false;
+               }
+       }
+
+       if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
+                       init_waitqueue_head(&sport->dma_wait);
+                       sport->lpuart_dma_tx_use = true;
+                       writeb(readb(sport->port.membase + UARTCR5) |
+                               UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+       } else {
+               sport->lpuart_dma_tx_use = false;
+       }
+
        uart_resume_port(&lpuart_reg, &sport->port);
 
        return 0;
index 858c30814497918afe491e24113a6d7a64c1cc6e..7957fb04e283ad42c23038af38a531a9e686ad79 100644 (file)
@@ -169,6 +169,7 @@ struct hw_bank {
  * @enabled_otg_timer_bits: bits of enabled otg timers
  * @next_otg_timer: next nearest enabled timer to be expired
  * @work: work for role changing
+ * @work_dr: work for role changing for non-OTG controllers
  * @wq: workqueue thread
  * @qh_pool: allocation pool for queue heads
  * @td_pool: allocation pool for transfer descriptors
@@ -215,6 +216,7 @@ struct ci_hdrc {
        unsigned                        enabled_otg_timer_bits;
        enum otg_fsm_timer              next_otg_timer;
        struct work_struct              work;
+       struct work_struct              work_dr;
        struct workqueue_struct         *wq;
 
        struct dma_pool                 *qh_pool;
index 2949289bb3c50b40064268bcde5f2343c83626e1..dcd9820aa4fbcf11e1f3a0b9ffbc134e4f27bd9a 100644 (file)
@@ -65,6 +65,10 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
 };
 
+static const struct ci_hdrc_imx_platform_flag vf610_usb_data = {
+       .flags = CI_HDRC_DUAL_ROLE_NOT_OTG,
+};
+
 static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
        { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
        { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
@@ -73,6 +77,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
        { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
        { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
        { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
+       { .compatible = "fsl,vf610-usb", .data = &vf610_usb_data},
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
@@ -301,9 +306,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                                &pdata);
        if (IS_ERR(data->ci_pdev)) {
                ret = PTR_ERR(data->ci_pdev);
-               dev_err(&pdev->dev,
-                       "Can't register ci_hdrc platform device, err=%d\n",
-                       ret);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "ci_hdrc_add_device failed, err=%d\n", ret);
                goto err_clk;
        }
 
@@ -344,6 +349,11 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
        return 0;
 }
 
+static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
+{
+       ci_hdrc_imx_remove(pdev);
+}
+
 #ifdef CONFIG_PM
 static int imx_controller_suspend(struct device *dev)
 {
@@ -461,6 +471,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
 static struct platform_driver ci_hdrc_imx_driver = {
        .probe = ci_hdrc_imx_probe,
        .remove = ci_hdrc_imx_remove,
+       .shutdown = ci_hdrc_imx_shutdown,
        .driver = {
                .name = "imx_usb",
                .of_match_table = ci_hdrc_imx_dt_ids,
index 939c6ad7106891689b3c7405f39bea7ae48fae9a..953368407b889e486106bbd7546e17512aa01a1d 100644 (file)
@@ -571,37 +571,71 @@ static irqreturn_t ci_irq(int irq, void *data)
        return ret;
 }
 
-static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
-                           void *ptr)
+static void usb_roleswitch_workqueue(struct work_struct *work)
 {
-       struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
-       struct ci_hdrc *ci = vbus->ci;
+       struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work_dr);
+       struct ci_hdrc_cable *id, *vbus;
+       int ret;
 
-       if (event)
-               vbus->state = true;
-       else
-               vbus->state = false;
+       pm_runtime_get_sync(ci->dev);
+
+       id = &ci->platdata->id_extcon;
+       if (!IS_ERR(id->edev)) {
+               int new_role;
 
-       vbus->changed = true;
+               ci_role_stop(ci);
+               hw_wait_phy_stable();
 
-       ci_irq(ci->irq, ci);
-       return NOTIFY_DONE;
+               ret = extcon_get_cable_state_(id->edev, EXTCON_USB_HOST);
+               if (ret) {
+                       new_role = CI_ROLE_HOST;
+                       dev_info(ci->dev, "switching to host role\n");
+               } else {
+                       new_role = CI_ROLE_GADGET;
+                       dev_info(ci->dev, "switching to gadget role\n");
+               }
+               ci_role_start(ci, new_role);
+       }
+
+       vbus = &ci->platdata->vbus_extcon;
+       if (!IS_ERR(vbus->edev)) {
+               ret = extcon_get_cable_state_(vbus->edev, EXTCON_USB);
+               if (ret) {
+                       usb_gadget_vbus_connect(&ci->gadget);
+               } else {
+                       usb_gadget_vbus_disconnect(&ci->gadget);
+               }
+       }
+
+       pm_runtime_put_sync(ci->dev);
+
+       enable_irq(ci->irq);
 }
 
-static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
-                         void *ptr)
+static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
+                            void *ptr)
 {
-       struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
-       struct ci_hdrc *ci = id->ci;
-
-       if (event)
-               id->state = false;
-       else
-               id->state = true;
+       struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
+       struct ci_hdrc *ci = cbl->ci;
+
+       if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) {
+               disable_irq_nosync(ci->irq);
+
+               /*
+                * This notifier might get called twice in succession,
+                * once for the ID pin and once for the VBUS pin. Make
+                * sure we only disable irq in case we successfully add
+                * work to the work queue.
+                */
+               if (!queue_work(system_power_efficient_wq, &ci->work_dr))
+                       enable_irq(ci->irq);
+       } else {
+               cbl->connected = event;
+               cbl->changed = true;
 
-       id->changed = true;
+               ci_irq(ci->irq, ci);
+       }
 
-       ci_irq(ci->irq, ci);
        return NOTIFY_DONE;
 }
 
@@ -718,27 +752,27 @@ static int ci_get_platdata(struct device *dev,
        }
 
        cable = &platdata->vbus_extcon;
-       cable->nb.notifier_call = ci_vbus_notifier;
+       cable->nb.notifier_call = ci_cable_notifier;
        cable->edev = ext_vbus;
 
        if (!IS_ERR(ext_vbus)) {
                ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
                if (ret)
-                       cable->state = true;
+                       cable->connected = true;
                else
-                       cable->state = false;
+                       cable->connected = false;
        }
 
        cable = &platdata->id_extcon;
-       cable->nb.notifier_call = ci_id_notifier;
+       cable->nb.notifier_call = ci_cable_notifier;
        cable->edev = ext_id;
 
        if (!IS_ERR(ext_id)) {
                ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
                if (ret)
-                       cable->state = false;
+                       cable->connected = true;
                else
-                       cable->state = true;
+                       cable->connected = false;
        }
        return 0;
 }
@@ -947,7 +981,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        ci_get_otg_capable(ci);
 
+       if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG)
+               INIT_WORK(&ci->work_dr, usb_roleswitch_workqueue);
+
+       ret = ci_extcon_register(ci);
+       if (ret)
+               goto stop;
+
        dr_mode = ci->platdata->dr_mode;
+
        /* initialize role(s) before the interrupt is requested */
        if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
                ret = ci_hdrc_host_init(ci);
@@ -986,7 +1028,13 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                         * role switch, the defalt role is gadget, and the
                         * user can switch it through debugfs.
                         */
-                       ci->role = CI_ROLE_GADGET;
+                       struct ci_hdrc_cable *id = &ci->platdata->id_extcon;
+                       if (!IS_ERR(id->edev)) {
+                               if (extcon_get_cable_state_(id->edev, EXTCON_USB_HOST))
+                                       ci->role = CI_ROLE_HOST;
+                               else
+                                       ci->role = CI_ROLE_GADGET;
+                       }
                }
        } else {
                ci->role = ci->roles[CI_ROLE_HOST]
@@ -995,9 +1043,20 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        }
 
        if (!ci_otg_is_fsm_mode(ci)) {
+
                /* only update vbus status for peripheral */
-               if (ci->role == CI_ROLE_GADGET)
-                       ci_handle_vbus_change(ci);
+               if (dr_mode == USB_DR_MODE_PERIPHERAL) {
+                       usb_gadget_vbus_connect(&ci->gadget);
+               } else if (ci->role == CI_ROLE_GADGET) {
+                       struct ci_hdrc_cable *vbus = &ci->platdata->vbus_extcon;
+
+                       /* Use vbus state from extcon if provided */
+                       if (!IS_ERR(vbus->edev) &&
+                           extcon_get_cable_state_(vbus->edev, EXTCON_USB))
+                               usb_gadget_vbus_connect(&ci->gadget);
+                       else
+                               ci_handle_vbus_change(ci);
+               }
 
                ret = ci_role_start(ci, ci->role);
                if (ret) {
@@ -1013,10 +1072,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        if (ret)
                goto stop;
 
-       ret = ci_extcon_register(ci);
-       if (ret)
-               goto stop;
-
        if (ci->supports_runtime_pm) {
                pm_runtime_set_active(&pdev->dev);
                pm_runtime_enable(&pdev->dev);
index f36a1ac3bfbdfc0861ebebd9fd4114d90452b03a..10236fe7152286f5556c519d14f0b6c6b207c68a 100644 (file)
@@ -44,7 +44,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
                else
                        val &= ~OTGSC_BSVIS;
 
-               if (cable->state)
+               if (cable->connected)
                        val |= OTGSC_BSV;
                else
                        val &= ~OTGSC_BSV;
@@ -62,10 +62,10 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
                else
                        val &= ~OTGSC_IDIS;
 
-               if (cable->state)
-                       val |= OTGSC_ID;
+               if (cable->connected)
+                       val &= ~OTGSC_ID; /* host */
                else
-                       val &= ~OTGSC_ID;
+                       val |= OTGSC_ID; /* device */
 
                if (cable->enabled)
                        val |= OTGSC_IDIE;
index 6abb6a10ee8218727592bc36fce7f2668ebf2e0f..41f6f312add062fc62b940cb8fd20eb6026a1305 100644 (file)
@@ -739,7 +739,7 @@ static inline struct gadget_info *os_desc_item_to_gadget_info(
 
 static ssize_t os_desc_use_show(struct config_item *item, char *page)
 {
-       return sprintf(page, "%d",
+       return sprintf(page, "%d\n",
                        os_desc_item_to_gadget_info(item)->use_os_desc);
 }
 
@@ -763,7 +763,7 @@ static ssize_t os_desc_use_store(struct config_item *item, const char *page,
 
 static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page)
 {
-       return sprintf(page, "%d",
+       return sprintf(page, "0x%02x\n",
                        os_desc_item_to_gadget_info(item)->b_vendor_code);
 }
 
@@ -788,9 +788,13 @@ static ssize_t os_desc_b_vendor_code_store(struct config_item *item,
 static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page)
 {
        struct gadget_info *gi = os_desc_item_to_gadget_info(item);
+       int res;
+
+       res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN,
+                             UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1);
+       page[res++] = '\n';
 
-       memcpy(page, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
-       return OS_STRING_QW_SIGN_LEN;
+       return res;
 }
 
 static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page,
@@ -902,7 +906,7 @@ static inline struct usb_os_desc_ext_prop
 
 static ssize_t ext_prop_type_show(struct config_item *item, char *page)
 {
-       return sprintf(page, "%d", to_usb_os_desc_ext_prop(item)->type);
+       return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type);
 }
 
 static ssize_t ext_prop_type_store(struct config_item *item,
index 0037104d66ac320f035446f2bf49bf3f4414d4a4..d2ca63c035247e69020466f5f2ab82ea384036ca 100644 (file)
@@ -82,4 +82,8 @@ config LOGO_M32R_CLUT224
        depends on M32R
        default y
 
+config LOGO_CUSTOM_CLUT224
+       bool "Custom 224-color Linux logo"
+       default n
+
 endif # LOGO
index 3b437813584cec4f3d73ba5364353ac4a5cbb29b..45d4b5346d0773a37ea24f2084dd9865fe9fc746 100644 (file)
@@ -18,6 +18,8 @@ obj-$(CONFIG_LOGO_M32R_CLUT224)               += logo_m32r_clut224.o
 
 obj-$(CONFIG_SPU_BASE)                 += logo_spe_clut224.o
 
+obj-$(CONFIG_LOGO_CUSTOM_CLUT224)      += logo_custom_clut224.o
+
 # How to generate logo's
 
 # Use logo-cfiles to retrieve list of .c files to be built
index 10fbfd8ab963f9e78905a5d932b13ae810639363..bef02e84a0768d5198a9af4f09edd28cc458a287 100644 (file)
@@ -110,6 +110,10 @@ const struct linux_logo * __init_refok fb_find_logo(int depth)
 #ifdef CONFIG_LOGO_M32R_CLUT224
                /* M32R Linux logo */
                logo = &logo_m32r_clut224;
+#endif
+#ifdef CONFIG_LOGO_CUSTOM_CLUT224
+               /* Custom Linux logo */
+               logo = &logo_custom_clut224;
 #endif
        }
        return logo;
index 8cba54a2a0a0f78a198ba9ae82a96f6b3f6631f8..8045cdea8cc91d2a6fb78018b519d966af631c3b 100644 (file)
@@ -81,6 +81,12 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set);
 int __drm_atomic_helper_set_config(struct drm_mode_set *set,
                struct drm_atomic_state *state);
 
+int drm_atomic_helper_disable_all(struct drm_device *dev,
+                                 struct drm_modeset_acquire_ctx *ctx);
+struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev);
+int drm_atomic_helper_resume(struct drm_device *dev,
+                            struct drm_atomic_state *state);
+
 int drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc,
                                        struct drm_property *property,
                                        uint64_t val);
index 3f0c6909dda17f9ec14959353a440da171a87ca6..69726121e02933490a1f697ccdc938484e1247ac 100644 (file)
@@ -122,6 +122,14 @@ enum subpixel_order {
 #define DRM_COLOR_FORMAT_RGB444                (1<<0)
 #define DRM_COLOR_FORMAT_YCRCB444      (1<<1)
 #define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
+
+#define DRM_BUS_FLAG_DE_LOW            (1<<0)
+#define DRM_BUS_FLAG_DE_HIGH           (1<<1)
+/* drive data on pos. edge */
+#define DRM_BUS_FLAG_PIXDATA_POSEDGE   (1<<2)
+/* drive data on neg. edge */
+#define DRM_BUS_FLAG_PIXDATA_NEGEDGE   (1<<3)
+
 /*
  * Describes a given display (e.g. CRT or flat panel) and its limitations.
  */
@@ -143,6 +151,7 @@ struct drm_display_info {
 
        const u32 *bus_formats;
        unsigned int num_bus_formats;
+       u32 bus_flags;
 
        /* Mask of supported hdmi deep color modes */
        u8 edid_hdmi_dc_modes;
index c54cf3d4a03f0f744e83dbe93a7534620764a6f6..5612984baa993f9b167d81e1dd8e5b8d98905235 100644 (file)
@@ -12,6 +12,7 @@ struct drm_mode_fb_cmd2;
 struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
        unsigned int preferred_bpp, unsigned int num_crtc,
        unsigned int max_conn_count);
+struct drm_fb_helper *drm_fbdev_cma_get_helper(struct drm_fbdev_cma *fbdev_cma);
 void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma);
 
 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma);
index 94938d89347cf2f8860f617f28ab68d5134bacce..c5576fbcb909f925a241a4f1ff9736bc8c8ad119 100644 (file)
@@ -138,7 +138,7 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
 struct drm_modeset_acquire_ctx *
 drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc);
 
-int drm_modeset_lock_all_crtcs(struct drm_device *dev,
-               struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_lock_all_ctx(struct drm_device *dev,
+                            struct drm_modeset_acquire_ctx *ctx);
 
 #endif /* DRM_MODESET_LOCK_H_ */
index 56c16aaea112c776543f1548936c55e6db49cb2b..43c9e2282daecb2cdb2cc4c74884e2afcf5103ae 100644 (file)
 #define VF610_CLK_SNVS                 182
 #define VF610_CLK_DAP                  183
 #define VF610_CLK_OCOTP         184
-#define VF610_CLK_END                  185
+#define VF610_CLK_TCON0                        185
+#define VF610_CLK_TCON1                        186
+#define VF610_CLK_DDRMC                        187
+#define VF610_CLK_WKPU                 188
+#define VF610_CLK_ESW                  189
+#define VF610_CLK_END                  190
 
 #endif /* __DT_BINDINGS_CLOCK_VF610_H */
diff --git a/include/linux/input/fusion_F0710A.h b/include/linux/input/fusion_F0710A.h
new file mode 100644 (file)
index 0000000..7d152cb
--- /dev/null
@@ -0,0 +1,20 @@
+/* linux/input/fusion_F0710A.h
+ *
+ * Platform data for Fusion F0710A driver
+ *
+ * Copyright (c) 2013 Toradex AG (stefan.agner@toradex.ch)
+ *
+ *  For licencing details see kernel-base/COPYING
+ */
+
+#ifndef __LINUX_I2C_FUSION_F0710A_H
+#define __LINUX_I2C_FUSION_F0710A_H
+
+/* Board specific touch screen initial values */
+struct fusion_f0710a_init_data {
+       int     (*pinmux_fusion_pins)(void);
+       int     gpio_int;
+       int     gpio_reset;
+};
+
+#endif /*  __LINUX_I2C_FUSION_F0710A_H */
index ca5bd91d12e169cd80eb6a546fe73c35df8ead91..2be299513819ddfe7d59b481c6ca908adc7e4bb0 100644 (file)
@@ -47,6 +47,7 @@ extern const struct linux_logo logo_superh_vga16;
 extern const struct linux_logo logo_superh_clut224;
 extern const struct linux_logo logo_m32r_clut224;
 extern const struct linux_logo logo_spe_clut224;
+extern const struct linux_logo logo_custom_clut224;
 
 extern const struct linux_logo *fb_find_logo(int depth);
 #ifdef CONFIG_FB_LOGO_EXTRA
index 75e543b78f53ec4b931b87adee9d389bd8d50164..3c02ed9a6c985e8556b8a15371dcd5f8e09bcec3 100644 (file)
@@ -26,6 +26,9 @@ extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);
 extern struct regmap *syscon_regmap_lookup_by_phandle(
                                        struct device_node *np,
                                        const char *property);
+extern int syscon_regmap_read_from_offset(struct device_node *np,
+                                         const char *s,
+                                         unsigned int *val);
 #else
 static inline struct regmap *syscon_node_to_regmap(struct device_node *np)
 {
@@ -48,6 +51,13 @@ static inline struct regmap *syscon_regmap_lookup_by_phandle(
 {
        return ERR_PTR(-ENOSYS);
 }
+
+static inline int syscon_regmap_read_from_offset(struct device_node *np,
+                                                const char *s,
+                                                unsigned int *val)
+{
+       return ERR_PTR(-ENOSYS);
+}
 #endif
 
 #endif /* __LINUX_MFD_SYSCON_H__ */
index f9be467d669531733b4889311029ac8d251a327e..b8b1706a9444267c20718ac2accbe7eaf97670eb 100644 (file)
@@ -12,7 +12,7 @@ struct ci_hdrc;
 
 /**
  * struct ci_hdrc_cable - structure for external connector cable state tracking
- * @state: current state of the line
+ * @connected: true if cable is connected, false otherwise
  * @changed: set to true when extcon event happen
  * @enabled: set to true if we've enabled the vbus or id interrupt
  * @edev: device which generate events
@@ -21,7 +21,7 @@ struct ci_hdrc;
  * @conn: used for notification registration
  */
 struct ci_hdrc_cable {
-       bool                            state;
+       bool                            connected;
        bool                            changed;
        bool                            enabled;
        struct extcon_dev               *edev;
diff --git a/include/linux/vf610_mscm.h b/include/linux/vf610_mscm.h
new file mode 100644 (file)
index 0000000..c551b50
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __VF610_MSCM__
+#define __VF610_MSCM__
+
+#include <linux/interrupt.h>
+
+int mscm_request_cpu2cpu_irq(unsigned int intid, irq_handler_t handler,
+                       const char *name, void *priv);
+void mscm_free_cpu2cpu_irq(unsigned int intid, void *priv);
+void mscm_trigger_cpu2cpu_irq(unsigned int intid, int cpuid);
+void mscm_enable_cpu2cpu_irq(unsigned int intid);
+void mscm_disable_cpu2cpu_irq(unsigned int intid);
+
+#endif /* __VF610_MSCM__ */
diff --git a/include/linux/vf610_sema4.h b/include/linux/vf610_sema4.h
new file mode 100644 (file)
index 0000000..a57de23
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 Toradex AG.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_VF610_SEMA4_H__
+#define __LINUX_VF610_SEMA4_H__
+
+#define SEMA4_NUM_DEVICES      1
+#define SEMA4_NUM_GATES                16
+
+#define SEMA4_UNLOCK   0x00
+#define SEMA4_A5_LOCK  0x01
+#define SEMA4_GATE_MASK        0x03
+
+#define CORE_MUTEX_VALID       (('c'<<24)|('m'<<24)|('t'<<24)|'x')
+
+/*
+ * The enumerates
+ */
+enum {
+       /* sema4 registers offset */
+       SEMA4_CP0INE    = 0x42,
+       SEMA4_CP1INE    = 0x4A,
+       SEMA4_CP0NTF    = 0x82,
+       SEMA4_CP1NTF    = 0x8A,
+};
+
+static const unsigned int idx_sema4[SEMA4_NUM_GATES] = {
+       1 << 15, 1 << 14, 1 << 13, 1 << 12,
+       1 << 11, 1 << 10, 1 << 9, 1 << 8,
+       1 << 7, 1 << 6, 1 << 5, 1 << 4,
+       1 << 3, 1 << 2, 1 << 1, 1 << 0,
+};
+
+struct vf610_sema4_mutex {
+       u32                     valid;
+       u32                     gate_num;
+       unsigned char           gate_val;
+       wait_queue_head_t       wait_q;
+};
+
+struct vf610_sema4_mutex_device {
+       struct device           *dev;
+       u16                     cpntf_val;
+       u16                     cpine_val;
+       void __iomem            *ioaddr;        /* Mapped address */
+       spinlock_t              lock;           /* Mutex */
+       int                     irq;
+
+       u16                     alloced;
+       struct vf610_sema4_mutex        *mutex_ptr[SEMA4_NUM_GATES];
+};
+
+struct vf610_sema4_mutex *
+       vf610_sema4_mutex_create(u32 dev_num, u32 mutex_num);
+#ifdef CONFIG_VF610_SEMA4
+int vf610_sema4_mutex_destroy(struct vf610_sema4_mutex *mutex_ptr);
+int vf610_sema4_mutex_trylock(struct vf610_sema4_mutex *mutex_ptr);
+int vf610_sema4_mutex_lock(struct vf610_sema4_mutex *mutex_ptr);
+int vf610_sema4_mutex_unlock(struct vf610_sema4_mutex *mutex_ptr);
+#else
+static inline int vf610_sema4_mutex_destroy(struct vf610_sema4_mutex *mutex_ptr)
+{
+       return 0;
+}
+static inline int vf610_sema4_mutex_trylock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       return 0;
+}
+static inline int vf610_sema4_mutex_lock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       return 0;
+}
+static inline int vf610_sema4_mutex_unlock(struct vf610_sema4_mutex *mutex_ptr)
+{
+       return 0;
+}
+#endif
+#endif /* __LINUX_VF610_SEMA4_H__ */
index 488a92224249f064922e4c019ab5065f07b87c74..2995f0cda7ec4f0e6975c60e6d59b708d959587c 100644 (file)
@@ -326,8 +326,6 @@ SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
        &wm9712_out3_mux_controls),
 SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
        &wm9712_spk_mux_controls),
-SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
-       &wm9712_capture_phone_mux_controls),
 SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
        &wm9712_capture_selectl_controls),
 SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
@@ -336,8 +334,6 @@ SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
        &wm9712_mic_src_controls),
 SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
        &wm9712_mic_src_controls),
-SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
-       &wm9712_diff_sel_controls),
 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
        &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
index 14dfdee05fd5ae842bebdd0092e47bd4bc88f605..7d60d5b03f6365c6d784da270be606159575727e 100644 (file)
@@ -15,6 +15,7 @@ config SND_SOC_FSL_ASRC
 config SND_SOC_FSL_SAI
        tristate "Synchronous Audio Interface (SAI) module support"
        select REGMAP_MMIO
+       select SND_SOC_AC97_BUS
        select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
        select SND_SOC_GENERIC_DMAENGINE_PCM
        help
@@ -217,6 +218,14 @@ config SND_SOC_PHYCORE_AC97
          Say Y if you want to add support for SoC audio on Phytec phyCORE
          and phyCARD boards in AC97 mode
 
+config SND_SOC_FSL_SAI_WM9712
+       tristate "SoC Audio support for Freescale SoC's using SAI and WM9712 codec"
+       select SND_SOC_FSL_SAI
+       select SND_SOC_WM9712
+       help
+         Say Y or M here if you want to add support for SoC audio on Freescale
+         SoC using SAI and the WM9712 (or compatible) codec.
+
 config SND_SOC_EUKREA_TLV320
        tristate "Eukrea TLV320"
        depends on ARCH_MXC && I2C
index d28dc25c9375fd813e0f14d2c48a5d4d0e08576c..43f5da761675541a31ffc086c84f913fec8a872b 100644 (file)
@@ -13,7 +13,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 # Freescale SSI/DMA/SAI/SPDIF Support
 snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
 snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
-snd-soc-fsl-sai-objs := fsl_sai.o
+snd-soc-fsl-sai-objs := fsl_sai.o fsl_sai_clk.o fsl_sai_ac97.o
 snd-soc-fsl-ssi-y := fsl_ssi.o
 snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
 snd-soc-fsl-spdif-objs := fsl_spdif.o
@@ -60,6 +60,7 @@ snd-soc-imx-mc13783-objs := imx-mc13783.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
+obj-$(CONFIG_SND_SOC_FSL_SAI_WM9712) += fsl_sai_wm9712.o
 obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
 obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
index b95fbc3f68ebbfcae42d95c64552e87bdeec78a4..8ca899dfca773286c9629d4f4239529c069c4a6b 100644 (file)
@@ -48,6 +48,7 @@
 
 /* SAI Transmit/Receive Control Register */
 #define FSL_SAI_CSR_TERE       BIT(31)
+#define FSL_SAI_CSR_BCE                BIT(28)
 #define FSL_SAI_CSR_FR         BIT(25)
 #define FSL_SAI_CSR_SR         BIT(24)
 #define FSL_SAI_CSR_xF_SHIFT   16
@@ -72,7 +73,9 @@
 #define FSL_SAI_CR1_RFW_MASK   0x1f
 
 /* SAI Transmit and Receive Configuration 2 Register */
+#define FSL_SAI_CR2_SYNC_MASK  (0x3 << 30)
 #define FSL_SAI_CR2_SYNC       BIT(30)
+#define FSL_SAI_CR2_BCS                BIT(29)
 #define FSL_SAI_CR2_MSEL_MASK  (0x3 << 26)
 #define FSL_SAI_CR2_MSEL_BUS   0
 #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26)
@@ -82,6 +85,7 @@
 #define FSL_SAI_CR2_BCP                BIT(25)
 #define FSL_SAI_CR2_BCD_MSTR   BIT(24)
 #define FSL_SAI_CR2_DIV_MASK   0xff
+#define FSL_SAI_CR2_DIV(x)     ((x) & 0xff)
 
 /* SAI Transmit and Receive Configuration 3 Register */
 #define FSL_SAI_CR3_TRCE       BIT(16)
 #define FSL_SAI_CR5_WNW_MASK   (0x1f << 24)
 #define FSL_SAI_CR5_W0W(x)     (((x) - 1) << 16)
 #define FSL_SAI_CR5_W0W_MASK   (0x1f << 16)
-#define FSL_SAI_CR5_FBT(x)     ((x) << 8)
+#define FSL_SAI_CR5_FBT(x)     ((x - 1) << 8)
 #define FSL_SAI_CR5_FBT_MASK   (0x1f << 8)
 
 /* SAI type */
diff --git a/sound/soc/fsl/fsl_sai_ac97.c b/sound/soc/fsl/fsl_sai_ac97.c
new file mode 100644 (file)
index 0000000..3bd4832
--- /dev/null
@@ -0,0 +1,1353 @@
+/*
+ * Freescale ALSA SoC Digital Audio Interface (SAI) AC97 driver.
+ *
+ * Copyright (C) 2013-2015 Toradex, Inc.
+ * Authors: Stefan Agner, Marcel Ziswiler
+ *
+ * This program is free software, you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or(at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#include "fsl_sai.h"
+#include "imx-pcm.h"
+
+struct imx_pcm_runtime_data {
+       unsigned int period;
+       int periods;
+       unsigned long offset;
+       struct snd_pcm_substream *substream;
+};
+
+#define EDMA_PRIO_HIGH         6
+#define SAI_AC97_DMABUF_SIZE   (13 * 4)
+#define SAI_AC97_RBUF_COUNT    (4)
+#define SAI_AC97_RBUF_FRAMES   (1024)
+#define SAI_AC97_RBUF_SIZE     (SAI_AC97_RBUF_FRAMES * SAI_AC97_DMABUF_SIZE)
+#define SAI_AC97_RBUF_SIZE_TOT (SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_SIZE)
+
+static struct fsl_sai_ac97 *info;
+
+struct fsl_sai_ac97 {
+       struct platform_device *pdev;
+
+       resource_size_t mapbase;
+
+       struct regmap *regmap;
+       struct clk *bus_clk;
+       struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
+
+       struct dma_chan         *dma_tx_chan;
+       struct dma_chan         *dma_rx_chan;
+       struct dma_async_tx_descriptor  *dma_tx_desc;
+       struct dma_async_tx_descriptor  *dma_rx_desc;
+
+       dma_cookie_t            dma_tx_cookie;
+       dma_cookie_t            dma_rx_cookie;
+
+       struct snd_dma_buffer rx_buf;
+       struct snd_dma_buffer tx_buf;
+
+       bool big_endian_regs;
+       bool big_endian_data;
+       bool is_dsp_mode;
+       bool sai_on_imx;
+
+       struct snd_dmaengine_dai_dma_data dma_params_rx;
+       struct snd_dmaengine_dai_dma_data dma_params_tx;
+
+
+       struct snd_card *card;
+
+       struct mutex lock;
+
+       int cmdbufid;
+       unsigned short reg;
+       unsigned short val;
+
+       struct snd_soc_platform platform;
+
+       atomic_t playing;
+       atomic_t capturing;
+
+       struct imx_pcm_runtime_data *iprtd_playback;
+       struct imx_pcm_runtime_data *iprtd_capture;
+};
+
+#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
+                      FSL_SAI_CSR_FEIE)
+
+
+struct ac97_tx {
+       /*
+        * Slot 0: TAG
+        * Bit 15 Codec Ready
+        * Bit 14:3 Slot Valid (Which of slot 1 to slot 12 contain valid data)
+        * Bit 2 Zero
+        * Bit 1:0 Codec ID
+        */
+       unsigned int reserved_2:4; /* Align the 16-bit Tag to 20-bit */
+       unsigned int codec_id:2;
+       unsigned int reserved_1:1;
+       unsigned int slot_valid:12;
+       unsigned int valid:1;
+       unsigned int align_32_0:12;
+
+       /*
+        * Slot 1: Command Address Port
+        * Bit(19) Read/Write command (1=read, 0=write)
+        * Bit(18:12) Control Register Index (64 16-bit locations,
+        * addressed on even byte boundaries)
+        * Bit(11:0) Reserved (Stuffed with 0’s)
+        */
+       unsigned int reserved_3:12;
+       unsigned int cmdindex:7;
+       unsigned int cmdread:1;
+       unsigned int align_32_1:12;
+
+
+       /*
+        * Slot 2: Command Data Port
+        * The command data port is used to deliver 16-bit
+        * control register write data in the event that
+        * the current command port operation is a write
+        * cycle. (as indicated by Slot 1, bit 19)
+        * Bit(19:4) Control Register Write Data (Completed
+        * with 0’s if current operation is a read)
+        * Bit(3:0) Reserved (Completed with 0’s)
+        */
+       unsigned int reserved_4:4;
+       unsigned int cmddata:16;
+       unsigned int align_32_2:12;
+
+       unsigned int slots_data[10];
+}  __attribute__((__packed__));
+
+struct ac97_rx {
+       /*
+        * Slot 0: TAG
+        * Bit 15 Codec Ready
+        * Bit 14:3 Slot Valid (Which of slot 1 to slot 12 contain valid data)
+        * Bit 2:0 Zero
+        */
+       unsigned int reserved_2:4; /* Align the 16-bit Tag to 20-bit */
+       unsigned int reserved_1:3;
+       unsigned int slot_valid:12;
+       unsigned int valid:1;
+       unsigned int align_32_0:12;
+
+       /*
+        * Slot 1: Status Address
+        */
+       unsigned int reserved_4:2;
+       unsigned int slot_req:10;
+       unsigned int regindex:7;
+       unsigned int reserved_3:1;
+       unsigned int align_32_1:12;
+
+       /*
+        * Slot 2: Status Data
+        * Bit 19:4 Control Register Read Data (Completed with 0’s if tagged
+        * “invalid” by AC‘97)
+        * Bit 3:0 RESERVED (Completed with 0’s)
+        */
+       unsigned int reserved_5:4;
+       unsigned int cmddata:16;
+       unsigned int align_32_2:12;
+
+       unsigned int slots_data[10];
+}  __attribute__((__packed__));
+
+static void fsl_dma_tx_complete(void *arg)
+{
+       struct fsl_sai_ac97 *sai = arg;
+       struct ac97_tx *aclink;
+       struct imx_pcm_runtime_data *iprtd = sai->iprtd_playback;
+       int i = 0;
+       struct dma_tx_state state;
+       enum dma_status status;
+       int bufid;
+
+       async_tx_ack(sai->dma_tx_desc);
+
+       status = dmaengine_tx_status(sai->dma_tx_chan, sai->dma_tx_cookie, &state);
+
+       /* Calculate the id of the running buffer */
+       if (state.residue % SAI_AC97_RBUF_SIZE == 0)
+               bufid = 4 - (state.residue / SAI_AC97_RBUF_SIZE);
+       else
+               bufid = 3 - (state.residue / SAI_AC97_RBUF_SIZE);
+
+       /* Calculate the id of the next free buffer */
+       bufid = (bufid + 1) % 4;
+
+       /* First frame of the just completed buffer... */
+       aclink = (struct ac97_tx *)(sai->tx_buf.area + (bufid * SAI_AC97_RBUF_SIZE));
+
+       if (atomic_read(&info->playing))
+       {
+               struct snd_dma_buffer *buf = &iprtd->substream->dma_buffer;
+               u16 *ptr = (u16 *)(buf->area + iprtd->offset);
+
+               /* Copy samples of the PCM stream into PCM slots 3/4 */
+               for (i = 0; i < SAI_AC97_RBUF_FRAMES; i++) {
+
+                       aclink->valid = 1;
+                       aclink->slot_valid |= (1 << 9 | 1 << 8);
+                       aclink->slots_data[0] = ptr[i * 2];
+                       aclink->slots_data[0] <<= 4;
+                       aclink->slots_data[1] = ptr[i * 2 + 1];
+                       aclink->slots_data[1] <<= 4;
+                       aclink++;
+               }
+
+               iprtd->offset += SAI_AC97_RBUF_FRAMES * 4;
+               iprtd->offset %= (SAI_AC97_RBUF_FRAMES * 4 * SAI_AC97_RBUF_COUNT);
+               snd_pcm_period_elapsed(iprtd->substream);
+       }
+       else if (aclink->slot_valid & (1 << 9 | 1 << 8))
+       {
+               /* There is nothing playing anymore, clean the samples */
+               for (i = 0; i < SAI_AC97_RBUF_FRAMES; i++) {
+                       aclink->valid = 0;
+                       aclink->slot_valid &= ~(1 << 9 | 1 << 8);
+                       aclink->slots_data[0] = 0;
+                       aclink->slots_data[1] = 0;
+                       aclink++;
+               }
+       }
+}
+
+static void fsl_dma_rx_complete(void *arg)
+{
+       struct fsl_sai_ac97 *sai = arg;
+       struct ac97_rx *aclink;
+       struct imx_pcm_runtime_data *iprtd = sai->iprtd_capture;
+       struct dma_tx_state state;
+       enum dma_status status;
+       int bufid;
+       int i;
+
+       async_tx_ack(sai->dma_rx_desc);
+
+       status = dmaengine_tx_status(sai->dma_rx_chan, sai->dma_rx_cookie, &state);
+
+       /* Calculate the id of the running buffer */
+       if (state.residue % SAI_AC97_RBUF_SIZE == 0)
+               bufid = 4 - (state.residue / SAI_AC97_RBUF_SIZE);
+       else
+               bufid = 3 - (state.residue / SAI_AC97_RBUF_SIZE);
+
+       /* Calculate the id of the last processed buffer */
+       bufid = (bufid + 3) % 4;
+
+       /* First frame of the just completed buffer... */
+       aclink = (struct ac97_rx *)(sai->rx_buf.area + (bufid * SAI_AC97_RBUF_SIZE));
+
+       if (atomic_read(&info->capturing))
+       {
+               struct snd_dma_buffer *buf = &iprtd->substream->dma_buffer;
+               u16 *ptr = (u16 *)buf->area;
+
+               /*
+                * Loop through all AC97 frames, but only some might have data:
+                * Depending on bit rate, the valid flag might not be set for
+                * all frames (see AC97 VBR specification)
+                */
+               for (i = 0; i < SAI_AC97_RBUF_FRAMES; i++, aclink++) {
+                       if (!aclink->valid)
+                               continue;
+
+                       if (aclink->slot_valid & (1 << 9)) {
+                               ptr[iprtd->offset / 2] = aclink->slots_data[0] >> 4;
+                               iprtd->offset+=2;
+                       }
+
+                       if (aclink->slot_valid & (1 << 8)) {
+                               ptr[iprtd->offset / 2] = aclink->slots_data[1] >> 4;
+                               iprtd->offset+=2;
+                       }
+
+                       iprtd->offset %= (SAI_AC97_RBUF_FRAMES * 4 * SAI_AC97_RBUF_COUNT);
+               }
+
+               snd_pcm_period_elapsed(iprtd->substream);
+       }
+}
+
+static int vf610_sai_ac97_read_write(struct snd_ac97 *ac97, bool isread,
+                                  unsigned short reg, unsigned short *val)
+{
+       enum dma_status rx_status;
+       enum dma_status tx_status;
+       struct dma_tx_state tx_state;
+       struct dma_tx_state rx_state;
+       struct ac97_tx *tx_aclink;
+       struct ac97_rx *rx_aclink;
+       int rxbufidstart, txbufidstart, txbufid, rxbufid, curbufid;
+       unsigned long flags;
+       int ret = 0;
+       int rxbufmaxcheck = 10;
+       int timeout = 10;
+
+       /*
+        * We need to disable interrupts to make sure we insert the message
+        * before the next AC97 frame has been sent
+        */
+       local_irq_save(flags);
+       tx_status = dmaengine_tx_status(info->dma_tx_chan, info->dma_tx_cookie,
+                                       &tx_state);
+       rx_status = dmaengine_tx_status(info->dma_rx_chan, info->dma_rx_cookie,
+                                       &rx_state);
+
+       /* Calculate next DMA buffer sent out to the AC97 codec */
+       rxbufidstart = (SAI_AC97_RBUF_SIZE_TOT - rx_state.residue) / SAI_AC97_DMABUF_SIZE;
+       rxbufidstart %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+       txbufidstart = (SAI_AC97_RBUF_SIZE_TOT - tx_state.residue) / SAI_AC97_DMABUF_SIZE;
+       txbufidstart %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+
+       /* Safety margin, use next buffer in case current buffer is DMA'ed now */
+       txbufid = txbufidstart + 1;
+       txbufid %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+       tx_aclink = (struct ac97_tx *)(info->tx_buf.area + (txbufid * SAI_AC97_DMABUF_SIZE));
+
+       /* Put our request into the next AC97 frame */
+       tx_aclink->valid = 1;
+       tx_aclink->slot_valid |= (1 << 11);
+
+       tx_aclink->cmdread = isread;
+       tx_aclink->cmdindex = reg;
+
+       if (!isread) {
+               tx_aclink->slot_valid |= (1 << 10);
+               tx_aclink->cmddata = *val;
+       }
+
+       local_irq_restore(flags);
+
+       /* Wait at least until TX frame is in FIFO... */
+       if (!isread) {
+               do {
+                       usleep_range(50, 200);
+                       tx_status = dmaengine_tx_status(info->dma_tx_chan, info->dma_tx_cookie,
+                                       &tx_state);
+                       curbufid = ((SAI_AC97_RBUF_SIZE_TOT - tx_state.residue) / SAI_AC97_DMABUF_SIZE);
+
+                       if (likely(txbufid > txbufidstart) &&
+                           (curbufid > txbufid || curbufid < txbufidstart))
+                              break;
+
+                       /* Wrap-around case */
+                       if (unlikely(txbufid < txbufidstart) &&
+                           (curbufid > txbufid && curbufid < txbufidstart))
+                               break;
+               } while (--timeout);
+               ret = !timeout ? -ETIMEDOUT : 0;
+               goto clear_command;
+       }
+
+       /*
+        * Look into every frame starting at the RX frame which was
+        * last copied by DMA at command insert time. Typically, the
+        * answer is in RX start frame +4. Factors which sum up to
+        * this delay are:
+        * - TX send delay (+1 safety margin, +2 TX FIFO)
+        * - AC97 codec sends back the answer in the next frame (+1)
+        *
+        * TX ring buffer
+        * |------|------|------|------|------|------|------|------|
+        * |      |      |      |txbuf |txbuf |      |      |      |
+        * |      |      |      |start |      |      |      |      |
+        * |------|------|------|------|------|------|------|------|
+        *
+        * RX ring buffer
+        * |------|------|------|------|------|------|------|------|
+        * |      |rxbuf |      |      |      |rxbuf |      |      |
+        * |      |start |      |      |      |      |      |      |
+        * |------|------|------|------|------|------|------|------|
+        *
+        */
+       rxbufid = rxbufidstart;
+       curbufid = rxbufid;
+       do {
+               while (rxbufid == curbufid && --timeout)
+               {
+                       /* Wait for frames being transmitted/received... */
+                       usleep_range(50, 200);
+                       rx_status = dmaengine_tx_status(info->dma_rx_chan, info->dma_rx_cookie,
+                                                       &rx_state);
+                       curbufid = ((SAI_AC97_RBUF_SIZE_TOT - rx_state.residue) / SAI_AC97_DMABUF_SIZE);
+               }
+
+               if (!timeout) {
+                       ret = -ETIMEDOUT;
+                       goto clear_command;
+               }
+
+               /* Ok, check frames... */
+               rx_aclink = (struct ac97_rx *)(info->rx_buf.area + rxbufid * SAI_AC97_DMABUF_SIZE);
+               if (rx_aclink->slot_valid & (1 << 11 | 1 << 10) &&
+                       rx_aclink->regindex == reg)
+               {
+                       *val = rx_aclink->cmddata;
+                       break;
+               }
+
+               rxbufmaxcheck--;
+               rxbufid++;
+               rxbufid %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+       } while (rxbufmaxcheck);
+
+       if (!rxbufmaxcheck) {
+               pr_err("%s: rx timeout, checked buffer %d to %d, current %d\n",
+                               __func__, rxbufidstart, rxbufid, curbufid);
+               ret = -ETIMEDOUT;
+       }
+
+clear_command:
+       /* Clear sent command... */
+       tx_aclink->slot_valid &= ~(1 << 11 | 1 << 10);
+       tx_aclink->cmdread = 0;
+       tx_aclink->cmdindex = 0;
+       tx_aclink->cmddata = 0;
+
+       return ret;
+}
+
+static unsigned short vf610_sai_ac97_read(struct snd_ac97 *ac97,
+                                       unsigned short reg)
+{
+       unsigned short val = 0;
+       int err;
+
+       err = vf610_sai_ac97_read_write(ac97, true, reg, &val);
+       pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+
+       if (err)
+               pr_err("failed to read register 0x%02x\n", reg);
+
+       return val;
+}
+
+static void vf610_sai_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+                            unsigned short val)
+{
+       int err;
+
+       err = vf610_sai_ac97_read_write(ac97, false, reg, &val);
+       pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+
+       if (err)
+               pr_err("failed to write register 0x%02x\n", reg);
+}
+
+
+static struct snd_ac97_bus_ops fsl_sai_ac97_ops = {
+       .read   = vf610_sai_ac97_read,
+       .write  = vf610_sai_ac97_write,
+};
+
+static int fsl_sai_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai)
+{
+       pr_debug("%s, %d\n", __func__, substream->stream);
+
+       return 0;
+}
+
+static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai)
+{
+       pr_debug("%s, %d\n", __func__, substream->stream);
+}
+
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
+       //.set_sysclk   = fsl_sai_set_dai_sysclk,
+       //.set_fmt      = fsl_sai_set_dai_fmt,
+       //.hw_params    = fsl_sai_hw_params,
+       //.trigger      = fsl_sai_trigger,
+       .startup        = fsl_sai_startup,
+       .shutdown       = fsl_sai_shutdown,
+};
+
+static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+       struct fsl_sai_ac97 *sai = dev_get_drvdata(cpu_dai->dev);
+
+       snd_soc_dai_set_drvdata(cpu_dai, sai);
+
+       /*
+        * Mark DAI as active since we use it for AC97 control messages,
+        * otherwise snd_soc_register_card would request pinctrl state
+        * "sleep"...
+        */
+       cpu_dai->active++;
+
+       return 0;
+}
+
+static int fsl_sai_dai_remove(struct snd_soc_dai *cpu_dai)
+{
+       cpu_dai->active--;
+
+       return 0;
+}
+static struct snd_soc_dai_driver fsl_sai_ac97_dai = {
+       .name = "fsl-sai-ac97-pcm",
+       .bus_control = true,
+       .probe = fsl_sai_dai_probe,
+       .remove = fsl_sai_dai_remove,
+       .playback = {
+               .stream_name = "PCM Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "PCM Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &fsl_sai_pcm_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_component = {
+       .name           = "fsl-sai",
+};
+
+static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_TCR1:
+       case FSL_SAI_TCR2:
+       case FSL_SAI_TCR3:
+       case FSL_SAI_TCR4:
+       case FSL_SAI_TCR5:
+       case FSL_SAI_TFR:
+       case FSL_SAI_TMR:
+       case FSL_SAI_RCSR:
+       case FSL_SAI_RCR1:
+       case FSL_SAI_RCR2:
+       case FSL_SAI_RCR3:
+       case FSL_SAI_RCR4:
+       case FSL_SAI_RCR5:
+       case FSL_SAI_RDR:
+       case FSL_SAI_RFR:
+       case FSL_SAI_RMR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TFR:
+       case FSL_SAI_RFR:
+       case FSL_SAI_TDR:
+       case FSL_SAI_RDR:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
+static bool fsl_sai_precious_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_RDR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_TCR1:
+       case FSL_SAI_TCR2:
+       case FSL_SAI_TCR3:
+       case FSL_SAI_TCR4:
+       case FSL_SAI_TCR5:
+       case FSL_SAI_TDR:
+       case FSL_SAI_TMR:
+       case FSL_SAI_RCSR:
+       case FSL_SAI_RCR1:
+       case FSL_SAI_RCR2:
+       case FSL_SAI_RCR3:
+       case FSL_SAI_RCR4:
+       case FSL_SAI_RCR5:
+       case FSL_SAI_RMR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct regmap_config fsl_sai_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .max_register = FSL_SAI_RMR,
+       .precious_reg = fsl_sai_precious_reg,
+       .readable_reg = fsl_sai_readable_reg,
+       .volatile_reg = fsl_sai_volatile_reg,
+       .writeable_reg = fsl_sai_writeable_reg,
+       .cache_type = REGCACHE_FLAT,
+};
+
+static struct snd_pcm_hardware snd_sai_ac97_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_RESUME,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .buffer_bytes_max = SAI_AC97_RBUF_FRAMES * 4 * SAI_AC97_RBUF_COUNT,
+       .period_bytes_min = SAI_AC97_RBUF_FRAMES * 4,
+       .period_bytes_max = SAI_AC97_RBUF_FRAMES * 4,
+       .periods_min = SAI_AC97_RBUF_COUNT,
+       .periods_max = SAI_AC97_RBUF_COUNT,
+       .fifo_size = 0,
+};
+
+static int snd_fsl_sai_pcm_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+       iprtd->periods = params_periods(params);
+       iprtd->period = params_period_bytes(params);
+       iprtd->offset = 0;
+
+       pr_debug("%s: period %d, periods %d\n", __func__,
+               iprtd->period, iprtd->periods);
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       return 0;
+}
+
+static int snd_fsl_sai_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       pr_debug("%s:, %p, cmd %d\n", __func__, substream, cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       atomic_set(&info->playing, 1);
+               else
+                       atomic_set(&info->capturing, 1);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       atomic_set(&info->playing, 0);
+               else
+                       atomic_set(&info->capturing, 0);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t snd_fsl_sai_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+       return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static int snd_fsl_sai_pcm_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret = 0;
+
+       ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
+               runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+
+       return ret;
+}
+
+static int snd_fsl_sai_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       pr_debug("%s, %p\n", __func__, substream);
+       return 0;
+}
+
+static int snd_fsl_sai_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct imx_pcm_runtime_data *iprtd;
+       int ret;
+
+       iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+
+       if (iprtd == NULL)
+               return -ENOMEM;
+
+       runtime->private_data = iprtd;
+       iprtd->substream = substream;
+
+       ret = snd_pcm_hw_constraint_integer(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               kfree(iprtd);
+               return ret;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               atomic_set(&info->playing, 0);
+               info->iprtd_playback = iprtd;
+       } else {
+               atomic_set(&info->capturing, 0);
+               info->iprtd_capture = iprtd;
+       }
+
+       snd_soc_set_runtime_hwparams(substream, &snd_sai_ac97_hardware);
+
+       return 0;
+}
+
+static int snd_fsl_sai_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               info->iprtd_playback = NULL;
+       else
+               info->iprtd_capture = NULL;
+
+       kfree(iprtd);
+
+       return 0;
+}
+
+static struct snd_pcm_ops fsl_sai_pcm_ops = {
+       .open           = snd_fsl_sai_open,
+       .close          = snd_fsl_sai_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = snd_fsl_sai_pcm_hw_params,
+       .prepare        = snd_fsl_sai_pcm_prepare,
+       .trigger        = snd_fsl_sai_pcm_trigger,
+       .pointer        = snd_fsl_sai_pcm_pointer,
+       .mmap           = snd_fsl_sai_pcm_mmap,
+};
+
+static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+       /* Allocate for buffers, 16-Bit stereo data.. */
+       size_t size = SAI_AC97_RBUF_FRAMES * 4 * SAI_AC97_RBUF_COUNT;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+                                          &buf->addr, GFP_KERNEL);
+       if (!buf->area)
+               return -ENOMEM;
+       buf->bytes = size;
+
+       return 0;
+}
+
+static int fsl_sai_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_pcm *pcm = rtd->pcm;
+       struct snd_card *card = rtd->card->snd_card;
+       int ret;
+
+       ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+       if (ret)
+               return ret;
+
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               ret = imx_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       return ret;
+       }
+
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+               ret = imx_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       return ret;
+       }
+
+       pr_debug("%s, %p\n", __func__, pcm);
+
+       return 0;
+}
+
+static void fsl_sai_pcm_free(struct snd_pcm *pcm)
+{
+       pr_debug("%s, %p\n", __func__, pcm);
+}
+
+static struct snd_soc_platform_driver ac97_software_pcm_platform = {
+       .ops            = &fsl_sai_pcm_ops,
+       .pcm_new        = fsl_sai_pcm_new,
+       .pcm_free       = fsl_sai_pcm_free,
+};
+
+
+static int fsl_sai_ac97_prepare_tx_dma(struct fsl_sai_ac97 *sai)
+{
+       struct dma_slave_config dma_tx_sconfig;
+       int ret;
+
+       dma_tx_sconfig.dst_addr = sai->mapbase + FSL_SAI_TDR;
+       dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       dma_tx_sconfig.dst_maxburst = 13;
+       dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+       ret = dmaengine_slave_config(sai->dma_tx_chan, &dma_tx_sconfig);
+       if (ret < 0) {
+               dev_err(&sai->pdev->dev,
+                               "DMA slave config failed, err = %d\n", ret);
+               dma_release_channel(sai->dma_tx_chan);
+               return ret;
+       }
+       sai->dma_tx_desc = dmaengine_prep_dma_cyclic(sai->dma_tx_chan,
+                       sai->tx_buf.addr, SAI_AC97_RBUF_SIZE_TOT,
+                       SAI_AC97_RBUF_SIZE, DMA_MEM_TO_DEV,
+                       DMA_PREP_INTERRUPT);
+       sai->dma_tx_desc->callback = fsl_dma_tx_complete;
+       sai->dma_tx_desc->callback_param = sai;
+
+       return 0;
+};
+
+static int fsl_sai_ac97_prepare_rx_dma(struct fsl_sai_ac97 *sai)
+{
+       struct dma_slave_config dma_rx_sconfig;
+       int ret;
+
+       dma_rx_sconfig.src_addr = sai->mapbase + FSL_SAI_RDR;
+       dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       dma_rx_sconfig.src_maxburst = 13;
+       dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+       ret = dmaengine_slave_config(sai->dma_rx_chan, &dma_rx_sconfig);
+       if (ret < 0) {
+               dev_err(&sai->pdev->dev,
+                               "DMA slave config failed, err = %d\n", ret);
+               dma_release_channel(sai->dma_rx_chan);
+               return ret;
+       }
+       sai->dma_rx_desc = dmaengine_prep_dma_cyclic(sai->dma_rx_chan,
+                       sai->rx_buf.addr, SAI_AC97_RBUF_SIZE_TOT,
+                       SAI_AC97_RBUF_SIZE, DMA_DEV_TO_MEM,
+                       DMA_PREP_INTERRUPT);
+       sai->dma_rx_desc->callback = fsl_dma_rx_complete;
+       sai->dma_rx_desc->callback_param = sai;
+
+       return 0;
+};
+
+static void fsl_sai_ac97_reset_sai(struct fsl_sai_ac97 *sai)
+{
+       /* TX */
+       /* Issue software reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR,
+                          FSL_SAI_CSR_SR);
+
+       udelay(2);
+       /* Release software reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR, 0);
+
+       /* FIFO reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_FR,
+                          FSL_SAI_CSR_FR);
+
+       /* RX */
+       /* Issue software reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR,
+                          FSL_SAI_CSR_SR);
+
+       udelay(2);
+       /* Release software reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR, 0);
+
+       /* FIFO reset */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_FR,
+                          FSL_SAI_CSR_FR);
+};
+
+static int fsl_sai_ac97_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct fsl_sai_ac97 *sai;
+       struct resource *res;
+       void __iomem *base;
+       char tmp[8];
+       int ret, i;
+
+       sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+       if (!sai)
+               return -ENOMEM;
+
+       info = sai;
+       sai->pdev = pdev;
+
+       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+               sai->sai_on_imx = true;
+
+       sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
+       if (sai->big_endian_regs)
+               fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
+       sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       sai->mapbase = res->start;
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                       "bus", base, &fsl_sai_regmap_config);
+
+       /* Compatible with old DTB cases */
+       if (IS_ERR(sai->regmap))
+               sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                               "sai", base, &fsl_sai_regmap_config);
+       if (IS_ERR(sai->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               return PTR_ERR(sai->regmap);
+       }
+
+       /* No error out for old DTB cases but only mark the clock NULL */
+       sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(sai->bus_clk)) {
+               dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+                               PTR_ERR(sai->bus_clk));
+               sai->bus_clk = NULL;
+       }
+
+       ret = clk_prepare_enable(sai->bus_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable bus clk: %d\n", ret);
+               return ret;
+       }
+
+       sai->mclk_clk[0] = sai->bus_clk;
+       for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
+               sprintf(tmp, "mclk%d", i);
+               sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+               if (IS_ERR(sai->mclk_clk[i])) {
+                       dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
+                                       i, PTR_ERR(sai->mclk_clk[i]));
+                       sai->mclk_clk[i] = NULL;
+               }
+       }
+
+       ret = snd_soc_set_ac97_ops_of_reset(&fsl_sai_ac97_ops, pdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to reset AC97 link: %d\n", ret);
+               goto err_disable_clock;
+       }
+
+       mutex_init(&info->lock);
+
+       /* clear transmit/receive configuration/status registers */
+       regmap_write(sai->regmap, FSL_SAI_TCSR, 0x0);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, 0x0);
+
+       /* pre allocate DMA buffers */
+       sai->tx_buf.dev.type = SNDRV_DMA_TYPE_DEV;
+       sai->tx_buf.dev.dev = &pdev->dev;
+       sai->tx_buf.private_data = NULL;
+       sai->tx_buf.area = dma_alloc_writecombine(&pdev->dev, SAI_AC97_RBUF_SIZE_TOT,
+                                          &sai->tx_buf.addr, GFP_KERNEL);
+       if (!sai->tx_buf.area) {
+               ret = -ENOMEM;
+               //goto failed_tx_buf;
+               return ret;
+       }
+       sai->tx_buf.bytes = SAI_AC97_RBUF_SIZE_TOT;
+
+       sai->rx_buf.dev.type = SNDRV_DMA_TYPE_DEV;
+       sai->rx_buf.dev.dev = &pdev->dev;
+       sai->rx_buf.private_data = NULL;
+       sai->rx_buf.area = dma_alloc_writecombine(&pdev->dev, SAI_AC97_RBUF_SIZE_TOT,
+                                          &sai->rx_buf.addr, GFP_KERNEL);
+       if (!sai->rx_buf.area) {
+               ret = -ENOMEM;
+               //goto failed_rx_buf;
+               return ret;
+       }
+       sai->rx_buf.bytes = SAI_AC97_RBUF_SIZE_TOT;
+
+       memset(sai->tx_buf.area, 0, SAI_AC97_RBUF_SIZE_TOT);
+       memset(sai->rx_buf.area, 0, SAI_AC97_RBUF_SIZE_TOT);
+
+       /* 1. Configuration of SAI clock mode */
+
+       /*
+        * Issue software reset and FIFO reset for Transmitter and Receiver
+        * sections before starting configuration.
+        */
+       fsl_sai_ac97_reset_sai(sai);
+
+       /* Configure FIFO watermark. FIFO watermark is used as an indicator for
+          DMA trigger when read or write data from/to FIFOs. */
+       /* Watermark level for all enabled transmit channels of one SAI module.
+        */
+       regmap_write(sai->regmap, FSL_SAI_TCR1, 13);
+       regmap_write(sai->regmap, FSL_SAI_RCR1, 13);
+
+       /* Configure the clocking mode, bitclock polarity, direction, and
+          divider. Clocking mode defines synchronous or asynchronous operation
+          for SAI module. Bitclock polarity configures polarity of the
+          bitclock. Bitclock direction configures direction of the bitclock.
+          Bus master has bitclock generated externally, slave has bitclock
+          generated internally */
+
+       /* TX */
+       /* The transmitter must be configured for asynchronous operation */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC_MASK, 0);
+
+       /* bit clock not swapped */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_BCS, 0);
+
+       /* Bitclock is active high (drive outputs on rising edge and sample
+        * inputs on falling edge
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_BCP, 0);
+
+       /* Bitclock is generated externally (Slave mode) */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_BCD_MSTR, 0);
+
+       /* RX */
+       /* The receiver must be configured for synchronous operation. */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC_MASK,
+                          FSL_SAI_CR2_SYNC);
+
+       /* bit clock not swapped */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_BCS, 0);
+
+       /* Bitclock is active high (drive outputs on rising edge and sample
+        * inputs on falling edge
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_BCP, 0);
+
+       /* Bitclock is generated externally (Slave mode) */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_BCD_MSTR, 0);
+
+       /* Configure frame size, frame sync width, MSB first, frame sync early,
+          polarity, and direction
+          Frame size – configures the number of words in each frame. AC97
+          requires 13 words per frame.
+          Frame sync width – configures the length of the frame sync in number
+          of bitclock. The sync width cannot be longer than the first word of
+          the frame. AC97 requires frame sync asserted for first word. */
+
+       /* Configures number of words in each frame. The value written should be
+        * one less than the number of words in the frame (part of define!)
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_FRSZ_MASK,
+                          FSL_SAI_CR4_FRSZ(13));
+
+       /* Configures length of the frame sync. The value written should be one
+        * less than the number of bitclocks.
+        * AC97 - 16 bits transmitted in first word.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_SYWD_MASK,
+                          FSL_SAI_CR4_SYWD(16));
+
+
+       /* MSB is transmitted first */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_MF,
+                          FSL_SAI_CR4_MF);
+
+       /* Frame sync asserted one bit before the first bit of the frame */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_FSE,
+                          FSL_SAI_CR4_FSE);
+
+       /* A new AC-link input frame begins with a low to high transition of
+        * SYNC. Frame sync is active high
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_FSP, 0);
+
+       /* Frame sync is generated internally (Master mode) */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR4, FSL_SAI_CR4_FSD_MSTR,
+                          FSL_SAI_CR4_FSD_MSTR);
+
+       /* RX */
+       /* Configures number of words in each frame. The value written should be
+        * one less than the number of words in the frame (part of define!)
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_FRSZ_MASK,
+                          FSL_SAI_CR4_FRSZ(13));
+
+       /* Configures length of the frame sync. The value written should be one
+        * less than the number of bitclocks.
+        * AC97 - 16 bits transmitted in first word.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_SYWD_MASK,
+                          FSL_SAI_CR4_SYWD(16));
+
+
+       /* MSB is transmitted first */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_MF,
+                          FSL_SAI_CR4_MF);
+
+       /* Frame sync asserted one bit before the first bit of the frame */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_FSE,
+                          FSL_SAI_CR4_FSE);
+
+       /* A new AC-link input frame begins with a low to high transition of
+        * SYNC. Frame sync is active high
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_FSP, 0);
+
+       /* Frame sync is generated internally (Master mode) */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR4, FSL_SAI_CR4_FSD_MSTR,
+                          FSL_SAI_CR4_FSD_MSTR);
+
+       /* Configure the Word 0 and next word sizes.
+          W0W – defines number of bits in the first word in each frame.
+          WNW – defines number of bits in each word for each word except the
+          first in the frame. */
+
+       /* TX */
+       /* Number of bits in first word in each frame. AC97 – 16-bit word is
+        * transmitted.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR5, FSL_SAI_CR5_W0W_MASK,
+                          FSL_SAI_CR5_W0W(16));
+
+       /* Number of bits in each word in each frame. AC97 – 20-bit word is
+        * transmitted.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR5, FSL_SAI_CR5_WNW_MASK,
+                          FSL_SAI_CR5_WNW(20));
+
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR5, FSL_SAI_CR5_W0W_MASK,
+                          FSL_SAI_CR5_W0W(16));
+
+       /* Configures the bit index for the first bit transmitted for each word
+        * in the frame. The value written must be greater than or equal to the
+        * word width when configured for MSB First.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR5, FSL_SAI_CR5_FBT_MASK,
+                          FSL_SAI_CR5_FBT(20));
+
+       /* RX */
+       /* Number of bits in first word in each frame. AC97 – 16-bit word is
+        * transmitted.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR5, FSL_SAI_CR5_W0W_MASK,
+                          FSL_SAI_CR5_W0W(16));
+
+       /* Number of bits in each word in each frame. AC97 – 20-bit word is
+        * transmitted.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR5, FSL_SAI_CR5_WNW_MASK,
+                          FSL_SAI_CR5_WNW(20));
+
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR5, FSL_SAI_CR5_W0W_MASK,
+                          FSL_SAI_CR5_W0W(16));
+
+       /* Configures the bit index for the first bit transmitted for each word
+        * in the frame. The value written must be greater than or equal to the
+        * word width when configured for MSB First.
+        */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR5, FSL_SAI_CR5_FBT_MASK,
+                          FSL_SAI_CR5_FBT(20));
+
+
+       /* Clear the Transmit and Receive Mask registers. */
+       regmap_write(sai->regmap, FSL_SAI_TMR, 0);
+       regmap_write(sai->regmap, FSL_SAI_RMR, 0);
+
+
+       sai->dma_tx_chan = dma_request_slave_channel(&pdev->dev, "tx");
+       if (!sai->dma_tx_chan) {
+               dev_err(&pdev->dev, "DMA tx channel request failed!\n");
+               return -ENODEV;
+       }
+
+       sai->dma_rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
+       if (!sai->dma_rx_chan) {
+               dev_err(&pdev->dev, "DMA rx channel request failed!\n");
+               return -ENODEV;
+       }
+
+       /* Enables a data channel for a transmit operation. */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR3, FSL_SAI_CR3_TRCE,
+                          FSL_SAI_CR3_TRCE);
+
+       /* Enables a data channel for a receive operation. */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCR3, FSL_SAI_CR3_TRCE,
+                          FSL_SAI_CR3_TRCE);
+
+
+       /* In synchronous mode, receiver is enabled only when both transmitter
+          and receiver are enabled. It is recommended that transmitter is
+          enabled last and disabled first. */
+       /* Enable receiver */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE);
+
+       /* Enable transmitter */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE);
+
+       platform_set_drvdata(pdev, sai);
+
+       ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
+                       &fsl_sai_ac97_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register Component: %d\n", ret);
+               goto err_disable_clock;
+       }
+
+       /* Register our own PCM device, which fills the AC97 frames... */
+       snd_soc_add_platform(&pdev->dev, &sai->platform, &ac97_software_pcm_platform);
+
+       /* Start the DMA engine */
+       fsl_sai_ac97_prepare_tx_dma(sai);
+       fsl_sai_ac97_prepare_rx_dma(sai);
+
+       sai->dma_tx_cookie = dmaengine_submit(sai->dma_tx_desc);
+       dma_async_issue_pending(sai->dma_tx_chan);
+
+       sai->dma_rx_cookie = dmaengine_submit(sai->dma_rx_desc);
+       dma_async_issue_pending(sai->dma_rx_chan);
+
+       return 0;
+
+err_disable_clock:
+       clk_disable_unprepare(sai->bus_clk);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_ac97_suspend(struct device *dev)
+{
+       struct fsl_sai_ac97 *sai = dev_get_drvdata(dev);
+
+       /* Disable receiver/transmitter */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE, 0x0);
+
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE, 0x0);
+
+       dmaengine_terminate_all(sai->dma_tx_chan);
+       dmaengine_terminate_all(sai->dma_rx_chan);
+
+       regcache_cache_only(sai->regmap, true);
+
+       return 0;
+}
+
+static int fsl_sai_ac97_resume(struct device *dev)
+{
+       struct fsl_sai_ac97 *sai = dev_get_drvdata(dev);
+
+       regcache_mark_dirty(sai->regmap);
+       regcache_cache_only(sai->regmap, false);
+       regcache_sync(sai->regmap);
+
+       /* Reset SAI */
+       fsl_sai_ac97_reset_sai(sai);
+
+       /* Enable receiver */
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE);
+
+       /* Enable transmitter */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE,
+                          FSL_SAI_CSR_FRDE | FSL_SAI_CSR_TERE);
+
+       /* Restart the DMA engine */
+       fsl_sai_ac97_prepare_tx_dma(sai);
+       fsl_sai_ac97_prepare_rx_dma(sai);
+
+       sai->dma_tx_cookie = dmaengine_submit(sai->dma_tx_desc);
+       dma_async_issue_pending(sai->dma_tx_chan);
+
+       sai->dma_rx_cookie = dmaengine_submit(sai->dma_rx_desc);
+       dma_async_issue_pending(sai->dma_rx_chan);
+
+       return 0;
+}
+#else
+#define fsl_sai_ac97_suspend   NULL
+#define fsl_sai_ac97_resume    NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_ac97_pm = {
+       .suspend = fsl_sai_ac97_suspend,
+       .resume = fsl_sai_ac97_resume,
+};
+static const struct of_device_id fsl_sai_ac97_ids[] = {
+       { .compatible = "fsl,vf610-sai-ac97", },
+       { /* sentinel */ }
+};
+
+static struct platform_driver fsl_sai_ac97_driver = {
+       .probe = fsl_sai_ac97_probe,
+       .driver = {
+               .name = "fsl-sai-ac97",
+               .owner = THIS_MODULE,
+               .of_match_table = fsl_sai_ac97_ids,
+               .pm = &fsl_sai_ac97_pm,
+       },
+};
+module_platform_driver(fsl_sai_ac97_driver);
+
+MODULE_DESCRIPTION("Freescale SoC SAI AC97 Interface");
+MODULE_AUTHOR("Stefan Agner, Marcel Ziswiler");
+MODULE_ALIAS("platform:fsl-sai-ac97");
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/fsl/fsl_sai_clk.c b/sound/soc/fsl/fsl_sai_clk.c
new file mode 100644 (file)
index 0000000..1d0b4cb
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Freescale SAI driver to use SAI as a clock source
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2015-2016 Toradex AG
+ *
+ * This program is free software, you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or(at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_sai.h"
+
+static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_TCR1:
+       case FSL_SAI_TCR2:
+       case FSL_SAI_TCR3:
+       case FSL_SAI_TCR4:
+       case FSL_SAI_TCR5:
+       case FSL_SAI_TFR:
+       case FSL_SAI_TMR:
+       case FSL_SAI_RCSR:
+       case FSL_SAI_RCR1:
+       case FSL_SAI_RCR2:
+       case FSL_SAI_RCR3:
+       case FSL_SAI_RCR4:
+       case FSL_SAI_RCR5:
+       case FSL_SAI_RDR:
+       case FSL_SAI_RFR:
+       case FSL_SAI_RMR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TFR:
+       case FSL_SAI_RFR:
+       case FSL_SAI_TDR:
+       case FSL_SAI_RDR:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
+static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_TCR1:
+       case FSL_SAI_TCR2:
+       case FSL_SAI_TCR3:
+       case FSL_SAI_TCR4:
+       case FSL_SAI_TCR5:
+       case FSL_SAI_TDR:
+       case FSL_SAI_TMR:
+       case FSL_SAI_RCSR:
+       case FSL_SAI_RCR1:
+       case FSL_SAI_RCR2:
+       case FSL_SAI_RCR3:
+       case FSL_SAI_RCR4:
+       case FSL_SAI_RCR5:
+       case FSL_SAI_RMR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct regmap_config fsl_sai_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .max_register = FSL_SAI_RMR,
+       .readable_reg = fsl_sai_readable_reg,
+       .volatile_reg = fsl_sai_volatile_reg,
+       .writeable_reg = fsl_sai_writeable_reg,
+       .cache_type = REGCACHE_FLAT,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_clk_suspend(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       /* disable AC97 master clock */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_BCE, 0);
+
+       regcache_cache_only(sai->regmap, true);
+
+       clk_disable_unprepare(sai->mclk_clk[1]);
+       clk_disable_unprepare(sai->bus_clk);
+
+       return 0;
+}
+
+static int fsl_sai_clk_resume(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       clk_prepare_enable(sai->bus_clk);
+       clk_prepare_enable(sai->mclk_clk[1]);
+
+       regcache_mark_dirty(sai->regmap);
+       regcache_cache_only(sai->regmap, false);
+       regcache_sync(sai->regmap);
+
+       /* enable AC97 master clock */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_BCE,
+                          FSL_SAI_CSR_BCE);
+
+       return 0;
+}
+#else
+#define fsl_sai_clk_suspend    NULL
+#define fsl_sai_clk_resume     NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_clk_pm = {
+       .suspend_late = fsl_sai_clk_suspend,
+       .resume_early = fsl_sai_clk_resume,
+};
+
+static int fsl_sai_clk_probe(struct platform_device *pdev)
+{
+       struct fsl_sai *sai;
+       struct resource *res;
+       void __iomem *base;
+       char tmp[8];
+       int ret;
+       int i;
+
+       sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+       if (!sai)
+               return -ENOMEM;
+
+       sai->pdev = pdev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                       "bus", base, &fsl_sai_regmap_config);
+
+       /* Compatible with old DTB cases */
+       if (IS_ERR(sai->regmap))
+               sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                               "sai", base, &fsl_sai_regmap_config);
+       if (IS_ERR(sai->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               return PTR_ERR(sai->regmap);
+       }
+
+       /* No error out for old DTB cases but only mark the clock NULL */
+       sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(sai->bus_clk)) {
+               dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+                               PTR_ERR(sai->bus_clk));
+               sai->bus_clk = NULL;
+       }
+
+       sai->mclk_clk[0] = sai->bus_clk;
+       for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
+               sprintf(tmp, "mclk%d", i);
+               sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+               if (IS_ERR(sai->mclk_clk[i])) {
+                       dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
+                                       i, PTR_ERR(sai->mclk_clk[i]));
+                       sai->mclk_clk[i] = NULL;
+               }
+       }
+
+       ret = clk_prepare_enable(sai->bus_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable bus clk: %d\n", ret);
+               return ret;
+       }
+
+       /* configure AC97 master clock */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+                          ~FSL_SAI_CR2_SYNC);
+
+
+       /* asynchronous aka independent operation */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC_MASK, 0);
+
+       /* Bit clock not swapped */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_BCS, 0);
+
+       /* Clock selected by CCM_CSCMR1[SAIn_CLK_SEL] */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_MSEL_MASK,
+                          FSL_SAI_CR2_MSEL_MCLK1);
+
+       /* Bitclock is generated internally (master mode) */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_BCD_MSTR,
+                          FSL_SAI_CR2_BCD_MSTR);
+
+       /* Divide by 6 */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_DIV_MASK,
+                          FSL_SAI_CR2_DIV(2));
+
+       ret = clk_prepare_enable(sai->mclk_clk[1]);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable mclk1: %d\n", ret);
+               return ret;
+       }
+
+       /* enable AC97 master clock */
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_BCE,
+                          FSL_SAI_CSR_BCE);
+
+       platform_set_drvdata(pdev, sai);
+
+       return 0;
+}
+
+static const struct of_device_id fsl_sai_ids[] = {
+       { .compatible = "fsl,vf610-sai-clk", },
+       { /* sentinel */ }
+};
+
+static struct platform_driver fsl_sai_driver = {
+       .probe = fsl_sai_clk_probe,
+       .driver = {
+               .name = "fsl-sai-clk",
+               .owner = THIS_MODULE,
+               .of_match_table = fsl_sai_ids,
+               .pm = &fsl_sai_clk_pm,
+       },
+};
+module_platform_driver(fsl_sai_driver);
+
+MODULE_DESCRIPTION("Freescale SoC SAI as clock generator");
+MODULE_AUTHOR("Stefan Agner, <stefan.agner@toradex.com>");
+MODULE_ALIAS("platform:fsl-sai-clk");
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/fsl/fsl_sai_wm9712.c b/sound/soc/fsl/fsl_sai_wm9712.c
new file mode 100644 (file)
index 0000000..617c5ab
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * fsl_sai_wm9712.c  --  SoC audio for Freescale SAI
+ *
+ * Copyright 2014 Stefan Agner, Toradex AG <stefan@agner.ch>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "fsl-sai-ac97-dt-driver"
+
+static struct snd_soc_dai_link fsl_sai_wm9712_dai = {
+       .name = "AC97 HiFi",
+       .stream_name = "AC97 HiFi",
+       .codec_dai_name = "wm9712-hifi",
+       .codec_name = "wm9712-codec",
+};
+
+static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_LINE("LineIn", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+
+static struct snd_soc_card snd_soc_card_fsl_sai_wm9712 = {
+       .name = "Colibri VF61 AC97 Audio",
+       .owner = THIS_MODULE,
+       .dai_link = &fsl_sai_wm9712_dai,
+       .num_links = 1,
+
+       .dapm_widgets = tegra_wm9712_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets),
+       .fully_routed = true,
+};
+
+static struct platform_device *codec;
+
+static int fsl_sai_wm9712_driver_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &snd_soc_card_fsl_sai_wm9712;
+       int ret;
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+
+       codec = platform_device_alloc("wm9712-codec", -1);
+       if (!codec)
+               return -ENOMEM;
+
+       ret = platform_device_add(codec);
+       if (ret)
+               goto codec_put;
+
+       ret = snd_soc_of_parse_card_name(card, "fsl,model");
+       if (ret)
+               goto codec_unregister;
+
+       ret = snd_soc_of_parse_audio_routing(card, "fsl,audio-routing");
+       if (ret)
+               goto codec_unregister;
+
+       fsl_sai_wm9712_dai.cpu_of_node = of_parse_phandle(np,
+                                      "fsl,ac97-controller", 0);
+       if (!fsl_sai_wm9712_dai.cpu_of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'fsl,ac97-controller' missing or invalid\n");
+               ret = -EINVAL;
+               goto codec_unregister;
+       }
+
+       fsl_sai_wm9712_dai.platform_of_node = fsl_sai_wm9712_dai.cpu_of_node;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+               goto codec_unregister;
+       }
+
+       return 0;
+
+codec_unregister:
+       platform_device_del(codec);
+
+codec_put:
+       platform_device_put(codec);
+       return ret;
+}
+
+static int fsl_sai_wm9712_driver_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       platform_device_unregister(codec);
+
+       return 0;
+}
+
+static const struct of_device_id fsl_sai_wm9712_of_match[] = {
+       { .compatible = "fsl,fsl-sai-audio-wm9712", },
+       {},
+};
+
+static struct platform_driver fsl_sai_wm9712_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = fsl_sai_wm9712_of_match,
+       },
+       .probe = fsl_sai_wm9712_driver_probe,
+       .remove = fsl_sai_wm9712_driver_remove,
+};
+module_platform_driver(fsl_sai_wm9712_driver);
+
+/* Module information */
+MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
+MODULE_DESCRIPTION("ALSA SoC Freescale SAI+WM9712");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, fsl_sai_wm9712_of_match);