diff options
author | Ke Qinghua <qinghua.ke@freescale.com> | 2014-07-01 11:28:51 +0800 |
---|---|---|
committer | Ke Qinghua <qinghua.ke@freescale.com> | 2014-07-01 11:47:26 +0800 |
commit | d5800fcb9c0a07366c920414326ecb61d362b7b1 (patch) | |
tree | 686d7f7c5291fae49b48dbc95355e06d0c42d945 | |
parent | ffa6a894db981abaed4e023cb2b198cc22e68f0f (diff) | |
parent | 58ad81506e01e6a9625bc096595113f1f6bbe521 (diff) |
Merge remote-tracking branch 'remotes/fsl-linux-sdk/imx_3.10.31_1.1.0_beta' into imx_3.10.y_android
Conflicts:
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
arch/arm/boot/dts/imx6qdl-sabresd.dtsi
drivers/hwmon/Makefile
drivers/hwmon/mma8x5x.c
drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c
drivers/usb/chipidea/otg_fsm.c
drivers/usb/chipidea/udc.c
drivers/usb/gadget/udc-core.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
64 files changed, 2038 insertions, 786 deletions
diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index 622e5ac90d28..845cc69bc9b7 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -45,8 +45,6 @@ clocks and IDs. gpu3d_shader_sel 30 ipu1_sel 31 ipu2_sel 32 - ldb_di0_sel 33 - ldb_di1_sel 34 ipu1_di0_pre_sel 35 ipu1_di1_pre_sel 36 ipu2_di0_pre_sel 37 @@ -240,6 +238,7 @@ clocks and IDs. asrc_mem 227 esai_ipg 228 esai_mem 229 + axi_alt_sel 230 Examples: diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt index 995c8bca40e2..b2b93b401710 100644 --- a/Documentation/usb/chipidea.txt +++ b/Documentation/usb/chipidea.txt @@ -26,14 +26,13 @@ cat /sys/kernel/debug/ci_hdrc.0/registers On B-device: echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req - if HNP polling is not supported, also need: - On A-device: - echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req - B-device should take host role and enumrate A-device. 4) A-device switch back to host. - On B-device: + On A-device: + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req + + or, on B-device: echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req A-device should switch back to host and enumrate B-device. @@ -1023,7 +1023,7 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \ Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ signing_key.priv signing_key.x509 x509.genkey \ extra_certificates signing_key.x509.keyid \ - signing_key.x509.signer + signing_key.x509.signer include/linux/version.h # clean - Delete most, but leave enough to build external modules # diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index e2bedf6d8917..511a1544a60d 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -56,9 +56,9 @@ busfreq { /* BUSFREQ */ compatible = "fsl,imx6_busfreq"; clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, - <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>, <&clks 22> , <&clks 8>; + <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>, <&clks 230>, <&clks 22> , <&clks 8>; clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", - "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "axi_sel", "pll3_pfd1_540m"; + "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "axi_alt_sel", "axi_sel", "pll3_pfd1_540m"; interrupts = <0 107 0x04>, <0 112 0x4>; interrupt-names = "irq_busfreq_0", "irq_busfreq_1"; fsl,max_ddr_freq = <400000000>; diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 5e2c044a2a16..fd5d0b31e1c4 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -12,7 +12,6 @@ / { aliases { - mmc0 = &usdhc3; mxcfb0 = &mxcfb1; mxcfb1 = &mxcfb2; mxcfb2 = &mxcfb3; @@ -492,12 +491,13 @@ #gpio-cells = <2>; }; - mma8x5x@1c { - compatible = "fsl,mma8x5x"; + mma8451@1c { + compatible = "fsl,mma8451"; reg = <0x1c>; position = <7>; interrupt-parent = <&gpio6>; - interrupts = <31 1>; + interrupts = <31 8>; + interrupt-route = <1>; }; mag3110@0e { @@ -743,17 +743,17 @@ }; }; &caam_sm { - status = "disable"; + status = "disabled"; }; &irq_sec_vio { - status = "disable"; + status = "disabled"; }; &caam_snvs { - status = "disable"; + status = "disabled"; }; &crypto { - status = "disable"; + status = "disabled"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index 79ce6b266153..1a9ab935c0cf 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -12,8 +12,6 @@ / { aliases { - mmc0 = &usdhc4; - mmc1 = &usdhc3; mxcfb0 = &mxcfb1; mxcfb1 = &mxcfb2; mxcfb2 = &mxcfb3; @@ -378,14 +376,15 @@ mclk_source = <0>; }; - mma8x5x@1c { - compatible = "fsl,mma8x5x"; + mma8451@1c { + compatible = "fsl,mma8451"; reg = <0x1c>; position = <0>; vdd-supply = <®_sensor>; vddio-supply = <®_sensor>; interrupt-parent = <&gpio1>; - interrupts = <18 1>; + interrupts = <18 8>; + interrupt-route = <1>; }; }; @@ -674,17 +673,17 @@ }; &caam_sm { - status = "disable"; + status = "disabled"; }; &irq_sec_vio { - status = "disable"; + status = "disabled"; }; &caam_snvs { - status = "disable"; + status = "disabled"; }; &crypto { - status = "disable"; + status = "disabled"; }; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index a0f0b7300a2c..cecc6c298c06 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -23,6 +23,10 @@ gpio5 = &gpio6; gpio6 = &gpio7; ipu0 = &ipu1; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + mmc3 = &usdhc4; serial0 = &uart1; serial1 = &uart2; serial2 = &uart3; diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts index ea28a4645e60..bf67dae099da 100644 --- a/arch/arm/boot/dts/imx6sl-evk.dts +++ b/arch/arm/boot/dts/imx6sl-evk.dts @@ -14,9 +14,6 @@ / { model = "Freescale i.MX6 SoloLite EVK Board(PFUZE100)"; compatible = "fsl,imx6sl-evk", "fsl,imx6sl"; - aliases { - mmc0 = &usdhc2; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index c5f6bae9a5f3..c4068b507773 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -13,6 +13,10 @@ / { aliases { + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + mmc3 = &usdhc4; serial0 = &uart1; serial1 = &uart2; serial2 = &uart3; diff --git a/arch/arm/boot/dts/imx6sx-19x19-arm2.dts b/arch/arm/boot/dts/imx6sx-19x19-arm2.dts index 2d2687ad4d3a..131f330decfe 100644 --- a/arch/arm/boot/dts/imx6sx-19x19-arm2.dts +++ b/arch/arm/boot/dts/imx6sx-19x19-arm2.dts @@ -23,6 +23,13 @@ csi1_v4l2_cap { compatible = "fsl,imx6sx-csi-v4l2", "fsl,imx6sl-csi-v4l2"; + csi_id = <0>; + status = "okay"; + }; + + csi2_v4l2_cap { + compatible = "fsl,imx6sx-csi-v4l2", "fsl,imx6sl-csi-v4l2"; + csi_id = <1>; status = "okay"; }; @@ -99,6 +106,10 @@ status = "okay"; }; +&csi2 { + status = "okay"; +}; + &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1_1>; @@ -492,6 +503,6 @@ &vadc { vadc_in = <0>; - csi_id = <0>; + csi_id = <1>; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6sx-sdb.dts b/arch/arm/boot/dts/imx6sx-sdb.dts index 9c58768aafa8..bd152a26aa8d 100644 --- a/arch/arm/boot/dts/imx6sx-sdb.dts +++ b/arch/arm/boot/dts/imx6sx-sdb.dts @@ -15,9 +15,6 @@ model = "Freescale i.MX6 SoloX SDB Board"; compatible = "fsl,imx6sx-sdb", "fsl,imx6sx"; - aliases { - mmc0 = &usdhc4; - }; pwm-backlight { compatible = "pwm-backlight"; @@ -141,8 +138,17 @@ hp-det-gpios = <&gpio1 17 1>; }; + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx6sx-sdb-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + }; + csi1_v4l2_cap { compatible = "fsl,imx6sx-csi-v4l2", "fsl,imx6sl-csi-v4l2"; + csi_id = <0>; status = "okay"; }; @@ -384,6 +390,9 @@ compatible = "fsl,mma8451"; reg = <0x1c>; position = <1>; + interrupt-parent = <&gpio6>; + interrupts = <2 8>; + interrupt-route = <2>; }; }; @@ -455,6 +464,12 @@ status = "disabled"; }; +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_2>; + status = "okay"; +}; + &ssi2 { status = "okay"; }; @@ -634,6 +649,6 @@ &vadc { vadc_in = <0>; - csi_id = <0>; + csi_id = <1>; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi index 2549b361c706..ab915d2e17e6 100644 --- a/arch/arm/boot/dts/imx6sx.dtsi +++ b/arch/arm/boot/dts/imx6sx.dtsi @@ -26,6 +26,12 @@ gpio6 = &gpio7; lcdif0 = &lcdif1; lcdif1 = &lcdif2; + csi0 = &csi1; + csi1 = &csi2; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + mmc3 = &usdhc4; serial0 = &uart1; serial1 = &uart2; serial2 = &uart3; @@ -553,8 +559,12 @@ compatible = "fsl,imx6q-gpc"; reg = <0x020dc000 0x4000>; interrupts = <0 89 0x04>; - clocks = <&clks IMX6SX_CLK_GPU>, <&clks IMX6SX_CLK_IPG>; - clock-names = "gpu3d_core", "ipg"; + clocks = <&clks IMX6SX_CLK_GPU>, <&clks IMX6SX_CLK_IPG>, + <&clks IMX6SX_CLK_PXP_AXI>, <&clks IMX6SX_CLK_DISPLAY_AXI>, + <&clks IMX6SX_CLK_LCDIF1_PIX>, <&clks IMX6SX_CLK_LCDIF_APB>, + <&clks IMX6SX_CLK_LCDIF2_PIX>, <&clks IMX6SX_CLK_CSI>; + clock-names = "gpu3d_core", "ipg", "pxp_axi", "disp_axi", "lcdif1_pix", + "lcdif_axi", "lcdif2_pix", "csi_mclk"; pu-supply = <&pu_dummy>; pcie-supply = <®_pcie>; fsl,mf-mix-wakeup-irq = <0x7c00000 0x3d00 0x0 0x400200>; @@ -1569,6 +1579,12 @@ MX6SX_PAD_ENET2_COL__SPDIF_IN 0x1b0b0 >; }; + + pinctrl_spdif_2: spdifgrp-2 { + fsl,pins = < + MX6SX_PAD_SD4_DATA4__SPDIF_OUT 0x1b0b0 + >; + }; }; uart1 { diff --git a/arch/arm/configs/imx_v7_android_defconfig b/arch/arm/configs/imx_v7_android_defconfig index cb2fef6bdbe3..2a35f1b5671d 100644 --- a/arch/arm/configs/imx_v7_android_defconfig +++ b/arch/arm/configs/imx_v7_android_defconfig @@ -96,7 +96,7 @@ CONFIG_RCU_FANOUT_LEAF=16 # CONFIG_RCU_BOOST is not set # CONFIG_RCU_NOCB_CPU is not set CONFIG_IKCONFIG=y -# CONFIG_IKCONFIG_PROC is not set +CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=18 CONFIG_CGROUPS=y CONFIG_CGROUP_DEBUG=y @@ -933,6 +933,7 @@ CONFIG_RPS=y CONFIG_RFS_ACCEL=y CONFIG_XPS=y # CONFIG_NETPRIO_CGROUP is not set +CONFIG_LLC2=y CONFIG_BQL=y # CONFIG_BPF_JIT is not set @@ -958,10 +959,7 @@ CONFIG_CAN_CALC_BITTIMING=y # CONFIG_CAN_MCP251X is not set CONFIG_HAVE_CAN_FLEXCAN=y CONFIG_CAN_FLEXCAN=y -# CONFIG_CAN_GRCAN is not set -# CONFIG_CAN_SJA1000 is not set -# CONFIG_CAN_C_CAN is not set -# CONFIG_CAN_CC770 is not set +CONFIG_CAN_M_CAN=y # # CAN USB interfaces @@ -1682,6 +1680,7 @@ CONFIG_INPUT_ISL29023=y # CONFIG_SERIO=y CONFIG_SERIO_SERPORT=y +CONFIG_VT_HW_CONSOLE_BINDING=y CONFIG_SERIO_LIBPS2=y # CONFIG_SERIO_RAW is not set # CONFIG_SERIO_ALTERA_PS2 is not set @@ -1740,7 +1739,7 @@ CONFIG_HW_RANDOM=y # CONFIG_RAW_DRIVER is not set # CONFIG_TCG_TPM is not set # CONFIG_DCC_TTY is not set -# CONFIG_MXS_VIIM is not set +CONFIG_MXS_VIIM=y CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y # CONFIG_I2C_COMPAT is not set @@ -2858,7 +2857,7 @@ CONFIG_USB_SERIAL_OPTION=y # CONFIG_USB_HSIC_USB3503 is not set CONFIG_USB_PHY=y CONFIG_USB_OTG_WAKELOCK=y -# CONFIG_NOP_USB_XCEIV is not set +CONFIG_NOP_USB_XCEIV=y # CONFIG_OMAP_CONTROL_USB is not set # CONFIG_OMAP_USB3 is not set # CONFIG_SAMSUNG_USBPHY is not set @@ -2891,14 +2890,14 @@ CONFIG_USB_FSL_USB2=y CONFIG_USB_LIBCOMPOSITE=y CONFIG_USB_F_ACM=y CONFIG_USB_U_SERIAL=y -# CONFIG_USB_ZERO is not set +CONFIG_USB_ZERO=y # CONFIG_USB_AUDIO is not set -# CONFIG_USB_ETH is not set -# CONFIG_USB_G_NCM is not set +CONFIG_USB_ETH=y +CONFIG_USB_G_NCM=y # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FUNCTIONFS is not set -# CONFIG_USB_MASS_STORAGE is not set -# CONFIG_USB_G_SERIAL is not set +CONFIG_USB_MASS_STORAGE=y +CONFIG_USB_G_SERIAL=y # CONFIG_USB_MIDI_GADGET is not set # CONFIG_USB_G_PRINTER is not set CONFIG_USB_G_ANDROID=y @@ -3608,7 +3607,7 @@ CONFIG_CRYPTO_CTS=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_LRW=y # CONFIG_CRYPTO_PCBC is not set -# CONFIG_CRYPTO_XTS is not set +CONFIG_CRYPTO_XTS=y # # Hash modes @@ -3624,19 +3623,19 @@ CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_CRC32 is not set CONFIG_CRYPTO_GHASH=y -# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_RMD128 is not set -# CONFIG_CRYPTO_RMD160 is not set -# CONFIG_CRYPTO_RMD256 is not set -# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=y +CONFIG_CRYPTO_RMD160=y +CONFIG_CRYPTO_RMD256=y +CONFIG_CRYPTO_RMD320=y CONFIG_CRYPTO_SHA1=y # CONFIG_CRYPTO_SHA1_ARM is not set CONFIG_CRYPTO_SHA256=y -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_TGR192 is not set -# CONFIG_CRYPTO_WP512 is not set +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TGR192=y +CONFIG_CRYPTO_WP512=y # # Ciphers @@ -3645,8 +3644,8 @@ CONFIG_CRYPTO_AES=y # CONFIG_CRYPTO_AES_ARM is not set # CONFIG_CRYPTO_ANUBIS is not set CONFIG_CRYPTO_ARC4=y -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_CAMELLIA is not set +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_CAMELLIA=y # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set CONFIG_CRYPTO_DES=y @@ -3656,7 +3655,7 @@ CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_SEED is not set # CONFIG_CRYPTO_SERPENT is not set # CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_TWOFISH is not set +CONFIG_CRYPTO_TWOFISH=y # # Compression diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c index e67bdbc21a83..d4529ab490ea 100644 --- a/arch/arm/mach-imx/busfreq-imx6.c +++ b/arch/arm/mach-imx/busfreq-imx6.c @@ -108,6 +108,7 @@ static struct clk *periph2_pre_clk; static struct clk *periph2_clk2_sel; static struct clk *periph2_clk2; static struct clk *step_clk; +static struct clk *axi_alt_sel_clk; static struct clk *axi_sel_clk; static struct clk *pll3_pfd1_540m; @@ -356,8 +357,7 @@ int reduce_bus_freq(void) else if (cpu_is_imx6sx()) enter_lpm_imx6sx(); else { - if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) - != periph_clk)) + if (cpu_is_imx6dl()) /* Set axi to periph_clk */ clk_set_parent(axi_sel_clk, periph_clk); @@ -515,10 +515,11 @@ int set_high_bus_freq(int high_bus_freq) dev_warn(busfreq_dev, "%s: %d: clk set parent fail!\n", __func__, __LINE__); - if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) - != pll3_pfd1_540m)) + if (cpu_is_imx6dl()) { /* Set axi to pll3_pfd1_540m */ - clk_set_parent(axi_sel_clk, pll3_pfd1_540m); + clk_set_parent(axi_alt_sel_clk, pll3_pfd1_540m); + clk_set_parent(axi_sel_clk, axi_alt_sel_clk); + } clk_disable_unprepare(pll2_400); } else { update_ddr_freq_imx6q(ddr_med_rate); @@ -921,6 +922,13 @@ static int busfreq_probe(struct platform_device *pdev) } if (cpu_is_imx6dl()) { + axi_alt_sel_clk = devm_clk_get(&pdev->dev, "axi_alt_sel"); + if (IS_ERR(axi_alt_sel_clk)) { + dev_err(busfreq_dev, "%s: failed to get axi_alt_sel_clk\n", + __func__); + return PTR_ERR(axi_alt_sel_clk); + } + axi_sel_clk = devm_clk_get(&pdev->dev, "axi_sel"); if (IS_ERR(axi_sel_clk)) { dev_err(busfreq_dev, "%s: failed to get axi_sel_clk\n", diff --git a/arch/arm/mach-imx/busfreq_ddr3.c b/arch/arm/mach-imx/busfreq_ddr3.c index 6854f78d3f47..f959c3c1ae08 100644 --- a/arch/arm/mach-imx/busfreq_ddr3.c +++ b/arch/arm/mach-imx/busfreq_ddr3.c @@ -105,7 +105,8 @@ unsigned long ddr3_dll_mx6sx[][2] = { {0x10, 0x0}, {0x30, 0x0}, {0x1C, 0x04008032}, - {0x1C, 0x00068031}, + {0x1C, 0x00008033}, + {0x1C, 0x00048031}, {0x1C, 0x05208030}, {0x1C, 0x04008040}, {0x818, 0x0}, diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 78e616286e1e..4497e29fb602 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -33,17 +33,18 @@ static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; -static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; +static const char *axi_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *axi_sels[] = { "periph", "axi_alt_sel", }; static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", }; static const char *gpu_axi_sels[] = { "axi", "ahb", }; static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", }; -static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", }; -static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; -static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; +static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi_podf", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", }; +static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi_podf", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; +static const char *ipu_sels[] = { "mmdc_ch0_axi_podf", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", }; static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; -static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi_podf", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; @@ -61,7 +62,7 @@ static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5 "dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; static const char *cko2_sels[] = { - "mmdc_ch0_axi", "mmdc_ch1_axi", "usdhc4", "usdhc1", + "mmdc_ch0_axi_podf", "mmdc_ch1_axi", "usdhc4", "usdhc1", "gpu2d_axi", "dummy", "ecspi_root", "gpu3d_axi", "usdhc3", "dummy", "arm", "ipu1", "ipu2", "vdo_axi", "osc", "gpu2d_core", @@ -116,7 +117,8 @@ enum mx6q_clks { ldb_di0_div_7, ldb_di1_div_7, ldb_di0_div_sel, ldb_di1_div_sel, pll4_audio_div, lvds1_sel, lvds1_in, lvds1_out, caam_mem, caam_aclk, caam_ipg, epit1, epit2, tzasc2, pll4_sel, lvds2_sel, lvds2_in, lvds2_out, - anaclk1, anaclk2, spdif1, asrc_ipg, asrc_mem, esai_ipg, esai_mem, clk_max + anaclk1, anaclk2, spdif1, asrc_ipg, asrc_mem, esai_ipg, esai_mem, + axi_alt_sel, clk_max }; static struct clk *clk[clk_max]; @@ -148,7 +150,36 @@ static struct clk_div_table video_div_table[] = { { } }; -static void init_ldb_clks(enum mx6q_clks new_parent) +/* + * Kernel parameter 'ldb_di_clk_sel' is used to select parent of ldb_di_clk, + * among the following clocks. + * 'pll5_video_div' + * 'pll2_pfd0_352m' + * 'pll2_pfd2_396m' + * 'mmdc_ch1_axi' + * 'pll3_usb_otg' + * Example format: ldb_di_clk_sel=pll5_video_div + * If the kernel parameter is absent or invalid, pll2_pfd0_352m will be + * selected by default. + */ +static int ldb_di_sel = 1; + +static int __init get_ldb_di_parent(char *p) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ldb_di_sels); i++) { + if (strcmp(p, ldb_di_sels[i]) == 0) { + ldb_di_sel = i; + break; + } + } + + return 0; +} +early_param("ldb_di_clk_sel", get_ldb_di_parent); + +static void init_ldb_clks(void) { u32 reg; @@ -222,8 +253,10 @@ static void init_ldb_clks(enum mx6q_clks new_parent) /* * Perform the LDB parent clock switch. */ - clk_set_parent(clk[ldb_di0_sel], clk[new_parent]); - clk_set_parent(clk[ldb_di1_sel], clk[new_parent]); + reg = readl_relaxed(ccm_base + 0x2c); + reg &= ~((7 << 9) | (7 << 12)); + reg |= ((ldb_di_sel << 9) | (ldb_di_sel << 12)); + writel_relaxed(reg, ccm_base + 0x2c); /* * Unbypass pll3_sw_clk. @@ -346,10 +379,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[gpt_3m] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); clk[video_27m] = imx_clk_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); - clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); - clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); - clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); - clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock); + clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); np = ccm_node; ccm_base = base = of_iomap(np, 0); @@ -363,7 +396,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[periph2_pre] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); clk[periph_clk2_sel] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); clk[periph2_clk2_sel] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); - clk[axi_sel] = imx_clk_mux("axi_sel", base + 0x14, 6, 2, axi_sels, ARRAY_SIZE(axi_sels)); + clk[axi_alt_sel] = imx_clk_mux("axi_alt_sel", base + 0x14, 7, 1, axi_alt_sels, ARRAY_SIZE(axi_alt_sels)); + clk[axi_sel] = imx_clk_mux("axi_sel", base + 0x14, 6, 1, axi_sels, ARRAY_SIZE(axi_sels)); clk[esai_sel] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); clk[spdif1_sel] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); clk[spdif_sel] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); @@ -374,8 +408,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); - clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); - clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); clk[ldb_di0_div_sel] = imx_clk_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); clk[ldb_di1_div_sel] = imx_clk_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); @@ -426,10 +458,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[gpu3d_shader] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); - clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); - clk[ldb_di0_div_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); - clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); - clk[ldb_di1_div_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); + clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", ldb_di_sels[ldb_di_sel], 2, 7); + clk[ldb_di0_div_7] = imx_clk_fixed_factor("ldb_di0_div_7", ldb_di_sels[ldb_di_sel], 1, 7); + clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", ldb_di_sels[ldb_di_sel], 2, 7); + clk[ldb_di1_div_7] = imx_clk_fixed_factor("ldb_di1_div_7", ldb_di_sels[ldb_di_sel], 1, 7); clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); @@ -525,8 +557,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[mlb] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); else clk[mlb] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); - clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); - clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); clk[openvg_axi] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); clk[pcie_axi] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); @@ -563,6 +593,14 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[cko1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); clk[cko2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); + /* + * These two clocks (mmdc_ch0_axi and mmdc_ch1_axi) were incorrectly + * implemented as gate at the beginning. To fix them with the minimized + * impact, let's point them to their dividers. + */ + clk[mmdc_ch0_axi] = clk[mmdc_ch0_axi_podf]; + clk[mmdc_ch1_axi] = clk[mmdc_ch1_axi_podf]; + for (i = 0; i < ARRAY_SIZE(clk); i++) if (IS_ERR(clk[i])) pr_err("i.MX6q clk %d: register failed with %ld\n", @@ -657,7 +695,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) } /* ipu clock initialization */ - init_ldb_clks(pll2_pfd0_352m); + init_ldb_clks(); clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); @@ -669,7 +707,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) if (cpu_is_imx6dl()) { clk_set_rate(clk[pll3_pfd1_540m], 540000000); clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); - clk_set_parent(clk[axi_sel], clk[pll3_pfd1_540m]); + clk_set_parent(clk[axi_alt_sel], clk[pll3_pfd1_540m]); + clk_set_parent(clk[axi_sel], clk[axi_alt_sel]); /* set epdc/pxp axi clock to 200Mhz */ clk_set_parent(clk[ipu2_sel], clk[pll2_pfd2_396m]); clk_set_rate(clk[ipu2], 200000000); diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index c9eb48a09159..c99b51148035 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c @@ -42,7 +42,7 @@ static bool uart_from_osc; static const char const *step_sels[] = { "osc", "pll2_pfd2", }; static const char const *pll1_sw_sels[] = { "pll1_sys", "step", }; static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", }; -static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", }; +static const char const *ocram_sels[] = { "periph", "ocram_alt_sel", }; static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; @@ -202,10 +202,10 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) clks[IMX6SL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); /* dev name parent_name flags reg shift width div: flags, div_table lock */ - clks[IMX6SL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); - clks[IMX6SL_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); - clks[IMX6SL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); - clks[IMX6SL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); clks[IMX6SL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); /* name parent_name reg idx */ diff --git a/arch/arm/mach-imx/clk-imx6sx.c b/arch/arm/mach-imx/clk-imx6sx.c index 0844e5ce2f32..5987850f0550 100644 --- a/arch/arm/mach-imx/clk-imx6sx.c +++ b/arch/arm/mach-imx/clk-imx6sx.c @@ -36,7 +36,8 @@ static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", }; static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", }; static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; -static const char *ocram_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; +static const char *ocram_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *ocram_sels[] = { "periph", "ocram_alt_sel", }; static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; static const char *gpu_axi_sels[] = { "pll2_pfd2_396m", "pll3_pfd0_720m", "pll3_pfd1_540m", "pll2_bus", }; static const char *gpu_core_sels[] = { "pll3_pfd1_540m", "pll3_pfd0_720m", "pll2_bus", "pll2_pfd2_396m", }; @@ -205,13 +206,13 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); clks[IMX6SX_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", - CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); clks[IMX6SX_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", - CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock); clks[IMX6SX_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", - CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); clks[IMX6SX_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", - CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); /* name reg shift width parent_names num_parents */ clks[IMX6SX_CLK_LVDS1_SEL] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); @@ -224,7 +225,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) /* name reg shift width parent_names num_parents */ clks[IMX6SX_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); clks[IMX6SX_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); - clks[IMX6SX_CLK_OCRAM_SEL] = imx_clk_mux("ocram_sel", base + 0x14, 6, 2, ocram_sels, ARRAY_SIZE(ocram_sels)); + clks[IMX6SX_CLK_OCRAM_ALT_SEL] = imx_clk_mux("ocram_alt_sel", base + 0x14, 7, 1, ocram_alt_sels, ARRAY_SIZE(ocram_alt_sels)); + clks[IMX6SX_CLK_OCRAM_SEL] = imx_clk_mux("ocram_sel", base + 0x14, 6, 1, ocram_sels, ARRAY_SIZE(ocram_sels)); clks[IMX6SX_CLK_PERIPH_PRE] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); clks[IMX6SX_CLK_PERIPH2_PRE] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels)); clks[IMX6SX_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); @@ -240,13 +242,13 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_SSI3_SEL] = imx_clk_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); clks[IMX6SX_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); clks[IMX6SX_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); - clks[IMX6SX_CLK_QSPI1_SEL] = imx_clk_mux_flags_reparent("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_QSPI1_SEL] = imx_clk_mux_flags("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels), CLK_SET_RATE_PARENT); clks[IMX6SX_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); clks[IMX6SX_CLK_VID_SEL] = imx_clk_mux("vid_sel", base + 0x20, 21, 3, vid_sels, ARRAY_SIZE(vid_sels)); clks[IMX6SX_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); clks[IMX6SX_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); clks[IMX6SX_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); - clks[IMX6SX_CLK_QSPI2_SEL] = imx_clk_mux_flags_reparent("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_QSPI2_SEL] = imx_clk_mux_flags("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels), CLK_SET_RATE_PARENT); clks[IMX6SX_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); clks[IMX6SX_CLK_AUDIO_SEL] = imx_clk_mux("audio_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); clks[IMX6SX_CLK_ENET_PRE_SEL] = imx_clk_mux("enet_pre_sel", base + 0x34, 15, 3, enet_pre_sels, ARRAY_SIZE(enet_pre_sels)); diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index b73756eec5b7..bd257ae0bc2b 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c @@ -439,7 +439,7 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, init.name = name; init.ops = ops; - init.flags = 0; + init.flags = CLK_SET_RATE_GATE; init.parent_names = &parent_name; init.num_parents = 1; diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 319a9660e4c2..0f9076909517 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -110,16 +110,6 @@ static inline struct clk *imx_clk_mux_flags(const char *name, int num_parents, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, - &imx_ccm_lock); -} - -/* we can use this helper to register the clock which needs the re-parent. */ -static inline struct clk *imx_clk_mux_flags_reparent(const char *name, - void __iomem *reg, u8 shift, u8 width, const char **parents, - int num_parents, unsigned long flags) -{ - return clk_register_mux(NULL, name, parents, num_parents, flags, reg, shift, width, 0, &imx_ccm_lock); } diff --git a/arch/arm/mach-imx/ddr3_freq_imx6.S b/arch/arm/mach-imx/ddr3_freq_imx6.S index 25406d45ccb8..8137f513c787 100644 --- a/arch/arm/mach-imx/ddr3_freq_imx6.S +++ b/arch/arm/mach-imx/ddr3_freq_imx6.S @@ -623,11 +623,6 @@ update_iomux: orr r0, r0, #(0x1 << 29) str r0, [r5, r2] - /* MMDC0_MAPSR adopt power down enable. */ - ldr r0, [r5, #MMDC0_MAPSR] - bic r0, r0, #0x01 - str r0, [r5, #MMDC0_MAPSR] - /* frc_msr + mu bypass */ ldr r0, =0x00000060 str r0, [r5, #MMDC0_MPMUR0] @@ -867,11 +862,6 @@ cont15: cmp r1, #0 bgt delay15 - /* MMDC0_MAPSR adopt power down enable. */ - ldr r0, [r5, #MMDC0_MAPSR] - bic r0, r0, #0x01 - str r0, [r5, #MMDC0_MAPSR] - /* enable MMDC power down timer. */ ldr r0, [r5, #MMDC0_MDPDC] orr r0, r0, #(0x55 << 8) @@ -920,6 +910,11 @@ poll_conreq_clear_2: done: + /* MMDC0_MAPSR adopt power down enable. */ + ldr r0, [r5, #MMDC0_MAPSR] + bic r0, r0, #0x01 + str r0, [r5, #MMDC0_MAPSR] + #ifdef CONFIG_CACHE_L2X0 /* Enable L2. */ ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR) diff --git a/arch/arm/mach-imx/ddr3_freq_imx6sx.S b/arch/arm/mach-imx/ddr3_freq_imx6sx.S index c63bf570100d..d0f692c39e64 100644 --- a/arch/arm/mach-imx/ddr3_freq_imx6sx.S +++ b/arch/arm/mach-imx/ddr3_freq_imx6sx.S @@ -59,7 +59,8 @@ add r9, r9, #4 cmp r9, #16 bne 2b - subs r8, r8, #1 + sub r8, r8, #1 + cmp r8, #0 bgt 1b .endm @@ -78,33 +79,7 @@ /* check whether periph2_clk is already from top path */ ldr r8, [r5, #CCM_CBCDR] ands r8, #(1 << 26) - bne skip_periph2_clk2_switch_400m - - /* change periph2_clk2 to be sourced from pll3_clk. */ - ldr r8, [r5, #CCM_CBCMR] - bic r8, r8, #(1 << 20) - str r8, [r5, #CCM_CBCMR] - - /* make sure periph2_clk2 freq not exceed 400MHz */ - ldr r8, [r5, #CCM_CBCDR] - bic r8, r8, #0x7 - orr r8, r8, #0x1 - str r8, [r5, #CCM_CBCDR] - - /* now switch periph2_clk to pll3_main_clk. */ - ldr r8, [r5, #CCM_CBCDR] - orr r8, r8, #(1 << 26) - str r8, [r5, #CCM_CBCDR] - - wait_for_ccm_handshake - -skip_periph2_clk2_switch_400m: - - /* now switch pre_periph2_clk to PFD_400MHz. */ - ldr r8, [r5, #CCM_CBCMR] - bic r8, r8, #(0x3 << 21) - orr r8, r8, #(0x1 << 21) - str r8, [r5, #CCM_CBCMR] + beq skip_periph2_clk2_switch_400m /* now switch periph2_clk back. */ ldr r8, [r5, #CCM_CBCDR] @@ -113,6 +88,13 @@ skip_periph2_clk2_switch_400m: wait_for_ccm_handshake + /* + * on i.MX6SX, pre_periph2_clk will be always from + * pll2_pfd2, so no need to set pre_periph2_clk + * parent, just set the mmdc divider directly. + */ +skip_periph2_clk2_switch_400m: + /* fabric_mmdc_podf to 0 */ ldr r8, [r5, #CCM_CBCDR] bic r8, r8, #(0x7 << 3) @@ -127,32 +109,7 @@ skip_periph2_clk2_switch_400m: /* check whether periph2_clk is already from top path */ ldr r8, [r5, #CCM_CBCDR] ands r8, #(1 << 26) - bne skip_periph2_clk2_switch_50m - - /* change periph2_clk2 to be sourced from pll3_clk. */ - ldr r8, [r5, #CCM_CBCMR] - bic r8, r8, #(1 << 20) - str r8, [r5, #CCM_CBCMR] - - ldr r8, [r5, #CCM_CBCDR] - bic r8, r8, #0x7 - orr r8, r8, #0x1 - str r8, [r5, #CCM_CBCDR] - - /* now switch periph2_clk to pll3_main_clk. */ - ldr r8, [r5, #CCM_CBCDR] - orr r8, r8, #(1 << 26) - str r8, [r5, #CCM_CBCDR] - - wait_for_ccm_handshake - -skip_periph2_clk2_switch_50m: - - /* now switch pre_periph2_clk to PFD_400MHz. */ - ldr r8, [r5, #CCM_CBCMR] - bic r8, r8, #(0x3 << 21) - orr r8, r8, #(0x1 << 21) - str r8, [r5, #CCM_CBCMR] + beq skip_periph2_clk2_switch_50m /* now switch periph2_clk back. */ ldr r8, [r5, #CCM_CBCDR] @@ -161,6 +118,13 @@ skip_periph2_clk2_switch_50m: wait_for_ccm_handshake + /* + * on i.MX6SX, pre_periph2_clk will be always from + * pll2_pfd2, so no need to set pre_periph2_clk + * parent, just set the mmdc divider directly. + */ +skip_periph2_clk2_switch_50m: + /* fabric_mmdc_podf to 7 so that mmdc is 400 / 8 = 50MHz */ ldr r8, [r5, #CCM_CBCDR] orr r8, r8, #(0x7 << 3) @@ -227,22 +191,24 @@ imx6sx_ddr3_freq_change_start: * and 2-4G is translated by TTBR1. */ + ldr r6, =iram_tlb_phys_addr + ldr r7, [r6] + /* Disable Branch Prediction, Z bit in SCTLR. */ - mrc p15, 0, r7, c1, c0, 0 - bic r7, r7, #0x800 - mcr p15, 0, r7, c1, c0, 0 + mrc p15, 0, r6, c1, c0, 0 + bic r6, r6, #0x800 + mcr p15, 0, r6, c1, c0, 0 /* Flush the Branch Target Address Cache (BTAC) */ ldr r6, =0x0 mcr p15, 0, r6, c7, c1, 6 - ldr r6, =iram_tlb_phys_addr - ldr r6, [r6] dsb isb /* Store the IRAM table in TTBR1 */ - mcr p15, 0, r6, c2, c0, 1 + mcr p15, 0, r7, c2, c0, 1 + /* Read TTBCR and set PD0=1, N = 1 */ mrc p15, 0, r6, c2, c0, 2 orr r6, r6, #0x11 @@ -259,9 +225,9 @@ imx6sx_ddr3_freq_change_start: isb /* Disable L1 data cache. */ - mrc p15, 0, r7, c1, c0, 0 - bic r7, r7, #0x4 - mcr p15, 0, r7, c1, c0, 0 + mrc p15, 0, r6, c1, c0, 0 + bic r6, r6, #0x4 + mcr p15, 0, r6, c1, c0, 0 ldr r4, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR) ldr r5, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR) @@ -309,20 +275,6 @@ wait_for_l2_to_idle: ldr r8, =4 do_delay - /* set CON_REG */ - ldr r8, =0x8000 - str r8, [r4, #MMDC0_MDSCR] -poll_conreq_set_1: - ldr r8, [r4, #MMDC0_MDSCR] - and r8, r8, #(0x4 << 12) - cmp r8, #(0x4 << 12) - bne poll_conreq_set_1 - - ldr r8, =0x00008010 - str r8, [r4, #MMDC0_MDSCR] - ldr r8, =0x00008018 - str r8, [r4, #MMDC0_MDSCR] - /* * if requested frequency is greater than * 300MHz go to DLL on mode. @@ -400,10 +352,10 @@ poll_dvfs_clear_1: ldr r8, =0x00018039 str r8, [r4, #MMDC0_MDSCR] - ldr r8, =0x08208030 + ldr r8, =0x04208030 str r8, [r4, #MMDC0_MDSCR] - ldr r8, =0x08208038 + ldr r8, =0x04208038 str r8, [r4, #MMDC0_MDSCR] ldr r8, =0x00088032 @@ -441,7 +393,8 @@ update_iomux: orr r11, r11, #0x28 str r11, [r6, r10] add r3, r3, #8 - subs r8, r8, #1 + sub r8, r8, #1 + cmp r8, #0 bgt update_iomux /* ODT disabled. */ @@ -453,16 +406,11 @@ update_iomux: orr r8, r8, #(1 << 29) str r8, [r4, #MMDC0_MPDGCTRL0] - /* MMDC0_MAPSR adopt power down enable. */ - ldr r8, [r4, #MMDC0_MAPSR] - bic r8, r8, #0x1 - str r8, [r4, #MMDC0_MAPSR] - /* frc_msr + mu bypass */ - ldr r8, =0x00000060 + ldr r8, =0x00000160 str r8, [r4, #MMDC0_MPMUR0] - ldr r8, =0x00000460 + ldr r8, =0x00000560 str r8, [r4, #MMDC0_MPMUR0] ldr r8, =0x00000c60 @@ -490,10 +438,6 @@ dll_on_mode: orr r8, r8, #(1 << 21) str r8, [r4, #MMDC0_MAPSR] - /* de-assert CON_REQ. */ - mov r8, #0x0 - str r8, [r4, #MMDC0_MDSCR] - /* poll DVFS ack. */ poll_dvfs_set_2: ldr r8, [r4, #MMDC0_MAPSR] @@ -546,7 +490,8 @@ update_iomux1: ldr r11, [r3, #0x4] str r11, [r6, r10] add r3, r3, #8 - subs r8, r8, #1 + sub r8, r8, #1 + cmp r8, #0 bgt update_iomux1 /* config MMDC timings to 400MHz. */ @@ -579,10 +524,10 @@ update_iomux1: do_delay /* reset dll. */ - ldr r8, =0x09208030 + ldr r8, =0x05208030 str r8, [r4, #MMDC0_MDSCR] - ldr r8, =0x09208038 + ldr r8, =0x05208038 str r8, [r4, #MMDC0_MDSCR] /* delay for while. */ @@ -632,11 +577,6 @@ update_iomux1: ldr r8, =40 do_delay - /* MMDC0_MAPSR adopt power down enable. */ - ldr r8, [r4, #MMDC0_MAPSR] - bic r8, r8, #0x01 - str r8, [r4, #MMDC0_MAPSR] - /* enable MMDC power down timer. */ ldr r8, [r4, #MMDC0_MDPDC] orr r8, r8, #(0x55 << 8) @@ -660,7 +600,8 @@ update_calib: ldr r11, [r1, #0x4] str r11, [r4, r10] add r1, r1, #8 - subs r8, r8, #1 + sub r8, r8, #1 + cmp r8, #0 bgt update_calib /* perform a force measurement. */ @@ -681,10 +622,11 @@ poll_conreq_clear_2: beq poll_conreq_clear_2 done: - /* Enable L1 data cache. */ - mrc p15, 0, r7, c1, c0, 0 - orr r7, r7, #0x4 - mcr p15, 0, r7, c1, c0, 0 + + /* MMDC0_MAPSR adopt power down enable. */ + ldr r8, [r4, #MMDC0_MAPSR] + bic r8, r8, #0x01 + str r8, [r4, #MMDC0_MAPSR] #ifdef CONFIG_CACHE_L2X0 /* Enable L2. */ @@ -693,6 +635,11 @@ done: str r7, [r8, #0x100] #endif + /* Enable L1 data cache. */ + mrc p15, 0, r7, c1, c0, 0 + orr r7, r7, #0x4 + mcr p15, 0, r7, c1, c0, 0 + /* Restore the TTBCR */ dsb isb diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index 07fac4b13bdd..9a0d6b4e87ce 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c @@ -63,6 +63,7 @@ static u32 gpc_saved_imrs[IMR_NUM]; static struct clk *gpu3d_clk, *gpu3d_shader_clk, *gpu2d_clk, *gpu2d_axi_clk; static struct clk *lcd_axi_clk, *lcd_pix_clk, *epdc_axi_clk, *epdc_pix_clk; static struct clk *pxp_axi_clk; +static struct clk *disp_axi_clk, *lcdif_axi_clk, *lcdif1_pix_clk, *lcdif2_pix_clk, *csi_mclk; static struct clk *openvg_axi_clk, *vpu_clk, *ipg_clk; static struct device *gpc_dev; static struct regulator *pu_reg; @@ -80,25 +81,43 @@ static int pu_dummy_enable; static void imx_disp_clk(bool enable) { - if (enable) { - clk_prepare_enable(lcd_axi_clk); - clk_prepare_enable(lcd_pix_clk); - clk_prepare_enable(epdc_axi_clk); - clk_prepare_enable(epdc_pix_clk); - clk_prepare_enable(pxp_axi_clk); - } else { - clk_disable_unprepare(lcd_axi_clk); - clk_disable_unprepare(lcd_pix_clk); - clk_disable_unprepare(epdc_axi_clk); - clk_disable_unprepare(epdc_pix_clk); - clk_disable_unprepare(pxp_axi_clk); + if (cpu_is_imx6sl()) { + if (enable) { + clk_prepare_enable(lcd_axi_clk); + clk_prepare_enable(lcd_pix_clk); + clk_prepare_enable(epdc_axi_clk); + clk_prepare_enable(epdc_pix_clk); + clk_prepare_enable(pxp_axi_clk); + } else { + clk_disable_unprepare(lcd_axi_clk); + clk_disable_unprepare(lcd_pix_clk); + clk_disable_unprepare(epdc_axi_clk); + clk_disable_unprepare(epdc_pix_clk); + clk_disable_unprepare(pxp_axi_clk); + } + } else if (cpu_is_imx6sx()) { + if (enable) { + clk_prepare_enable(lcdif_axi_clk); + clk_prepare_enable(lcdif1_pix_clk); + clk_prepare_enable(lcdif2_pix_clk); + clk_prepare_enable(pxp_axi_clk); + clk_prepare_enable(csi_mclk); + clk_prepare_enable(disp_axi_clk); + } else { + clk_disable_unprepare(lcdif_axi_clk); + clk_disable_unprepare(lcdif1_pix_clk); + clk_disable_unprepare(lcdif2_pix_clk); + clk_disable_unprepare(pxp_axi_clk); + clk_disable_unprepare(csi_mclk); + clk_disable_unprepare(disp_axi_clk); + } } } static void imx_gpc_dispmix_on(void) { - if (cpu_is_imx6sl() && - imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) { + if ((cpu_is_imx6sl() && + imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) || cpu_is_imx6sx()) { imx_disp_clk(true); writel_relaxed(0x0, gpc_base + GPC_PGC_DISP_PGCR_OFFSET); @@ -113,8 +132,8 @@ static void imx_gpc_dispmix_on(void) static void imx_gpc_dispmix_off(void) { - if (cpu_is_imx6sl() && - imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) { + if ((cpu_is_imx6sl() && + imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) || cpu_is_imx6sx()) { imx_disp_clk(true); writel_relaxed(0xFFFFFFFF, @@ -138,9 +157,17 @@ static void imx_gpc_mf_mix_off(void) if ((gpc_wake_irqs[i] & gpc_mf_irqs[i]) != 0) return; - pr_info("Turn off M/F mix!\n"); - /* turn off mega/fast mix */ - writel_relaxed(0x1, gpc_base + GPC_PGC_MF_PDN); + /* + * As some drivers are not ready for this + * Mega/Fast mix off feature, so just skip the + * setting of turning off M/F mix, if anyone + * want to enable this feature, just add below + * two lines back. + * + * pr_info("Turn off M/F mix!\n"); + * writel_relaxed(0x1, gpc_base + GPC_PGC_MF_PDN); + * + */ } void imx_gpc_pre_suspend(bool arm_power_off) @@ -585,7 +612,14 @@ static int imx_gpc_probe(struct platform_device *pdev) } else if (cpu_is_imx6sx()) { gpu3d_clk = devm_clk_get(gpc_dev, "gpu3d_core"); ipg_clk = devm_clk_get(gpc_dev, "ipg"); - if (IS_ERR(gpu3d_clk) || IS_ERR(ipg_clk)) { + pxp_axi_clk = devm_clk_get(gpc_dev, "pxp_axi"); + disp_axi_clk = devm_clk_get(gpc_dev, "disp_axi"); + lcdif1_pix_clk = devm_clk_get(gpc_dev, "lcdif1_pix"); + lcdif_axi_clk = devm_clk_get(gpc_dev, "lcdif_axi"); + lcdif2_pix_clk = devm_clk_get(gpc_dev, "lcdif2_pix"); + csi_mclk = devm_clk_get(gpc_dev, "csi_mclk"); + if (IS_ERR(gpu3d_clk) || IS_ERR(ipg_clk) || IS_ERR(pxp_axi_clk) || IS_ERR(disp_axi_clk) || + IS_ERR(lcdif1_pix_clk) || IS_ERR(lcdif_axi_clk) || IS_ERR(lcdif2_pix_clk) || IS_ERR(csi_mclk)) { dev_err(gpc_dev, "failed to get clk!\n"); return -ENOENT; } diff --git a/drivers/char/imx_mcc/Kconfig b/drivers/char/imx_mcc/Kconfig index d60cf66cd8fd..9625efa2df7f 100644 --- a/drivers/char/imx_mcc/Kconfig +++ b/drivers/char/imx_mcc/Kconfig @@ -13,3 +13,11 @@ config IMX_MCC_DEMO depends on SOC_IMX6SX && IMX_SEMA4 help Support for IMX MCC DEMO, most people should say N here. + +config IMX_MCC_TTY + bool "imx pty for mcc interface" + depends on SOC_IMX6SX && IMX_SEMA4 + help + This enables a pty node for imx6sx mcc interface. + +#end imx mcc diff --git a/drivers/char/imx_mcc/Makefile b/drivers/char/imx_mcc/Makefile index a0d60f341d07..f5bda928e23c 100644 --- a/drivers/char/imx_mcc/Makefile +++ b/drivers/char/imx_mcc/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_IMX_SEMA4) += imx_sema4.o obj-$(CONFIG_IMX_MCC_DEMO) += imx_mcc_demo.o +obj-$(CONFIG_IMX_MCC_TTY) += imx_mcc_tty.o diff --git a/drivers/char/imx_mcc/imx_mcc_tty.c b/drivers/char/imx_mcc/imx_mcc_tty.c new file mode 100644 index 000000000000..843b5fcbd010 --- /dev/null +++ b/drivers/char/imx_mcc/imx_mcc_tty.c @@ -0,0 +1,253 @@ +/* + * imx_mcc_tty.c - pty demo driver used to test imx mcc + * posix pty interface. + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + */ + +/* + * 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/slab.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/mcc_common.h> +#include <linux/mcc_api.h> + +/** + * struct mcctty_port - Wrapper struct for imx mcc pty port. + * @port: TTY port data + * @rx_lock: Lock for rx_buf. + * @rx_buf: Read buffer + */ +struct mcctty_port { + struct delayed_work read; + struct tty_port port; + spinlock_t rx_lock; + char *rx_buf; +}; + +static struct mcctty_port mcc_tty_port; + +enum { + MCC_NODE_A9 = 0, + MCC_NODE_M4 = 0, + + MCC_A9_PORT = 1, + MCC_M4_PORT = 2, +}; + +/* mcc tty/pingpong demo */ +static MCC_ENDPOINT mcc_endpoint_a9_pingpong = {0, MCC_NODE_A9, MCC_A9_PORT}; +static MCC_ENDPOINT mcc_endpoint_m4_pingpong = {1, MCC_NODE_M4, MCC_M4_PORT}; +struct mcc_pp_msg { + unsigned int data; +}; + +static void mcctty_delay_work(struct work_struct *work) +{ + struct mcctty_port *cport = &mcc_tty_port; + int ret, space; + unsigned char *cbuf; + struct mcc_pp_msg pp_msg; + MCC_MEM_SIZE num_of_received_bytes; + MCC_INFO_STRUCT mcc_info; + + /* start mcc tty recv here */ + ret = mcc_initialize(MCC_NODE_A9); + if (ret) + pr_err("failed to initialize mcc.\n"); + + ret = mcc_get_info(MCC_NODE_A9, &mcc_info); + if (ret) + pr_err("failed to get mcc info.\n"); + pr_info("\nA9 mcc prepares run, MCC version is %s\n", + mcc_info.version_string); + + pr_info("imx mcc tty/pingpong demo begin.\n"); + ret = mcc_create_endpoint(&mcc_endpoint_a9_pingpong, + MCC_A9_PORT); + if (ret) + pr_err("failed to create a9 mcc ep.\n"); + + while (1) { + ret = mcc_recv_copy(&mcc_endpoint_a9_pingpong, &pp_msg, + sizeof(struct mcc_pp_msg), + &num_of_received_bytes, 0xffffffff); + + if (MCC_SUCCESS != ret) { + pr_err("A9 Main task receive error: %d\n", ret); + } else { + /* flush the recv-ed data to tty node */ + spin_lock_bh(&cport->rx_lock); + space = tty_prepare_flip_string(&cport->port, &cbuf, + num_of_received_bytes); + if ((space <= 0) || (cport->rx_buf == NULL)) + goto pp_unlock; + + memcpy(cport->rx_buf, &pp_msg.data, + num_of_received_bytes); + memcpy(cbuf, cport->rx_buf, space); + tty_flip_buffer_push(&cport->port); +pp_unlock: + spin_unlock_bh(&cport->rx_lock); + } + } +} + +static struct tty_port_operations mcctty_port_ops = { }; + +static int mcctty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + return tty_port_install(&mcc_tty_port.port, driver, tty); +} + +static int mcctty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void mcctty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static int mcctty_write(struct tty_struct *tty, const unsigned char *buf, + int total) +{ + int i, ret = 0; + struct mcc_pp_msg pp_msg; + + if (NULL == buf) + return 0; + + for (i = 0; i < total; i++) { + pp_msg.data = (unsigned int)buf[i]; + + /* + * wait until the remote endpoint is created by + * the other core + */ + ret = mcc_send(&mcc_endpoint_m4_pingpong, &pp_msg, + sizeof(struct mcc_pp_msg), + 0xffffffff); + + while (MCC_ERR_ENDPOINT == ret) { + pr_err("\n send err ret %d, re-send\n", ret); + ret = mcc_send(&mcc_endpoint_m4_pingpong, &pp_msg, + sizeof(struct mcc_pp_msg), + 0xffffffff); + msleep(5000); + } + } + + return total; +} + +static int mcctty_write_room(struct tty_struct *tty) +{ + int room; + + /* report the space in the mcc buffer */ + room = MCC_ATTR_BUFFER_SIZE_IN_BYTES; + + return room; +} + +static const struct tty_operations imxmcctty_ops = { + .install = mcctty_install, + .open = mcctty_open, + .close = mcctty_close, + .write = mcctty_write, + .write_room = mcctty_write_room, +}; + +static struct tty_driver *mcctty_driver; + +static int __init imxmcctty_init(void) +{ + int ret; + struct mcctty_port *cport = &mcc_tty_port; + + mcctty_driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_UNNUMBERED_NODE); + if (IS_ERR(mcctty_driver)) + return PTR_ERR(mcctty_driver); + + mcctty_driver->driver_name = "mcc_tty"; + mcctty_driver->name = "ttyMCC"; + mcctty_driver->major = TTYAUX_MAJOR; + mcctty_driver->minor_start = 3; + mcctty_driver->type = TTY_DRIVER_TYPE_PTY; + mcctty_driver->init_termios = tty_std_termios; + mcctty_driver->init_termios.c_cflag |= CLOCAL; + + tty_set_operations(mcctty_driver, &imxmcctty_ops); + + tty_port_init(&cport->port); + cport->port.ops = &mcctty_port_ops; + spin_lock_init(&cport->rx_lock); + spin_lock_init(&cport->tx_lock); + + ret = tty_register_driver(mcctty_driver); + if (ret < 0) { + pr_err("Couldn't install mcc tty driver: err %d\n", ret); + goto error; + } else + pr_info("Install mcc tty driver!\n"); + + /* Allocate the buffer we use for reading data */ + cport->rx_buf = kzalloc(MCC_ATTR_BUFFER_SIZE_IN_BYTES, + GFP_KERNEL); + if (!cport->rx_buf) { + ret = -ENOMEM; + goto error; + } + + INIT_DELAYED_WORK(&cport->read, mcctty_delay_work); + schedule_delayed_work(&cport->read, HZ/100); + return 0; + +error: + tty_unregister_driver(mcctty_driver); + put_tty_driver(mcctty_driver); + tty_port_destroy(&cport->port); + mcctty_driver = NULL; + + return ret; +} + +static void imxmcctty_exit(void) +{ + int ret = 0; + struct mcctty_port *cport = &mcc_tty_port; + + /* stop reading, null the read buffer. */ + kfree(cport->rx_buf); + cport->rx_buf = NULL; + + /* destory the mcc tty endpoint here */ + ret = mcc_destroy_endpoint(&mcc_endpoint_a9_pingpong); + if (ret) + pr_err("failed to destory a9 mcc ep.\n"); + else + pr_info("destory a9 mcc ep.\n"); + + tty_unregister_driver(mcctty_driver); + tty_port_destroy(&cport->port); + put_tty_driver(mcctty_driver); +} + +module_init(imxmcctty_init); +module_exit(imxmcctty_exit); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 0811633fcc4d..2718b1139948 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -143,7 +143,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, init.ops = &clk_mux_ro_ops; else init.ops = &clk_mux_ops; - init.flags = flags | CLK_IS_BASIC; + init.flags = flags | CLK_IS_BASIC | CLK_IS_BASIC_MUX; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 02e75d4e0a77..91894db43c50 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1567,6 +1567,7 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) */ int clk_set_parent(struct clk *clk, struct clk *parent) { + struct clk *child; int ret = 0; u8 p_index = 0; unsigned long p_rate = 0; @@ -1593,6 +1594,18 @@ int clk_set_parent(struct clk *clk, struct clk *parent) goto out; } + /* check two consecutive basic mux clocks */ + if (clk->flags & CLK_IS_BASIC_MUX) { + hlist_for_each_entry(child, &clk->children, child_node) { + if (child->flags & CLK_IS_BASIC_MUX) { + pr_err("%s: failed to switch parent of %s due to child mux %s\n", + __func__, clk->name, child->name); + ret = -EBUSY; + goto out; + } + } + } + /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(clk, parent); diff --git a/drivers/cpufreq/cpufreq-imx6.c b/drivers/cpufreq/cpufreq-imx6.c index c49c8eff8e79..2e457e0c1215 100644 --- a/drivers/cpufreq/cpufreq-imx6.c +++ b/drivers/cpufreq/cpufreq-imx6.c @@ -405,6 +405,12 @@ static int imx6_cpufreq_probe(struct platform_device *pdev) } imx6_soc_opp[i].arm_freq = freq; imx6_soc_opp[i].soc_volt = volt; +#ifdef CONFIG_MX6_VPU_352M + if (imx6_soc_opp[i].arm_freq == 792000) { + pr_info("increase SOC/PU voltage for VPU352MHz\n"); + imx6_soc_opp[i].soc_volt = 1250000; + } +#endif soc_opp_count++; } rcu_read_unlock(); diff --git a/drivers/dma/pxp/pxp_dma_v2.c b/drivers/dma/pxp/pxp_dma_v2.c index 2010c0171b5a..a310569c40e6 100644 --- a/drivers/dma/pxp/pxp_dma_v2.c +++ b/drivers/dma/pxp/pxp_dma_v2.c @@ -377,11 +377,8 @@ static void pxp_set_ctrl(struct pxps *pxp) ctrl |= BM_PXP_CTRL_VFLIP; if (proc_data->hflip) ctrl |= BM_PXP_CTRL_HFLIP; - if (proc_data->rotate) { + if (proc_data->rotate) ctrl |= BF_PXP_CTRL_ROTATE(proc_data->rotate / 90); - if (proc_data->rot_pos) - ctrl |= BM_PXP_CTRL_ROT_POS; - } /* In default, the block size is set to 8x8 * But block size can be set to 16x16 due to @@ -409,25 +406,20 @@ static void pxp_set_outbuf(struct pxps *pxp) __raw_writel(out_params->paddr, pxp->base + HW_PXP_OUT_BUF); - if (proc_data->rotate == 90 || proc_data->rotate == 270) { - if (proc_data->rot_pos == 1) - __raw_writel(BF_PXP_OUT_LRC_X(proc_data->drect.height - 1) | - BF_PXP_OUT_LRC_Y(proc_data->drect.width - 1), - pxp->base + HW_PXP_OUT_LRC); - else - __raw_writel(BF_PXP_OUT_LRC_X(proc_data->drect.width - 1) | - BF_PXP_OUT_LRC_Y(proc_data->drect.height - 1), - pxp->base + HW_PXP_OUT_LRC); - } else - __raw_writel(BF_PXP_OUT_LRC_X(proc_data->drect.width - 1) | - BF_PXP_OUT_LRC_Y(proc_data->drect.height - 1), + if (proc_data->rotate == 90 || proc_data->rotate == 270) + __raw_writel(BF_PXP_OUT_LRC_X(out_params->height - 1) | + BF_PXP_OUT_LRC_Y(out_params->width - 1), + pxp->base + HW_PXP_OUT_LRC); + else + __raw_writel(BF_PXP_OUT_LRC_X(out_params->width - 1) | + BF_PXP_OUT_LRC_Y(out_params->height - 1), pxp->base + HW_PXP_OUT_LRC); if (out_params->pixel_fmt == PXP_PIX_FMT_RGB24) { __raw_writel(out_params->stride * 3, pxp->base + HW_PXP_OUT_PITCH); } else if (out_params->pixel_fmt == PXP_PIX_FMT_BGRA32 || - out_params->pixel_fmt == PXP_PIX_FMT_RGB32) { + out_params->pixel_fmt == PXP_PIX_FMT_RGB32) { __raw_writel(out_params->stride << 2, pxp->base + HW_PXP_OUT_PITCH); } else if (out_params->pixel_fmt == PXP_PIX_FMT_RGB565) { @@ -512,22 +504,9 @@ static void pxp_set_oln(int layer_no, struct pxps *pxp) __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_LRC); } else { __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_ULC); - if (pxp_conf->proc_data.rotate == 90 || - pxp_conf->proc_data.rotate == 270) { - if (pxp_conf->proc_data.rot_pos == 1) { - __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->height - 1) | - BF_PXP_OUT_AS_LRC_Y(olparams_data->width - 1), - pxp->base + HW_PXP_OUT_AS_LRC); - } else { - __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | - BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), - pxp->base + HW_PXP_OUT_AS_LRC); - } - } else { - __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | + __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), pxp->base + HW_PXP_OUT_AS_LRC); - } } if ((olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) | @@ -589,12 +568,44 @@ static void pxp_set_s0param(struct pxps *pxp) struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_proc_data *proc_data = &pxp_conf->proc_data; struct pxp_layer_param *out_params = &pxp_conf->out_param; - u32 s0param; + u32 s0param_ulc, s0param_lrc; - if (proc_data->drect.left != 0 || proc_data->drect.top != 0) { - out_params->paddr += (proc_data->drect.top * out_params->stride + - proc_data->drect.left) * 2; - proc_data->drect.left = proc_data->drect.top = 0; + /* contains the coordinate for the PS in the OUTPUT buffer. */ + if ((pxp_conf->s0_param).width == 0 && + (pxp_conf->s0_param).height == 0) { + __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_PS_ULC); + __raw_writel(0x0, pxp->base + HW_PXP_OUT_PS_LRC); + } else { + switch (proc_data->rotate) { + case 0: + s0param_ulc = BF_PXP_OUT_PS_ULC_X(proc_data->drect.left); + s0param_ulc |= BF_PXP_OUT_PS_ULC_Y(proc_data->drect.top); + s0param_lrc = BF_PXP_OUT_PS_LRC_X(((s0param_ulc & BM_PXP_OUT_PS_ULC_X) >> 16) + proc_data->drect.width - 1); + s0param_lrc |= BF_PXP_OUT_PS_LRC_Y((s0param_ulc & BM_PXP_OUT_PS_ULC_Y) + proc_data->drect.height - 1); + break; + case 90: + s0param_ulc = BF_PXP_OUT_PS_ULC_Y(out_params->width - (proc_data->drect.left + proc_data->drect.width)); + s0param_ulc |= BF_PXP_OUT_PS_ULC_X(proc_data->drect.top); + s0param_lrc = BF_PXP_OUT_PS_LRC_X(((s0param_ulc & BM_PXP_OUT_PS_ULC_X) >> 16) + proc_data->drect.height - 1); + s0param_lrc |= BF_PXP_OUT_PS_LRC_Y((s0param_ulc & BM_PXP_OUT_PS_ULC_Y) + proc_data->drect.width - 1); + break; + case 180: + s0param_ulc = BF_PXP_OUT_PS_ULC_X(out_params->width - (proc_data->drect.left + proc_data->drect.width)); + s0param_ulc |= BF_PXP_OUT_PS_ULC_Y(out_params->height - (proc_data->drect.top + proc_data->drect.height)); + s0param_lrc = BF_PXP_OUT_PS_LRC_X(((s0param_ulc & BM_PXP_OUT_PS_ULC_X) >> 16) + proc_data->drect.width - 1); + s0param_lrc |= BF_PXP_OUT_PS_LRC_Y((s0param_ulc & BM_PXP_OUT_PS_ULC_Y) + proc_data->drect.height - 1); + break; + case 270: + s0param_ulc = BF_PXP_OUT_PS_ULC_X(out_params->height - (proc_data->drect.top + proc_data->drect.height)); + s0param_ulc |= BF_PXP_OUT_PS_ULC_Y(proc_data->drect.left); + s0param_lrc = BF_PXP_OUT_PS_LRC_X(((s0param_ulc & BM_PXP_OUT_PS_ULC_X) >> 16) + proc_data->drect.height - 1); + s0param_lrc |= BF_PXP_OUT_PS_LRC_Y((s0param_ulc & BM_PXP_OUT_PS_ULC_Y) + proc_data->drect.width - 1); + break; + default: + return; + } + __raw_writel(s0param_ulc, pxp->base + HW_PXP_OUT_PS_ULC); + __raw_writel(s0param_lrc, pxp->base + HW_PXP_OUT_PS_LRC); } /* Since user apps always pass the rotated drect @@ -608,33 +619,6 @@ static void pxp_set_s0param(struct pxps *pxp) proc_data->drect.width = proc_data->drect.height; proc_data->drect.height = temp; } - - /* contains the coordinate for the PS in the OUTPUT buffer. */ - if ((pxp_conf->s0_param).width == 0 && - (pxp_conf->s0_param).height == 0) { - __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_PS_ULC); - __raw_writel(0x0, pxp->base + HW_PXP_OUT_PS_LRC); - } else { - s0param = BF_PXP_OUT_PS_ULC_X(proc_data->drect.left); - s0param |= BF_PXP_OUT_PS_ULC_Y(proc_data->drect.top); - __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_ULC); - /* In PXP, the two different rotation - * position requires different settings - * on OUT_PS_LRC register - */ - if (proc_data->rot_pos == 1) { - s0param = BF_PXP_OUT_PS_LRC_X(proc_data->drect.left + - proc_data->drect.height - 1); - s0param |= BF_PXP_OUT_PS_LRC_Y(proc_data->drect.top + - proc_data->drect.width - 1); - } else { - s0param = BF_PXP_OUT_PS_LRC_X(proc_data->drect.left + - proc_data->drect.width - 1); - s0param |= BF_PXP_OUT_PS_LRC_Y(proc_data->drect.top + - proc_data->drect.height - 1); - } - __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_LRC); - } } /* crop behavior is re-designed in h/w. */ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cce31c3e5f76..d51103f5316a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1582,4 +1582,10 @@ config SENSORS_MMA8X5X If you say yes here you get support for the Freescale MMA8451/MMA8452/MMA8453/MMA8652/MMA8653 sensors. +config MXC_MMA8x5x + tristate "MMA8x5x device driver" + depends on I2C && SYSFS + default n + help + This configure is used by android sensor driver. endif # HWMON diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 76d65717d6b2..4fcafff867c3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -142,7 +142,9 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o -obj-$(CONFIG_SENSORS_MMA8X5X) += mma8x5x.o +obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o +obj-$(CONFIG_MXC_MMA8x5x) += mma8x5x.o + obj-$(CONFIG_PMBUS) += pmbus/ ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/mma8x5x.c b/drivers/hwmon/mma8x5x.c index 4b13a2521f86..d77dc515043f 100644 --- a/drivers/hwmon/mma8x5x.c +++ b/drivers/hwmon/mma8x5x.c @@ -34,6 +34,7 @@ #include <linux/hwmon.h> #include <linux/input.h> #include <linux/miscdevice.h> +#include <linux/regulator/consumer.h> #define MMA8X5X_I2C_ADDR 0x1D #define MMA8451_ID 0x1A @@ -155,12 +156,14 @@ struct mma8x5x_data { int position; u8 chip_id; int mode; - int awaken; // is just awake from suspend + int awaken; s64 period_rel; int fifo_wakeup; int fifo_timeout; + u32 int_pin; }; -static struct mma8x5x_data * p_mma8x5x_data = NULL; + +static struct mma8x5x_data *p_mma8x5x_data; /* Addresses scanned */ static const unsigned short normal_i2c[] = { 0x1c, 0x1d, I2C_CLIENT_END }; @@ -205,7 +208,8 @@ static int mma8x5x_data_convert(struct mma8x5x_data *pdata, for (i = 0; i < 3; i++) { data[i] = 0; for (j = 0; j < 3; j++) - data[i] += rawdata[j] * mma8x5x_position_setting[position][i][j]; + data[i] += rawdata[j] * + mma8x5x_position_setting[position][i][j]; } axis_data->x = data[0]; axis_data->y = data[1]; @@ -216,7 +220,8 @@ static int mma8x5x_check_id(int id) { int i = 0; - for (i = 0; i < sizeof(mma8x5x_chip_id) / sizeof(mma8x5x_chip_id[0]); i++) + for (i = 0; i < sizeof(mma8x5x_chip_id) / + sizeof(mma8x5x_chip_id[0]); i++) if (id == mma8x5x_chip_id[i]) return 1; return 0; @@ -226,7 +231,8 @@ static char *mma8x5x_id2name(u8 id) return mma8x5x_names[(id >> 4) - 1]; } -static int mma8x5x_i2c_read_fifo(struct i2c_client *client,u8 reg, char * buf, int len) +static int mma8x5x_i2c_read_fifo(struct i2c_client *client, + u8 reg, char *buf, int len) { char send_buf[] = {reg}; struct i2c_msg msgs[] = { @@ -251,42 +257,51 @@ static int mma8x5x_i2c_read_fifo(struct i2c_client *client,u8 reg, char * buf, i } /*period is ms, return the real period per event*/ -static s64 mma8x5x_odr_set(struct i2c_client * client, int period){ +static s64 mma8x5x_odr_set(struct i2c_client *client, int period) +{ u8 odr; u8 val; s64 period_rel; val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); - i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,val &(~0x01)); //standby + /*Standby*/ + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & (~0x01)); val &= ~(0x07 << 3); - if(period >= 640){ /*1.56HZ*/ + if (period >= 640) { + /*1.56HZ*/ odr = 0x7; period_rel = 640 * NSEC_PER_MSEC; - } - else if(period >= 160){ /*6.25HZ*/ + } else if (period >= 160) { + /*6.25HZ*/ odr = 0x06; period_rel = 160 * NSEC_PER_MSEC; - } - else if(period >= 80){ /*12.5HZ*/ + } else if (period >= 80) { + /*12.5HZ*/ odr = 0x05; period_rel = 80 * NSEC_PER_MSEC; - }else if(period >= 20){ /*50HZ*/ + } else if (period >= 20) { + /*50HZ*/ odr = 0x04; period_rel = 20 * NSEC_PER_MSEC; - }else if(period >= 10){ /*100HZ*/ + } else if (period >= 10) { + /*100HZ*/ odr = 0x03; period_rel = 10 * NSEC_PER_MSEC; - }else if(period >= 5){ /*200HZ*/ + } else if (period >= 5) { + /*200HZ*/ odr = 0x02; period_rel = 5 * NSEC_PER_MSEC; - }else if((period * 2) >= 5){ /*400HZ*/ + } else if ((period * 2) >= 5) { + /*400HZ*/ odr = 0x01; period_rel = 2500 * NSEC_PER_USEC; - }else{ /*800HZ*/ + } else { + /*800HZ*/ odr = 0x00; period_rel = 1250 * NSEC_PER_USEC; } val |= (odr << 3); - i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,val); //standby + /*Standby*/ + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val); return period_rel; } static int mma8x5x_device_init(struct i2c_client *client) @@ -335,66 +350,93 @@ static int mma8x5x_read_data(struct i2c_client *client, data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; return 0; } -static int mma8x5x_fifo_interrupt(struct i2c_client *client,int enable) + +static int mma8x5x_fifo_interrupt(struct i2c_client *client, int enable) { - u8 val,sys_mode; - sys_mode = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG1); - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,(sys_mode & (~0x01))); //standby - val = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG4); + u8 val, sys_mode; + sys_mode = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); + /*standby*/ + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, + (sys_mode & (~0x01))); + val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG4); val &= ~(0x01 << 6); - if(enable) + if (enable) val |= (0x01 << 6); - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG4,val); - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,sys_mode); + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG4, val); + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, sys_mode); return 0; } -static int mma8x5x_fifo_setting(struct mma8x5x_data *pdata,int time_out,int is_overwrite) + +static int mma8x5x_fifo_setting(struct mma8x5x_data *pdata, + int time_out, int is_overwrite) { - u8 val,sys_mode,pin_cfg; + u8 val, sys_mode, pin_cfg; struct i2c_client *client = pdata->client; - sys_mode = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG1); - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,(sys_mode & (~0x01))); //standby - pin_cfg = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG5); - val = i2c_smbus_read_byte_data(client,MMA8X5X_F_SETUP); + sys_mode = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); + /*standby*/ + i2c_smbus_write_byte_data(client, + MMA8X5X_CTRL_REG1, + (sys_mode & (~0x01))); + pin_cfg = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG5); + val = i2c_smbus_read_byte_data(client, MMA8X5X_F_SETUP); val &= ~(0x03 << 6); - if(time_out > 0){ - if(is_overwrite) + if (time_out > 0) { + if (is_overwrite) val |= (0x01 << 6); else val |= (0x02 << 6); } - i2c_smbus_write_byte_data(client,MMA8X5X_F_SETUP,val); - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG5,pin_cfg |(0x01 << 6));//route to pin 1 - i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,sys_mode); - if(time_out > 0){ - pdata->period_rel = mma8x5x_odr_set(client,time_out/32); //fifo len is 32 + i2c_smbus_write_byte_data(client, MMA8X5X_F_SETUP, val); + /*route to pin 1*/ + if (pdata->int_pin == 1) + i2c_smbus_write_byte_data(client, + MMA8X5X_CTRL_REG5, + pin_cfg | (0x01 << 6)); + /*route to pin 1*/ + else + i2c_smbus_write_byte_data(client, + MMA8X5X_CTRL_REG5, + pin_cfg & ~(0x01 << 6)); + + i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, sys_mode); + + if (time_out > 0) { + /*fifo len is 32*/ + pdata->period_rel = mma8x5x_odr_set(client, time_out/32); } return 0; } -static int mma8x5x_read_fifo_data(struct mma8x5x_data *pdata){ - int count,cnt; - u8 buf[256],val; - int i,index; +static int mma8x5x_read_fifo_data(struct mma8x5x_data *pdata) +{ + int count, cnt; + u8 buf[256], val; + int i, index; struct i2c_client *client = pdata->client; struct mma8x5x_fifo *pfifo = &pdata->fifo; struct timespec ts; - val = i2c_smbus_read_byte_data(client,MMA8X5X_STATUS); - if(val & (0x01 << 7)) //fifo overflow - { + val = i2c_smbus_read_byte_data(client, MMA8X5X_STATUS); + /*FIFO overflow*/ + if (val & (0x01 << 7)) { cnt = (val & 0x3f); - count = mma8x5x_i2c_read_fifo(client,MMA8X5X_OUT_X_MSB,buf,MMA8X5X_BUF_SIZE * cnt); - if(count > 0){ + count = mma8x5x_i2c_read_fifo(client, MMA8X5X_OUT_X_MSB, + buf, MMA8X5X_BUF_SIZE * cnt); + if (count > 0) { ktime_get_ts(&ts); - for(i = 0; i < count/MMA8X5X_BUF_SIZE ;i++){ + for (i = 0; i < count/MMA8X5X_BUF_SIZE ; i++) { index = MMA8X5X_BUF_SIZE * i; - pfifo->fifo_data[i].x = ((buf[index] << 8) & 0xff00) | buf[index + 1]; - pfifo->fifo_data[i].y = ((buf[index + 2] << 8) & 0xff00) | buf[index + 3]; - pfifo->fifo_data[i].z = ((buf[index + 4] << 8) & 0xff00) | buf[index + 5]; - mma8x5x_data_convert(pdata, &pfifo->fifo_data[i]); + pfifo->fifo_data[i].x = + ((buf[index] << 8) & 0xff00) | buf[index + 1]; + pfifo->fifo_data[i].y = + ((buf[index + 2] << 8) & 0xff00) | buf[index + 3]; + pfifo->fifo_data[i].z = + ((buf[index + 4] << 8) & 0xff00) | buf[index + 5]; + mma8x5x_data_convert(pdata, + &pfifo->fifo_data[i]); } pfifo->period = pdata->period_rel; pfifo->count = count / MMA8X5X_BUF_SIZE; - pfifo->timestamp = ((s64)ts.tv_sec) * NSEC_PER_SEC + ts.tv_nsec; + pfifo->timestamp = ((s64)ts.tv_sec) * NSEC_PER_SEC + + ts.tv_nsec; return 0; } } @@ -406,7 +448,7 @@ static void mma8x5x_report_data(struct mma8x5x_data *pdata) struct mma8x5x_data_axis data; int ret; ret = mma8x5x_read_data(pdata->client, &data); - if(!ret){ + if (!ret) { mma8x5x_data_convert(pdata, &data); input_report_abs(pdata->idev, ABS_X, data.x); input_report_abs(pdata->idev, ABS_Y, data.y); @@ -414,9 +456,10 @@ static void mma8x5x_report_data(struct mma8x5x_data *pdata) input_sync(pdata->idev); } } -static void mma8x5x_work(struct mma8x5x_data * pdata){ +static void mma8x5x_work(struct mma8x5x_data *pdata) +{ int delay; - if(pdata->active == MMA_ACTIVED){ + if (pdata->active == MMA_ACTIVED) { delay = msecs_to_jiffies(pdata->delay); if (delay >= HZ) delay = round_jiffies_relative(delay); @@ -425,7 +468,9 @@ static void mma8x5x_work(struct mma8x5x_data * pdata){ } static void mma8x5x_dev_poll(struct work_struct *work) { - struct mma8x5x_data *pdata = container_of(work, struct mma8x5x_data,work.work); + struct mma8x5x_data *pdata = container_of(work, + struct mma8x5x_data, + work.work); mma8x5x_report_data(pdata); mma8x5x_work(pdata); } @@ -434,24 +479,24 @@ static irqreturn_t mma8x5x_irq_handler(int irq, void *dev) int ret; u8 int_src; struct mma8x5x_data *pdata = (struct mma8x5x_data *)dev; - int_src = i2c_smbus_read_byte_data(pdata->client,MMA8X5X_INT_SOURCE); - if(int_src & (0x01 << 6)){ + int_src = i2c_smbus_read_byte_data(pdata->client, MMA8X5X_INT_SOURCE); + if (int_src & (0x01 << 6)) { ret = mma8x5x_read_fifo_data(pdata); - if(!ret){ + if (!ret) { atomic_set(&pdata->fifo_ready, 1); wake_up(&pdata->fifo_wq); } - if(pdata->awaken) /*is just awken from suspend*/ - { - mma8x5x_fifo_setting(pdata,pdata->fifo_timeout,0); //10s timeout - mma8x5x_fifo_interrupt(pdata->client,1); + /*is just awken from suspend*/ + if (pdata->awaken) { + /*10s timeout*/ + mma8x5x_fifo_setting(pdata, pdata->fifo_timeout, 0); + mma8x5x_fifo_interrupt(pdata->client, 1); pdata->awaken = 0; } } return IRQ_HANDLED; } - static ssize_t mma8x5x_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -480,34 +525,46 @@ static ssize_t mma8x5x_enable_store(struct device *dev, unsigned long enable; u8 val = 0; - enable = simple_strtoul(buf, NULL, 10); + ret = strict_strtol(buf, 10, &enable); + if (ret) { + dev_err(dev, "string to long error\n"); + return ret; + } mutex_lock(&pdata->data_lock); enable = (enable > 0) ? 1 : 0; if (enable && pdata->active == MMA_STANDBY) { val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); - ret = i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1, val | 0x01); + ret = i2c_smbus_write_byte_data(client, + MMA8X5X_CTRL_REG1, + val | 0x01); if (!ret) { pdata->active = MMA_ACTIVED; - if(pdata->fifo_timeout <= 0) //continuous mode + /*continuous mode*/ + if (pdata->fifo_timeout <= 0) mma8x5x_work(pdata); - else{ /*fifo mode*/ - mma8x5x_fifo_setting(pdata,pdata->fifo_timeout,0); //no overwirte fifo - mma8x5x_fifo_interrupt(client,1); + else { + /*fifo mode*/ + mma8x5x_fifo_setting(pdata, + pdata->fifo_timeout, 0); + mma8x5x_fifo_interrupt(client, 1); } - printk(KERN_INFO"mma enable setting active \n"); + printk(KERN_INFO"mma enable setting active\n"); } - } else if (enable == 0 && pdata->active == MMA_ACTIVED) { + } else if (enable == 0 && pdata->active == MMA_ACTIVED) { val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); - ret = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xFE); + ret = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, + val & 0xFE); if (!ret) { pdata->active = MMA_STANDBY; - if(pdata->fifo_timeout <= 0) //continuous mode + if (pdata->fifo_timeout <= 0) + /*continuous mode*/ cancel_delayed_work_sync(&pdata->work); - else{ /*fifo mode*/ - mma8x5x_fifo_setting(pdata,0,0); //no overwirte fifo - mma8x5x_fifo_interrupt(client,0); + else { + /*fifo mode*/ + mma8x5x_fifo_setting(pdata, 0, 0); + mma8x5x_fifo_interrupt(client, 0); } - printk(KERN_INFO"mma enable setting inactive \n"); + printk(KERN_INFO"mma enable setting inactive\n"); } } mutex_unlock(&pdata->data_lock); @@ -530,14 +587,20 @@ static ssize_t mma8x5x_delay_store(struct device *dev, const char *buf, size_t count) { struct mma8x5x_data *pdata = dev_get_drvdata(dev); - struct i2c_client * client = pdata->client; - int delay; - delay = simple_strtoul(buf, NULL, 10); + struct i2c_client *client = pdata->client; + int ret; + long delay; + ret = strict_strtol(buf, 10, &delay); + if (ret) { + dev_err(dev, "string to long error\n"); + return ret; + } + mutex_lock(&pdata->data_lock); cancel_delayed_work_sync(&pdata->work); - pdata->delay = delay; - if(pdata->active == MMA_ACTIVED && pdata->fifo_timeout <= 0){ - mma8x5x_odr_set(client,delay); + pdata->delay = (int)delay; + if (pdata->active == MMA_ACTIVED && pdata->fifo_timeout <= 0) { + mma8x5x_odr_set(client, (int)delay); mma8x5x_work(pdata); } mutex_unlock(&pdata->data_lock); @@ -551,9 +614,12 @@ static ssize_t mma8x5x_fifo_show(struct device *dev, struct mma8x5x_data *pdata = dev_get_drvdata(dev); mutex_lock(&pdata->data_lock); count = sprintf(&buf[count], "period poll :%d ms\n", pdata->delay); - count += sprintf(&buf[count],"period fifo :%lld ns\n",pdata->period_rel); - count += sprintf(&buf[count],"timeout :%d ms\n", pdata->fifo_timeout); - count += sprintf(&buf[count],"interrupt wake up: %s\n", (pdata->fifo_wakeup ? "yes" : "no")); /*is the interrupt enable*/ + count += sprintf(&buf[count], "period fifo :%lld ns\n", + pdata->period_rel); + count += sprintf(&buf[count], "timeout :%d ms\n", pdata->fifo_timeout); + /*is the interrupt enable*/ + count += sprintf(&buf[count], "interrupt wake up: %s\n", + (pdata->fifo_wakeup ? "yes" : "no")); mutex_unlock(&pdata->data_lock); return count; } @@ -563,27 +629,30 @@ static ssize_t mma8x5x_fifo_store(struct device *dev, const char *buf, size_t count) { struct mma8x5x_data *pdata = dev_get_drvdata(dev); - struct i2c_client * client = pdata->client; - int period,timeout,wakeup; - sscanf(buf,"%d,%d,%d",&period,&timeout,&wakeup); - printk("period %d ,timeout is %d, wake up is :%d\n",period,timeout,wakeup); - if(timeout > 0){ + struct i2c_client *client = pdata->client; + int period, timeout, wakeup; + sscanf(buf, "%d,%d,%d", &period, &timeout, &wakeup); + printk(KERN_INFO"period %d ,timeout is %d, wake up is :%d\n", + period, timeout, wakeup); + if (timeout > 0) { mutex_lock(&pdata->data_lock); cancel_delayed_work_sync(&pdata->work); pdata->delay = period; mutex_unlock(&pdata->data_lock); - mma8x5x_fifo_setting(pdata,timeout,0); //no overwirte fifo - mma8x5x_fifo_interrupt(client,1); + /*no overwirte fifo*/ + mma8x5x_fifo_setting(pdata, timeout, 0); + mma8x5x_fifo_interrupt(client, 1); pdata->fifo_timeout = timeout; pdata->fifo_wakeup = wakeup; - }else{ - mma8x5x_fifo_setting(pdata,timeout,0); //no overwirte fifo - mma8x5x_fifo_interrupt(client,0); + } else { + /*no overwirte fifo*/ + mma8x5x_fifo_setting(pdata, timeout, 0); + mma8x5x_fifo_interrupt(client, 0); pdata->fifo_timeout = timeout; pdata->fifo_wakeup = wakeup; mutex_lock(&pdata->data_lock); pdata->delay = period; - if(pdata->active == MMA_ACTIVED) + if (pdata->active == MMA_ACTIVED) mma8x5x_work(pdata); mutex_unlock(&pdata->data_lock); } @@ -606,10 +675,15 @@ static ssize_t mma8x5x_position_store(struct device *dev, const char *buf, size_t count) { struct mma8x5x_data *pdata = dev_get_drvdata(dev); - int position; - position = simple_strtoul(buf, NULL, 10); + int ret; + long position; + ret = strict_strtol(buf, 10, &position); + if (ret) { + dev_err(dev, "string to long error\n"); + return ret; + } mutex_lock(&pdata->data_lock); - pdata->position = position; + pdata->position = (int)position; mutex_unlock(&pdata->data_lock); return count; } @@ -646,12 +720,13 @@ static int mma8x5x_detect(struct i2c_client *client, chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I); if (!mma8x5x_check_id(chip_id)) return -ENODEV; - printk(KERN_INFO "check %s i2c address 0x%x \n", + printk(KERN_INFO"check %s i2c address 0x%x\n", mma8x5x_id2name(chip_id), client->addr); strlcpy(info->type, "mma8x5x", I2C_NAME_SIZE); return 0; } -static int mma8x5x_open(struct inode *inode, struct file *file){ +static int mma8x5x_open(struct inode *inode, struct file *file) +{ int err; err = nonseekable_open(inode, file); if (err) @@ -659,27 +734,33 @@ static int mma8x5x_open(struct inode *inode, struct file *file){ file->private_data = p_mma8x5x_data; return 0; } -static ssize_t mma8x5x_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){ +static ssize_t mma8x5x_read(struct file *file, + char __user *buf, + size_t size, loff_t *ppos) +{ struct mma8x5x_data *pdata = file->private_data; int ret = 0; if (!(file->f_flags & O_NONBLOCK)) { - ret = wait_event_interruptible(pdata->fifo_wq, (atomic_read(&pdata->fifo_ready) != 0)); + ret = wait_event_interruptible(pdata->fifo_wq, + (atomic_read(&pdata->fifo_ready) != 0)); if (ret) return ret; } if (!atomic_read(&pdata->fifo_ready)) return -ENODEV; - if(size < sizeof(struct mma8x5x_fifo)){ - printk(KERN_ERR "the buffer leght less than need\n"); + if (size < sizeof(struct mma8x5x_fifo)) { + printk(KERN_ERR"the buffer leght less than need\n"); return -ENOMEM; } - if(!copy_to_user(buf,&pdata->fifo,sizeof(struct mma8x5x_fifo))){ - atomic_set(&pdata->fifo_ready,0); + if (!copy_to_user(buf, &pdata->fifo, sizeof(struct mma8x5x_fifo))) { + atomic_set(&pdata->fifo_ready, 0); return size; } return -ENOMEM ; } -static unsigned int mma8x5x_poll(struct file * file, struct poll_table_struct * wait){ +static unsigned int mma8x5x_poll(struct file *file, + struct poll_table_struct *wait) +{ struct mma8x5x_data *pdata = file->private_data; poll_wait(file, &pdata->fifo_wq, wait); if (atomic_read(&pdata->fifo_ready)) @@ -707,8 +788,29 @@ static int mma8x5x_probe(struct i2c_client *client, struct input_dev *idev; struct mma8x5x_data *pdata; struct i2c_adapter *adapter; - struct device_node *of_node = client->dev.of_node; - u32 pos = 0; + struct device_node *of_node = client->dev.of_node; + u32 pos = 0; + struct regulator *vdd, *vdd_io; + u32 irq_flag; + struct irq_data *irq_data; + + vdd = devm_regulator_get(&client->dev, "vdd"); + if (!IS_ERR(vdd)) { + result = regulator_enable(vdd); + if (result) { + dev_err(&client->dev, "vdd set voltage error\n"); + return result; + } + } + + vdd_io = devm_regulator_get(&client->dev, "vddio"); + if (!IS_ERR(vdd_io)) { + result = regulator_enable(vdd_io); + if (result) { + dev_err(&client->dev, "vddio set voltage error\n"); + return result; + } + } adapter = to_i2c_adapter(client->dev.parent); result = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | @@ -716,7 +818,6 @@ static int mma8x5x_probe(struct i2c_client *client, if (!result) goto err_out; - printk(KERN_ERR"%s here 7", __func__); chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I); if (!mma8x5x_check_id(chip_id)) { @@ -734,37 +835,30 @@ static int mma8x5x_probe(struct i2c_client *client, goto err_out; } - printk(KERN_ERR"%s here 6", __func__); /* Initialize the MMA8X5X chip */ - memset(pdata,0,sizeof(struct mma8x5x_data)); + memset(pdata, 0, sizeof(struct mma8x5x_data)); pdata->client = client; pdata->chip_id = chip_id; pdata->mode = MODE_2G; pdata->fifo_wakeup = 0; pdata->fifo_timeout = 0; - printk(KERN_ERR"%s here 0", __func__); - result = of_property_read_u32(of_node, "position",&pos ); - - printk(KERN_ERR"%s here result=%d pos=%d", __func__, result, pos); - if (result) - pos = 1; + result = of_property_read_u32(of_node, "position", &pos); + if (result) + pos = 1; pdata->position = (int)pos; p_mma8x5x_data = pdata; mutex_init(&pdata->data_lock); i2c_set_clientdata(client, pdata); - printk(KERN_ERR"%s here 12", __func__); mma8x5x_device_init(client); - printk(KERN_ERR"%s here 13", __func__); idev = input_allocate_device(); if (!idev) { result = -ENOMEM; dev_err(&client->dev, "alloc input device failed!\n"); goto err_alloc_input_device; } - printk(KERN_ERR"%s here 1", __func__); idev->name = "FreescaleAccelerometer"; idev->uniq = mma8x5x_id2name(pdata->chip_id); idev->id.bustype = BUS_I2C; @@ -772,9 +866,8 @@ static int mma8x5x_probe(struct i2c_client *client, input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0); input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0); input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0); - dev_set_drvdata(&idev->dev,pdata); - pdata->idev= idev ; - printk(KERN_ERR"%s here 2", __func__); + dev_set_drvdata(&idev->dev, pdata); + pdata->idev = idev; result = input_register_device(pdata->idev); if (result) { dev_err(&client->dev, "register input device failed!\n"); @@ -789,28 +882,53 @@ static int mma8x5x_probe(struct i2c_client *client, goto err_create_sysfs; } init_waitqueue_head(&pdata->fifo_wq); - if(client->irq){ - printk(KERN_ERR"%s here 3", __func__); - result= request_threaded_irq(client->irq,NULL, mma8x5x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->dev.driver->name, pdata); + + if (client->irq) { + irq_data = irq_get_irq_data(client->irq); + irq_flag = irqd_get_trigger_type(irq_data); + irq_flag |= IRQF_ONESHOT; + result = request_threaded_irq(client->irq, NULL, + mma8x5x_irq_handler, + irq_flag, + client->dev.driver->name, + pdata); if (result < 0) { - dev_err(&client->dev, "failed to register MMA8x5x irq %d!\n", + dev_err(&client->dev, + "failed to register MMA8x5x irq %d!\n", client->irq); goto err_register_irq; - }else{ + } else { result = misc_register(&mma8x5x_dev); if (result) { - dev_err(&client->dev,"register fifo device error\n"); + dev_err(&client->dev, + "register fifo device error\n"); goto err_reigster_dev; } } + + result = of_property_read_u32(of_node, + "interrupt-route", + &pdata->int_pin); + if (result) { + result = -EINVAL; + dev_err(&client->dev, + "Can't find interrupt-pin value\n"); + goto err_reigster_dev; + + } + if (pdata->int_pin == 0 || pdata->int_pin > 2) { + result = -EINVAL; + dev_err(&client->dev, + "The interrupt-pin value is invalid\n"); + goto err_reigster_dev; + } } printk(KERN_INFO"mma8x5x device driver probe successfully\n"); return 0; err_reigster_dev: - free_irq(client->irq,pdata); + free_irq(client->irq, pdata); err_register_irq: - sysfs_remove_group(&idev->dev.kobj,&mma8x5x_attr_group); + sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group); err_create_sysfs: input_unregister_device(pdata->idev); err_register_input_device: @@ -820,13 +938,14 @@ err_alloc_input_device: err_out: return result; } + static int mma8x5x_remove(struct i2c_client *client) { struct mma8x5x_data *pdata = i2c_get_clientdata(client); struct input_dev *idev = pdata->idev; mma8x5x_device_stop(client); if (pdata) { - sysfs_remove_group(&idev->dev.kobj,&mma8x5x_attr_group); + sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group); input_unregister_device(pdata->idev); input_free_device(pdata->idev); kfree(pdata); @@ -839,17 +958,19 @@ static int mma8x5x_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mma8x5x_data *pdata = i2c_get_clientdata(client); - if(pdata->fifo_timeout <= 0){ + if (pdata->fifo_timeout <= 0) { if (pdata->active == MMA_ACTIVED) mma8x5x_device_stop(client); - }else{ - if (pdata->active == MMA_ACTIVED){ - if (pdata->fifo_wakeup){ - mma8x5x_fifo_setting(pdata,10000,0); //10s timeout , overwrite - mma8x5x_fifo_interrupt(client,1); - }else{ - mma8x5x_fifo_interrupt(client,0); - mma8x5x_fifo_setting(pdata,10000,1); //10s timeout , overwrite + } else { + if (pdata->active == MMA_ACTIVED) { + if (pdata->fifo_wakeup) { + /*10s timeout , overwrite*/ + mma8x5x_fifo_setting(pdata, 10000, 0); + mma8x5x_fifo_interrupt(client, 1); + } else { + mma8x5x_fifo_interrupt(client, 0); + /*10s timeout , overwrite*/ + mma8x5x_fifo_setting(pdata, 10000, 1); } } } @@ -861,15 +982,18 @@ static int mma8x5x_resume(struct device *dev) int val = 0; struct i2c_client *client = to_i2c_client(dev); struct mma8x5x_data *pdata = i2c_get_clientdata(client); - if(pdata->fifo_timeout <= 0){ + if (pdata->fifo_timeout <= 0) { if (pdata->active == MMA_ACTIVED) { - val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); - i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val | 0x01); + val = i2c_smbus_read_byte_data(client, + MMA8X5X_CTRL_REG1); + i2c_smbus_write_byte_data(client, + MMA8X5X_CTRL_REG1, val | 0x01); } - }else{ + } else { if (pdata->active == MMA_ACTIVED) { - mma8x5x_fifo_interrupt(client,1); - pdata->awaken = 1; //awake from suspend + mma8x5x_fifo_interrupt(client, 1); + /*Awake from suspend*/ + pdata->awaken = 1; } } return 0; @@ -878,9 +1002,14 @@ static int mma8x5x_resume(struct device *dev) #endif static const struct i2c_device_id mma8x5x_id[] = { - { "mma8x5x", 0 }, - { } + {"mma8451", 0}, + {"mma8452", 0}, + {"mma8453", 0}, + {"mma8652", 0}, + {"mma8653", 0}, + {} }; + MODULE_DEVICE_TABLE(i2c, mma8x5x_id); static SIMPLE_DEV_PM_OPS(mma8x5x_pm_ops, mma8x5x_suspend, mma8x5x_resume); diff --git a/drivers/media/platform/mxc/capture/csi_v4l2_capture.c b/drivers/media/platform/mxc/capture/csi_v4l2_capture.c index 3da162860205..3f759b635f8b 100644 --- a/drivers/media/platform/mxc/capture/csi_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/csi_v4l2_capture.c @@ -48,8 +48,6 @@ static int req_buf_number; static int csi_v4l2_master_attach(struct v4l2_int_device *slave); static void csi_v4l2_master_detach(struct v4l2_int_device *slave); static u8 camera_power(cam_data *cam, bool cameraOn); -static struct v4l2_format cam_input_fmt; -static bool bswapenable; /*! Information about this driver. */ static struct v4l2_int_master csi_v4l2_master = { @@ -57,15 +55,6 @@ static struct v4l2_int_master csi_v4l2_master = { .detach = csi_v4l2_master_detach, }; -static struct v4l2_int_device csi_v4l2_int_device = { - .module = THIS_MODULE, - .name = "csi_v4l2_cap", - .type = v4l2_int_type_master, - .u = { - .master = &csi_v4l2_master, - }, -}; - static struct v4l2_queryctrl pxp_controls[] = { { .id = V4L2_CID_HFLIP, @@ -307,11 +296,12 @@ static int pxp_process_update(cam_data *cam) /* * Configure PxP for processing of new v4l2 buf */ - pxp_conf->s0_param.pixel_fmt = v4l2_fmt_2_pxp_fmt(cam_input_fmt.fmt.pix.pixelformat); + pxp_conf->s0_param.pixel_fmt = + v4l2_fmt_2_pxp_fmt(cam->input_fmt.fmt.pix.pixelformat); pxp_conf->s0_param.color_key = -1; pxp_conf->s0_param.color_key_enable = false; - pxp_conf->s0_param.width = cam_input_fmt.fmt.pix.width; - pxp_conf->s0_param.height = cam_input_fmt.fmt.pix.height; + pxp_conf->s0_param.width = cam->input_fmt.fmt.pix.width; + pxp_conf->s0_param.height = cam->input_fmt.fmt.pix.height; pxp_conf->ol_param[0].combine_enable = false; @@ -462,12 +452,12 @@ next: list_del(cam->ready_q.next); list_add_tail(&ready_frame->queue, &cam->working_q); - __raw_writel(ready_frame->paddress, + csi_write(cam->csi_soc, ready_frame->paddress, cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : CSI_CSIDMASA_FB2); ready_frame->csi_buf_num = cam->ping_pong_csi; } else { - __raw_writel(cam->dummy_frame.paddress, + csi_write(cam->csi_soc, cam->dummy_frame.paddress, cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : CSI_CSIDMASA_FB2); } @@ -488,10 +478,10 @@ static int csi_cap_image(cam_data *cam) { unsigned int value; - value = __raw_readl(CSI_CSICR3); - __raw_writel(value | BIT_FRMCNT_RST, CSI_CSICR3); - value = __raw_readl(CSI_CSISR); - __raw_writel(value, CSI_CSISR); + value = csi_read(cam->csi_soc, CSI_CSICR3); + csi_write(cam->csi_soc, value | BIT_FRMCNT_RST, CSI_CSICR3); + value = csi_read(cam->csi_soc, CSI_CSISR); + csi_write(cam->csi_soc, value, CSI_CSISR); return 0; } @@ -711,7 +701,7 @@ static int csi_streamon(cam_data *cam) frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); list_del(cam->ready_q.next); list_add_tail(&frame->queue, &cam->working_q); - __raw_writel(frame->paddress, CSI_CSIDMASA_FB1); + csi_write(cam->csi_soc, frame->paddress, CSI_CSIDMASA_FB1); frame->csi_buf_num = 1; if (list_empty(&cam->ready_q)) { @@ -723,7 +713,7 @@ static int csi_streamon(cam_data *cam) frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); list_del(cam->ready_q.next); list_add_tail(&frame->queue, &cam->working_q); - __raw_writel(frame->paddress, CSI_CSIDMASA_FB2); + csi_write(cam->csi_soc, frame->paddress, CSI_CSIDMASA_FB2); frame->csi_buf_num = 2; spin_unlock_irqrestore(&cam->queue_int_lock, flags); @@ -733,12 +723,13 @@ static int csi_streamon(cam_data *cam) local_irq_save(flags); for (timeout = 1000000; timeout > 0; timeout--) { - if (__raw_readl(CSI_CSISR) & BIT_SOF_INT) { - val = __raw_readl(CSI_CSICR3); - __raw_writel(val | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + if (csi_read(cam->csi_soc, CSI_CSISR) & BIT_SOF_INT) { + val = csi_read(cam->csi_soc, CSI_CSICR3); + csi_write(cam->csi_soc, val | BIT_DMA_REFLASH_RFF, + CSI_CSICR3); /* Wait DMA reflash done */ for (timeout2 = 1000000; timeout2 > 0; timeout2--) { - if (__raw_readl(CSI_CSICR3) & + if (csi_read(cam->csi_soc, CSI_CSICR3) & BIT_DMA_REFLASH_RFF) cpu_relax(); else @@ -750,9 +741,9 @@ static int csi_streamon(cam_data *cam) return -ETIME; } - csi_dmareq_rff_enable(); - csi_enable_int(1); - csi_enable(1); + csi_dmareq_rff_enable(cam->csi_soc); + csi_enable_int(cam, 1); + csi_enable(cam, 1); break; } else cpu_relax(); @@ -781,21 +772,21 @@ static int csi_streamoff(cam_data *cam) if (cam->capture_on == false) return 0; - csi_dmareq_rff_disable(); - csi_disable_int(); + csi_dmareq_rff_disable(cam->csi_soc); + csi_disable_int(cam); cam->capture_on = false; /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ - __raw_writel(0, CSI_CSIDMASA_FB1); - __raw_writel(0, CSI_CSIDMASA_FB2); + csi_write(cam->csi_soc, 0, CSI_CSIDMASA_FB1); + csi_write(cam->csi_soc, 0, CSI_CSIDMASA_FB2); if (strcmp(csi_capture_inputs[cam->current_input].name, "Vadc") == 0) { - csi_buf_stride_set(0); - csi_deinterlace_enable(false); - csi_tvdec_enable(false); + csi_buf_stride_set(cam, 0); + csi_deinterlace_enable(cam, false); + csi_tvdec_enable(cam, false); } - csi_enable(0); + csi_enable(cam, 0); csi_free_frames(cam); csi_free_frame_buf(cam); @@ -814,11 +805,13 @@ static int start_preview(cam_data *cam) { unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base; - __raw_writel(fb_addr, CSI_CSIDMASA_FB1); - __raw_writel(fb_addr, CSI_CSIDMASA_FB2); - __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + csi_write(cam->csi_soc, fb_addr, CSI_CSIDMASA_FB1); + csi_write(cam->csi_soc, fb_addr, CSI_CSIDMASA_FB2); + csi_write(cam->csi_soc, + csi_read(cam->csi_soc, CSI_CSICR3) | BIT_DMA_REFLASH_RFF, + CSI_CSICR3); - csi_enable_int(0); + csi_enable_int(cam, 0); return 0; } @@ -832,12 +825,14 @@ static int start_preview(cam_data *cam) */ static int stop_preview(cam_data *cam) { - csi_disable_int(); + csi_disable_int(cam); /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ - __raw_writel(0, CSI_CSIDMASA_FB1); - __raw_writel(0, CSI_CSIDMASA_FB2); - __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + csi_write(cam->csi_soc, 0, CSI_CSIDMASA_FB1); + csi_write(cam->csi_soc, 0, CSI_CSIDMASA_FB2); + csi_write(cam->csi_soc, + csi_read(cam->csi_soc, CSI_CSICR3) | BIT_DMA_REFLASH_RFF, + CSI_CSICR3); return 0; } @@ -948,7 +943,8 @@ static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) } /* disable swap function */ - csi_format_swap16(false); + csi_format_swap16(cam, false); + cam->bswapenable = false; switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB565: @@ -962,17 +958,17 @@ static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) case V4L2_PIX_FMT_UYVY: size = f->fmt.pix.width * f->fmt.pix.height * 2; bytesperline = f->fmt.pix.width * 2; - if (cam_input_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { - csi_format_swap16(true); - bswapenable = true; + if (cam->input_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + csi_format_swap16(cam, true); + cam->bswapenable = true; } break; case V4L2_PIX_FMT_YUYV: size = f->fmt.pix.width * f->fmt.pix.height * 2; bytesperline = f->fmt.pix.width * 2; - if (cam_input_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) { - csi_format_swap16(true); - bswapenable = true; + if (cam->input_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) { + csi_format_swap16(cam, true); + cam->bswapenable = true; } break; case V4L2_PIX_FMT_YUV420: @@ -1000,8 +996,8 @@ static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) else size = f->fmt.pix.sizeimage; - if (cam_input_fmt.fmt.pix.sizeimage > f->fmt.pix.sizeimage) - f->fmt.pix.sizeimage = cam_input_fmt.fmt.pix.sizeimage; + if (cam->input_fmt.fmt.pix.sizeimage > f->fmt.pix.sizeimage) + f->fmt.pix.sizeimage = cam->input_fmt.fmt.pix.sizeimage; cam->v2f.fmt.pix = f->fmt.pix; @@ -1084,32 +1080,36 @@ static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) } vidioc_int_g_ifparm(cam->sensor, &ifparm); - cam_input_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vidioc_int_g_fmt_cap(cam->sensor, &cam_input_fmt); + cam->input_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam->input_fmt); pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", - cam_input_fmt.fmt.pix.width, cam_input_fmt.fmt.pix.height); + cam->input_fmt.fmt.pix.width, cam->input_fmt.fmt.pix.height); - f = &cam_input_fmt; + f = &cam->input_fmt; switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_YUV444: size = f->fmt.pix.width * f->fmt.pix.height * 4; - csi_set_32bit_imagpara(f->fmt.pix.width, + csi_set_32bit_imagpara(cam, + f->fmt.pix.width, f->fmt.pix.height); break; case V4L2_PIX_FMT_UYVY: size = f->fmt.pix.width * f->fmt.pix.height * 2; - csi_set_16bit_imagpara(f->fmt.pix.width, + csi_set_16bit_imagpara(cam, + f->fmt.pix.width, f->fmt.pix.height); break; case V4L2_PIX_FMT_YUYV: size = f->fmt.pix.width * f->fmt.pix.height * 2; - csi_set_16bit_imagpara(f->fmt.pix.width, + csi_set_16bit_imagpara(cam, + f->fmt.pix.width, f->fmt.pix.height); break; case V4L2_PIX_FMT_YUV420: size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; - csi_set_12bit_imagpara(f->fmt.pix.width, + csi_set_12bit_imagpara(cam, + f->fmt.pix.width, f->fmt.pix.height); break; case V4L2_PIX_FMT_YUV422P: @@ -1125,8 +1125,8 @@ static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) f->fmt.pix.sizeimage = size; cam->crop_bounds.top = cam->crop_bounds.left = 0; - cam->crop_bounds.width = cam_input_fmt.fmt.pix.width; - cam->crop_bounds.height = cam_input_fmt.fmt.pix.height; + cam->crop_bounds.width = cam->input_fmt.fmt.pix.width; + cam->crop_bounds.height = cam->input_fmt.fmt.pix.height; cam->crop_current.width = cam->crop_bounds.width; cam->crop_current.height = cam->crop_bounds.height; @@ -1201,9 +1201,9 @@ static int csi_v4l_s_std(cam_data *cam, v4l2_std_id e) strcpy(cam->standard.name, video_fmts[video_index].name); /* Enable csi PAL/NTSC deinterlace mode */ - csi_buf_stride_set(video_fmts[video_index].active_width); - csi_deinterlace_mode(cam->standard.id); - csi_deinterlace_enable(true); + csi_buf_stride_set(cam, video_fmts[video_index].active_width); + csi_deinterlace_mode(cam, cam->standard.id); + csi_deinterlace_enable(cam, true); /* crop will overwrite */ cam->crop_bounds.width = video_fmts[video_index].active_width; @@ -1231,7 +1231,7 @@ static int csi_v4l_g_std(cam_data *cam, v4l2_std_id *e) { struct v4l2_format tv_fmt; - pr_debug("In csi_v4l2_g_std\n"); + pr_debug("In csi_v4l2_g_std, cam->csi %d\n", cam->csi); if (cam->device_type == 1) { /* Use this function to get what the TV-In device detects the @@ -1256,13 +1256,13 @@ static int csi_v4l_g_std(cam_data *cam, v4l2_std_id *e) return 0; } -static void csi_input_select(int input_select) +static void csi_input_select(cam_data *cam) { - if (strcmp(csi_capture_inputs[input_select].name, "Vadc") == 0) + if (strcmp(csi_capture_inputs[cam->current_input].name, "Vadc") == 0) /* Enable csi tvdec */ - csi_tvdec_enable(true); + csi_tvdec_enable(cam, true); else - csi_tvdec_enable(false); + csi_tvdec_enable(cam, false); } /*! @@ -1332,8 +1332,8 @@ static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) * If want to do preview on LCD, use PxP CSC to convert from UYVY * to RGB565; but for encoding, usually we don't use RGB format. */ - if (cam->v2f.fmt.pix.pixelformat != cam_input_fmt.fmt.pix.pixelformat - && !bswapenable) { + if (cam->v2f.fmt.pix.pixelformat != cam->input_fmt.fmt.pix.pixelformat + && !cam->bswapenable) { sg_dma_address(&cam->sg[0]) = buf->m.offset; /* last frame buffer as pxp output buffer */ sg_dma_address(&cam->sg[1]) = @@ -1467,7 +1467,6 @@ static int csi_v4l_close(struct file *file) file->private_data = NULL; vidioc_int_s_power(cam->sensor, 0); clk_disable_unprepare(sensor->sensor_clk); - csi_clk_disable(); } return err; @@ -1510,19 +1509,23 @@ static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count, return -ENOMEM; } cam->still_counter = 0; - __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB2); - __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB1); - __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, - CSI_CSICR3); - __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR); - __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, - CSI_CSICR3); - csi_enable_int(1); - csi_enable(1); + csi_write(cam->csi_soc, cam->still_buf[0], CSI_CSIDMASA_FB2); + csi_write(cam->csi_soc, cam->still_buf[0], CSI_CSIDMASA_FB1); + csi_write(cam->csi_soc, + csi_read(cam->csi_soc, CSI_CSICR3) | + BIT_DMA_REFLASH_RFF, + CSI_CSICR3); + csi_write(cam->csi_soc, csi_read(cam->csi_soc, CSI_CSISR), + CSI_CSISR); + csi_write(cam->csi_soc, + csi_read(cam->csi_soc, CSI_CSICR3) | BIT_FRMCNT_RST, + CSI_CSICR3); + csi_enable_int(cam, 1); + csi_enable(cam, 1); } wait_event_interruptible(cam->still_queue, cam->still_counter); - csi_disable_int(); + csi_disable_int(cam); err = copy_to_user(buf, cam->still_buf_vaddr, cam->v2f.fmt.pix.sizeimage); @@ -1965,7 +1968,7 @@ static long csi_v4l_do_ioctl(struct file *file, cam->current_input = *index; - csi_input_select(cam->current_input); + csi_input_select(cam); break; } case VIDIOC_G_OUTPUT: @@ -2066,11 +2069,20 @@ static struct video_device csi_v4l_template = { * * @return status 0 Success */ -static void init_camera_struct(cam_data *cam) +static void init_camera_struct(cam_data *cam, struct platform_device *pdev) { struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + struct device_node *np = pdev->dev.of_node; + int ret = 0; + int csi_id; pr_debug("In MVC: %s\n", __func__); + ret = of_property_read_u32(np, "csi_id", &csi_id); + if (ret) { + dev_err(&pdev->dev, "csi_id missing or invalid\n"); + return; + } + proc_data->hflip = 0; proc_data->vflip = 0; proc_data->rotate = 0; @@ -2082,6 +2094,9 @@ static void init_camera_struct(cam_data *cam) sema_init(&cam->param_lock, 1); sema_init(&cam->busy_lock, 1); + /* TODO sanity check */ + cam->csi_soc = csi_get_soc(csi_id); + cam->video_dev = video_device_alloc(); if (cam->video_dev == NULL) return; @@ -2126,11 +2141,18 @@ static void init_camera_struct(cam_data *cam) cam->crop_bounds.height = 480; cam->crop_current = cam->crop_defrect = cam->crop_bounds; + cam->csi = csi_id; cam->enc_callback = camera_callback; csi_start_callback(cam); init_waitqueue_head(&cam->power_queue); spin_lock_init(&cam->queue_int_lock); spin_lock_init(&cam->dqueue_int_lock); + + cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); + cam->self->module = THIS_MODULE; + sprintf(cam->self->name, "csi_v4l2_cap%d", cam->csi); + cam->self->type = v4l2_int_type_master; + cam->self->u.master = &csi_v4l2_master; } /*! @@ -2172,14 +2194,13 @@ static int csi_v4l2_probe(struct platform_device *pdev) err = -ENOMEM; goto out; } - memset(&cam_input_fmt, 0, sizeof(cam_input_fmt)); - init_camera_struct(g_cam); + memset(&g_cam->input_fmt, 0, sizeof(g_cam->input_fmt)); + init_camera_struct(g_cam, pdev); platform_set_drvdata(pdev, (void *)g_cam); /* Set up the v4l2 device and register it */ - csi_v4l2_int_device.priv = g_cam; - /* This function contains a bug that won't let this be rmmod'd. */ - v4l2_int_device_register(&csi_v4l2_int_device); + g_cam->self->priv = g_cam; + v4l2_int_device_register(g_cam->self); /* register v4l video device */ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr) @@ -2209,7 +2230,7 @@ static int csi_v4l2_remove(struct platform_device *pdev) "-- setting ops to NULL\n"); } else { pr_info("V4L2 freeing image input device\n"); - v4l2_int_device_unregister(&csi_v4l2_int_device); + v4l2_int_device_unregister(g_cam->self); csi_stop_callback(g_cam); video_unregister_device(g_cam->video_dev); platform_set_drvdata(pdev, NULL); @@ -2304,17 +2325,23 @@ static struct platform_driver csi_v4l2_driver = { static int csi_v4l2_master_attach(struct v4l2_int_device *slave) { cam_data *cam = slave->u.slave->master->priv; + struct sensor_data *sdata = slave->priv; struct v4l2_format cam_fmt; pr_debug("In MVC: %s\n", __func__); pr_debug(" slave.name = %s\n", slave->name); pr_debug(" master.name = %s\n", slave->u.slave->master->name); - cam->sensor = slave; if (slave == NULL) { pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); return -1; } + if (sdata->csi != cam->csi) { + pr_debug("%s: csi doesn't match\n", __func__); + return -1; + } + + cam->sensor = slave; cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); diff --git a/drivers/media/platform/mxc/capture/fsl_csi.c b/drivers/media/platform/mxc/capture/fsl_csi.c index 2839e9fa0d8e..602899d70ce0 100644 --- a/drivers/media/platform/mxc/capture/fsl_csi.c +++ b/drivers/media/platform/mxc/capture/fsl_csi.c @@ -33,9 +33,9 @@ #include "mxc_v4l2_capture.h" #include "fsl_csi.h" -void __iomem *csi_regbase; -EXPORT_SYMBOL(csi_regbase); -static int irq_nr; +#define CSI_MAX_NUM 2 +struct csi_soc csi_array[CSI_MAX_NUM], *csi; + static csi_irq_callback_t g_callback; static void *g_callback_data; static struct clk *disp_axi_clk; @@ -61,9 +61,10 @@ EXPORT_SYMBOL(csi_clk_disable); static irqreturn_t csi_irq_handler(int irq, void *data) { cam_data *cam = (cam_data *) data; - unsigned long status = __raw_readl(CSI_CSISR); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long status = __raw_readl(csi->regbase + CSI_CSISR); - __raw_writel(status, CSI_CSISR); + __raw_writel(status, csi->regbase + CSI_CSISR); if (status & BIT_HRESP_ERR_INT) pr_warning("Hresponse error is detected.\n"); @@ -100,24 +101,25 @@ static irqreturn_t csi_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static void csihw_reset_frame_count(void) +static void csihw_reset_frame_count(struct csi_soc *csi) { - __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3); + __raw_writel(__raw_readl(csi->regbase + CSI_CSICR3) | BIT_FRMCNT_RST, + csi->regbase + CSI_CSICR3); } -static void csihw_reset(void) +static void csihw_reset(struct csi_soc *csi) { - csihw_reset_frame_count(); - __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1); - __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2); - __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3); + csihw_reset_frame_count(csi); + __raw_writel(CSICR1_RESET_VAL, csi->regbase + CSI_CSICR1); + __raw_writel(CSICR2_RESET_VAL, csi->regbase + CSI_CSICR2); + __raw_writel(CSICR3_RESET_VAL, csi->regbase + CSI_CSICR3); } /*! * csi_init_interface * Init csi interface */ -void csi_init_interface(void) +static void csi_init_interface(struct csi_soc *csi) { unsigned int val = 0; unsigned int imag_para; @@ -129,22 +131,22 @@ void csi_init_interface(void) val |= BIT_FCC; val |= 1 << SHIFT_MCLKDIV; val |= BIT_MCLKEN; - __raw_writel(val, CSI_CSICR1); + __raw_writel(val, csi->regbase + CSI_CSICR1); imag_para = (640 << 16) | 960; - __raw_writel(imag_para, CSI_CSIIMAG_PARA); + __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); val = 0x1010; val |= BIT_DMA_REFLASH_RFF; - __raw_writel(val, CSI_CSICR3); + __raw_writel(val, csi->regbase + CSI_CSICR3); } -EXPORT_SYMBOL(csi_init_interface); -void csi_format_swap16(bool enable) +void csi_format_swap16(cam_data *cam, bool enable) { + struct csi_soc *csi = &csi_array[cam->csi]; unsigned int val; - val = __raw_readl(CSI_CSICR1); + val = __raw_readl(csi->regbase + CSI_CSICR1); if (enable) { val |= BIT_PACK_DIR; val |= BIT_SWAP16_EN; @@ -153,7 +155,7 @@ void csi_format_swap16(bool enable) val &= ~BIT_SWAP16_EN; } - __raw_writel(val, CSI_CSICR1); + __raw_writel(val, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_format_swap16); @@ -172,7 +174,8 @@ void csi_start_callback(void *data) { cam_data *cam = (cam_data *) data; - if (request_irq(irq_nr, csi_irq_handler, 0, "csi", cam) < 0) + if (request_irq(csi_array[cam->csi].irq_nr, csi_irq_handler, 0, "csi", + cam) < 0) pr_debug("CSI error: irq request fail\n"); } @@ -182,13 +185,14 @@ void csi_stop_callback(void *data) { cam_data *cam = (cam_data *) data; - free_irq(irq_nr, cam); + free_irq(csi_array[cam->csi].irq_nr, cam); } EXPORT_SYMBOL(csi_stop_callback); -void csi_enable_int(int arg) +void csi_enable_int(cam_data *cam, int arg) { - unsigned long cr1 = __raw_readl(CSI_CSICR1); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); cr1 |= BIT_SOF_INTEN; if (arg == 1) { @@ -196,69 +200,76 @@ void csi_enable_int(int arg) cr1 |= BIT_FB1_DMA_DONE_INTEN; cr1 |= BIT_FB2_DMA_DONE_INTEN; } - __raw_writel(cr1, CSI_CSICR1); + __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_enable_int); -void csi_disable_int(void) +void csi_disable_int(cam_data *cam) { - unsigned long cr1 = __raw_readl(CSI_CSICR1); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); cr1 &= ~BIT_SOF_INTEN; cr1 &= ~BIT_FB1_DMA_DONE_INTEN; cr1 &= ~BIT_FB2_DMA_DONE_INTEN; - __raw_writel(cr1, CSI_CSICR1); + __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_disable_int); -void csi_enable(int arg) +void csi_enable(cam_data *cam, int arg) { - unsigned long cr = __raw_readl(CSI_CSICR18); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr = __raw_readl(csi->regbase + CSI_CSICR18); if (arg == 1) cr |= BIT_CSI_ENABLE; else cr &= ~BIT_CSI_ENABLE; - __raw_writel(cr, CSI_CSICR18); + __raw_writel(cr, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_enable); -void csi_buf_stride_set(u32 stride) +void csi_buf_stride_set(cam_data *cam, u32 stride) { - __raw_writel(stride, CSI_CSIFBUF_PARA); + struct csi_soc *csi = &csi_array[cam->csi]; + + __raw_writel(stride, csi->regbase + CSI_CSIFBUF_PARA); } EXPORT_SYMBOL(csi_buf_stride_set); -void csi_deinterlace_enable(bool enable) +void csi_deinterlace_enable(cam_data *cam, bool enable) { - unsigned long cr18 = __raw_readl(CSI_CSICR18); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); if (enable == true) cr18 |= BIT_DEINTERLACE_EN; else cr18 &= ~BIT_DEINTERLACE_EN; - __raw_writel(cr18, CSI_CSICR18); + __raw_writel(cr18, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_deinterlace_enable); -void csi_deinterlace_mode(int mode) +void csi_deinterlace_mode(cam_data *cam, int mode) { - unsigned long cr18 = __raw_readl(CSI_CSICR18); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); if (mode == V4L2_STD_NTSC) cr18 |= BIT_NTSC_EN; else cr18 &= ~BIT_NTSC_EN; - __raw_writel(cr18, CSI_CSICR18); + __raw_writel(cr18, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_deinterlace_mode); -void csi_tvdec_enable(bool enable) +void csi_tvdec_enable(cam_data *cam, bool enable) { - unsigned long cr18 = __raw_readl(CSI_CSICR18); - unsigned long cr1 = __raw_readl(CSI_CSICR1); + struct csi_soc *csi = &csi_array[cam->csi]; + unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); + unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); if (enable == true) { cr18 |= (BIT_TVDECODER_IN_EN | BIT_BASEADDR_SWITCH_EN); @@ -270,71 +281,85 @@ void csi_tvdec_enable(bool enable) cr1 |= BIT_SOF_POL | BIT_REDGE; } - __raw_writel(cr18, CSI_CSICR18); - __raw_writel(cr1, CSI_CSICR1); + __raw_writel(cr18, csi->regbase + CSI_CSICR18); + __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_tvdec_enable); -void csi_set_32bit_imagpara(int width, int height) +void csi_set_32bit_imagpara(cam_data *cam, int width, int height) { + struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; - unsigned long cr3 = __raw_readl(CSI_CSICR3); + unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | height; - __raw_writel(imag_para, CSI_CSIIMAG_PARA); + __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ - __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_32bit_imagpara); -void csi_set_16bit_imagpara(int width, int height) +void csi_set_16bit_imagpara(cam_data *cam, int width, int height) { + struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; - unsigned long cr3 = __raw_readl(CSI_CSICR3); + unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | (height * 2); - __raw_writel(imag_para, CSI_CSIIMAG_PARA); + __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ - __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_16bit_imagpara); -void csi_set_12bit_imagpara(int width, int height) +void csi_set_12bit_imagpara(cam_data *cam, int width, int height) { + struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; - unsigned long cr3 = __raw_readl(CSI_CSICR3); + unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | (height * 3 / 2); - __raw_writel(imag_para, CSI_CSIIMAG_PARA); + __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ - __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_12bit_imagpara); -void csi_dmareq_rff_enable(void) +void csi_dmareq_rff_enable(struct csi_soc *csi) { - unsigned long cr3 = __raw_readl(CSI_CSICR3); + unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); cr3 |= BIT_DMA_REQ_EN_RFF; cr3 |= BIT_HRESP_ERR_EN; - __raw_writel(cr3, CSI_CSICR3); + __raw_writel(cr3, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_dmareq_rff_enable); -void csi_dmareq_rff_disable(void) +void csi_dmareq_rff_disable(struct csi_soc *csi) { - unsigned long cr3 = __raw_readl(CSI_CSICR3); + unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); cr3 &= ~BIT_DMA_REQ_EN_RFF; cr3 &= ~BIT_HRESP_ERR_EN; - __raw_writel(cr3, CSI_CSICR3); + __raw_writel(cr3, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_dmareq_rff_disable); +struct csi_soc *csi_get_soc(int id) +{ + if (id >= CSI_MAX_NUM) + return ERR_PTR(-ENODEV); + else if (!csi_array[id].online) + return ERR_PTR(-ENODEV); + else + return &(csi_array[id]); +} +EXPORT_SYMBOL_GPL(csi_get_soc); + static const struct of_device_id fsl_csi_dt_ids[] = { { .compatible = "fsl,imx6sl-csi", }, { /* sentinel */ } @@ -345,6 +370,13 @@ static int csi_probe(struct platform_device *pdev) { int ret = 0; struct resource *res; + int id; + + id = of_alias_get_id(pdev->dev.of_node, "csi"); + if (id < 0) { + dev_dbg(&pdev->dev, "can not get alias id\n"); + return id; + } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -352,7 +384,10 @@ static int csi_probe(struct platform_device *pdev) ret = -ENODEV; goto err; } - irq_nr = res->start; + + csi = &csi_array[id]; + csi->irq_nr = res->start; + csi->online = false; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -360,8 +395,8 @@ static int csi_probe(struct platform_device *pdev) ret = -ENODEV; goto err; } - csi_regbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!csi_regbase) { + csi->regbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!csi->regbase) { dev_err(&pdev->dev, "ioremap failed with csi base\n"); ret = -ENOMEM; goto err; @@ -385,11 +420,11 @@ static int csi_probe(struct platform_device *pdev) } csi_clk_enable(); - csihw_reset(); - csi_init_interface(); - csi_dmareq_rff_disable(); - csi_clk_disable(); + csihw_reset(csi); + csi_init_interface(csi); + csi_dmareq_rff_disable(csi); + csi->online = true; err: return ret; } diff --git a/drivers/media/platform/mxc/capture/fsl_csi.h b/drivers/media/platform/mxc/capture/fsl_csi.h index 78b393ef9151..1bb15075473f 100644 --- a/drivers/media/platform/mxc/capture/fsl_csi.h +++ b/drivers/media/platform/mxc/capture/fsl_csi.h @@ -110,30 +110,24 @@ #define CSI_MCLK_I2C 8 #endif -extern void __iomem *csi_regbase; -#define CSI_CSICR1 (csi_regbase) -#define CSI_CSICR2 (csi_regbase + 0x4) -#define CSI_CSICR3 (csi_regbase + 0x8) -#define CSI_STATFIFO (csi_regbase + 0xC) -#define CSI_CSIRXFIFO (csi_regbase + 0x10) -#define CSI_CSIRXCNT (csi_regbase + 0x14) -#define CSI_CSISR (csi_regbase + 0x18) - -#define CSI_CSIDBG (csi_regbase + 0x1C) -#define CSI_CSIDMASA_STATFIFO (csi_regbase + 0x20) -#define CSI_CSIDMATS_STATFIFO (csi_regbase + 0x24) -#define CSI_CSIDMASA_FB1 (csi_regbase + 0x28) -#define CSI_CSIDMASA_FB2 (csi_regbase + 0x2C) -#define CSI_CSIFBUF_PARA (csi_regbase + 0x30) -#define CSI_CSIIMAG_PARA (csi_regbase + 0x34) - -#define CSI_CSICR18 (csi_regbase + 0x48) -#define CSI_CSICR19 (csi_regbase + 0x4c) - -static inline void csi_clear_status(unsigned long status) -{ - __raw_writel(status, CSI_CSISR); -} +#define CSI_CSICR1 0x0 +#define CSI_CSICR2 0x4 +#define CSI_CSICR3 0x8 +#define CSI_STATFIFO 0xC +#define CSI_CSIRXFIFO 0x10 +#define CSI_CSIRXCNT 0x14 +#define CSI_CSISR 0x18 + +#define CSI_CSIDBG 0x1C +#define CSI_CSIDMASA_STATFIFO 0x20 +#define CSI_CSIDMATS_STATFIFO 0x24 +#define CSI_CSIDMASA_FB1 0x28 +#define CSI_CSIDMASA_FB2 0x2C +#define CSI_CSIFBUF_PARA 0x30 +#define CSI_CSIIMAG_PARA 0x34 + +#define CSI_CSICR18 0x48 +#define CSI_CSICR19 0x4c struct csi_signal_cfg_t { unsigned data_width:3; @@ -193,24 +187,39 @@ struct csi_config_t { unsigned int rxcnt; }; +struct csi_soc { + bool online; + int irq_nr; + void __iomem *regbase; +}; + typedef void (*csi_irq_callback_t) (void *data, unsigned long status); -void csi_init_interface(void); -void csi_set_32bit_imagpara(int width, int height); -void csi_set_16bit_imagpara(int width, int height); -void csi_set_12bit_imagpara(int width, int height); -void csi_format_swap16(bool enable); +void csi_set_32bit_imagpara(cam_data *cam, int width, int height); +void csi_set_16bit_imagpara(cam_data *cam, int width, int height); +void csi_set_12bit_imagpara(cam_data *cam, int width, int height); +void csi_format_swap16(cam_data *cam, bool enable); int csi_read_mclk_flag(void); void csi_start_callback(void *data); void csi_stop_callback(void *data); -void csi_enable_int(int arg); -void csi_buf_stride_set(u32 stride); -void csi_deinterlace_mode(int mode); -void csi_deinterlace_enable(bool enable); -void csi_tvdec_enable(bool enable); -void csi_enable(int arg); -void csi_disable_int(void); +void csi_enable_int(cam_data *cam, int arg); +void csi_buf_stride_set(cam_data *cam, u32 stride); +void csi_deinterlace_mode(cam_data *cam, int mode); +void csi_deinterlace_enable(cam_data *cam, bool enable); +void csi_tvdec_enable(cam_data *cam, bool enable); +void csi_enable(cam_data *cam, int arg); +void csi_disable_int(cam_data *cam); void csi_clk_enable(void); void csi_clk_disable(void); -void csi_dmareq_rff_enable(void); -void csi_dmareq_rff_disable(void); +void csi_dmareq_rff_enable(struct csi_soc *csi); +void csi_dmareq_rff_disable(struct csi_soc *csi); +static inline int csi_read(struct csi_soc *csi, unsigned int offset) +{ + return __raw_readl(csi->regbase + offset); +} +static inline void csi_write(struct csi_soc *csi, unsigned int value, unsigned int offset) +{ + __raw_writel(value, csi->regbase + offset); +} + +struct csi_soc *csi_get_soc(int id); diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 09a421f20f7e..b8ea5b9b2d27 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -157,6 +157,8 @@ typedef struct _cam_data { /* v4l2 format */ struct v4l2_format v2f; + struct v4l2_format input_fmt; /* camera in */ + bool bswapenable; int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */ int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */ struct v4l2_mxc_offset offset; @@ -219,6 +221,7 @@ typedef struct _cam_data { struct v4l2_int_device *self; int sensor_index; void *ipu; + void *csi_soc; enum imx_v4l2_devtype devtype; /* v4l2 buf elements related to PxP DMA */ diff --git a/drivers/media/platform/mxc/capture/mxc_vadc.c b/drivers/media/platform/mxc/capture/mxc_vadc.c index bb114e1f9994..a5aa3f128268 100644 --- a/drivers/media/platform/mxc/capture/mxc_vadc.c +++ b/drivers/media/platform/mxc/capture/mxc_vadc.c @@ -885,6 +885,8 @@ static int vadc_probe(struct platform_device *pdev) return ret; } + vadc->sen.csi = csi_id; + /* remap GPR register */ vadc->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "gpr"); diff --git a/drivers/net/can/m_can.c b/drivers/net/can/m_can.c index 4397cfe2ec31..ed6d86bfdd02 100644 --- a/drivers/net/can/m_can.c +++ b/drivers/net/can/m_can.c @@ -83,7 +83,23 @@ enum m_can_reg { M_CAN_TXEFA = 0xf8, }; +/* m_can lec values */ +enum m_can_lec_type { + LEC_NO_ERROR = 0, + LEC_STUFF_ERROR, + LEC_FORM_ERROR, + LEC_ACK_ERROR, + LEC_BIT1_ERROR, + LEC_BIT0_ERROR, + LEC_CRC_ERROR, + LEC_UNUSED, +}; +/* Test Register (TEST) */ +#define TEST_LBCK BIT(4) + /* CC Control Register(CCCR) */ +#define CCCR_TEST BIT(7) +#define CCCR_MON BIT(5) #define CCCR_CCE BIT(1) #define CCCR_INIT BIT(0) @@ -97,6 +113,19 @@ enum m_can_reg { #define BTR_SJW_SHIFT 0 #define BTR_SJW_MASK 0xf +/* Error Counter Register(ECR) */ +#define ECR_RP BIT(15) +#define ECR_REC_SHIFT 8 +#define ECR_REC_MASK (0x7f << ECR_REC_SHIFT) +#define ECR_TEC_SHIFT 0 +#define ECR_TEC_MASK 0xff + +/* Protocol Status Register(PSR) */ +#define PSR_BO BIT(7) +#define PSR_EW BIT(6) +#define PSR_EP BIT(5) +#define PSR_LEC_MASK 0x7 + /* Interrupt Register(IR) */ #define IR_ALL_INT 0xffffffff #define IR_STE BIT(31) @@ -131,10 +160,11 @@ enum m_can_reg { #define IR_RF0F BIT(2) #define IR_RF0W BIT(1) #define IR_RF0N BIT(0) -#define IR_ERR_ALL (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \ - IR_WDI | IR_BO | IR_EW | IR_EP | IR_ELO | IR_BEU | \ - IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ - IR_RF0L) +#define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) +#define IR_ERR_BUS (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \ + IR_WDI | IR_ELO | IR_BEU | IR_BEC | IR_TOO | IR_MRAF | \ + IR_TSW | IR_TEFL | IR_RF1L | IR_RF0L) +#define IR_ERR_ALL (IR_ERR_STATE | IR_ERR_BUS) /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ #define RXFC_FWM_OFF 24 @@ -320,12 +350,175 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) return num_rx_pkts; } +static int m_can_handle_lost_msg(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(dev, "msg lost in rxf0\n"); + + skb = alloc_can_err_skb(dev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_errors++; + stats->rx_over_errors++; + + netif_receive_skb(skb); + + return 1; +} + +static int m_can_handle_bus_err(struct net_device *dev, + enum m_can_lec_type lec_type) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* early exit if no lec update */ + if (lec_type == LEC_UNUSED) + return 0; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + /* + * check for 'last error code' which tells us the + * type of the last error to occur on the CAN bus + */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + + switch (lec_type) { + case LEC_STUFF_ERROR: + netdev_dbg(dev, "stuff error\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case LEC_FORM_ERROR: + netdev_dbg(dev, "form error\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case LEC_ACK_ERROR: + netdev_dbg(dev, "ack error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL); + break; + case LEC_BIT1_ERROR: + netdev_dbg(dev, "bit1 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case LEC_BIT0_ERROR: + netdev_dbg(dev, "bit0 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case LEC_CRC_ERROR: + netdev_dbg(dev, "CRC error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + break; + default: + break; + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int m_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct m_can_priv *priv = netdev_priv(dev); + unsigned int ecr; + + ecr = m_can_read(priv, M_CAN_ECR); + bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; + bec->txerr = ecr & ECR_TEC_MASK; + + return 0; +} + +static int m_can_handle_state_change(struct net_device *dev, + enum can_state new_state) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + unsigned int ecr; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + m_can_get_berr_counter(dev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + ecr = m_can_read(priv, M_CAN_ECR); + if (ecr & ECR_RP) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * disable all interrupts in bus-off mode to ensure that + * the CPU is not hogged down + */ + m_can_enable_all_interrupts(priv, false); + can_bus_off(dev); + break; + default: + break; + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + static int m_can_poll(struct napi_struct *napi, int quota) { struct net_device *dev = napi->dev; struct m_can_priv *priv = netdev_priv(dev); u32 work_done = 0; - u32 irqstatus; + u32 irqstatus, psr; irqstatus = m_can_read(priv, M_CAN_IR); if (irqstatus) @@ -337,6 +530,48 @@ static int m_can_poll(struct napi_struct *napi, int quota) if (!irqstatus) goto end; + psr = m_can_read(priv, M_CAN_PSR); + if (irqstatus & IR_ERR_STATE) { + if ((psr & PSR_EW) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_WARNING); + } + + if ((psr & PSR_EP) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((psr & PSR_BO) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_BUS_OFF); + } + } + + if (irqstatus & IR_ERR_BUS) { + if (irqstatus & IR_RF0L) + work_done += m_can_handle_lost_msg(dev); + + /* handle lec errors on the bus */ + if (psr & LEC_UNUSED) + work_done += m_can_handle_bus_err(dev, + psr & LEC_UNUSED); + + /* other unproccessed error interrupts */ + if (irqstatus & IR_WDI) + netdev_err(dev, "Message RAM Watchdog event due to missing READY\n"); + if (irqstatus & IR_TOO) + netdev_err(dev, "Timeout reached\n"); + if (irqstatus & IR_MRAF) + netdev_err(dev, "Message RAM access failure occurred\n"); + } + if (irqstatus & IR_RF0N) /* handle events corresponding to receive message objects */ work_done += m_can_do_rx_poll(dev, (quota - work_done)); @@ -369,31 +604,18 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (ir & IR_ALL_INT) m_can_write(priv, M_CAN_IR, ir); - if (ir & IR_ERR_ALL) { - netdev_dbg(dev, "bus error\n"); - /* TODO: handle bus error */ - } - - /* save irqstatus for later using */ - priv->irqstatus = ir; - /* * schedule NAPI in case of * - rx IRQ - * - state change IRQ(TODO) - * - bus error IRQ and bus error reporting (TODO) + * - state change IRQ + * - bus error IRQ and bus error reporting */ - if (ir & IR_RF0N) { + if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) { + priv->irqstatus = ir; m_can_enable_all_interrupts(priv, false); napi_schedule(&priv->napi); } - /* FIFO overflow */ - if (ir & IR_RF0L) { - dev->stats.rx_over_errors++; - dev->stats.rx_errors++; - } - /* transmission complete interrupt */ if (ir & IR_TC) { netdev_dbg(dev, "tx complete\n"); @@ -443,14 +665,13 @@ static int m_can_set_bittiming(struct net_device *dev) * - configure rx fifo * - accept non-matching frame into fifo 0 * - configure tx buffer + * - configure mode * - setup bittiming - * - TODO: - * 1) other working modes support like monitor, loopback... - * 2) lec error status report enable */ static void m_can_chip_config(struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); + u32 cccr, test; m_can_config_endisable(priv, true); @@ -477,6 +698,22 @@ static void m_can_chip_config(struct net_device *dev) m_can_write(priv, M_CAN_RXF1C, (priv->rxf1_elems << RXFC_FS_OFF) | RXFC_FWM_1 | (priv->mram_off + priv->rxf1_off)); + cccr = m_can_read(priv, M_CAN_CCCR); + cccr &= ~(CCCR_TEST | CCCR_MON); + test = m_can_read(priv, M_CAN_TEST); + test &= ~TEST_LBCK; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cccr |= CCCR_MON; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + cccr |= CCCR_TEST; + test |= TEST_LBCK; + } + + m_can_write(priv, M_CAN_CCCR, cccr); + m_can_write(priv, M_CAN_TEST, test); + /* enable all interrupts */ m_can_write(priv, M_CAN_IR, IR_ALL_INT); m_can_write(priv, M_CAN_IE, IR_ALL_INT); @@ -515,14 +752,6 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode) return 0; } -static int m_can_get_berr_counter(const struct net_device *dev, - struct can_berr_counter *bec) -{ - /* TODO */ - - return 0; -} - static void free_m_can_dev(struct net_device *dev) { free_candev(dev); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 1c4f4d50d574..853b19f0e613 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1601,7 +1601,7 @@ fec_enet_interrupt(int irq, void *dev_id) fep->work_ts = 0; } - if (fep->work_tx || fep->work_rx) { + if ((fep->work_tx || fep->work_rx) && fep->link) { ret = IRQ_HANDLED; /* Disable the RX interrupt */ @@ -2359,8 +2359,7 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) { struct fec_enet_private *fep = netdev_priv(ndev); - if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) && - wol->wolopts != 0) + if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) return -EINVAL; if (wol->wolopts & ~WAKE_MAGIC) diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index eb255e807c06..ce028e159efe 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -186,7 +186,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) return hvc_driver; } -static int __init hvc_console_setup(struct console *co, char *options) +static int hvc_console_setup(struct console *co, char *options) { if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) return -ENODEV; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 642239015b46..3ee7217e25b2 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1089,6 +1089,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) { unsigned int addr = 0; unsigned int modem = 0; + unsigned int brk = 0; struct gsm_dlci *dlci; int len = clen; u8 *dp = data; @@ -1115,6 +1116,16 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) if (len == 0) return; } + len--; + if (len > 0) { + while (gsm_read_ea(&brk, *dp++) == 0) { + len--; + if (len == 0) + return; + } + modem <<= 7; + modem |= (brk & 0x7f); + } tty = tty_port_tty_get(&dlci->port); gsm_process_modem(tty, dlci, modem, clen); if (tty) { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 59d26ef538d8..3723c0ebb316 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1267,12 +1267,13 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) * * Locking: None */ -static void tty_line_name(struct tty_driver *driver, int index, char *p) +static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p) { if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE) - strcpy(p, driver->name); + return sprintf(p, "%s", driver->name); else - sprintf(p, "%s%d", driver->name, index + driver->name_base); + return sprintf(p, "%s%d", driver->name, + index + driver->name_base); } /** @@ -3538,9 +3539,19 @@ static ssize_t show_cons_active(struct device *dev, if (i >= ARRAY_SIZE(cs)) break; } - while (i--) - count += sprintf(buf + count, "%s%d%c", - cs[i]->name, cs[i]->index, i ? ' ':'\n'); + while (i--) { + int index = cs[i]->index; + struct tty_driver *drv = cs[i]->device(cs[i], &index); + + /* don't resolve tty0 as some programs depend on it */ + if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR)) + count += tty_line_name(drv, index, buf + count); + else + count += sprintf(buf + count, "%s%d", + cs[i]->name, cs[i]->index); + + count += sprintf(buf + count, "%c", i ? ' ':'\n'); + } console_unlock(); return count; diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index cbd333edbec0..8819bf14b2de 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -79,6 +79,10 @@ struct ci_role_driver { int (*start)(struct ci_hdrc *); void (*stop)(struct ci_hdrc *); irqreturn_t (*irq)(struct ci_hdrc *); + /* Save before suspend */ + void (*save)(struct ci_hdrc *); + /* Restore after power lost */ + void (*restore)(struct ci_hdrc *); const char *name; }; @@ -154,6 +158,8 @@ struct ci_hdrc { bool is_otg; struct otg_fsm fsm; struct ci_otg_fsm_timer_list *fsm_timer; + struct timer_list hnp_polling_timer; + bool hnp_polling_req; struct work_struct work; struct workqueue_struct *wq; @@ -181,12 +187,24 @@ struct ci_hdrc { struct dentry *debugfs; bool id_event; bool b_sess_valid_event; + bool vbus_glitch_check_event; /* imx28 needs swp instruction for writing */ bool imx28_write_fix; bool supports_runtime_pm; bool in_lpm; bool wakeup_int; struct timer_list timer; + /* register save area for suspend&resume */ + u32 pm_command; + u32 pm_status; + u32 pm_intr_enable; + u32 pm_frame_index; + u32 pm_segment; + u32 pm_frame_list; + u32 pm_async_next; + u32 pm_configured_flag; + u32 pm_portsc; + struct work_struct power_lost_work; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 9a53efd1ad44..c0f6f902aaad 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -512,6 +512,16 @@ static int imx_controller_resume(struct device *dev) data->in_lpm = false; if (data->usbmisc_data) { + ret = imx_usbmisc_power_lost_check(data->usbmisc_data); + /* re-init if resume from power lost */ + if (ret > 0) { + ret = imx_usbmisc_init(data->usbmisc_data); + if (ret) { + dev_err(dev, "usbmisc init failed, ret=%d\n", + ret); + goto clk_disable; + } + } ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); if (ret) { dev_err(dev, diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 3376c88c1ae6..a195a8961a0f 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -34,5 +34,6 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool); /* Call it before setting portsc.suspendM */ int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *); int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *, bool); +int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index f6b76604cf17..b6c57161985c 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -457,7 +457,7 @@ static irqreturn_t ci_irq(int irq, void *data) * and disconnection events. */ if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { - ci->b_sess_valid_event = true; + ci->vbus_glitch_check_event = true; hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); ci_otg_queue_work(ci); return IRQ_HANDLED; @@ -611,6 +611,52 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) } } +static enum ci_role ci_get_role(struct ci_hdrc *ci) +{ + if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { + if (ci->is_otg) { + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); + return ci_otg_role(ci); + } else { + /* + * If the controller is not OTG capable, but support + * role switch, the defalt role is gadget, and the + * user can switch it through debugfs. + */ + return CI_ROLE_GADGET; + } + } else { + return ci->roles[CI_ROLE_HOST] + ? CI_ROLE_HOST + : CI_ROLE_GADGET; + } +} + +static void ci_start_new_role(struct ci_hdrc *ci) +{ + enum ci_role role = ci_get_role(ci); + + if (ci->role != role) + ci_handle_id_switch(ci); + + if (role == CI_ROLE_GADGET) + ci_handle_vbus_connected(ci); +} + +static void ci_power_lost_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, + power_lost_work); + + pm_runtime_get_sync(ci->dev); + if (ci_otg_is_fsm_mode(ci)) + ci_hdrc_otg_fsm_restart(ci); + else + ci_start_new_role(ci); + pm_runtime_put_sync(ci->dev); + enable_irq(ci->irq); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -715,24 +761,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { - if (ci->is_otg) { - ci->role = ci_otg_role(ci); - hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); - } else { - /* - * If the controller is not OTG capable, but support - * role switch, the defalt role is gadget, and the - * user can switch it through debugfs. - */ - ci->role = CI_ROLE_GADGET; - } - } else { - ci->role = ci->roles[CI_ROLE_HOST] - ? CI_ROLE_HOST - : CI_ROLE_GADGET; - } - + ci->role = ci_get_role(ci); /* Notify vbus connected event if it is existed */ if (ci->role == CI_ROLE_GADGET) ci_handle_vbus_connected(ci); @@ -762,6 +791,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ci_otg_is_fsm_mode(ci)) ci_hdrc_otg_fsm_start(ci); + /* Init workqueue for controller power lost handling */ + INIT_WORK(&ci->power_lost_work, ci_power_lost_work); + setup_timer(&ci->timer, delay_runtime_pm_put_timer, (unsigned long)ci); ret = dbg_create_files(ci); @@ -838,6 +870,13 @@ static int ci_controller_suspend(struct device *dev) if (ci->in_lpm) return 0; + /* + * The registers for suspended host will not be updated + * during system suspend. + */ + if (ci->roles[ci->role]->save) + ci->roles[ci->role]->save(ci); + if (ci_otg_is_fsm_mode(ci)) ci_otg_fsm_suspend_for_srp(ci); @@ -881,11 +920,10 @@ static int ci_controller_resume(struct device *dev) ci->wakeup_int = false; enable_irq(ci->irq); mod_timer(&ci->timer, jiffies + msecs_to_jiffies(2000)); + if (ci_otg_is_fsm_mode(ci)) + ci_otg_fsm_wakeup_by_srp(ci); } - if (ci_otg_is_fsm_mode(ci)) - ci_otg_fsm_wakeup_by_srp(ci); - return 0; } @@ -909,12 +947,35 @@ static int ci_resume(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); int ret; + bool power_lost = false; + u32 sample_reg_val; + + /* Check if controller resume from power lost */ + sample_reg_val = hw_read(ci, OP_ENDPTLISTADDR, ~0); + if (sample_reg_val == 0) + power_lost = true; + else if (sample_reg_val == 0xFFFFFFFF) + /* Restore value 0 if it was set for power lost check */ + hw_write(ci, OP_ENDPTLISTADDR, ~0, 0); if (device_may_wakeup(dev)) disable_irq_wake(ci->irq); ret = ci_controller_resume(dev); - if (!ret && ci->supports_runtime_pm) { + if (ret) + return ret; + + if (power_lost) { + /* re-init for phy */ + usb_phy_shutdown(ci->transceiver); + ci_usb_phy_init(ci); + if (ci->roles[ci->role]->restore) + ci->roles[ci->role]->restore(ci); + disable_irq_nosync(ci->irq); + schedule_work(&ci->power_lost_work); + } + + if (ci->supports_runtime_pm) { pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index db0a78576331..b14810a940f2 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -321,8 +321,67 @@ static void host_stop(struct ci_hdrc *ci) if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) regulator_disable(ci->platdata->reg_vbus); } + ci->hcd = NULL; } +bool ci_hdrc_host_has_device(struct ci_hdrc *ci) +{ + struct usb_device *roothub; + int i; + + if ((ci->role == CI_ROLE_HOST) && ci->hcd) { + roothub = ci->hcd->self.root_hub; + for (i = 0; i < roothub->maxchild; ++i) { + if (usb_hub_find_child(roothub, (i + 1))) + return true; + } + } + return false; +} + +void ci_hdrc_host_save_for_power_lost(struct ci_hdrc *ci) +{ + struct ehci_hcd *ehci = hcd_to_ehci(ci->hcd); + + /* save EHCI registers */ + ci->pm_command = ehci_readl(ehci, &ehci->regs->command); + ci->pm_command &= ~CMD_RUN; + ci->pm_status = ehci_readl(ehci, &ehci->regs->status); + ci->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); + ci->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); + ci->pm_segment = ehci_readl(ehci, &ehci->regs->segment); + ci->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); + ci->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); + ci->pm_configured_flag = + ehci_readl(ehci, &ehci->regs->configured_flag); + ci->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); +} + +void ci_hdrc_host_restore_from_power_lost(struct ci_hdrc *ci) +{ + struct ehci_hcd *ehci = hcd_to_ehci(ci->hcd); + unsigned long flags; + u32 tmp; + + hw_device_reset(ci, USBMODE_CM_HC); + + spin_lock_irqsave(&ehci->lock, flags); + /* restore EHCI registers */ + ehci_writel(ehci, ci->pm_portsc, &ehci->regs->port_status[0]); + ehci_writel(ehci, ci->pm_command, &ehci->regs->command); + ehci_writel(ehci, ci->pm_intr_enable, &ehci->regs->intr_enable); + ehci_writel(ehci, ci->pm_frame_index, &ehci->regs->frame_index); + ehci_writel(ehci, ci->pm_segment, &ehci->regs->segment); + ehci_writel(ehci, ci->pm_frame_list, &ehci->regs->frame_list); + ehci_writel(ehci, ci->pm_async_next, &ehci->regs->async_next); + ehci_writel(ehci, ci->pm_configured_flag, + &ehci->regs->configured_flag); + + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp |= CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + spin_unlock_irqrestore(&ehci->lock, flags); +} void ci_hdrc_host_destroy(struct ci_hdrc *ci) { @@ -344,6 +403,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci) rdrv->start = host_start; rdrv->stop = host_stop; rdrv->irq = host_irq; + rdrv->save = ci_hdrc_host_save_for_power_lost; + rdrv->restore = ci_hdrc_host_restore_from_power_lost; rdrv->name = "host"; ci->roles[CI_ROLE_HOST] = rdrv; diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h index 5707bf379bfb..0b01b635e6e4 100644 --- a/drivers/usb/chipidea/host.h +++ b/drivers/usb/chipidea/host.h @@ -5,6 +5,7 @@ int ci_hdrc_host_init(struct ci_hdrc *ci); void ci_hdrc_host_destroy(struct ci_hdrc *ci); +bool ci_hdrc_host_has_device(struct ci_hdrc *ci); #else @@ -18,6 +19,11 @@ static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci) } +static inline bool ci_hdrc_host_has_device(struct ci_hdrc *ci) +{ + return false; +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 70ac5c4028af..1e0389965b83 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -24,6 +24,7 @@ #include "bits.h" #include "otg.h" #include "otg_fsm.h" +#include "host.h" /** * hw_read_otgsc returns otgsc register bits value. @@ -57,6 +58,27 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) return role; } +#define CI_VBUS_CONNECT_TIMEOUT_MS 500 +static int ci_is_vbus_glitch(struct ci_hdrc *ci) +{ + /* + * Handling vbus glitch + * + * We only need to consider glitch for without usb connection, + * With usb connection, we consider it as real disconnection. + * + * If the vbus can't higher than AVV in timeout value, we think + * it is a vbus glitch + */ + if (hw_wait_reg(ci, OP_OTGSC, OTGSC_AVV, OTGSC_AVV, + CI_VBUS_CONNECT_TIMEOUT_MS)) { + dev_warn(ci->dev, "there is a vbus glitch\n"); + return 1; + } + + return 0; +} + void ci_handle_vbus_connected(struct ci_hdrc *ci) { u32 otgsc; @@ -71,7 +93,7 @@ void ci_handle_vbus_connected(struct ci_hdrc *ci) otgsc = hw_read(ci, OP_OTGSC, ~0); - if (otgsc & OTGSC_BSV) + if ((otgsc & OTGSC_BSV) && !ci_is_vbus_glitch(ci)) usb_gadget_vbus_connect(&ci->gadget); } @@ -87,25 +109,18 @@ void ci_handle_vbus_change(struct ci_hdrc *ci) } #define CI_VBUS_STABLE_TIMEOUT_MS 5000 -static void ci_handle_id_switch(struct ci_hdrc *ci) +void ci_handle_id_switch(struct ci_hdrc *ci) { enum ci_role role = ci_otg_role(ci); - struct usb_device *roothub; - int i; if (role != ci->role) { dev_dbg(ci->dev, "switching from %s to %s\n", ci_role(ci)->name, ci->roles[role]->name); - if ((ci->role == CI_ROLE_HOST) && ci->hcd) { - roothub = ci->hcd->self.root_hub; - for (i = 0; i < roothub->maxchild; ++i) { - while (usb_hub_find_child(roothub, (i + 1))) { - enable_irq(ci->irq); - usleep_range(10000, 15000); - disable_irq_nosync(ci->irq); - } - } + while (ci_hdrc_host_has_device(ci)) { + enable_irq(ci->irq); + usleep_range(10000, 15000); + disable_irq_nosync(ci->irq); } ci_role_stop(ci); @@ -115,6 +130,33 @@ static void ci_handle_id_switch(struct ci_hdrc *ci) ci_role_start(ci, role); } } + +static void ci_handle_vbus_glitch(struct ci_hdrc *ci) +{ + bool valid_vbus_change = false; + + if (hw_read_otgsc(ci, OTGSC_BSV)) { + if (!ci_is_vbus_glitch(ci)) { + if (ci_otg_is_fsm_mode(ci)) { + ci->fsm.b_sess_vld = 1; + ci->fsm.b_ssend_srp = 0; + otg_del_timer(&ci->fsm, B_SSEND_SRP); + otg_del_timer(&ci->fsm, B_SRP_FAIL); + } + valid_vbus_change = true; + } + } else { + if (ci->vbus_active || (ci_otg_is_fsm_mode(ci) && + ci->fsm.b_sess_vld)) + valid_vbus_change = true; + } + + if (valid_vbus_change) { + ci->b_sess_valid_event = true; + ci_otg_queue_work(ci); + } +} + /** * ci_otg_work - perform otg (vbus/id) event handle * @work: work struct @@ -123,6 +165,15 @@ static void ci_otg_work(struct work_struct *work) { struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + if (ci->vbus_glitch_check_event) { + ci->vbus_glitch_check_event = false; + pm_runtime_get_sync(ci->dev); + ci_handle_vbus_glitch(ci); + pm_runtime_put_sync(ci->dev); + enable_irq(ci->irq); + return; + } + if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) { enable_irq(ci->irq); return; diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 05ad33707b72..c42ac78d3035 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -17,6 +17,7 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci); void ci_hdrc_otg_destroy(struct ci_hdrc *ci); enum ci_role ci_otg_role(struct ci_hdrc *ci); void ci_handle_vbus_change(struct ci_hdrc *ci); +void ci_handle_id_switch(struct ci_hdrc *ci); void ci_handle_vbus_connected(struct ci_hdrc *ci); static inline void ci_otg_queue_work(struct ci_hdrc *ci) { diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index fc9d865d0f25..6faab2a06053 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -29,6 +29,7 @@ #include "bits.h" #include "otg.h" #include "otg_fsm.h" +#include "host.h" static struct ci_otg_fsm_timer *otg_timer_initializer (struct ci_hdrc *ci, void (*function)(void *, unsigned long), @@ -82,6 +83,11 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr, return count; } ci->fsm.a_bus_req = 1; + if (ci->transceiver->state == OTG_STATE_A_PERIPHERAL) { + ci->gadget.host_request_flag = 1; + mutex_unlock(&ci->fsm.lock); + return count; + } } ci_otg_queue_work(ci); @@ -160,8 +166,14 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr, mutex_lock(&ci->fsm.lock); if (buf[0] == '0') ci->fsm.b_bus_req = 0; - else if (buf[0] == '1') + else if (buf[0] == '1') { ci->fsm.b_bus_req = 1; + if (ci->transceiver->state == OTG_STATE_B_PERIPHERAL) { + ci->gadget.host_request_flag = 1; + mutex_unlock(&ci->fsm.lock); + return count; + } + } ci_otg_queue_work(ci); mutex_unlock(&ci->fsm.lock); @@ -369,6 +381,14 @@ static void b_data_pulse_end(void *ptr, unsigned long indicator) ci_otg_queue_work(ci); } +static void hnp_polling_timer_work(unsigned long arg) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)arg; + + ci->hnp_polling_req = true; + ci_otg_queue_work(ci); +} + /* Initialize timers */ static int ci_otg_init_timers(struct ci_hdrc *ci) { @@ -439,9 +459,17 @@ static int ci_otg_init_timers(struct ci_hdrc *ci) if (ci->fsm_timer->timer_list[B_SESS_VLD] == NULL) return -ENOMEM; + setup_timer(&ci->hnp_polling_timer, hnp_polling_timer_work, + (unsigned long)ci); return 0; } +static void ci_otg_add_hnp_polling_timer(struct ci_hdrc *ci) +{ + mod_timer(&ci->hnp_polling_timer, + jiffies + msecs_to_jiffies(T_HOST_REQ_POLL)); +} + /* -------------------------------------------------------------*/ /* Operations that will be called from OTG Finite State Machine */ /* -------------------------------------------------------------*/ @@ -449,8 +477,12 @@ static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) { struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); - if (t < NUM_OTG_FSM_TIMERS) - ci_otg_add_timer(ci, t); + if (t < NUM_OTG_FSM_TIMERS) { + if (t == HNP_POLLING) + ci_otg_add_hnp_polling_timer(ci); + else + ci_otg_add_timer(ci, t); + } return; } @@ -605,6 +637,14 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) } pm_runtime_get_sync(ci->dev); + if (ci->hnp_polling_req) { + ci->hnp_polling_req = false; + if (otg_hnp_polling(&ci->fsm) != HOST_REQUEST_FLAG) { + pm_runtime_put_sync(ci->dev); + return 0; + } + } + if (otg_statemachine(&ci->fsm)) { if (ci->transceiver->state == OTG_STATE_A_IDLE) { /* @@ -763,13 +803,8 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) } } else if (otg_int_src & OTGSC_BSVIS) { hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); - ci->b_sess_valid_event = true; - if (otgsc & OTGSC_BSV) { - fsm->b_sess_vld = 1; - ci_otg_del_timer(ci, B_SSEND_SRP); - ci_otg_del_timer(ci, B_SRP_FAIL); - fsm->b_ssend_srp = 0; - } else { + ci->vbus_glitch_check_event = true; + if (!(otgsc & OTGSC_BSV) && fsm->b_sess_vld) { fsm->b_sess_vld = 0; if (fsm->id) ci_otg_add_timer(ci, B_SSEND_SRP); @@ -863,4 +898,41 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) { sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group); + del_timer_sync(&ci->hnp_polling_timer); +} + +/* Restart OTG fsm if resume from power lost */ +void ci_hdrc_otg_fsm_restart(struct ci_hdrc *ci) +{ + struct otg_fsm *fsm = &ci->fsm; + int id_status = fsm->id; + + /* Update fsm if power lost in peripheral state */ + if (ci->transceiver->state == OTG_STATE_B_PERIPHERAL) { + fsm->b_sess_vld = 0; + otg_statemachine(fsm); + } + + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); + hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE); + + /* Update fsm variables for restart */ + fsm->id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0; + if (fsm->id) { + fsm->b_ssend_srp = + hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1; + fsm->b_sess_vld = + hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0; + } else if (fsm->id != id_status) { + /* ID changes to be 0 */ + fsm->a_bus_drop = 0; + fsm->a_bus_req = 1; + ci->id_event = true; + } + + if (ci_hdrc_host_has_device(ci) && + !hw_read(ci, OP_PORTSC, PORTSC_CCS)) + fsm->b_conn = 0; + + ci_otg_fsm_work(ci); } diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 94c085f456a9..3399e901b419 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -64,6 +64,8 @@ #define TB_SESS_VLD (1000) +#define T_HOST_REQ_POLL (1500) /* HNP polling interval 1s~2s */ + enum ci_otg_fsm_timer_index { /* * CI specific timers, start from the end @@ -96,6 +98,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci); irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci); void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci); void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci); +void ci_hdrc_otg_fsm_restart(struct ci_hdrc *ci); #else @@ -124,6 +127,11 @@ static inline void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) } +static inline void ci_hdrc_otg_fsm_restart(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index abe3da77f123..d84aefde1a79 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -827,7 +827,10 @@ __acquires(hwep->lock) return -ENOMEM; req->complete = isr_get_status_complete; - req->length = 2; + if (setup->wIndex == OTG_STS_SELECTOR) + req->length = 1; + else + req->length = 2; req->buf = kzalloc(req->length, gfp_flags); if (req->buf == NULL) { retval = -ENOMEM; @@ -835,8 +838,16 @@ __acquires(hwep->lock) } if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { - /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = ci->remote_wakeup << 1; + if ((setup->wIndex == OTG_STS_SELECTOR) && + ci_otg_is_fsm_mode(ci)) { + if (ci->gadget.host_request_flag) + *(u8 *)req->buf = HOST_REQUEST_FLAG; + else + *(u8 *)req->buf = 0; + } else { + /* Assume that device is bus powered for now. */ + *(u16 *)req->buf = ci->remote_wakeup << 1; + } } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? @@ -1044,8 +1055,9 @@ __acquires(ci->lock) type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && type != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto delegate; - if (le16_to_cpu(req.wLength) != 2 || - le16_to_cpu(req.wValue) != 0) + if ((le16_to_cpu(req.wLength) != 2 && + le16_to_cpu(req.wLength) != 1) || + le16_to_cpu(req.wValue) != 0) break; err = isr_get_status_response(ci, &req); break; @@ -1925,6 +1937,29 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci) hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); } +static void udc_suspend_for_power_lost(struct ci_hdrc *ci) +{ + /* + * Set OP_ENDPTLISTADDR to be non-zero for + * checking if controller resume from power lost + * in non-host mode. + */ + if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0) + hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0); +} + +/* Power lost with device mode */ +static void udc_resume_from_power_lost(struct ci_hdrc *ci) +{ + /* Force disconnect if power lost with vbus on */ + if (ci->vbus_active) + usb_gadget_vbus_disconnect(&ci->gadget); + + if (ci->is_otg) + hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, + OTGSC_BSVIS | OTGSC_BSVIE); +} + /** * ci_hdrc_gadget_init - initialize device related bits * ci: the controller @@ -1945,6 +1980,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) rdrv->start = udc_id_switch_for_device; rdrv->stop = udc_id_switch_for_host; rdrv->irq = udc_irq; + rdrv->save = udc_suspend_for_power_lost; + rdrv->restore = udc_resume_from_power_lost; rdrv->name = "gadget"; ci->roles[CI_ROLE_GADGET] = rdrv; diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index d263ae438d17..acda7a93a612 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -75,6 +75,8 @@ struct usbmisc_ops { int (*hsic_set_connect)(struct imx_usbmisc_data *data); /* It's called during suspend/resume */ int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); + /* It's called when system resume from usb power lost */ + int (*power_lost_check)(struct imx_usbmisc_data *data); }; struct imx_usbmisc { @@ -255,6 +257,24 @@ static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + data->index * 4); + spin_unlock_irqrestore(&usbmisc->lock, flags); + /* + * Here use a power on reset value to judge + * if the controller experienced a power lost + */ + if (val == 0x30001000) + return 1; + else + return 0; +} + static int usbmisc_imx6q_hsic_set_connect(struct imx_usbmisc_data *data) { unsigned long flags; @@ -358,6 +378,7 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .hsic_set_connect = usbmisc_imx6q_hsic_set_connect, .hsic_set_clk = usbmisc_imx6q_hsic_set_clk, + .power_lost_check = usbmisc_imx6sx_power_lost_check, }; int imx_usbmisc_init(struct imx_usbmisc_data *data) @@ -410,6 +431,16 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) } EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); +int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *data) +{ + if (!usbmisc) + return -ENODEV; + if (!usbmisc->ops->power_lost_check) + return 0; + return usbmisc->ops->power_lost_check(data); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_power_lost_check); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index f51dc5e9a23b..76611f8c589f 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -84,6 +84,8 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) fsm->b_ase0_brst_tmout = 0; break; case OTG_STATE_B_HOST: + if (fsm->otg->gadget) + fsm->otg->gadget->host_request_flag = 0; break; case OTG_STATE_A_IDLE: fsm->adp_prb = 0; @@ -98,6 +100,8 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) break; case OTG_STATE_A_HOST: otg_del_timer(fsm, A_WAIT_ENUM); + if (fsm->otg->gadget) + fsm->otg->gadget->host_request_flag = 0; break; case OTG_STATE_A_SUSPEND: otg_del_timer(fsm, A_AIDL_BDIS); @@ -169,6 +173,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_set_protocol(fsm, PROTO_HOST); usb_bus_start_enum(fsm->otg->host, fsm->otg->host->otg_port); + otg_add_timer(fsm, HNP_POLLING); break; case OTG_STATE_A_IDLE: otg_drv_vbus(fsm, 0); @@ -203,6 +208,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) */ if (!fsm->a_bus_req || fsm->a_suspend_req_inf) otg_add_timer(fsm, A_WAIT_ENUM); + otg_add_timer(fsm, HNP_POLLING); break; case OTG_STATE_A_SUSPEND: otg_drv_vbus(fsm, 1); @@ -364,3 +370,60 @@ int otg_statemachine(struct otg_fsm *fsm) return state_changed; } EXPORT_SYMBOL_GPL(otg_statemachine); + +/* + * Called by host to poll peripheral if it wants to be host + * Return value: + * - host request flag(1) if the device wants to be host, + * - host request flag(0) if the device keeps peripheral role, + * - otherwise, error code. + */ +int otg_hnp_polling(struct otg_fsm *fsm) +{ + struct usb_device *udev; + u8 host_req_flag; + int retval; + enum usb_otg_state state = fsm->otg->phy->state; + + if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST) + return -EINVAL; + + udev = usb_hub_find_child(fsm->otg->host->root_hub, 1); + if (!udev) { + dev_err(fsm->otg->host->controller, + "no usb dev connected, can't start HNP polling\n"); + return -ENODEV; + } + + /* Get host request flag from connected USB device */ + retval = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + USB_REQ_GET_STATUS, + USB_DIR_IN | USB_RECIP_DEVICE, + 0, + OTG_STS_SELECTOR, + &host_req_flag, + 1, + USB_CTRL_GET_TIMEOUT); + if (retval == 1) { + if (host_req_flag == HOST_REQUEST_FLAG) { + if (state == OTG_STATE_A_HOST) + fsm->a_bus_req = 0; + else if (state == OTG_STATE_B_HOST) + fsm->b_bus_req = 0; + retval = HOST_REQUEST_FLAG; + } else if (host_req_flag == 0) { + /* Continue polling */ + otg_add_timer(fsm, HNP_POLLING); + retval = 0; + } else { + dev_err(&udev->dev, "host request flag is invalid\n"); + retval = -EINVAL; + } + } else { + dev_err(&udev->dev, "Get one byte OTG status failed\n"); + retval = -EIO; + } + return retval; +} +EXPORT_SYMBOL_GPL(otg_hnp_polling); diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 415c88a6ce0e..bf72adb2c906 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -114,7 +114,6 @@ void usb_gadget_set_state(struct usb_gadget *gadget, enum usb_device_state state) { gadget->state = state; - sysfs_notify(&gadget->dev.kobj, NULL, "state"); schedule_work(&gadget->work); } EXPORT_SYMBOL_GPL(usb_gadget_set_state); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 305a3134bfa9..8555ef3c7d3b 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -40,6 +40,7 @@ #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) @@ -129,7 +130,8 @@ static const struct mxs_phy_data imx6sl_phy_data = { }; static const struct mxs_phy_data imx6sx_phy_data = { - .flags = MXS_PHY_HAS_ANATOP, + .flags = MXS_PHY_HAS_ANATOP | + MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, }; static const struct of_device_id mxs_phy_dt_ids[] = { @@ -221,11 +223,6 @@ static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) return false; } -#ifdef CONFIG_USB_OTG_FSM -static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) -{ -} -#else static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) { void __iomem *base = mxs_phy->phy.io_priv; @@ -258,6 +255,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) usleep_range(500, 1000); } +static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 phyctrl = readl(base + HW_USBPHY_CTRL); + + if (IS_ENABLED(CONFIG_USB_OTG) && + !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE)) + return true; + + return false; +} + static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) { bool vbus_is_on = false; @@ -272,13 +281,12 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); - if (on && !vbus_is_on) + if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) __mxs_phy_disconnect_line(mxs_phy, true); else __mxs_phy_disconnect_line(mxs_phy, false); } -#endif static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) { @@ -308,7 +316,17 @@ static int mxs_phy_init(struct usb_phy *phy) static void mxs_phy_shutdown(struct usb_phy *phy) { struct mxs_phy *mxs_phy = to_mxs_phy(phy); - + u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | + BM_USBPHY_CTRL_ENDPDMCHG_WKUP | + BM_USBPHY_CTRL_ENIDCHG_WKUP | + BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | + BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | + BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | + BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | + BM_USBPHY_CTRL_ENAUTO_PWRON_PLL; + + writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR); + writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD); writel(BM_USBPHY_CTRL_CLKGATE, phy->io_priv + HW_USBPHY_CTRL_SET); diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 5e4a26056025..63b072385b82 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -419,6 +419,9 @@ static int mxsfb_check_var(struct fb_var_screeninfo *var, if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 16)) + var->bits_per_pixel = 32; + switch (var->bits_per_pixel) { case 16: /* always expect RGB 565 */ @@ -434,11 +437,11 @@ static int mxsfb_check_var(struct fb_var_screeninfo *var, rgb = def_rgb666; break; case STMLCDIF_18BIT: - if (pixfmt_is_equal(var, def_rgb888)) - rgb = def_rgb888; - else + if (pixfmt_is_equal(var, def_rgb666)) /* 24 bit to 18 bit mapping */ rgb = def_rgb666; + else + rgb = def_rgb888; break; case STMLCDIF_24BIT: /* real 24 bit */ @@ -664,9 +667,7 @@ static int mxsfb_set_par(struct fb_info *fb_info) */ break; case STMLCDIF_18BIT: - if (pixfmt_is_equal(&fb_info->var, def_rgb888)) - break; - else + if (pixfmt_is_equal(&fb_info->var, def_rgb666)) /* 24 bit to 18 bit mapping */ ctrl |= CTRL_DF24; /* ignore the upper 2 bits in * each colour component diff --git a/include/dt-bindings/clock/imx6sx-clock.h b/include/dt-bindings/clock/imx6sx-clock.h index 421d8bb76f2f..0e3710cdd5f6 100644 --- a/include/dt-bindings/clock/imx6sx-clock.h +++ b/include/dt-bindings/clock/imx6sx-clock.h @@ -251,6 +251,7 @@ #define IMX6SX_CLK_SAI2_IPG 238 #define IMX6SX_CLK_ESAI_IPG 239 #define IMX6SX_CLK_ESAI_MEM 240 -#define IMX6SX_CLK_CLK_END 241 +#define IMX6SX_CLK_OCRAM_ALT_SEL 241 +#define IMX6SX_CLK_CLK_END 242 #endif /* __DT_BINDINGS_CLOCK_IMX6SX_H */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4e9f36a15033..0c71babde4a7 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -28,6 +28,12 @@ #define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ +/* + * Basic mux clk, can't switch parent while there is another basic mux clk + * being its child. Otherwise, a glitch might be propagated to downstream + * clocks through this child mux. + */ +#define CLK_IS_BASIC_MUX BIT(8) struct clk_hw; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 08c6877f7a2b..28e99a181b35 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -536,6 +536,7 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; + unsigned host_request_flag:1; const char *name; struct device dev; unsigned out_epnum; diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index b6ba1bfb86f2..d4bf33b1ad6f 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. +/* Copyright (C) 2007-2014 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 @@ -40,6 +40,14 @@ #define PROTO_HOST (1) #define PROTO_GADGET (2) +#define OTG_STS_SELECTOR 0xF000 /* OTG status selector, according to + * OTG and EH 2.0 Charpter 6.2.3 + * Table:6-4 + */ +#define HOST_REQUEST_FLAG 1 /* Host request flag, according to + * OTG and EH 2.0 Charpter 6.2.3 + * Table:6-5 + */ enum otg_fsm_timer { /* Standard OTG timers */ A_WAIT_VRISE, @@ -53,6 +61,7 @@ enum otg_fsm_timer { B_SE0_SRP, B_SRP_FAIL, A_WAIT_ENUM, + HNP_POLLING, NUM_OTG_FSM_TIMERS, }; @@ -239,6 +248,7 @@ static inline int otg_start_gadget(struct otg_fsm *fsm, int on) return fsm->ops->start_gadget(fsm, on); } +int otg_hnp_polling(struct otg_fsm *fsm); int otg_statemachine(struct otg_fsm *fsm); #endif /* __LINUX_USB_OTG_FSM_H */ diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 8df108966155..868ee2ca547e 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -987,6 +987,15 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_SRFM: return true; default: return false; |