diff -urN linux-3.12.orig/arch/arm/boot/dts/imx25.dtsi linux-3.12.work/arch/arm/boot/dts/imx25.dtsi --- linux-3.12.orig/arch/arm/boot/dts/imx25.dtsi 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/boot/dts/imx25.dtsi 2014-08-03 18:07:11.966855559 +0200 @@ -10,6 +10,7 @@ */ #include "skeleton.dtsi" +#include "imx25-pinfunc.h" / { aliases { @@ -173,12 +174,12 @@ status = "disabled"; }; - iomuxc@43fac000{ + iomuxc: iomuxc@43fac000 { compatible = "fsl,imx25-iomuxc"; reg = <0x43fac000 0x4000>; }; - audmux@43fb0000 { + audmux: audmux@43fb0000 { compatible = "fsl,imx25-audmux", "fsl,imx31-audmux"; reg = <0x43fb0000 0x4000>; status = "disabled"; @@ -266,6 +267,11 @@ compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; reg = <0x50034000 0x4000>; interrupts = <12>; + clocks = <&clks 117>; + clock-names = "ipg"; + dmas = <&sdma 28 1 0>, + <&sdma 29 1 0>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -436,13 +442,14 @@ #interrupt-cells = <2>; }; - sdma@53fd4000 { + sdma: sdma@53fd4000 { compatible = "fsl,imx25-sdma", "fsl,imx35-sdma"; reg = <0x53fd4000 0x4000>; clocks = <&clks 112>, <&clks 68>; clock-names = "ipg", "ahb"; #dma-cells = <3>; interrupts = <34>; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx25.bin"; }; wdog@53fdc000 { diff -urN linux-3.12.orig/arch/arm/boot/dts/imx25-pinfunc.h linux-3.12.work/arch/arm/boot/dts/imx25-pinfunc.h --- linux-3.12.orig/arch/arm/boot/dts/imx25-pinfunc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/boot/dts/imx25-pinfunc.h 2014-08-03 17:17:27.779881934 +0200 @@ -0,0 +1,494 @@ +/* + * Copyright 2013 EukrĂ©a Electromatique + * Based on imx35-pinfunc.h in the same directory Which is: + * Copyright 2013 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 version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __DTS_IMX25_PINFUNC_H +#define __DTS_IMX25_PINFUNC_H + +/* + * The pin function ID is a tuple of + * + */ + +#define MX25_PAD_A10__A10 0x008 0x000 0x000 0x00 0x000 +#define MX25_PAD_A10__GPIO_4_0 0x008 0x000 0x000 0x05 0x000 + +#define MX25_PAD_A13__A13 0x00c 0x22C 0x000 0x00 0x000 +#define MX25_PAD_A13__GPIO_4_1 0x00c 0x22C 0x000 0x05 0x000 + +#define MX25_PAD_A14__A14 0x010 0x230 0x000 0x10 0x000 +#define MX25_PAD_A14__GPIO_2_0 0x010 0x230 0x000 0x15 0x000 + +#define MX25_PAD_A15__A15 0x014 0x234 0x000 0x10 0x000 +#define MX25_PAD_A15__GPIO_2_1 0x014 0x234 0x000 0x15 0x000 + +#define MX25_PAD_A16__A16 0x018 0x000 0x000 0x10 0x000 +#define MX25_PAD_A16__GPIO_2_2 0x018 0x000 0x000 0x15 0x000 + +#define MX25_PAD_A17__A17 0x01c 0x238 0x000 0x10 0x000 +#define MX25_PAD_A17__GPIO_2_3 0x01c 0x238 0x000 0x15 0x000 + +#define MX25_PAD_A18__A18 0x020 0x23c 0x000 0x10 0x000 +#define MX25_PAD_A18__GPIO_2_4 0x020 0x23c 0x000 0x15 0x000 +#define MX25_PAD_A18__FEC_COL 0x020 0x23c 0x504 0x17 0x000 + +#define MX25_PAD_A19__A19 0x024 0x240 0x000 0x10 0x000 +#define MX25_PAD_A19__FEC_RX_ER 0x024 0x240 0x518 0x17 0x000 +#define MX25_PAD_A19__GPIO_2_5 0x024 0x240 0x000 0x15 0x000 + +#define MX25_PAD_A20__A20 0x028 0x244 0x000 0x10 0x000 +#define MX25_PAD_A20__GPIO_2_6 0x028 0x244 0x000 0x15 0x000 +#define MX25_PAD_A20__FEC_RDATA2 0x028 0x244 0x50c 0x17 0x000 + +#define MX25_PAD_A21__A21 0x02c 0x248 0x000 0x10 0x000 +#define MX25_PAD_A21__GPIO_2_7 0x02c 0x248 0x000 0x15 0x000 +#define MX25_PAD_A21__FEC_RDATA3 0x02c 0x248 0x510 0x17 0x000 + +#define MX25_PAD_A22__A22 0x030 0x000 0x000 0x10 0x000 +#define MX25_PAD_A22__GPIO_2_8 0x030 0x000 0x000 0x15 0x000 + +#define MX25_PAD_A23__A23 0x034 0x24c 0x000 0x10 0x000 +#define MX25_PAD_A23__GPIO_2_9 0x034 0x24c 0x000 0x15 0x000 + +#define MX25_PAD_A24__A24 0x038 0x250 0x000 0x10 0x000 +#define MX25_PAD_A24__GPIO_2_10 0x038 0x250 0x000 0x15 0x000 +#define MX25_PAD_A24__FEC_RX_CLK 0x038 0x250 0x514 0x17 0x000 + +#define MX25_PAD_A25__A25 0x03c 0x254 0x000 0x10 0x000 +#define MX25_PAD_A25__GPIO_2_11 0x03c 0x254 0x000 0x15 0x000 +#define MX25_PAD_A25__FEC_CRS 0x03c 0x254 0x508 0x17 0x000 + +#define MX25_PAD_EB0__EB0 0x040 0x258 0x000 0x10 0x000 +#define MX25_PAD_EB0__AUD4_TXD 0x040 0x258 0x464 0x14 0x000 +#define MX25_PAD_EB0__GPIO_2_12 0x040 0x258 0x000 0x15 0x000 + +#define MX25_PAD_EB1__EB1 0x044 0x25c 0x000 0x10 0x000 +#define MX25_PAD_EB1__AUD4_RXD 0x044 0x25c 0x460 0x14 0x000 +#define MX25_PAD_EB1__GPIO_2_13 0x044 0x25c 0x000 0x15 0x000 + +#define MX25_PAD_OE__OE 0x048 0x260 0x000 0x10 0x000 +#define MX25_PAD_OE__AUD4_TXC 0x048 0x260 0x000 0x14 0x000 +#define MX25_PAD_OE__GPIO_2_14 0x048 0x260 0x000 0x15 0x000 + +#define MX25_PAD_CS0__CS0 0x04c 0x000 0x000 0x00 0x000 +#define MX25_PAD_CS0__GPIO_4_2 0x04c 0x000 0x000 0x05 0x000 + +#define MX25_PAD_CS1__CS1 0x050 0x000 0x000 0x00 0x000 +#define MX25_PAD_CS1__NF_CE3 0x050 0x000 0x000 0x01 0x000 +#define MX25_PAD_CS1__GPIO_4_3 0x050 0x000 0x000 0x05 0x000 + +#define MX25_PAD_CS4__CS4 0x054 0x264 0x000 0x10 0x000 +#define MX25_PAD_CS4__NF_CE1 0x054 0x264 0x000 0x01 0x000 +#define MX25_PAD_CS4__UART5_CTS 0x054 0x264 0x000 0x13 0x000 +#define MX25_PAD_CS4__GPIO_3_20 0x054 0x264 0x000 0x15 0x000 + +#define MX25_PAD_CS5__CS5 0x058 0x268 0x000 0x10 0x000 +#define MX25_PAD_CS5__NF_CE2 0x058 0x268 0x000 0x01 0x000 +#define MX25_PAD_CS5__UART5_RTS 0x058 0x268 0x574 0x13 0x000 +#define MX25_PAD_CS5__GPIO_3_21 0x058 0x268 0x000 0x15 0x000 + +#define MX25_PAD_NF_CE0__NF_CE0 0x05c 0x26c 0x000 0x10 0x000 +#define MX25_PAD_NF_CE0__GPIO_3_22 0x05c 0x26c 0x000 0x15 0x000 + +#define MX25_PAD_ECB__ECB 0x060 0x270 0x000 0x10 0x000 +#define MX25_PAD_ECB__UART5_TXD_MUX 0x060 0x270 0x000 0x13 0x000 +#define MX25_PAD_ECB__GPIO_3_23 0x060 0x270 0x000 0x15 0x000 + +#define MX25_PAD_LBA__LBA 0x064 0x274 0x000 0x10 0x000 +#define MX25_PAD_LBA__UART5_RXD_MUX 0x064 0x274 0x578 0x13 0x000 +#define MX25_PAD_LBA__GPIO_3_24 0x064 0x274 0x000 0x15 0x000 + +#define MX25_PAD_BCLK__BCLK 0x068 0x000 0x000 0x00 0x000 +#define MX25_PAD_BCLK__GPIO_4_4 0x068 0x000 0x000 0x05 0x000 + +#define MX25_PAD_RW__RW 0x06c 0x278 0x000 0x10 0x000 +#define MX25_PAD_RW__AUD4_TXFS 0x06c 0x278 0x474 0x14 0x000 +#define MX25_PAD_RW__GPIO_3_25 0x06c 0x278 0x000 0x15 0x000 + +#define MX25_PAD_NFWE_B__NFWE_B 0x070 0x000 0x000 0x10 0x000 +#define MX25_PAD_NFWE_B__GPIO_3_26 0x070 0x000 0x000 0x15 0x000 + +#define MX25_PAD_NFRE_B__NFRE_B 0x074 0x000 0x000 0x10 0x000 +#define MX25_PAD_NFRE_B__GPIO_3_27 0x074 0x000 0x000 0x15 0x000 + +#define MX25_PAD_NFALE__NFALE 0x078 0x000 0x000 0x10 0x000 +#define MX25_PAD_NFALE__GPIO_3_28 0x078 0x000 0x000 0x15 0x000 + +#define MX25_PAD_NFCLE__NFCLE 0x07c 0x000 0x000 0x10 0x000 +#define MX25_PAD_NFCLE__GPIO_3_29 0x07c 0x000 0x000 0x15 0x000 + +#define MX25_PAD_NFWP_B__NFWP_B 0x080 0x000 0x000 0x10 0x000 +#define MX25_PAD_NFWP_B__GPIO_3_30 0x080 0x000 0x000 0x15 0x000 + +#define MX25_PAD_NFRB__NFRB 0x084 0x27c 0x000 0x10 0x000 +#define MX25_PAD_NFRB__GPIO_3_31 0x084 0x27c 0x000 0x15 0x000 + +#define MX25_PAD_D15__D15 0x088 0x280 0x000 0x00 0x000 +#define MX25_PAD_D15__LD16 0x088 0x280 0x000 0x01 0x000 +#define MX25_PAD_D15__GPIO_4_5 0x088 0x280 0x000 0x05 0x000 + +#define MX25_PAD_D14__D14 0x08c 0x284 0x000 0x00 0x000 +#define MX25_PAD_D14__LD17 0x08c 0x284 0x000 0x01 0x000 +#define MX25_PAD_D14__GPIO_4_6 0x08c 0x284 0x000 0x05 0x000 + +#define MX25_PAD_D13__D13 0x090 0x288 0x000 0x00 0x000 +#define MX25_PAD_D13__LD18 0x090 0x288 0x000 0x01 0x000 +#define MX25_PAD_D13__GPIO_4_7 0x090 0x288 0x000 0x05 0x000 + +#define MX25_PAD_D12__D12 0x094 0x28c 0x000 0x00 0x000 +#define MX25_PAD_D12__GPIO_4_8 0x094 0x28c 0x000 0x05 0x000 + +#define MX25_PAD_D11__D11 0x098 0x290 0x000 0x00 0x000 +#define MX25_PAD_D11__GPIO_4_9 0x098 0x290 0x000 0x05 0x000 + +#define MX25_PAD_D10__D10 0x09c 0x294 0x000 0x00 0x000 +#define MX25_PAD_D10__GPIO_4_10 0x09c 0x294 0x000 0x05 0x000 +#define MX25_PAD_D10__USBOTG_OC 0x09c 0x294 0x57c 0x06 0x000 + +#define MX25_PAD_D9__D9 0x0a0 0x298 0x000 0x00 0x000 +#define MX25_PAD_D9__GPIO_4_11 0x0a0 0x298 0x000 0x05 0x000 +#define MX25_PAD_D9__USBH2_PWR 0x0a0 0x298 0x000 0x06 0x000 + +#define MX25_PAD_D8__D8 0x0a4 0x29c 0x000 0x00 0x000 +#define MX25_PAD_D8__GPIO_4_12 0x0a4 0x29c 0x000 0x05 0x000 +#define MX25_PAD_D8__USBH2_OC 0x0a4 0x29c 0x580 0x06 0x000 + +#define MX25_PAD_D7__D7 0x0a8 0x2a0 0x000 0x00 0x000 +#define MX25_PAD_D7__GPIO_4_13 0x0a8 0x2a0 0x000 0x05 0x000 + +#define MX25_PAD_D6__D6 0x0ac 0x2a4 0x000 0x00 0x000 +#define MX25_PAD_D6__GPIO_4_14 0x0ac 0x2a4 0x000 0x05 0x000 + +#define MX25_PAD_D5__D5 0x0b0 0x2a8 0x000 0x00 0x000 +#define MX25_PAD_D5__GPIO_4_15 0x0b0 0x2a8 0x000 0x05 0x000 + +#define MX25_PAD_D4__D4 0x0b4 0x2ac 0x000 0x00 0x000 +#define MX25_PAD_D4__GPIO_4_16 0x0b4 0x2ac 0x000 0x05 0x000 + +#define MX25_PAD_D3__D3 0x0b8 0x2b0 0x000 0x00 0x000 +#define MX25_PAD_D3__GPIO_4_17 0x0b8 0x2b0 0x000 0x05 0x000 + +#define MX25_PAD_D2__D2 0x0bc 0x2b4 0x000 0x00 0x000 +#define MX25_PAD_D2__GPIO_4_18 0x0bc 0x2b4 0x000 0x05 0x000 + +#define MX25_PAD_D1__D1 0x0c0 0x2b8 0x000 0x00 0x000 +#define MX25_PAD_D1__GPIO_4_19 0x0c0 0x2b8 0x000 0x05 0x000 + +#define MX25_PAD_D0__D0 0x0c4 0x2bc 0x000 0x00 0x000 +#define MX25_PAD_D0__GPIO_4_20 0x0c4 0x2bc 0x000 0x05 0x000 + +#define MX25_PAD_LD0__LD0 0x0c8 0x2c0 0x000 0x10 0x000 +#define MX25_PAD_LD0__CSI_D0 0x0c8 0x2c0 0x488 0x12 0x000 +#define MX25_PAD_LD0__GPIO_2_15 0x0c8 0x2c0 0x000 0x15 0x000 + +#define MX25_PAD_LD1__LD1 0x0cc 0x2c4 0x000 0x10 0x000 +#define MX25_PAD_LD1__CSI_D1 0x0cc 0x2c4 0x48c 0x12 0x000 +#define MX25_PAD_LD1__GPIO_2_16 0x0cc 0x2c4 0x000 0x15 0x000 + +#define MX25_PAD_LD2__LD2 0x0d0 0x2c8 0x000 0x10 0x000 +#define MX25_PAD_LD2__GPIO_2_17 0x0d0 0x2c8 0x000 0x15 0x000 + +#define MX25_PAD_LD3__LD3 0x0d4 0x2cc 0x000 0x10 0x000 +#define MX25_PAD_LD3__GPIO_2_18 0x0d4 0x2cc 0x000 0x15 0x000 + +#define MX25_PAD_LD4__LD4 0x0d8 0x2d0 0x000 0x10 0x000 +#define MX25_PAD_LD4__GPIO_2_19 0x0d8 0x2d0 0x000 0x15 0x000 + +#define MX25_PAD_LD5__LD5 0x0dc 0x2d4 0x000 0x10 0x000 +#define MX25_PAD_LD5__GPIO_1_19 0x0dc 0x2d4 0x000 0x15 0x000 + +#define MX25_PAD_LD6__LD6 0x0e0 0x2d8 0x000 0x10 0x000 +#define MX25_PAD_LD6__GPIO_1_20 0x0e0 0x2d8 0x000 0x15 0x000 + +#define MX25_PAD_LD7__LD7 0x0e4 0x2dc 0x000 0x10 0x000 +#define MX25_PAD_LD7__GPIO_1_21 0x0e4 0x2dc 0x000 0x15 0x000 + +#define MX25_PAD_LD8__LD8 0x0e8 0x2e0 0x000 0x10 0x000 +#define MX25_PAD_LD8__FEC_TX_ERR 0x0e8 0x2e0 0x000 0x15 0x000 + +#define MX25_PAD_LD9__LD9 0x0ec 0x2e4 0x000 0x10 0x000 +#define MX25_PAD_LD9__FEC_COL 0x0ec 0x2e4 0x504 0x15 0x001 + +#define MX25_PAD_LD10__LD10 0x0f0 0x2e8 0x000 0x10 0x000 +#define MX25_PAD_LD10__FEC_RX_ER 0x0f0 0x2e8 0x518 0x15 0x001 + +#define MX25_PAD_LD11__LD11 0x0f4 0x2ec 0x000 0x10 0x000 +#define MX25_PAD_LD11__FEC_RDATA2 0x0f4 0x2ec 0x50c 0x15 0x001 + +#define MX25_PAD_LD12__LD12 0x0f8 0x2f0 0x000 0x10 0x000 +#define MX25_PAD_LD12__FEC_RDATA3 0x0f8 0x2f0 0x510 0x15 0x001 + +#define MX25_PAD_LD13__LD13 0x0fc 0x2f4 0x000 0x10 0x000 +#define MX25_PAD_LD13__FEC_TDATA2 0x0fc 0x2f4 0x000 0x15 0x000 + +#define MX25_PAD_LD14__LD14 0x100 0x2f8 0x000 0x10 0x000 +#define MX25_PAD_LD14__FEC_TDATA3 0x100 0x2f8 0x000 0x15 0x000 + +#define MX25_PAD_LD15__LD15 0x104 0x2fc 0x000 0x10 0x000 +#define MX25_PAD_LD15__FEC_RX_CLK 0x104 0x2fc 0x514 0x15 0x001 + +#define MX25_PAD_HSYNC__HSYNC 0x108 0x300 0x000 0x10 0x000 +#define MX25_PAD_HSYNC__GPIO_1_22 0x108 0x300 0x000 0x15 0x000 + +#define MX25_PAD_VSYNC__VSYNC 0x10c 0x304 0x000 0x10 0x000 +#define MX25_PAD_VSYNC__GPIO_1_23 0x10c 0x304 0x000 0x15 0x000 + +#define MX25_PAD_LSCLK__LSCLK 0x110 0x308 0x000 0x10 0x000 +#define MX25_PAD_LSCLK__GPIO_1_24 0x110 0x308 0x000 0x15 0x000 + +#define MX25_PAD_OE_ACD__OE_ACD 0x114 0x30c 0x000 0x10 0x000 +#define MX25_PAD_OE_ACD__GPIO_1_25 0x114 0x30c 0x000 0x15 0x000 + +#define MX25_PAD_CONTRAST__CONTRAST 0x118 0x310 0x000 0x10 0x000 +#define MX25_PAD_CONTRAST__PWM4_PWMO 0x118 0x310 0x000 0x14 0x000 +#define MX25_PAD_CONTRAST__FEC_CRS 0x118 0x310 0x508 0x15 0x001 + +#define MX25_PAD_PWM__PWM 0x11c 0x314 0x000 0x10 0x000 +#define MX25_PAD_PWM__GPIO_1_26 0x11c 0x314 0x000 0x15 0x000 +#define MX25_PAD_PWM__USBH2_OC 0x11c 0x314 0x580 0x16 0x001 + +#define MX25_PAD_CSI_D2__CSI_D2 0x120 0x318 0x000 0x10 0x000 +#define MX25_PAD_CSI_D2__UART5_RXD_MUX 0x120 0x318 0x578 0x11 0x001 +#define MX25_PAD_CSI_D2__GPIO_1_27 0x120 0x318 0x000 0x15 0x000 +#define MX25_PAD_CSI_D2__CSPI3_MOSI 0x120 0x318 0x000 0x17 0x000 + +#define MX25_PAD_CSI_D3__CSI_D3 0x124 0x31c 0x000 0x10 0x000 +#define MX25_PAD_CSI_D3__GPIO_1_28 0x124 0x31c 0x000 0x15 0x000 +#define MX25_PAD_CSI_D3__CSPI3_MISO 0x124 0x31c 0x4b4 0x17 0x001 + +#define MX25_PAD_CSI_D4__CSI_D4 0x128 0x320 0x000 0x10 0x000 +#define MX25_PAD_CSI_D4__UART5_RTS 0x128 0x320 0x574 0x11 0x001 +#define MX25_PAD_CSI_D4__GPIO_1_29 0x128 0x320 0x000 0x15 0x000 +#define MX25_PAD_CSI_D4__CSPI3_SCLK 0x128 0x320 0x000 0x17 0x000 + +#define MX25_PAD_CSI_D5__CSI_D5 0x12c 0x324 0x000 0x10 0x000 +#define MX25_PAD_CSI_D5__GPIO_1_30 0x12c 0x324 0x000 0x15 0x000 +#define MX25_PAD_CSI_D5__CSPI3_RDY 0x12c 0x324 0x000 0x17 0x000 + +#define MX25_PAD_CSI_D6__CSI_D6 0x130 0x328 0x000 0x10 0x000 +#define MX25_PAD_CSI_D6__GPIO_1_31 0x130 0x328 0x000 0x15 0x000 + +#define MX25_PAD_CSI_D7__CSI_D7 0x134 0x32c 0x000 0x10 0x000 +#define MX25_PAD_CSI_D7__GPIO_1_6 0x134 0x32c 0x000 0x15 0x000 + +#define MX25_PAD_CSI_D8__CSI_D8 0x138 0x330 0x000 0x10 0x000 +#define MX25_PAD_CSI_D8__GPIO_1_7 0x138 0x330 0x000 0x15 0x000 + +#define MX25_PAD_CSI_D9__CSI_D9 0x13c 0x334 0x000 0x10 0x000 +#define MX25_PAD_CSI_D9__GPIO_4_21 0x13c 0x334 0x000 0x15 0x000 + +#define MX25_PAD_CSI_MCLK__CSI_MCLK 0x140 0x338 0x000 0x10 0x000 +#define MX25_PAD_CSI_MCLK__GPIO_1_8 0x140 0x338 0x000 0x15 0x000 + +#define MX25_PAD_CSI_VSYNC__CSI_VSYNC 0x144 0x33c 0x000 0x10 0x000 +#define MX25_PAD_CSI_VSYNC__GPIO_1_9 0x144 0x33c 0x000 0x15 0x000 + +#define MX25_PAD_CSI_HSYNC__CSI_HSYNC 0x148 0x340 0x000 0x10 0x000 +#define MX25_PAD_CSI_HSYNC__GPIO_1_10 0x148 0x340 0x000 0x15 0x000 + +#define MX25_PAD_CSI_PIXCLK__CSI_PIXCLK 0x14c 0x344 0x000 0x10 0x000 +#define MX25_PAD_CSI_PIXCLK__GPIO_1_11 0x14c 0x344 0x000 0x15 0x000 + +#define MX25_PAD_I2C1_CLK__I2C1_CLK 0x150 0x348 0x000 0x10 0x000 +#define MX25_PAD_I2C1_CLK__GPIO_1_12 0x150 0x348 0x000 0x15 0x000 + +#define MX25_PAD_I2C1_DAT__I2C1_DAT 0x154 0x34c 0x000 0x10 0x000 +#define MX25_PAD_I2C1_DAT__GPIO_1_13 0x154 0x34c 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_MOSI__CSPI1_MOSI 0x158 0x350 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_MOSI__GPIO_1_14 0x158 0x350 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_MISO__CSPI1_MISO 0x15c 0x354 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_MISO__GPIO_1_15 0x15c 0x354 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_SS0__CSPI1_SS0 0x160 0x358 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_SS0__GPIO_1_16 0x160 0x358 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_SS1__CSPI1_SS1 0x164 0x35c 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_SS1__GPIO_1_17 0x164 0x35c 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_SCLK__CSPI1_SCLK 0x168 0x360 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_SCLK__GPIO_1_18 0x168 0x360 0x000 0x15 0x000 + +#define MX25_PAD_CSPI1_RDY__CSPI1_RDY 0x16c 0x364 0x000 0x10 0x000 +#define MX25_PAD_CSPI1_RDY__GPIO_2_22 0x16c 0x364 0x000 0x15 0x000 + +#define MX25_PAD_UART1_RXD__UART1_RXD 0x170 0x368 0x000 0x10 0x000 +#define MX25_PAD_UART1_RXD__GPIO_4_22 0x170 0x368 0x000 0x15 0x000 + +#define MX25_PAD_UART1_TXD__UART1_TXD 0x174 0x36c 0x000 0x10 0x000 +#define MX25_PAD_UART1_TXD__GPIO_4_23 0x174 0x36c 0x000 0x15 0x000 + +#define MX25_PAD_UART1_RTS__UART1_RTS 0x178 0x370 0x000 0x10 0x000 +#define MX25_PAD_UART1_RTS__CSI_D0 0x178 0x370 0x488 0x11 0x001 +#define MX25_PAD_UART1_RTS__GPIO_4_24 0x178 0x370 0x000 0x15 0x000 + +#define MX25_PAD_UART1_CTS__UART1_CTS 0x17c 0x374 0x000 0x10 0x000 +#define MX25_PAD_UART1_CTS__CSI_D1 0x17c 0x374 0x48c 0x11 0x001 +#define MX25_PAD_UART1_CTS__GPIO_4_25 0x17c 0x374 0x000 0x15 0x000 + +#define MX25_PAD_UART2_RXD__UART2_RXD 0x180 0x378 0x000 0x10 0x000 +#define MX25_PAD_UART2_RXD__GPIO_4_26 0x180 0x378 0x000 0x15 0x000 + +#define MX25_PAD_UART2_TXD__UART2_TXD 0x184 0x37c 0x000 0x10 0x000 +#define MX25_PAD_UART2_TXD__GPIO_4_27 0x184 0x37c 0x000 0x15 0x000 + +#define MX25_PAD_UART2_RTS__UART2_RTS 0x188 0x380 0x000 0x10 0x000 +#define MX25_PAD_UART2_RTS__FEC_COL 0x188 0x380 0x504 0x12 0x002 +#define MX25_PAD_UART2_RTS__GPIO_4_28 0x188 0x380 0x000 0x15 0x000 + +#define MX25_PAD_UART2_CTS__FEC_RX_ER 0x18c 0x384 0x518 0x12 0x002 +#define MX25_PAD_UART2_CTS__UART2_CTS 0x18c 0x384 0x000 0x10 0x000 +#define MX25_PAD_UART2_CTS__GPIO_4_29 0x18c 0x384 0x000 0x15 0x000 + +#define MX25_PAD_SD1_CMD__SD1_CMD 0x190 0x388 0x000 0x10 0x000 +#define MX25_PAD_SD1_CMD__FEC_RDATA2 0x190 0x388 0x50c 0x12 0x002 +#define MX25_PAD_SD1_CMD__GPIO_2_23 0x190 0x388 0x000 0x15 0x000 + +#define MX25_PAD_SD1_CLK__SD1_CLK 0x194 0x38c 0x000 0x10 0x000 +#define MX25_PAD_SD1_CLK__FEC_RDATA3 0x194 0x38c 0x510 0x12 0x002 +#define MX25_PAD_SD1_CLK__GPIO_2_24 0x194 0x38c 0x000 0x15 0x000 + +#define MX25_PAD_SD1_DATA0__SD1_DATA0 0x198 0x390 0x000 0x10 0x000 +#define MX25_PAD_SD1_DATA0__GPIO_2_25 0x198 0x390 0x000 0x15 0x000 + +#define MX25_PAD_SD1_DATA1__SD1_DATA1 0x19c 0x394 0x000 0x10 0x000 +#define MX25_PAD_SD1_DATA1__AUD7_RXD 0x19c 0x394 0x478 0x13 0x000 +#define MX25_PAD_SD1_DATA1__GPIO_2_26 0x19c 0x394 0x000 0x15 0x000 + +#define MX25_PAD_SD1_DATA2__SD1_DATA2 0x1a0 0x398 0x000 0x10 0x000 +#define MX25_PAD_SD1_DATA2__FEC_RX_CLK 0x1a0 0x398 0x514 0x15 0x002 +#define MX25_PAD_SD1_DATA2__GPIO_2_27 0x1a0 0x398 0x000 0x15 0x000 + +#define MX25_PAD_SD1_DATA3__SD1_DATA3 0x1a4 0x39c 0x000 0x10 0x000 +#define MX25_PAD_SD1_DATA3__FEC_CRS 0x1a4 0x39c 0x508 0x10 0x002 +#define MX25_PAD_SD1_DATA3__GPIO_2_28 0x1a4 0x39c 0x000 0x15 0x000 + +#define MX25_PAD_KPP_ROW0__KPP_ROW0 0x1a8 0x3a0 0x000 0x10 0x000 +#define MX25_PAD_KPP_ROW0__GPIO_2_29 0x1a8 0x3a0 0x000 0x15 0x000 + +#define MX25_PAD_KPP_ROW1__KPP_ROW1 0x1ac 0x3a4 0x000 0x10 0x000 +#define MX25_PAD_KPP_ROW1__GPIO_2_30 0x1ac 0x3a4 0x000 0x15 0x000 + +#define MX25_PAD_KPP_ROW2__KPP_ROW2 0x1b0 0x3a8 0x000 0x10 0x000 +#define MX25_PAD_KPP_ROW2__CSI_D0 0x1b0 0x3a8 0x488 0x13 0x002 +#define MX25_PAD_KPP_ROW2__GPIO_2_31 0x1b0 0x3a8 0x000 0x15 0x000 + +#define MX25_PAD_KPP_ROW3__KPP_ROW3 0x1b4 0x3ac 0x000 0x10 0x000 +#define MX25_PAD_KPP_ROW3__CSI_LD1 0x1b4 0x3ac 0x48c 0x13 0x002 +#define MX25_PAD_KPP_ROW3__GPIO_3_0 0x1b4 0x3ac 0x000 0x15 0x000 + +#define MX25_PAD_KPP_COL0__KPP_COL0 0x1b8 0x3b0 0x000 0x10 0x000 +#define MX25_PAD_KPP_COL0__UART4_RXD_MUX 0x1b8 0x3b0 0x570 0x11 0x001 +#define MX25_PAD_KPP_COL0__AUD5_TXD 0x1b8 0x3b0 0x000 0x12 0x000 +#define MX25_PAD_KPP_COL0__GPIO_3_1 0x1b8 0x3b0 0x000 0x15 0x000 + +#define MX25_PAD_KPP_COL1__KPP_COL1 0x1bc 0x3b4 0x000 0x10 0x000 +#define MX25_PAD_KPP_COL1__UART4_TXD_MUX 0x1bc 0x3b4 0x000 0x11 0x000 +#define MX25_PAD_KPP_COL1__AUD5_RXD 0x1bc 0x3b4 0x000 0x12 0x000 +#define MX25_PAD_KPP_COL1__GPIO_3_2 0x1bc 0x3b4 0x000 0x15 0x000 + +#define MX25_PAD_KPP_COL2__KPP_COL2 0x1c0 0x3b8 0x000 0x10 0x000 +#define MX25_PAD_KPP_COL2__UART4_RTS 0x1c0 0x3b8 0x000 0x11 0x000 +#define MX25_PAD_KPP_COL2__AUD5_TXC 0x1c0 0x3b8 0x000 0x12 0x000 +#define MX25_PAD_KPP_COL2__GPIO_3_3 0x1c0 0x3b8 0x000 0x15 0x000 + +#define MX25_PAD_KPP_COL3__KPP_COL3 0x1c4 0x3bc 0x000 0x10 0x000 +#define MX25_PAD_KPP_COL3__UART4_CTS 0x1c4 0x3bc 0x000 0x11 0x000 +#define MX25_PAD_KPP_COL3__AUD5_TXFS 0x1c4 0x3bc 0x000 0x12 0x000 +#define MX25_PAD_KPP_COL3__GPIO_3_4 0x1c4 0x3bc 0x000 0x15 0x000 + +#define MX25_PAD_FEC_MDC__FEC_MDC 0x1c8 0x3c0 0x000 0x10 0x000 +#define MX25_PAD_FEC_MDC__AUD4_TXD 0x1c8 0x3c0 0x464 0x12 0x001 +#define MX25_PAD_FEC_MDC__GPIO_3_5 0x1c8 0x3c0 0x000 0x15 0x000 + +#define MX25_PAD_FEC_MDIO__FEC_MDIO 0x1cc 0x3c4 0x000 0x10 0x000 +#define MX25_PAD_FEC_MDIO__AUD4_RXD 0x1cc 0x3c4 0x460 0x12 0x001 +#define MX25_PAD_FEC_MDIO__GPIO_3_6 0x1cc 0x3c4 0x000 0x15 0x000 + +#define MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x1d0 0x3c8 0x000 0x10 0x000 +#define MX25_PAD_FEC_TDATA0__GPIO_3_7 0x1d0 0x3c8 0x000 0x15 0x000 + +#define MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x1d4 0x3cc 0x000 0x10 0x000 +#define MX25_PAD_FEC_TDATA1__AUD4_TXFS 0x1d4 0x3cc 0x474 0x12 0x001 +#define MX25_PAD_FEC_TDATA1__GPIO_3_8 0x1d4 0x3cc 0x000 0x15 0x000 + +#define MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x1d8 0x3d0 0x000 0x10 0x000 +#define MX25_PAD_FEC_TX_EN__GPIO_3_9 0x1d8 0x3d0 0x000 0x15 0x000 + +#define MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x1dc 0x3d4 0x000 0x10 0x000 +#define MX25_PAD_FEC_RDATA0__GPIO_3_10 0x1dc 0x3d4 0x000 0x15 0x000 + +#define MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x1e0 0x3d8 0x000 0x10 0x000 +#define MX25_PAD_FEC_RDATA1__GPIO_3_11 0x1e0 0x3d8 0x000 0x15 0x000 + +#define MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x1e4 0x3dc 0x000 0x10 0x000 +#define MX25_PAD_FEC_RX_DV__CAN2_RX 0x1e4 0x3dc 0x484 0x14 0x000 +#define MX25_PAD_FEC_RX_DV__GPIO_3_12 0x1e4 0x3dc 0x000 0x15 0x000 + +#define MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1e8 0x3e0 0x000 0x10 0x000 +#define MX25_PAD_FEC_TX_CLK__GPIO_3_13 0x1e8 0x3e0 0x000 0x15 0x000 + +#define MX25_PAD_RTCK__RTCK 0x1ec 0x3e4 0x000 0x10 0x000 +#define MX25_PAD_RTCK__OWIRE 0x1ec 0x3e4 0x000 0x11 0x000 +#define MX25_PAD_RTCK__GPIO_3_14 0x1ec 0x3e4 0x000 0x15 0x000 + +#define MX25_PAD_DE_B__DE_B 0x1f0 0x3ec 0x000 0x10 0x000 +#define MX25_PAD_DE_B__GPIO_2_20 0x1f0 0x3ec 0x000 0x15 0x000 + +#define MX25_PAD_TDO__TDO 0x000 0x3e8 0x000 0x00 0x000 + +#define MX25_PAD_GPIO_A__GPIO_A 0x1f4 0x3f0 0x000 0x10 0x000 +#define MX25_PAD_GPIO_A__CAN1_TX 0x1f4 0x3f0 0x000 0x16 0x000 +#define MX25_PAD_GPIO_A__USBOTG_PWR 0x1f4 0x3f0 0x000 0x12 0x000 + +#define MX25_PAD_GPIO_B__GPIO_B 0x1f8 0x3f4 0x000 0x10 0x000 +#define MX25_PAD_GPIO_B__CAN1_RX 0x1f8 0x3f4 0x480 0x16 0x001 +#define MX25_PAD_GPIO_B__USBOTG_OC 0x1f8 0x3f4 0x57c 0x12 0x001 + +#define MX25_PAD_GPIO_C__GPIO_C 0x1fc 0x3f8 0x000 0x10 0x000 +#define MX25_PAD_GPIO_C__CAN2_TX 0x1fc 0x3f8 0x000 0x16 0x000 + +#define MX25_PAD_GPIO_D__GPIO_D 0x200 0x3fc 0x000 0x10 0x000 +#define MX25_PAD_GPIO_E__LD16 0x204 0x400 0x000 0x02 0x000 +#define MX25_PAD_GPIO_D__CAN2_RX 0x200 0x3fc 0x484 0x16 0x001 + +#define MX25_PAD_GPIO_E__GPIO_E 0x204 0x400 0x000 0x10 0x000 +#define MX25_PAD_GPIO_F__LD17 0x208 0x404 0x000 0x02 0x000 +#define MX25_PAD_GPIO_E__AUD7_TXD 0x204 0x400 0x000 0x14 0x000 + +#define MX25_PAD_GPIO_F__GPIO_F 0x208 0x404 0x000 0x10 0x000 +#define MX25_PAD_GPIO_F__AUD7_TXC 0x208 0x404 0x000 0x14 0x000 + +#define MX25_PAD_EXT_ARMCLK__EXT_ARMCLK 0x20c 0x000 0x000 0x10 0x000 +#define MX25_PAD_EXT_ARMCLK__GPIO_3_15 0x20c 0x000 0x000 0x15 0x000 + +#define MX25_PAD_UPLL_BYPCLK__UPLL_BYPCLK 0x210 0x000 0x000 0x10 0x000 +#define MX25_PAD_UPLL_BYPCLK__GPIO_3_16 0x210 0x000 0x000 0x15 0x000 + +#define MX25_PAD_VSTBY_REQ__VSTBY_REQ 0x214 0x408 0x000 0x10 0x000 +#define MX25_PAD_VSTBY_REQ__AUD7_TXFS 0x214 0x408 0x000 0x14 0x000 +#define MX25_PAD_VSTBY_REQ__GPIO_3_17 0x214 0x408 0x000 0x15 0x000 +#define MX25_PAD_VSTBY_ACK__VSTBY_ACK 0x218 0x40c 0x000 0x10 0x000 +#define MX25_PAD_VSTBY_ACK__GPIO_3_18 0x218 0x40c 0x000 0x15 0x000 + +#define MX25_PAD_POWER_FAIL__POWER_FAIL 0x21c 0x410 0x000 0x10 0x000 +#define MX25_PAD_POWER_FAIL__AUD7_RXD 0x21c 0x410 0x478 0x14 0x001 +#define MX25_PAD_POWER_FAIL__GPIO_3_19 0x21c 0x410 0x000 0x15 0x000 + +#define MX25_PAD_CLKO__CLKO 0x220 0x414 0x000 0x10 0x000 +#define MX25_PAD_CLKO__GPIO_2_21 0x220 0x414 0x000 0x15 0x000 + +#define MX25_PAD_BOOT_MODE0__BOOT_MODE0 0x224 0x000 0x000 0x00 0x000 +#define MX25_PAD_BOOT_MODE0__GPIO_4_30 0x224 0x000 0x000 0x05 0x000 +#define MX25_PAD_BOOT_MODE1__BOOT_MODE1 0x228 0x000 0x000 0x00 0x000 +#define MX25_PAD_BOOT_MODE1__GPIO_4_31 0x228 0x000 0x000 0x05 0x000 + +#endif /* __DTS_IMX25_PINFUNC_H */ diff -urN linux-3.12.orig/arch/arm/boot/dts/imx25-vmx25.dts linux-3.12.work/arch/arm/boot/dts/imx25-vmx25.dts --- linux-3.12.orig/arch/arm/boot/dts/imx25-vmx25.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/boot/dts/imx25-vmx25.dts 2014-08-03 18:03:36.110068279 +0200 @@ -0,0 +1,260 @@ +/* + * Copyright 2014 Voipac + * + * 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 + */ + +/dts-v1/; +#include "imx25.dtsi" + +/ { + model = "Voipac VMX25"; + compatible = "voipac,imx25-vmx25", "fsl,imx25"; + +/* + memory { + reg = <0x80000000 0x02000000 0x90000000 0x02000000>; + }; +*/ + + + clocks { + clk_24M: codec_clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + + regulators { + compatible = "simple-bus"; + + reg_tps65053_l1: tps65053-3v3 { + compatible = "regulator-fixed"; + regulator-name = "3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_tps65053_l2: tps65053-qvdd { + compatible = "regulator-fixed"; + regulator-name = "qvdd"; + regulator-min-microvolt = <1450000>; + regulator-max-microvolt = <1450000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_tps65053_vldo1: tps65053-1v5 { + compatible = "regulator-fixed"; + regulator-name = "1v5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_tps65053_vldo2: tps65053-fec-phy { + compatible = "regulator-fixed"; + regulator-name = "fec-phy"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio4 9 0>; + }; + + reg_tps65053_vldo3: tps65053-vdd-batt { + compatible = "regulator-fixed"; + regulator-name = "vdd-batt"; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + regulator-always-on; + }; + + + reg_lp2980_3v3: lp2980-vdda { + compatible = "regulator-fixed"; + regulator-name = "vdda"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: reg_3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + + sound { + compatible = "fsl,imx25-pdk-sgtl5000", + "fsl,imx-audio-sgtl5000"; + model = "imx25-pdk-sgtl5000"; + ssi-controller = <&ssi1>; + audio-codec = <&codec>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + mux-int-port = <1>; + mux-ext-port = <4>; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; + +&iomuxc { + imx25_vmx25 { + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX25_PAD_RW__AUD4_TXFS 0xe0 + MX25_PAD_OE__AUD4_TXC 0xe0 + MX25_PAD_EB0__AUD4_TXD 0xe0 + MX25_PAD_EB1__AUD4_RXD 0xe0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX25_PAD_UART1_TXD__UART1_TXD 0x80000000 + MX25_PAD_UART1_RXD__UART1_RXD 0x80000000 + MX25_PAD_UART1_CTS__UART1_CTS 0x80000000 + MX25_PAD_UART1_RTS__UART1_RTS 0x80000000 + >; + }; + + pinctrl_fec: fecgrp { + fsl,pins = < + MX25_PAD_D11__GPIO_4_9 0xD0 /* FEC PHY power on pin */ + MX25_PAD_D13__GPIO_4_7 0xD0 /* FEC reset */ + MX25_PAD_FEC_MDC__FEC_MDC 0x80000000 + MX25_PAD_FEC_MDIO__FEC_MDIO 0x80000000 + MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000 + MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000 + MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 + MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000 + MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000 + MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000 + MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x80000000 + >; + }; + + pinctrl_nfc: nfcgrp { + fsl,pins = < + MX25_PAD_NF_CE0__NF_CE0 0x80000000 + MX25_PAD_NFWE_B__NFWE_B 0x80000000 + MX25_PAD_NFRE_B__NFRE_B 0x80000000 + MX25_PAD_NFALE__NFALE 0x80000000 + MX25_PAD_NFCLE__NFCLE 0x80000000 + MX25_PAD_NFWP_B__NFWP_B 0x80000000 + MX25_PAD_NFRB__NFRB 0x80000000 + MX25_PAD_D7__D7 0x80000000 + MX25_PAD_D6__D6 0x80000000 + MX25_PAD_D5__D5 0x80000000 + MX25_PAD_D4__D4 0x80000000 + MX25_PAD_D3__D3 0x80000000 + MX25_PAD_D2__D2 0x80000000 + MX25_PAD_D1__D1 0x80000000 + MX25_PAD_D0__D0 0x80000000 + >; + }; + + pinctrl_led: ledgrp { + fsl,pins = < + MX25_PAD_A18__GPIO_2_4 0xC0 + MX25_PAD_A19__GPIO_2_5 0xC0 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000 + MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000 + >; + }; + + pinctrl_audmux1: audmux1grp { + fsl,pins = < + MX25_PAD_EB0__AUD4_TXD 0x80000000 + MX25_PAD_EB1__AUD4_RXD 0x80000000 + MX25_PAD_RW__AUD4_TXFS 0x80000000 + MX25_PAD_OE__AUD4_TXC 0x80000000 + >; + }; + + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + MX25_PAD_SD1_CMD__SD1_CMD 0x00d5 + MX25_PAD_SD1_CLK__SD1_CLK 0x00d5 + MX25_PAD_SD1_DATA0__SD1_DATA0 0x00d5 + MX25_PAD_SD1_DATA1__SD1_DATA1 0x00d5 + MX25_PAD_SD1_DATA2__SD1_DATA2 0x00d5 + MX25_PAD_SD1_DATA3__SD1_DATA3 0x00d5 + MX25_PAD_BCLK__GPIO_4_4 0x80000000 /* card detect GPIO */ + MX25_PAD_D10__GPIO_4_10 0x80000000 /* SD CLK switch GPIO */ + >; + }; + }; +}; + +&fec { + phy-mode = "rmii"; + status = "okay"; +}; + +&nfc { + nand-on-flash-bbt; + status = "okay"; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&clks 1>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + }; + + eeprom_i2c_mod: at24c64@56 { + compatible = "at,24c64"; + pagesize = <32>; + reg = <0x56>; + }; + + eeprom_i2c_bb: at24c512@57 { + compatible = "at,24c512"; + pagesize = <128>; + reg = <0x57>; + }; +}; + +&ssi1 { + codec-handle = <&codec>; + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; diff -urN linux-3.12.orig/arch/arm/boot/dts/Makefile linux-3.12.work/arch/arm/boot/dts/Makefile --- linux-3.12.orig/arch/arm/boot/dts/Makefile 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/boot/dts/Makefile 2014-08-03 17:17:27.787881965 +0200 @@ -116,6 +116,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx25-karo-tx25.dtb \ imx25-pdk.dtb \ + imx25-vmx25.dtb \ imx27-apf27.dtb \ imx27-apf27dev.dtb \ imx27-pdk.dtb \ diff -urN linux-3.12.orig/arch/arm/mach-imx/clk-imx25.c linux-3.12.work/arch/arm/mach-imx/clk-imx25.c --- linux-3.12.orig/arch/arm/mach-imx/clk-imx25.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/clk-imx25.c 2014-08-03 17:17:27.799882013 +0200 @@ -260,7 +260,17 @@ clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27"); clk_register_clkdev(clk[usbotg_ahb], "ahb", "imx-udc-mx27"); clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27"); + clk_register_clkdev(clk[ipg], "ipg", "usbmisc_imx.0"); + clk_register_clkdev(clk[usbotg_ahb], "ahb", "usbmisc_imx.0"); + clk_register_clkdev(clk[usb_div], "per", "usbmisc_imx.0"); + clk_register_clkdev(clk[ipg], "ipg", "imx_usb.0"); + clk_register_clkdev(clk[usbotg_ahb], "ahb", "imx_usb.0"); + clk_register_clkdev(clk[usb_div], "per", "imx_usb.0"); + clk_register_clkdev(clk[ipg], "ipg", "imx_usb.1"); + clk_register_clkdev(clk[usbotg_ahb], "ahb", "imx_usb.1"); + clk_register_clkdev(clk[usb_div], "per", "imx_usb.1"); clk_register_clkdev(clk[nfc_ipg_per], NULL, "imx25-nand.0"); + clk_register_clkdev(clk[owire_ipg_per], NULL, "mxc_w1.0"); /* i.mx25 has the i.mx35 type cspi */ clk_register_clkdev(clk[cspi1_ipg], NULL, "imx35-cspi.0"); clk_register_clkdev(clk[cspi2_ipg], NULL, "imx35-cspi.1"); diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/devices-common.h linux-3.12.work/arch/arm/mach-imx/devices/devices-common.h --- linux-3.12.orig/arch/arm/mach-imx/devices/devices-common.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/devices-common.h 2014-08-03 17:17:27.803882028 +0200 @@ -69,6 +69,26 @@ const struct imx_fsl_usb2_udc_data *data, const struct fsl_usb2_platform_data *pdata); +#include +struct imx_mxc_usbmisc_data { + int id; + resource_size_t iobase; + resource_size_t iosize; +}; +struct platform_device *__init imx_add_mxc_usbmisc( + const struct imx_mxc_usbmisc_data *data, + const struct imx_usbmisc_platform_data *pdata); + +struct imx_mxc_usb_data { + int id; + resource_size_t iobase; + resource_size_t iosize; + resource_size_t irq; +}; +struct platform_device *__init imx_add_mxc_usb( + const struct imx_mxc_usb_data *data, + const struct imx_usb_platform_data *pdata); + #include struct platform_device *__init imx_add_gpio_keys( const struct gpio_keys_platform_data *pdata); diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/Kconfig linux-3.12.work/arch/arm/mach-imx/devices/Kconfig --- linux-3.12.orig/arch/arm/mach-imx/devices/Kconfig 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/Kconfig 2014-08-03 17:17:27.803882028 +0200 @@ -8,6 +8,12 @@ config IMX_HAVE_PLATFORM_FSL_USB2_UDC bool +config IMX_HAVE_PLATFORM_USBMISC + bool + +config IMX_HAVE_PLATFORM_CHIPIDEA + bool + config IMX_HAVE_PLATFORM_GPIO_KEYS bool default y if SOC_IMX51 diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/Makefile linux-3.12.work/arch/arm/mach-imx/devices/Makefile --- linux-3.12.orig/arch/arm/mach-imx/devices/Makefile 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/Makefile 2014-08-03 17:17:27.803882028 +0200 @@ -3,6 +3,8 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_FEC) += platform-fec.o obj-$(CONFIG_IMX_HAVE_PLATFORM_FLEXCAN) += platform-flexcan.o obj-$(CONFIG_IMX_HAVE_PLATFORM_FSL_USB2_UDC) += platform-fsl-usb2-udc.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_USBMISC) += platform-usbmisc.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_CHIPIDEA) += platform-chipidea.o obj-$(CONFIG_IMX_HAVE_PLATFORM_GPIO_KEYS) += platform-gpio_keys.o obj-y += platform-gpio-mxc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX21_HCD) += platform-imx21-hcd.o diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/platform-chipidea.c linux-3.12.work/arch/arm/mach-imx/devices/platform-chipidea.c --- linux-3.12.orig/arch/arm/mach-imx/devices/platform-chipidea.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/platform-chipidea.c 2014-08-03 17:17:27.803882028 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Voipac + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include "../hardware.h" +#include "devices-common.h" + +#define imx_mxc_usb_data_entry(soc, _id, _hwtype, _size) \ + [_id] = { \ + .id = _id, \ + .iobase = soc ## _USB_ ## _hwtype ## _BASE_ADDR, \ + .iosize = _size, \ + .irq = soc ## _INT_USB_ ## _hwtype, \ + } + +#ifdef CONFIG_SOC_IMX25 +const struct imx_mxc_usb_data imx25_mxc_usb_data[] __initconst = { +#define imx25_mxc_usb_data_entry(_id, _hwtype) \ + imx_mxc_usb_data_entry(MX25, _id, _hwtype, SZ_512) + imx25_mxc_usb_data_entry(0, OTG), + imx25_mxc_usb_data_entry(1, HS), +}; +#endif /* ifdef CONFIG_SOC_IMX25 */ + +struct platform_device *__init imx_add_mxc_usb( + const struct imx_mxc_usb_data *data, + const struct imx_usb_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return imx_add_platform_device("imx_usb", data->id, + res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); +} diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/platform-mxc_w1.c linux-3.12.work/arch/arm/mach-imx/devices/platform-mxc_w1.c --- linux-3.12.orig/arch/arm/mach-imx/devices/platform-mxc_w1.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/platform-mxc_w1.c 2014-08-03 17:17:27.803882028 +0200 @@ -19,6 +19,11 @@ imx_mxc_w1_data_entry_single(MX21); #endif /* ifdef CONFIG_SOC_IMX21 */ +#ifdef CONFIG_SOC_IMX25 +const struct imx_mxc_w1_data imx25_mxc_w1_data __initconst = + imx_mxc_w1_data_entry_single(MX25); +#endif /* ifdef CONFIG_SOC_IMX25 */ + #ifdef CONFIG_SOC_IMX27 const struct imx_mxc_w1_data imx27_mxc_w1_data __initconst = imx_mxc_w1_data_entry_single(MX27); diff -urN linux-3.12.orig/arch/arm/mach-imx/devices/platform-usbmisc.c linux-3.12.work/arch/arm/mach-imx/devices/platform-usbmisc.c --- linux-3.12.orig/arch/arm/mach-imx/devices/platform-usbmisc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices/platform-usbmisc.c 2014-08-03 17:17:27.803882028 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Voipac + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include "../hardware.h" +#include "devices-common.h" + +#define imx_mxc_usbmisc_data_entry(soc, _id, _hwid, _size) \ + [_id] = { \ + .id = _id, \ + .iobase = soc ## _USBMISC ## _hwid ## _BASE_ADDR, \ + .iosize = _size, \ + } + +#ifdef CONFIG_SOC_IMX25 +const struct imx_mxc_usbmisc_data imx25_mxc_usbmisc_data[] __initconst = { +#define imx25_mxc_usbmisc_data_entry(_id, _hwid) \ + imx_mxc_usbmisc_data_entry(MX25, _id, _hwid, SZ_16) + imx25_mxc_usbmisc_data_entry(0, 1), +}; +#endif /* ifdef CONFIG_SOC_IMX25 */ + +struct platform_device *__init imx_add_mxc_usbmisc( + const struct imx_mxc_usbmisc_data *data, + const struct imx_usbmisc_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, + }; + + return imx_add_platform_device("usbmisc_imx", data->id, + res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); +} diff -urN linux-3.12.orig/arch/arm/mach-imx/devices-imx25.h linux-3.12.work/arch/arm/mach-imx/devices-imx25.h --- linux-3.12.orig/arch/arm/mach-imx/devices-imx25.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/devices-imx25.h 2014-08-03 17:17:27.811882059 +0200 @@ -22,6 +22,17 @@ #define imx25_add_fsl_usb2_udc(pdata) \ imx_add_fsl_usb2_udc(&imx25_fsl_usb2_udc_data, pdata) +extern const struct imx_mxc_usbmisc_data imx25_mxc_usbmisc_data[]; +#define imx25_add_mxc_usbmisc(id, pdata) \ + imx_add_mxc_usbmisc(&imx25_mxc_usbmisc_data[id], pdata) +#define imx25_add_mxc_usbmisc0(pdata) imx25_add_mxc_usbmisc(0, pdata) + +extern const struct imx_mxc_usb_data imx25_mxc_usb_data[]; +#define imx25_add_mxc_usb(id, pdata) \ + imx_add_mxc_usb(&imx25_mxc_usb_data[id], pdata) +#define imx25_add_mxc_usb0(pdata) imx25_add_mxc_usb(0, pdata) +#define imx25_add_mxc_usb1(pdata) imx25_add_mxc_usb(1, pdata) + extern struct imx_imxdi_rtc_data imx25_imxdi_rtc_data; #define imx25_add_imxdi_rtc() \ imx_add_imxdi_rtc(&imx25_imxdi_rtc_data) @@ -73,6 +84,10 @@ #define imx25_add_mxc_nand(pdata) \ imx_add_mxc_nand(&imx25_mxc_nand_data, pdata) +extern const struct imx_mxc_w1_data imx25_mxc_w1_data; +#define imx25_add_mxc_w1() \ + imx_add_mxc_w1(&imx25_mxc_w1_data) + extern const struct imx_sdhci_esdhc_imx_data imx25_sdhci_esdhc_imx_data[]; #define imx25_add_sdhci_esdhc_imx(id, pdata) \ imx_add_sdhci_esdhc_imx(&imx25_sdhci_esdhc_imx_data[id], pdata) diff -urN linux-3.12.orig/arch/arm/mach-imx/Kconfig linux-3.12.work/arch/arm/mach-imx/Kconfig --- linux-3.12.orig/arch/arm/mach-imx/Kconfig 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/Kconfig 2015-11-29 17:54:22.951258536 +0100 @@ -251,6 +251,139 @@ endchoice +config MACH_VMX25 + bool "Support Voipac VMX25 module" + select IMX_HAVE_PLATFORM_FLEXCAN + select IMX_HAVE_PLATFORM_FSL_USB2_UDC + select IMX_HAVE_PLATFORM_USBMISC + select IMX_HAVE_PLATFORM_CHIPIDEA + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMXDI_RTC + select IMX_HAVE_PLATFORM_IMX_FB + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_KEYPAD + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_MXC_NAND + select IMX_HAVE_PLATFORM_IMX_SSI + select IMX_HAVE_PLATFORM_SPI_IMX + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_MXC_W1 + select LEDS_GPIO_REGISTER + select SOC_IMX25 + help + Include support for Voipac VMX25 processor module + +config VMX25_DEBUG + bool "Enable verbose debug messages" + depends on MACH_VMX25 + help + Enable verbose debug messages. + +choice + prompt "SD slot selection" + depends on MACH_VMX25 + default VMX25_SD_ON_BOARD if MACH_VMX_BASEBOARD + +config VMX25_SD_ON_MODULE + bool "Use integrated SD slot" + depends on MACH_VMX25 + help + Provide SD CLK signal to vmx25 on module SD slot. + +config VMX25_SD_ON_BOARD + bool "Use external SD slot" + depends on MACH_VMX25 + help + Provide SD CLK signal to vmx25 baseboard SD slot. + +endchoice + +choice + prompt "I2C memory" + depends on MACH_VMX25 + default MACH_VMX25_MODULE_I2C_NONE + +config MACH_VMX25_MODULE_I2C_NONE + bool "None" + +config MACH_VMX25_MODULE_I2C_AT24C512 + bool "AT24C512" + help + Basic/Pro/Max module configuration + +endchoice + +choice + prompt "SPI memory" + depends on MACH_VMX25 + default MACH_VMX25_MODULE_SPI_NONE + +config MACH_VMX25_MODULE_SPI_NONE + bool "None" + +config MACH_VMX25_MODULE_SPI_SST25VF016B + bool "SST25VF016B" + help + Basic module configuration + +config MACH_VMX25_MODULE_SPI_SST25VF032B + bool "SST25VF032B" + help + Pro/Max module configuration + +config MACH_VMX25_MODULE_SPI_SST26VF032B + bool "SST26VF032B" + help + Basic/Pro/Max module configuration + +endchoice + +choice + prompt "Baseboard" + depends on MACH_VMX25 + default MACH_VMX_BASEBOARD + +config MACH_VMX_BASEBOARD + bool "Voipac development baseboard" + help + This adds board specific devices that can be found on Voipac's + development baseboard. + +endchoice + +choice + prompt "I2C memory" + depends on MACH_VMX25 + depends on MACH_VMX_BASEBOARD + default MACH_VMX_BASEBOARD_I2C_AT24C512 + +config MACH_VMX_BASEBOARD_I2C_NONE + bool "None" + +config MACH_VMX_BASEBOARD_I2C_AT24C512 + bool "AT24C512" + help + Default configuration + +endchoice + +choice + prompt "SPI memory" + depends on MACH_VMX25 + depends on MACH_VMX_BASEBOARD + default MACH_VMX_BASEBOARD_SPI_SST25VF032B + +config MACH_VMX_BASEBOARD_SPI_NONE + bool "None" + +config MACH_VMX_BASEBOARD_SPI_SST25VF032B + bool "SST25VF032B" + help + Default configuration + +endchoice + config MACH_IMX25_DT bool "Support i.MX25 platforms from device tree" select SOC_IMX25 diff -urN linux-3.12.orig/arch/arm/mach-imx/mach-vmx25.c linux-3.12.work/arch/arm/mach-imx/mach-vmx25.c --- linux-3.12.orig/arch/arm/mach-imx/mach-vmx25.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/mach-vmx25.c 2015-11-29 17:50:35.747263024 +0100 @@ -0,0 +1,979 @@ +/* + * arch/arm/mach-mx25/vmx-baseboard.c + * + * Copyright (C) 2014 Voipac + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * + * This file adds support for devices found on Voipac baseboard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#include +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "devices-imx25.h" +#include "vmx-common.h" +#include "hardware.h" +#include "iomux-mx25.h" +#include "mx25.h" + +extern void __init vmx_baseboard_init(void); + +// NOTES +// missing IMX SGTL5000 +// missing IMX ADC, IMX TOUCH +// missing FM radio +// missing PWM + +vmx_module_t vmx25_module; + +#define VMX25_RESET_OUT_B_GPIO IMX_GPIO_NR(3, 18) + +static iomux_v3_cfg_t vmx25_gpio_pads[] = { + MX25_PAD_VSTBY_ACK__GPIO_3_18, // RESET_OUT_B +}; + +static int __init vmx25_module_hw_init(void * pData) +{ + int ret = 0; + + + VMXPRINTK(KERN_INFO "%s: Configuring MODULE pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_gpio_pads, + ARRAY_SIZE(vmx25_gpio_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(VMX25_RESET_OUT_B_GPIO, "RESET OUT B"); + + gpio_direction_output(VMX25_RESET_OUT_B_GPIO, 1); + + return ret; +} + +static int __init vmx25_module_dev_init(void * pData) +{ + int ret = 0; + + vmx25_module.gpio_reset = VMX25_RESET_OUT_B_GPIO; + + return ret; +} + +#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) +static iomux_v3_cfg_t vmx25_uart_pads[][4] = { + { + MX25_PAD_UART1_TXD__UART1_TXD, + MX25_PAD_UART1_RXD__UART1_RXD, + MX25_PAD_UART1_CTS__UART1_CTS, + MX25_PAD_UART1_RTS__UART1_RTS, + }, + { + MX25_PAD_UART2_TXD__UART2_TXD, + MX25_PAD_UART2_RXD__UART2_RXD, + MX25_PAD_UART2_CTS__UART2_CTS, + MX25_PAD_UART2_RTS__UART2_RTS, + }, + { + // UART 3 is not useable on VMX25 (conflicts with SPI) + }, + { + // UART 4 is not useable on VMX25 (conflicts with SSI) + }, + { + MX25_PAD_ECB__UART5_TXD_MUX, + MX25_PAD_LBA__UART5_RXD_MUX, + MX25_PAD_CS4__UART5_CTS, + MX25_PAD_CS5__UART5_RTS, + } +}; + +static const struct imxuart_platform_data uart_pdata[] __initconst = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, + { + .flags = IMXUART_HAVE_RTSCTS, + }, + { + .flags = 0, + }, + { + .flags = 0, + }, + { + .flags = IMXUART_HAVE_RTSCTS, + } +}; + +static int __init vmx25_uart_hw_init(void * pData) +{ + int ret = 0; + e_uart_id_t index = (e_uart_id_t) pData; + + VMXASSERT(IS_SUPPORTED_UART(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring UART%d pads\n", __FUNCTION__, index); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_uart_pads[index], + ARRAY_SIZE(vmx25_uart_pads[index])); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_uart_dev_init(void * pData) +{ + int ret = 0; + uint index = (uint) pData; + + VMXASSERT(IS_SUPPORTED_UART(index)); + + imx25_add_imx_uart(index, &uart_pdata[index]); + + return ret; +} +#endif // CONFIG_SERIAL_IMX + +#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) +static iomux_v3_cfg_t vmx25_nand_pads[] = { + MX25_PAD_NF_CE0__NF_CE0, + MX25_PAD_NFWE_B__NFWE_B, + MX25_PAD_NFRE_B__NFRE_B, + MX25_PAD_NFALE__NFALE, + MX25_PAD_NFCLE__NFCLE, + MX25_PAD_NFWP_B__NFWP_B, + MX25_PAD_NFRB__NFRB, + MX25_PAD_D7__D7, + MX25_PAD_D6__D6, + MX25_PAD_D5__D5, + MX25_PAD_D4__D4, + MX25_PAD_D3__D3, + MX25_PAD_D2__D2, + MX25_PAD_D1__D1, + MX25_PAD_D0__D0, +}; + +static struct mtd_partition vmx25_nand_partitions[] = { + { + .name = "nand0.barebox", + .offset = 0, + .size = 512 * 1024}, + { + .name = "nand0.environment", + .offset = MTDPART_OFS_APPEND, + .size = 512 * 1024}, + { + .name = "nand0.kernel", + .offset = MTDPART_OFS_APPEND, + .size = 4 * 1024 * 1024}, + { + .name = "nand0.rootfs", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + }, +}; + +static const struct mxc_nand_platform_data vmx25_nand_pdata __initconst = { + .parts = vmx25_nand_partitions, + .nr_parts = ARRAY_SIZE(vmx25_nand_partitions), + .width = 1, + .hw_ecc = 1, + .flash_bbt = 1, +}; + +static int __init vmx25_nand_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring NAND pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_nand_pads, + ARRAY_SIZE(vmx25_nand_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_nand_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_mxc_nand(&vmx25_nand_pdata); + + return ret; +} +#endif // CONFIG_MTD_NAND_MXC + +#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE) +#define VMX25_FEC_PWR_GPIO IMX_GPIO_NR(4, 9) +#define VMX25_FEC_RST_GPIO IMX_GPIO_NR(4, 7) + +static iomux_v3_cfg_t vmx25_fec_pads[] = { + MX25_PAD_FEC_MDC__FEC_MDC, + MX25_PAD_FEC_MDIO__FEC_MDIO, + MX25_PAD_FEC_TDATA0__FEC_TDATA0, + MX25_PAD_FEC_TDATA1__FEC_TDATA1, + MX25_PAD_FEC_TX_EN__FEC_TX_EN, + MX25_PAD_FEC_RDATA0__FEC_RDATA0, + MX25_PAD_FEC_RDATA1__FEC_RDATA1, + MX25_PAD_FEC_RX_DV__FEC_RX_DV, + MX25_PAD_FEC_TX_CLK__FEC_TX_CLK, + MX25_PAD_D11__GPIO_4_9, // FEC PHY power + MX25_PAD_D13__GPIO_4_7, // FEC reset +}; + +static const struct fec_platform_data vmx25_fec_pdata __initconst = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static int __init vmx25_fec_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring FEC pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_fec_pads, + ARRAY_SIZE(vmx25_fec_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(VMX25_FEC_PWR_GPIO, "FEC PHY enable"); + gpio_request(VMX25_FEC_RST_GPIO, "FEC PHY reset"); + + + // turn off PHY power and lift reset + gpio_direction_output(VMX25_FEC_PWR_GPIO, 0); // drop PHY power + gpio_direction_output(VMX25_FEC_RST_GPIO, 0); // assert reset + + udelay(100); + + // turn on PHY power and lift reset + gpio_set_value(VMX25_FEC_PWR_GPIO, 1); + gpio_set_value(VMX25_FEC_RST_GPIO, 1); + + return ret; +} + +static int __init vmx25_fec_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_fec(&vmx25_fec_pdata); + + return ret; +} +#endif // CONFIG_FEC + +#if defined(CONFIG_RTC_DRV_IMXDI) || defined(CONFIG_RTC_DRV_IMXDI_MODULE) +static int __init vmx25_rtc_hw_init(void * pData) +{ + int ret = 0; + + return ret; +} + +static int __init vmx25_rtc_dev_init(void * pData) +{ + int ret = 0; + struct platform_device * p_dev; + + p_dev = imx25_add_imxdi_rtc(); + + device_init_wakeup(&p_dev->dev, true); + + return ret; +} +#endif // CONFIG_RTC_DRV_IMXDI + +#if defined(CONFIG_IMX2_WDT) || defined(CONFIG_IMX2_WDT_MODULE) +static int __init vmx25_wdt_hw_init(void * pData) +{ + int ret = 0; + + return ret; +} + +static int __init vmx25_wdt_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_imx2_wdt(); + + return ret; +} +#endif // CONFIG_IMX2_WDT + +#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE) +static iomux_v3_cfg_t vmx25_can_pads[][2] = { + { + // conflicts with gpio controling USB + MX25_PAD_GPIO_A__CAN1_TX, + MX25_PAD_GPIO_B__CAN1_RX, + }, + { + MX25_PAD_GPIO_C__CAN2_TX, + MX25_PAD_GPIO_D__CAN2_RX, + } +}; + +static int __init vmx25_can_hw_init(void * pData) +{ + int ret; + e_flexcan_id_t index = (e_flexcan_id_t) pData; + + VMXASSERT(IS_SUPPORTED_FLEXCAN(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring CAN%d pads\n", __FUNCTION__, index); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_can_pads[index], + ARRAY_SIZE(vmx25_can_pads[index])); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_can_dev_init(void * pData) +{ + int ret = 0; + e_flexcan_id_t index = (e_flexcan_id_t) pData; + + VMXASSERT(IS_SUPPORTED_FLEXCAN(index)); + + imx25_add_flexcan(index); + + return ret; +} +#endif // CONFIG_CAN_FLEXCAN + + +#if defined(CONFIG_MMC_SDHCI_ESDHC_IMX) || defined(CONFIG_MMC_SDHCI_ESDHC_IMX_MODULE) +#define VMX25_SDHC_CLK_SEL_GPIO IMX_GPIO_NR(4, 10) +#define VMX25_SDHC_SD1_CD_GPIO IMX_GPIO_NR(4, 4) + +typedef enum { + e_sdhc_id_1 = 0, + e_sdhc_id_2 = 1 +} e_sdhc_id_t; +#define IS_SUPPORTED_SDHC(a) (((a) == e_sdhc_id_1)) + +static iomux_v3_cfg_t vmx25_sdhc_pads[][8] = { + { + MX25_PAD_SD1_CMD__SD1_CMD, + MX25_PAD_SD1_CLK__SD1_CLK, + MX25_PAD_SD1_DATA0__SD1_DATA0, + MX25_PAD_SD1_DATA1__SD1_DATA1, + MX25_PAD_SD1_DATA2__SD1_DATA2, + MX25_PAD_SD1_DATA3__SD1_DATA3, + MX25_PAD_BCLK__GPIO_4_4, // card detect GPIO + MX25_PAD_D10__GPIO_4_10, // SD CLK switch GPIO + }, + { + // Empty + } +}; + +static struct esdhc_platform_data vmx25_sdhc_pdata[] = { + { + .cd_gpio = VMX25_SDHC_SD1_CD_GPIO, + .wp_type = ESDHC_WP_NONE, + .cd_type = ESDHC_CD_GPIO, + .max_bus_width = 4, + }, + { + // Empty + } +}; + +static int __init vmx25_sdhc_hw_init(void * pData) +{ + int ret; + e_sdhc_id_t index = (e_sdhc_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SDHC(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring SDHC%d pads\n", __FUNCTION__, index); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_sdhc_pads[index], + ARRAY_SIZE(vmx25_sdhc_pads[index])); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(VMX25_SDHC_CLK_SEL_GPIO, "SD CLK select"); + gpio_direction_output(VMX25_SDHC_CLK_SEL_GPIO, 0); + +#if defined(CONFIG_VMX25_SD_ON_MODULE) + gpio_set_value(VMX25_SDHC_CLK_SEL_GPIO, 1); + vmx25_sdhc_pdata[index].wp_type = ESDHC_WP_NONE; + vmx25_sdhc_pdata[index].cd_type = ESDHC_CD_NONE; +#elif defined(CONFIG_VMX25_SD_ON_BOARD) + gpio_set_value(VMX25_SDHC_CLK_SEL_GPIO, 0); + vmx25_sdhc_pdata[index].wp_type = ESDHC_WP_NONE; + vmx25_sdhc_pdata[index].cd_type = ESDHC_CD_GPIO; +#else +#error "Choice active SD slot internal/external" +#endif + + return ret; +} + +static int __init vmx25_sdhc_dev_init(void * pData) +{ + int ret = 0; + e_sdhc_id_t index = (e_sdhc_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SDHC(index)); + + imx25_add_sdhci_esdhc_imx(index, &vmx25_sdhc_pdata[index]); + + return ret; +} +#endif // CONFIG_MMC_SDHCI_ESDHC_IMX + +#if defined(CONFIG_USB_CHIPIDEA) || defined(CONFIG_USB_CHIPIDEA_MODULE) +#define VMX25_USBOTG_PWR_GPIO IMX_GPIO_NR(1, 0) +#define VMX25_USBOTG_OC_GPIO IMX_GPIO_NR(1, 1) +#define VMX25_USBH2_PWR_GPIO IMX_GPIO_NR(4, 11) +#define VMX25_USBH2_OC_GPIO IMX_GPIO_NR(4, 12) + +static iomux_v3_cfg_t vmx25_usb_pads[] = { +// USBOTG +// MX25_PAD_GPIO_A__USBOTG_PWR, + MX25_PAD_GPIO_B__USBOTG_OC, + MX25_PAD_GPIO_A__GPIO_A, +// MX25_PAD_GPIO_B__GPIO_B, +// USBHOST +// MX25_PAD_D9__USBH2_PWR, + MX25_PAD_D8__USBH2_OC, + MX25_PAD_D9__GPIO_4_11, +// MX25_PAD_D8__GPIO_4_12, +}; + +static int __init vmx25_usb_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring USB pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_usb_pads, + ARRAY_SIZE(vmx25_usb_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(VMX25_USBOTG_PWR_GPIO, "USBOTG PWR"); +// gpio_request(VMX25_USBOTG_OC_GPIO, "USBOTG OC"); + + gpio_direction_output(VMX25_USBOTG_PWR_GPIO, 1); +// gpio_direction_input (VMX25_USBOTG_OC_GPIO); + + gpio_request(VMX25_USBH2_PWR_GPIO, "USBH2 PWR"); +// gpio_request(VMX25_USBH2_OC_GPIO, "USBH2 OC"); + + gpio_direction_output(VMX25_USBH2_PWR_GPIO, 1); +// gpio_direction_input (VMX25_USBH2_OC_GPIO); + + + return ret; +} + +static int vmx25_phy_init(struct usb_phy *otg) +{ + return 0; +} + + +static int vmx25_otg_set_vbus(struct usb_otg *otg, bool on) +{ + return 0; +} + +static struct usb_otg vmx25_phy_otg = { + .set_vbus = vmx25_otg_set_vbus, +}; + +static struct usb_phy vmx25_usbotg_phy = { + .label = "PHY", + .init = vmx25_phy_init, + .otg = &vmx25_phy_otg, +}; + +static struct usb_phy vmx25_usbh2_phy = { + .label = "PHY", + .init = vmx25_phy_init, +}; + +static const struct imx_usbmisc_platform_data vmx25_usbmisc_pdata __initconst = { + .name = "fsl,imx25-usbmisc", +}; + +static const struct imx_usb_platform_data vmx25_usbotg_pdata __initconst = { + .name = "fsl,imx27-usb", + .phy_mode = USBPHY_INTERFACE_MODE_UTMI, + .dr_mode = USB_DR_MODE_OTG, + .phy = &vmx25_usbotg_phy, + .usbmisc_index = 0, + .usbmisc_disable_oc = 1, + .usbmisc_evdo = 0, +}; + +static const struct imx_usb_platform_data vmx25_usbhost1_pdata __initconst = { + .name = "fsl,imx27-usb", + .phy_mode = USBPHY_INTERFACE_MODE_SERIAL, + .dr_mode = USB_DR_MODE_HOST, + .phy = &vmx25_usbh2_phy, + .usbmisc_index = 1, + .usbmisc_disable_oc = 1, + .usbmisc_evdo = 0, +}; + +static int __init vmx25_usb_dev_init(void * pData) +{ + int ret = 0; + + VMXPRINTK(KERN_INFO "%s: Configuring USB device\n", __FUNCTION__); + imx25_add_mxc_usbmisc0(&vmx25_usbmisc_pdata); + imx25_add_mxc_usb0(&vmx25_usbotg_pdata); + imx25_add_mxc_usb1(&vmx25_usbhost1_pdata); + + return ret; +} +#endif // CONFIG_USB_CHIPIDEA + +#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) +static iomux_v3_cfg_t vmx25_i2c_pads[][2] = { + { + MX25_PAD_I2C1_CLK__I2C1_CLK, + MX25_PAD_I2C1_DAT__I2C1_DAT, + }, + { + // Empty + }, + { + // Empty + } +}; + +static const struct imxi2c_platform_data vmx25_i2c_pdata[] __initconst = { + { + .bitrate = 100000, + }, + { + // Empty + }, + { + // Empty + } +}; + +static struct at24_platform_data vmx25_module_eeprom = { + .byte_len = SZ_512K / 8, + .page_size = 128, + .flags = AT24_FLAG_ADDR16, +}; + +static struct i2c_board_info vmx25_i2c1_boardinfo[] __initdata = { +#if defined(CONFIG_MACH_VMX25_MODULE_I2C_AT24C512) + { + I2C_BOARD_INFO("24c512", 0x56), + .platform_data = &vmx25_module_eeprom, + }, +#endif +}; + +static int __init vmx25_i2c_hw_init(void * pData) +{ + int ret; + e_i2c_id_t index = (e_i2c_id_t) pData; + + VMXASSERT(IS_SUPPORTED_I2C(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring I2C%d pads\n", __FUNCTION__, index); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_i2c_pads[index], + ARRAY_SIZE(vmx25_i2c_pads[index])); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_i2c_dev_init(void * pData) +{ + int ret = 0; + e_i2c_id_t index = (e_i2c_id_t) pData; + + VMXASSERT(IS_SUPPORTED_I2C(index)); + + if (index == e_i2c_id_1) { + i2c_register_board_info(index, vmx25_i2c1_boardinfo, + ARRAY_SIZE(vmx25_i2c1_boardinfo)); + } + + imx25_add_imx_i2c(index, &vmx25_i2c_pdata[index]); + + return ret; +} +#endif // CONFIG_I2C_IMX + + +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +#define VMX25_SPI_SS0_GPIO IMX_GPIO_NR(1, 16) +#define VMX25_SPI_SS1_GPIO IMX_GPIO_NR(1, 17) +#define VMX25_SPI_MCP251X_CS_GPIO IMX_GPIO_NR(2, 10) +#define VMX25_SPI_TSC2049_CS_GPIO IMX_GPIO_NR(2, 11) + +static iomux_v3_cfg_t vmx25_spi_pads[][7] = { + { + MX25_PAD_CSPI1_MOSI__CSPI1_MOSI, + MX25_PAD_CSPI1_MISO__CSPI1_MISO, + MX25_PAD_CSPI1_SCLK__CSPI1_SCLK, +// MX25_PAD_CSPI1_RDY__CSPI1_RDY, + MX25_PAD_CSPI1_SS0__GPIO_1_16, // CS Module SPI flash + MX25_PAD_CSPI1_SS1__GPIO_1_17, // CS Baseboard SPI flash + MX25_PAD_A24__GPIO_2_10, // CS CONFIG_CAN_MCP251X + MX25_PAD_A25__GPIO_2_11, // CS CONFIG_TOUCHSCREEN_ADS7846 + }, + { + // Empty + }, + { + // Empty + } +}; + +static int vmx25_spi_chipselect[][4] = { + { + VMX25_SPI_SS0_GPIO, + VMX25_SPI_SS1_GPIO, + VMX25_SPI_MCP251X_CS_GPIO, // CONFIG_CAN_MCP251X + VMX25_SPI_TSC2049_CS_GPIO, // CONFIG_TOUCHSCREEN_ADS7846 + }, + { + // Empty + }, + { + // Empty + } +}; + +static const struct spi_imx_master vmx25_spi_pdata[] __initconst = { + { + .chipselect = vmx25_spi_chipselect[e_spi_id_1], + .num_chipselect = ARRAY_SIZE(vmx25_spi_chipselect[e_spi_id_1]), + }, + { + // Empty + }, + { + // Empty + } +}; + +static struct mtd_partition vmx25_spi_partitions[] = { + { + .name = "vmx_module_rom(spi)", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM + }, + { + .name = "vmx_module_rwm(spi)", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct flash_platform_data vmx25_spi_flash_data = { + .name = "m25p80", + .parts = vmx25_spi_partitions, + .nr_parts = ARRAY_SIZE(vmx25_spi_partitions), +#if defined(MACH_VMX25_MODULE_SPI_SST25VF016B) + .type = "sst25vf016b" +#endif +#if defined(MACH_VMX25_MODULE_SPI_SST25VF032B) + .type = "sst25vf032b" +#endif +#if defined(MACH_VMX25_MODULE_SPI_SST26VF032B) + .type = "sst26vf032b" +#endif +}; + +static struct spi_board_info vmx25_spi1_boardinfo[] __initdata = { +#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) +#if !defined(MACH_VMX25_MODULE_SPI_NONE) + { + // the modalias must be the same as spi device driver name + .modalias = "m25p80", // Name of spi_driver for this device + .max_speed_hz = 25000000, // max spi clock (SCK) speed in HZ + .bus_num = 0, // Framework bus number + .chip_select = 0, // On vmx it's SPI0_SS0 + .platform_data = &vmx25_spi_flash_data, + .mode = SPI_MODE_3, + }, +#endif +#else +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 2000000, + .bus_num = 0, + .chip_select = 0, + }, +#endif // CONFIG_SPI_SPIDEV +#endif // CONFIG_MTD_M25P80 +}; + +static int __init vmx25_spi_hw_init(void * pData) +{ + int ret; + e_spi_id_t index = (e_spi_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SPI(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring SPI%d pads\n", __FUNCTION__, index); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_spi_pads[index], + ARRAY_SIZE(vmx25_spi_pads[index])); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_spi_dev_init(void * pData) +{ + int ret = 0; + e_spi_id_t index = (e_spi_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SPI(index)); + + if (index == e_spi_id_1) { + spi_register_board_info(vmx25_spi1_boardinfo, + ARRAY_SIZE(vmx25_spi1_boardinfo)); + } + + imx25_add_spi_imx(index, &vmx25_spi_pdata[index]); + + return ret; +} +#endif // CONFIG_SPI_IMX + +#if defined(CONFIG_SND_SOC_IMX_SSI) || defined(CONFIG_SND_SOC_IMX_SSI_MODULE) +static iomux_v3_cfg_t vmx25_ssi_pads[] = { + MX25_PAD_EB0__AUD4_TXD, + MX25_PAD_EB1__AUD4_RXD, + MX25_PAD_RW__AUD4_TXFS, + MX25_PAD_OE__AUD4_TXC, +}; + +static struct imx_ssi_platform_data vmx25_ssi_pdata = { + .flags = IMX_SSI_SYN | IMX_SSI_NET | IMX_SSI_USE_I2S_SLAVE, +}; + +static int __init vmx25_ssi_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring SSI pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_ssi_pads, + ARRAY_SIZE(vmx25_ssi_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_ssi_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_imx_ssi(0, &vmx25_ssi_pdata); + + return ret; +} +#endif // SND_SOC_IMX_SSI + +#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE) +static iomux_v3_cfg_t vmx25_owire_pads[] = { + MX25_PAD_RTCK__OWIRE, +}; + +static int __init vmx25_owire_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring OWIRE pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_owire_pads, + ARRAY_SIZE(vmx25_owire_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init vmx25_owire_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_mxc_w1(); + + return ret; +} +#endif // W1_MASTER_MXC + +static const vmx_dev_list_t vmx25_dev_list[] = +{ + VMX_DEV_LIST(true, true, "imx25 module", + vmx25_module_hw_init, vmx25_module_dev_init, NULL), +#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 uart1", + vmx25_uart_hw_init, vmx25_uart_dev_init, e_uart_id_1), + VMX_DEV_LIST(true, true, "imx25 uart2", + vmx25_uart_hw_init, vmx25_uart_dev_init, e_uart_id_2), + VMX_DEV_LIST(true, true, "imx25 uart5", + vmx25_uart_hw_init, vmx25_uart_dev_init, e_uart_id_5), +#endif +#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) + VMX_DEV_LIST(true, true, "imx25 nand", + vmx25_nand_hw_init, vmx25_nand_dev_init, NULL), +#endif +#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE) + VMX_DEV_LIST(true, true, "imx25 fec", + vmx25_fec_hw_init, vmx25_fec_dev_init, NULL), +#endif +#if defined(CONFIG_RTC_DRV_IMXDI) || defined(CONFIG_RTC_DRV_IMXDI_MODULE) + VMX_DEV_LIST(true, true, "imx25 rtc", + vmx25_rtc_hw_init, vmx25_rtc_dev_init, NULL), +#endif +#if defined(CONFIG_IMX2_WDT) || defined(CONFIG_IMX2_WDT_MODULE) + VMX_DEV_LIST(true, true, "imx25 wdt", + vmx25_wdt_hw_init, vmx25_wdt_dev_init, NULL), +#endif +#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE) + VMX_DEV_LIST(true, true, "imx25 can2", + vmx25_can_hw_init, vmx25_can_dev_init, e_flexcan_id_2), +#endif +#if defined(CONFIG_MMC_SDHCI_ESDHC_IMX) || defined(CONFIG_MMC_SDHCI_ESDHC_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 sdhc1", + vmx25_sdhc_hw_init, vmx25_sdhc_dev_init, e_sdhc_id_1), +#endif +#if defined(CONFIG_USB_CHIPIDEA) || defined(CONFIG_USB_CHIPIDEA_MODULE) + VMX_DEV_LIST(true, true, "imx25 usb", + vmx25_usb_hw_init, vmx25_usb_dev_init, NULL), +#endif +#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 i2c1", + vmx25_i2c_hw_init, vmx25_i2c_dev_init, e_i2c_id_1), +#endif +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 spi1", + vmx25_spi_hw_init, vmx25_spi_dev_init, e_spi_id_1), +#endif +#if defined(CONFIG_SND_SOC_IMX_SSI) || defined(CONFIG_SND_SOC_IMX_SSI_MODULE) + VMX_DEV_LIST(true, true, "imx25 ssi0", + vmx25_ssi_hw_init, vmx25_ssi_dev_init, NULL), +#endif +#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE) + VMX_DEV_LIST(true, true, "imx25 w1", + vmx25_owire_hw_init, vmx25_owire_dev_init, NULL), +#endif + }; + +static void __init vmx25_timer_init(void) +{ + mx25_clocks_init(); +} + +static void __init vmx25_board_init(void) +{ + int i; + int vmx25_dev_count; + + imx25_soc_init(); + + vmx25_dev_count = ARRAY_SIZE(vmx25_dev_list); + for (i = 0; i < vmx25_dev_count; i++) { + vmx_register_device(&vmx25_dev_list[i]); + } + +#ifdef CONFIG_MACH_VMX_BASEBOARD + vmx_baseboard_init(); +#endif +} + +MACHINE_START(VMX25, "Voipac VMX25 module (Freescale i.MX25)") + /* Maintainer: */ + .atag_offset = 0x100, + .map_io = mx25_map_io, + .init_early = imx25_init_early, + .init_irq = mx25_init_irq, + .handle_irq = imx25_handle_irq, + .init_time = vmx25_timer_init, + .init_machine = vmx25_board_init, + .restart = mxc_restart, +MACHINE_END diff -urN linux-3.12.orig/arch/arm/mach-imx/Makefile linux-3.12.work/arch/arm/mach-imx/Makefile --- linux-3.12.orig/arch/arm/mach-imx/Makefile 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/Makefile 2014-08-03 17:17:27.815882076 +0200 @@ -52,6 +52,8 @@ obj-$(CONFIG_MACH_MX25_3DS) += mach-mx25_3ds.o obj-$(CONFIG_MACH_EUKREA_CPUIMX25SD) += mach-eukrea_cpuimx25.o obj-$(CONFIG_MACH_EUKREA_MBIMXSD25_BASEBOARD) += eukrea_mbimxsd25-baseboard.o +obj-$(CONFIG_MACH_VMX25) += mach-vmx25.o vmx-common.o +obj-$(CONFIG_MACH_VMX_BASEBOARD) += vmx-baseboard.o obj-$(CONFIG_MACH_IMX25_DT) += imx25-dt.o # i.MX27 based machines diff -urN linux-3.12.orig/arch/arm/mach-imx/mx25.h linux-3.12.work/arch/arm/mach-imx/mx25.h --- linux-3.12.orig/arch/arm/mach-imx/mx25.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/mx25.h 2014-08-03 17:17:27.815882076 +0200 @@ -13,6 +13,7 @@ #define MX25_CAN1_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0x88000) #define MX25_CAN2_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0x8c000) #define MX25_I2C2_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0x98000) +#define MX25_OWIRE_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0x9c000) #define MX25_CSPI1_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0xa4000) #define MX25_IOMUXC_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0xac000) @@ -56,6 +57,7 @@ * Freescale internal sources confirm only the latter value to work. */ #define MX25_USB_HS_BASE_ADDR (MX25_USB_BASE_ADDR + 0x0400) +#define MX25_USBMISC1_BASE_ADDR (MX25_USB_BASE_ADDR + 0x0600) #define MX25_CSI_BASE_ADDR 0x53ff8000 #define MX25_IO_P2V(x) IMX_IO_P2V(x) diff -urN linux-3.12.orig/arch/arm/mach-imx/vmx-baseboard.c linux-3.12.work/arch/arm/mach-imx/vmx-baseboard.c --- linux-3.12.orig/arch/arm/mach-imx/vmx-baseboard.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/vmx-baseboard.c 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,689 @@ +/* + * arch/arm/mach-imx/vmx-baseboard.c + * + * Copyright (C) 2014 Voipac + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * + * This file adds support for devices found on Voipac baseboard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "devices-imx25.h" +#include "vmx-common.h" +#include "hardware.h" +#include "iomux-mx25.h" +#include "mx25.h" + +// RADIO +// I2C devices +// SPI devicess +// IMX KEYPAD +// GPIO LED +// IMX LCD + +extern vmx_module_t vmx25_module; + +#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) +#define BB_I2C_SI4705_IRQ_GPIO IMX_GPIO_NR(3, 16) +#define BB_I2C_FT5X06_IRQ_GPIO IMX_GPIO_NR(2, 22) +#define BB_I2C_FT5X06_WAKE_GPIO IMX_GPIO_NR(2, 5) + +static iomux_v3_cfg_t bb_i2c1_dev_pads[] = { + MX25_PAD_UPLL_BYPCLK__GPIO_3_16, // SI4705 irq + MX25_PAD_CSPI1_RDY__GPIO_2_22, // FT5X06 irq + MX25_PAD_A19__GPIO_2_5, // FT5X06 wake +}; + +static struct at24_platform_data bb_eeprom = { + .byte_len = SZ_512K / 8, + .page_size = 128, + .flags = AT24_FLAG_ADDR16, +}; + +static struct edt_ft5x06_platform_data bb_edt_ft5x06 = { + .irq_pin = BB_I2C_FT5X06_IRQ_GPIO, +// .reset_pin = VMX25_I2C_FT5X06_RST_GPIO, + .reset_pin = -1, + .use_parameters = false, +// .gain = 7, +// .threshold = 40, +// .offset = 0, +// .report_rate = 8, +}; + +static struct i2c_board_info bb_i2c1_boardinfo[] __initdata = { + { + I2C_BOARD_INFO("sgtl5000-i2c", 0x0a), + }, + { + I2C_BOARD_INFO("edt-ft5x06", 0x38), + .irq = BB_I2C_FT5X06_IRQ_GPIO, + .platform_data = &bb_edt_ft5x06, + }, + { + I2C_BOARD_INFO("radio-si470x", 0x63), + .irq = BB_I2C_SI4705_IRQ_GPIO, + }, +#if defined(CONFIG_MACH_VMX_BASEBOARD_I2C_AT24C512) + { + I2C_BOARD_INFO("24c512", 0x57), + .platform_data = &bb_eeprom, + }, +#endif + { + I2C_BOARD_INFO("ds1339", 0x68), + }, +}; + +static void __init bb_hw_init_si470x(void) +{ + gpio_request(BB_I2C_SI4705_IRQ_GPIO, "I2C SI4705 IRQ"); + + gpio_direction_output(BB_I2C_SI4705_IRQ_GPIO, 0); + + gpio_set_value(vmx25_module.gpio_reset, 0); + mdelay(50); + gpio_set_value(vmx25_module.gpio_reset, 1); + + gpio_direction_input(BB_I2C_SI4705_IRQ_GPIO); + gpio_free(BB_I2C_SI4705_IRQ_GPIO); + + return; +} + +static void __init bb_hw_init_ft5x06(void) +{ + gpio_request(BB_I2C_FT5X06_IRQ_GPIO, "I2C FT5X06 IRQ"); + gpio_direction_input(BB_I2C_FT5X06_IRQ_GPIO); + gpio_free(BB_I2C_FT5X06_IRQ_GPIO); + + gpio_request(BB_I2C_FT5X06_WAKE_GPIO, "I2C FT5X06 WAKE"); + gpio_direction_output(BB_I2C_FT5X06_WAKE_GPIO, 1); + gpio_free(BB_I2C_FT5X06_WAKE_GPIO); + + return; +} + +static int __init bb_i2c_hw_init(void * pData) +{ + int ret; + e_i2c_id_t index = (e_i2c_id_t) pData; + + VMXASSERT(IS_SUPPORTED_I2C(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring I2C%d pads\n", __FUNCTION__, index); + + if (e_i2c_id_1 == index) { + ret = mxc_iomux_v3_setup_multiple_pads(bb_i2c1_dev_pads, + ARRAY_SIZE(bb_i2c1_dev_pads)); + + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + bb_hw_init_si470x(); + bb_hw_init_ft5x06(); + } + + return ret; +} + +static int __init bb_i2c_dev_init(void * pData) +{ + int i; + int ret = 0; + e_i2c_id_t index = (e_i2c_id_t) pData; + + VMXASSERT(IS_SUPPORTED_I2C(index)); + + if (e_i2c_id_1 == index) { + // Configure + for (i = 0; i < ARRAY_SIZE(bb_i2c1_boardinfo); i++) { + if (0 != bb_i2c1_boardinfo[i].irq) { + bb_i2c1_boardinfo[i].irq = + gpio_to_irq(bb_i2c1_boardinfo[i].irq); + } + } + + // Register + i2c_register_board_info(index, bb_i2c1_boardinfo, + ARRAY_SIZE(bb_i2c1_boardinfo)); + } + + return ret; +} +#endif // CONFIG_I2C_IMX + +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +#define BB_SPI_MCP251X_IRQ_GPIO IMX_GPIO_NR(2, 6) +#define BB_SPI_TSC2049_IRQ_GPIO IMX_GPIO_NR(2, 7) + +static iomux_v3_cfg_t bb_spi1_dev_pads[] = { + MX25_PAD_A20__GPIO_2_6, // MCP251X IRQ + MX25_PAD_A21__GPIO_2_7, // TSC2049 IRQ + +}; + +static struct mtd_partition bb_spi_partitions[] = { + { + .name = "vmx_baseboard_rom(spi)", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM + }, + { + .name = "vmx_baseboard_rwm(spi)", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct flash_platform_data bb_spi_flash_data = { + .name = "m25p80", + .parts = bb_spi_partitions, + .nr_parts = ARRAY_SIZE(bb_spi_partitions), + .type = "sst25vf032b" +}; + +static struct mcp251x_platform_data bb_mcp251x_info = { + .oscillator_frequency = 16000000 +}; + +static int ads7846_get_pendown_state(void) +{ + return !gpio_get_value(BB_SPI_TSC2049_IRQ_GPIO); +} + +static struct ads7846_platform_data bb_ads7846_config __initdata = { + .model = 7846, + .vref_delay_usecs = 100, + .x_plate_ohms = 300, + .y_plate_ohms = 600, + /* debounce Filter */ + .debounce_max = 10, /* max number of additional readings per sample */ + .debounce_rep = 1, /* additional consecutive good readings required after the first two */ + .debounce_tol = 5, /* tolerance used for filtering */ + + .get_pendown_state = ads7846_get_pendown_state, + .keep_vref_on = 0, + .vref_mv = 0 +}; + +static struct spi_board_info bb_spi1_boardinfo[] __initdata = { +#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) +#if !defined(MACH_VMX_BASEBOARD_SPI_NONE) + { + .modalias = "m25p80", + .max_speed_hz = 25000000, + .bus_num = 0, + .chip_select = 1, // On vmx it's SPI0_SS1 + .platform_data = &bb_spi_flash_data, + .mode = SPI_MODE_3, + }, +#endif +#else +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 2000000, + .bus_num = 0, + .chip_select = 1, + }, +#endif // CONFIG_SPI_SPIDEV +#endif // CONFIG_MTD_M25P80 +#if defined(CONFIG_CAN_MCP251X) || defined(CONFIG_CAN_MCP251X_MODULE) + { + .modalias = "mcp2515", + .irq = BB_SPI_MCP251X_IRQ_GPIO, + .max_speed_hz = 2000000, + .bus_num = 0, + .chip_select = 2, + .platform_data = &bb_mcp251x_info, + .mode = SPI_MODE_0, + }, +#endif // CONFIG_CAN_MCP251X +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) + { + .modalias = "ads7846", + .irq = BB_SPI_TSC2049_IRQ_GPIO, + .max_speed_hz = (125000 * 26), // AD speed x (cmd + sample + before, after) + .chip_select = 3, + .platform_data = &bb_ads7846_config, + .mode = SPI_MODE_2, + }, +#endif // CONFIG_TOUCHSCREEN_ADS7846 +}; + +static int __init bb_spi_hw_init(void * pData) +{ + int ret; + e_spi_id_t index = (e_spi_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SPI(index)); + + VMXPRINTK(KERN_INFO "%s: Configuring SPI%d pads\n", __FUNCTION__, index); + + if (e_spi_id_1 == index) { + ret = mxc_iomux_v3_setup_multiple_pads(bb_spi1_dev_pads, + ARRAY_SIZE(bb_spi1_dev_pads)); + + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(BB_SPI_MCP251X_IRQ_GPIO, "SPI MCP251X IRQ"); + gpio_request(BB_SPI_TSC2049_IRQ_GPIO, "SPI TSC2049 IRQ"); + + gpio_direction_input(BB_SPI_MCP251X_IRQ_GPIO); + gpio_direction_input(BB_SPI_TSC2049_IRQ_GPIO); + + gpio_free(BB_SPI_MCP251X_IRQ_GPIO); + gpio_free(BB_SPI_TSC2049_IRQ_GPIO); + } + + return ret; +} + +static int __init bb_spi_dev_init(void * pData) +{ + int i; + int ret = 0; + e_spi_id_t index = (e_spi_id_t) pData; + + VMXASSERT(IS_SUPPORTED_SPI(index)); + + if (index == e_spi_id_1) { + // Configure + for (i = 0; i < ARRAY_SIZE(bb_spi1_boardinfo); i++) { + if (0 != bb_spi1_boardinfo[i].irq) { + bb_spi1_boardinfo[i].irq = + gpio_to_irq(bb_spi1_boardinfo[i].irq); + } + } + + // Register + spi_register_board_info(bb_spi1_boardinfo, + ARRAY_SIZE(bb_spi1_boardinfo)); + } + + return ret; +} +#endif // CONFIG_SPI_IMX + +#if defined(CONFIG_KEYBOARD_IMX) || defined(CONFIG_KEYBOARD_IMX_MODULE) +static iomux_v3_cfg_t bb_kpd_pads[] = { + MX25_PAD_KPP_ROW0__KPP_ROW0, + MX25_PAD_KPP_ROW1__KPP_ROW1, + MX25_PAD_KPP_ROW2__KPP_ROW2, + MX25_PAD_KPP_ROW3__KPP_ROW3, + MX25_PAD_KPP_COL0__KPP_COL0, + MX25_PAD_KPP_COL1__KPP_COL1, + MX25_PAD_KPP_COL2__KPP_COL2, + MX25_PAD_KPP_COL3__KPP_COL3, +}; + +static const uint32_t bb_kpd_keymap[] = { + /* specify your keymap with KEY(row, col, keycode), */ +// KEY(0, 0, KEY_POWER), + KEY(0, 1, KEY_HOME), + KEY(0, 3, KEY_BACK), + KEY(2, 1, KEY_MENU), + KEY(2, 3, KEY_ZOOM), +}; + +static struct matrix_keymap_data bb_kpd_pdata = { + .keymap = bb_kpd_keymap, + .keymap_size = ARRAY_SIZE(bb_kpd_keymap), +}; + +static int __init bb_kpd_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring KEYPAD pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(bb_kpd_pads, + ARRAY_SIZE(bb_kpd_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init bb_kpd_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_imx_keypad(&bb_kpd_pdata); + + return ret; +} +#endif // KEYBOARD_IMX + +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) +#define BB_LED_RED_GPIO IMX_GPIO_NR(2, 4) +#define BB_LED_ORANGE_GPIO IMX_GPIO_NR(2, 5) + +static iomux_v3_cfg_t bb_led_pads[] = { + MX25_PAD_A18__GPIO_2_4, + MX25_PAD_A19__GPIO_2_5, +}; + +static struct gpio_led bb_led[] = { + { + .name = "red", + .default_trigger = "heartbeat", + .gpio = BB_LED_RED_GPIO, + }, +#if !defined(CONFIG_TOUCHSCREEN_EDT_FT5X06) && !defined(CONFIG_TOUCHSCREEN_EDT_FT5X06) + { + .name = "orange", + .default_trigger = "none", + .gpio = BB_LED_ORANGE_GPIO, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + } +#endif // TOUCHSCREEN_EDT_FT5X06 +}; + +static struct gpio_led_platform_data bb_led_pdata = { + .leds = bb_led, + .num_leds = ARRAY_SIZE(bb_led), +}; + +static int __init bb_led_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring LED pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(bb_led_pads, + ARRAY_SIZE(bb_led_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + return ret; +} + +static int __init bb_led_dev_init(void * pData) +{ + int ret = 0; + + gpio_led_register_device(-1, &bb_led_pdata); + + return ret; +} +#endif // LEDS_GPIO + + + +#if defined(CONFIG_FB_IMX) || defined(CONFIG_FB_IMX_MODULE) +#define BB_LCDC_PSAVE_GPIO IMX_GPIO_NR(4, 3) +#define BB_LCDC_PWM_GPIO IMX_GPIO_NR(1, 26) + +static iomux_v3_cfg_t vmx25_lcdc_pads[] = { + MX25_PAD_LD0__LD0, + MX25_PAD_LD1__LD1, + MX25_PAD_LD2__LD2, + MX25_PAD_LD3__LD3, + MX25_PAD_LD4__LD4, + MX25_PAD_LD5__LD5, + MX25_PAD_LD6__LD6, + MX25_PAD_LD7__LD7, + MX25_PAD_LD8__LD8, + MX25_PAD_LD9__LD9, + MX25_PAD_LD10__LD10, + MX25_PAD_LD11__LD11, + MX25_PAD_LD12__LD12, + MX25_PAD_LD13__LD13, + MX25_PAD_LD14__LD14, + MX25_PAD_LD15__LD15, + MX25_PAD_D15__LD16, + MX25_PAD_D14__LD17, + MX25_PAD_HSYNC__HSYNC, + MX25_PAD_VSYNC__VSYNC, + MX25_PAD_LSCLK__LSCLK, + MX25_PAD_OE_ACD__OE_ACD, + MX25_PAD_CONTRAST__CONTRAST, + MX25_PAD_CS1__GPIO_4_3, // LCD Power Save (active low) + MX25_PAD_PWM__GPIO_1_26, // LCD Backlight brightness 0: full 1: off +// MX25_PAD_A18__GPIO_2_4, // LCD Reset (active LOW) +// MX25_PAD_A19__GPIO_2_5, // LCD Power Enable 0: off 1: on +}; + +static struct imx_fb_videomode vmx25_fb_modes[] = { + { + .mode = { + .name = "VGA-16@60", + + .pixclock = KHZ2PICOS(24000), + .xres = 640, + .yres = 480, + + .hsync_len = 44, // (max 63) HSYNC + .left_margin = 76, // (max 255) Back porch + .right_margin = 16, // (max 255) Front porch + + .vsync_len = 2, // (max 63) VSYNC + .upper_margin = 33, // (max 255) Back porch + .lower_margin = 10, // (max 255) Front porch + }, + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | PCR_BPIX_16 | + PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, + .bpp = 16, + }, { + .mode = { + .name = "SVGA-16@60", + .pixclock = KHZ2PICOS(40000), + + .xres = 800, + .yres = 600, + + .hsync_len = 63, // (max 63) HSYNC + .left_margin = 170, // (max 255) Back porch + .right_margin = 24, // (max 255) Front porch + + .vsync_len = 5, // (max 63) VSYNC + .upper_margin = 23, // (max 255) Back porch + .lower_margin = 2, // (max 255) Front porch + }, + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | PCR_BPIX_16 | + PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, + .bpp = 16, + }, { + // fH=31.5KHz HSYNC=770px, fV=60Hz VSYNC=525lines, fCLK=24000 + // OPTREX t-51750gd065j-lw + .mode = { + .name = "OPTREX-LCD", + .pixclock = KHZ2PICOS(24000), + + .xres = 640, + .yres = 480, + + .hsync_len = 34, + .left_margin = 76+8, // Back porch + .right_margin = 10, // Front porch + + .vsync_len = 5, + .upper_margin = 20, // Back porch + .lower_margin = 20, // Front porch + + }, + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | PCR_BPIX_16 | + PCR_LPPOL | PCR_CLKPOL | PCR_SCLK_SEL, + .bpp = 16, + }, { + // fH=30KHz HSYNC=1000px, fV=60Hz VSYNC=500lines, fCLK=30000 + // DATAIMAGE FG0700K5DSSWAGT1 + .mode = { + .name = "WVGA-LCD", + .pixclock = KHZ2PICOS(30000), + + .xres = 800, + .yres = 480, + + .hsync_len = 64, + .left_margin = 120, /* Back porch */ + .right_margin = 16, /* Front porch */ + + .vsync_len = 2, + .upper_margin = 16, /* Back porch */ + .lower_margin = 2, /* Front porch */ + }, + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | PCR_BPIX_16 | + PCR_FLMPOL | PCR_LPPOL | PCR_CLKPOL | PCR_SCLK_SEL, + .bpp = 16, + }, +}; + +static void vmx25_lcdc_power (int on); +static void vmx25_lcdc_backlight(int on); + +static struct imx_fb_platform_data vmx25_fb_pdata = { + .mode = vmx25_fb_modes, + .num_modes = ARRAY_SIZE(vmx25_fb_modes), + + .pwmr = 0, + .lscr1 = 0, + .dmacr = 0x00040060, + + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + + .init = NULL, + .exit = NULL, + .lcd_power = vmx25_lcdc_power, + .backlight_power = vmx25_lcdc_backlight, +}; + +static void vmx25_lcdc_power(int on) +{ + VMXPRINTK(KERN_INFO "%s: Switching LCD PSAVE %s\n", __FUNCTION__, on ? "off" : "on"); + if (on) { + gpio_set_value(BB_LCDC_PSAVE_GPIO, 1); + } else { + gpio_set_value(BB_LCDC_PSAVE_GPIO, 0); + } +} + +static void vmx25_lcdc_backlight(int on) +{ + VMXPRINTK(KERN_INFO "%s: Switching LCD PWM %s\n", __FUNCTION__, on ? "on" : "off"); + if (on) { + gpio_set_value(BB_LCDC_PWM_GPIO, 0); + } else { + gpio_set_value(BB_LCDC_PWM_GPIO, 1); + } +} + +static int __init vmx25_lcdc_hw_init(void * pData) +{ + int ret; + + VMXPRINTK(KERN_INFO "%s: Configuring LCDC pads\n", __FUNCTION__); + + ret = mxc_iomux_v3_setup_multiple_pads(vmx25_lcdc_pads, + ARRAY_SIZE(vmx25_lcdc_pads)); + if (ret) { + VMXPRINTK(KERN_ERR "error setting vmx25 pads !\n"); + return ret; + } + + gpio_request(BB_LCDC_PSAVE_GPIO, "LCDC PSAVE"); + gpio_request(BB_LCDC_PWM_GPIO, "LCDC PWM"); + + gpio_direction_output(BB_LCDC_PSAVE_GPIO, 1); + gpio_direction_output(BB_LCDC_PWM_GPIO, 0); + + gpio_free(BB_LCDC_PSAVE_GPIO); + gpio_free(BB_LCDC_PWM_GPIO); + + return ret; +} + +static int __init vmx25_lcdc_dev_init(void * pData) +{ + int ret = 0; + + imx25_add_imx_fb(&vmx25_fb_pdata); + + return ret; +} +#endif // FB_IMX + +static const vmx_dev_list_t baseboard_dev_list[] __initconst = { +#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) + VMX_DEV_LIST(true, true, "i2c1 devs", + bb_i2c_hw_init, bb_i2c_dev_init, e_i2c_id_1), +#endif +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) + VMX_DEV_LIST(true, true, "spi1 devs", + bb_spi_hw_init, bb_spi_dev_init, e_spi_id_1), +#endif +#if defined(CONFIG_KEYBOARD_IMX) || defined(CONFIG_KEYBOARD_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 keypad", + bb_kpd_hw_init, bb_kpd_dev_init, NULL), +#endif +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) + VMX_DEV_LIST(true, true, "gpio LEDs", + bb_led_hw_init, bb_led_dev_init, NULL), +#endif +#if defined(CONFIG_FB_IMX) || defined(CONFIG_FB_IMX_MODULE) + VMX_DEV_LIST(true, true, "imx25 LCD", + vmx25_lcdc_hw_init, vmx25_lcdc_dev_init, NULL), +#endif +}; + +void __init vmx_baseboard_init(void) +{ + int i; + int baseboard_dev_count; + + baseboard_dev_count = ARRAY_SIZE(baseboard_dev_list); + for (i = 0; i < baseboard_dev_count; i++) { + vmx_register_device(&baseboard_dev_list[i]); + } +} diff -urN linux-3.12.orig/arch/arm/mach-imx/vmx-common.c linux-3.12.work/arch/arm/mach-imx/vmx-common.c --- linux-3.12.orig/arch/arm/mach-imx/vmx-common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/vmx-common.c 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,79 @@ +/* + * arch/arm/mach-imx/vmx-common.c + * + * Copyright (C) 2014 Voipac + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * + * This file adds support for devices found on Voipac baseboard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "devices-imx25.h" +#include "vmx-common.h" +#include "hardware.h" +#include "iomux-mx25.h" +#include "mx25.h" + +int vmx_register_device(const vmx_dev_list_t * p_vmx_dev) +{ + int status = 0; + + VMXASSERT(NULL != p_vmx_dev); + + do { + printk(KERN_INFO "Register: %s\n", p_vmx_dev->s_name); + + // HW init + if (p_vmx_dev->b_init_hw && (NULL != p_vmx_dev->f_init_hw)) { + status = p_vmx_dev->f_init_hw(p_vmx_dev->p_data); + } + + if (0 != status) { + break; + } + + // Device init + if (p_vmx_dev->b_init_dev && (NULL != p_vmx_dev->f_init_dev)) { + status = p_vmx_dev->f_init_dev(p_vmx_dev->p_data); + } + } while (0); + + return status; +} diff -urN linux-3.12.orig/arch/arm/mach-imx/vmx-common.h linux-3.12.work/arch/arm/mach-imx/vmx-common.h --- linux-3.12.orig/arch/arm/mach-imx/vmx-common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/arch/arm/mach-imx/vmx-common.h 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,103 @@ +/* + * arch/arm/mach-imx/vmx-common.h + * + * Copyright (C) 2014 Voipac + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * + * This file adds support for devices found on Voipac baseboard + */ + +static inline void vmxfail(char *expr, char *file, int line) +{ + printk(KERN_ERR "VMX assertion: %s, file: %s, line: %d", + expr, file, line); + BUG(); +} + +#ifdef CONFIG_VMX25_DEBUG +#define VMXASSERT(expr) \ + (likely(expr) ? (void)0 : vmxfail(#expr, __FILE__, __LINE__)) +#else +#define VMXASSERT(expr) \ + ((void)0) +#endif + +#ifdef CONFIG_VMX25_PRINT +#define VMXPRINTK(args...) \ + printk(args) +#else +#define VMXPRINTK(args...) \ + ((void)0) +#endif + + +typedef int (*vmx_init_t)(void * pData); + +typedef struct vmx_dev_list_s { + const char * s_name; + bool b_init_hw; + bool b_init_dev; + vmx_init_t f_init_hw; + vmx_init_t f_init_dev; + void * p_data; +} vmx_dev_list_t; +#define VMX_DEV_LIST(m_inithw, m_initdev, m_name, \ + m_fcninit, m_fcndev, m_datahw) \ + { \ + .s_name = m_name, \ + .b_init_hw = m_inithw, \ + .b_init_dev = m_initdev, \ + .f_init_hw = m_fcninit, \ + .f_init_dev = m_fcndev, \ + .p_data = (void *) m_datahw \ + } + +typedef enum { + e_uart_id_1 = 0, + e_uart_id_2 = 1, + e_uart_id_3 = 2, + e_uart_id_4 = 3, + e_uart_id_5 = 4, +} e_uart_id_t; +#define IS_SUPPORTED_UART(a) (((a) == e_uart_id_1) || \ + ((a) == e_uart_id_2) || \ + ((a) == e_uart_id_5)) +typedef enum { + e_i2c_id_1 = 0, + e_i2c_id_2 = 1, + e_i2c_id_3 = 2 +} e_i2c_id_t; +#define IS_SUPPORTED_I2C(a) ((a) == e_i2c_id_1) + +typedef enum { + e_spi_id_1 = 0, + e_spi_id_2 = 1, + e_spi_id_3 = 2 +} e_spi_id_t; +#define IS_SUPPORTED_SPI(a) ((a) == e_spi_id_1) + +typedef enum { + e_flexcan_id_1 = 0, + e_flexcan_id_2 = 1 +} e_flexcan_id_t; +#define IS_SUPPORTED_FLEXCAN(a) (((a) == e_flexcan_id_1) || \ + ((a) == e_flexcan_id_2)) + +typedef struct vmx_module_s { + unsigned gpio_reset; +} vmx_module_t; + +int vmx_register_device(const vmx_dev_list_t * p_vmx_dev); diff -urN linux-3.12.orig/arch/arm/tools/mach-types linux-3.12.work/arch/arm/tools/mach-types --- linux-3.12.orig/arch/arm/tools/mach-types 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/arch/arm/tools/mach-types 2014-08-03 17:17:27.819882091 +0200 @@ -503,6 +503,7 @@ craneboard MACH_CRANEBOARD CRANEBOARD 2932 smdk6450 MACH_SMDK6450 SMDK6450 2938 brownstone MACH_BROWNSTONE BROWNSTONE 2957 +vmx25 MACH_VMX25 VMX25 2958 flexibity MACH_FLEXIBITY FLEXIBITY 2965 mx50_rdp MACH_MX50_RDP MX50_RDP 2988 universal_c210 MACH_UNIVERSAL_C210 UNIVERSAL_C210 2989 diff -urN linux-3.12.orig/drivers/mmc/core/core.c linux-3.12.work/drivers/mmc/core/core.c --- linux-3.12.orig/drivers/mmc/core/core.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/mmc/core/core.c 2014-08-03 17:17:27.819882091 +0200 @@ -1342,9 +1342,9 @@ mmc->regulator_enabled = false; } - if (result) - dev_err(mmc_dev(mmc), - "could not set regulator OCR (%d)\n", result); +// if (result) +// dev_err(mmc_dev(mmc), +// "could not set regulator OCR (%d)\n", result); return result; } EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); diff -urN linux-3.12.orig/drivers/mmc/host/sdhci-esdhc-imx.c linux-3.12.work/drivers/mmc/host/sdhci-esdhc-imx.c --- linux-3.12.orig/drivers/mmc/host/sdhci-esdhc-imx.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/mmc/host/sdhci-esdhc-imx.c 2014-08-03 17:17:27.819882091 +0200 @@ -390,27 +390,59 @@ struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - u32 f_host = clk_get_rate(pltfm_host->clk); - - if (boarddata->f_max && (boarddata->f_max < f_host)) + if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock)) return boarddata->f_max; else - return f_host; + return pltfm_host->clock; } static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - return clk_get_rate(pltfm_host->clk) / 256 / 16; + return pltfm_host->clock / 256 / 16; } static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - - esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); + unsigned int host_clock = pltfm_host->clock; + int pre_div = 1; + int div = 1; + u32 temp; + + if (clock == 0) { + goto out; + } + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + while (host_clock / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host_clock / pre_div / div > clock && div < 16) + div++; + + host->mmc->actual_clock = host_clock / pre_div / div; + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->mmc->actual_clock); + + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + mdelay(1); +out: + host->clock = clock; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -563,7 +595,7 @@ } pltfm_host->clk = imx_data->clk_per; - + pltfm_host->clock = clk_get_rate(pltfm_host->clk); clk_prepare_enable(imx_data->clk_per); clk_prepare_enable(imx_data->clk_ipg); clk_prepare_enable(imx_data->clk_ahb); diff -urN linux-3.12.orig/drivers/mtd/devices/m25p80.c linux-3.12.work/drivers/mtd/devices/m25p80.c --- linux-3.12.orig/drivers/mtd/devices/m25p80.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/mtd/devices/m25p80.c 2015-11-29 17:49:18.335264553 +0100 @@ -824,6 +824,7 @@ { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { "sst26vf032b", INFO(0xbf2642, 0, 64 * 1024, 64, SECT_4K) }, /* ST Microelectronics -- newer production may have feature updates */ { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, diff -urN linux-3.12.orig/drivers/usb/chipidea/bits.h linux-3.12.work/drivers/usb/chipidea/bits.h --- linux-3.12.orig/drivers/usb/chipidea/bits.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/bits.h 2014-08-03 17:17:27.819882091 +0200 @@ -44,17 +44,28 @@ #define DEVICEADDR_USBADR (0x7FUL << 25) /* PORTSC */ +#define PORTSC_CCS BIT(0) +#define PORTSC_CSC BIT(1) +#define PORTSC_PEC BIT(3) +#define PORTSC_OCC BIT(5) #define PORTSC_FPR BIT(6) #define PORTSC_SUSP BIT(7) #define PORTSC_HSP BIT(9) +#define PORTSC_PP BIT(12) #define PORTSC_PTC (0x0FUL << 16) +#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) /* PTS and PTW for non lpm version only */ +#define PORTSC_PFSC BIT(24) #define PORTSC_PTS(d) \ (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0)) #define PORTSC_PTW BIT(28) #define PORTSC_STS BIT(29) +#define PORTSC_W1C_BITS \ + (PORTSC_CSC | PORTSC_PEC | PORTSC_OCC) + /* DEVLC */ +#define DEVLC_PFSC BIT(23) #define DEVLC_PSPD (0x03UL << 25) #define DEVLC_PSPD_HS (0x02UL << 25) #define DEVLC_PTW BIT(27) @@ -69,6 +80,8 @@ /* OTGSC */ #define OTGSC_IDPU BIT(5) +#define OTGSC_HADP BIT(6) +#define OTGSC_HABA BIT(7) #define OTGSC_ID BIT(8) #define OTGSC_AVV BIT(9) #define OTGSC_ASV BIT(10) diff -urN linux-3.12.orig/drivers/usb/chipidea/ci.h linux-3.12.work/drivers/usb/chipidea/ci.h --- linux-3.12.orig/drivers/usb/chipidea/ci.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/ci.h 2014-08-03 17:17:27.819882091 +0200 @@ -17,6 +17,7 @@ #include #include #include +#include /****************************************************************************** * DEFINE @@ -26,6 +27,35 @@ #define ENDPT_MAX 32 /****************************************************************************** + * REGISTERS + *****************************************************************************/ +/* register indices */ +enum ci_hw_regs { + CAP_CAPLENGTH, + CAP_HCCPARAMS, + CAP_DCCPARAMS, + CAP_TESTMODE, + CAP_LAST = CAP_TESTMODE, + OP_USBCMD, + OP_USBSTS, + OP_USBINTR, + OP_DEVICEADDR, + OP_ENDPTLISTADDR, + OP_PORTSC, + OP_DEVLC, + OP_OTGSC, + OP_USBMODE, + OP_ENDPTSETUPSTAT, + OP_ENDPTPRIME, + OP_ENDPTFLUSH, + OP_ENDPTSTAT, + OP_ENDPTCOMPLETE, + OP_ENDPTCTRL, + /* endptctrl1..15 follow */ + OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, +}; + +/****************************************************************************** * STRUCTURES *****************************************************************************/ /** @@ -98,7 +128,7 @@ void __iomem *cap; void __iomem *op; size_t size; - void __iomem **regmap; + void __iomem *regmap[OP_LAST + 1]; }; /** @@ -110,6 +140,8 @@ * @roles: array of supported roles for this controller * @role: current role * @is_otg: if the device is otg-capable + * @fsm: otg finite state machine + * @fsm_timer: pointer to timer list of otg fsm * @work: work for role changing * @wq: workqueue thread * @qh_pool: allocation pool for queue heads @@ -145,6 +177,8 @@ struct ci_role_driver *roles[CI_ROLE_END]; enum ci_role role; bool is_otg; + struct otg_fsm fsm; + struct ci_otg_fsm_timer_list *fsm_timer; struct work_struct work; struct workqueue_struct *wq; @@ -167,8 +201,6 @@ struct ci_hdrc_platform_data *platdata; int vbus_active; - /* FIXME: some day, we'll not use global phy */ - bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; struct dentry *debugfs; @@ -211,38 +243,6 @@ ci->roles[role]->stop(ci); } -/****************************************************************************** - * REGISTERS - *****************************************************************************/ -/* register size */ -#define REG_BITS (32) - -/* register indices */ -enum ci_hw_regs { - CAP_CAPLENGTH, - CAP_HCCPARAMS, - CAP_DCCPARAMS, - CAP_TESTMODE, - CAP_LAST = CAP_TESTMODE, - OP_USBCMD, - OP_USBSTS, - OP_USBINTR, - OP_DEVICEADDR, - OP_ENDPTLISTADDR, - OP_PORTSC, - OP_DEVLC, - OP_OTGSC, - OP_USBMODE, - OP_ENDPTSETUPSTAT, - OP_ENDPTPRIME, - OP_ENDPTFLUSH, - OP_ENDPTSTAT, - OP_ENDPTCOMPLETE, - OP_ENDPTCTRL, - /* endptctrl1..15 follow */ - OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, -}; - /** * hw_read: reads from a hw register * @reg: register index @@ -324,6 +324,24 @@ return (val & mask) >> __ffs(mask); } +/** + * ci_otg_is_fsm_mode: runtime check if otg controller + * is in otg fsm mode. + */ +static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) +{ +#ifdef CONFIG_USB_OTG_FSM + return ci->is_otg && ci->roles[CI_ROLE_HOST] && + ci->roles[CI_ROLE_GADGET]; +#else + return false; +#endif +} + +u32 hw_read_intr_enable(struct ci_hdrc *ci); + +u32 hw_read_intr_status(struct ci_hdrc *ci); + int hw_device_reset(struct ci_hdrc *ci, u32 mode); int hw_port_test_set(struct ci_hdrc *ci, u8 mode); diff -urN linux-3.12.orig/drivers/usb/chipidea/ci_hdrc_imx.c linux-3.12.work/drivers/usb/chipidea/ci_hdrc_imx.c --- linux-3.12.orig/drivers/usb/chipidea/ci_hdrc_imx.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/ci_hdrc_imx.c 2014-08-03 17:17:27.819882091 +0200 @@ -47,45 +47,55 @@ struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; + struct clk *ipgclk, *ahbclk, *perclk; struct imx_usbmisc_data *usbmisc_data; }; /* Common functions shared by usbmisc drivers */ -static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) +static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev, struct imx_usb_platform_data *pdata_imx) { struct device_node *np = dev->of_node; struct of_phandle_args args; - struct imx_usbmisc_data *data; + struct imx_usbmisc_data *data = NULL; int ret; - /* - * In case the fsl,usbmisc property is not present this device doesn't - * need usbmisc. Return NULL (which is no error here) - */ - if (!of_get_property(np, "fsl,usbmisc", NULL)) - return NULL; - - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", - 0, &args); - if (ret) { - dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", - ret); - return ERR_PTR(ret); - } + if (np) { + /* + * In case the fsl,usbmisc property is not present this device doesn't + * need usbmisc. Return NULL (which is no error here) + */ + if (!of_get_property(np, "fsl,usbmisc", NULL)) + return NULL; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", + 0, &args); + if (ret) { + dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", + ret); + return ERR_PTR(ret); + } - data->index = args.args[0]; - of_node_put(args.np); + data->index = args.args[0]; + of_node_put(args.np); - if (of_find_property(np, "disable-over-current", NULL)) - data->disable_oc = 1; + if (of_find_property(np, "disable-over-current", NULL)) + data->disable_oc = 1; - if (of_find_property(np, "external-vbus-divider", NULL)) - data->evdo = 1; + if (of_find_property(np, "external-vbus-divider", NULL)) + data->evdo = 1; + } else if (pdata_imx) { + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + data->index = pdata_imx->usbmisc_index; + data->disable_oc = pdata_imx->usbmisc_disable_oc; + data->evdo = pdata_imx->usbmisc_evdo; + } return data; } @@ -96,15 +106,37 @@ { struct ci_hdrc_imx_data *data; struct ci_hdrc_platform_data pdata = { - .name = "ci_hdrc_imx", + .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | CI_HDRC_DISABLE_STREAMING, }; int ret; - const struct of_device_id *of_id = - of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); - const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data; + const struct of_device_id *of_id; + const struct ci_hdrc_imx_platform_flag *imx_platform_flag = NULL; + struct imx_usb_platform_data *pdata_imx = dev_get_platdata(&pdev->dev); + + of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); + if (of_id) { + imx_platform_flag = of_id->data; + } else if (pdata_imx) { + int i; + + pdata.phy_mode = pdata_imx->phy_mode; + pdata.dr_mode = pdata_imx->dr_mode; + + for (i = 0; i < ARRAY_SIZE(ci_hdrc_imx_dt_ids); i++) { + if (!strcmp(ci_hdrc_imx_dt_ids[i].compatible, + pdata_imx->name)) { + imx_platform_flag = ci_hdrc_imx_dt_ids[i].data; + break; + } + } + } + + if (!imx_platform_flag) { + return -ENODEV; + } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { @@ -112,34 +144,58 @@ return -ENOMEM; } - data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); + data->usbmisc_data = usbmisc_get_init_data(&pdev->dev, pdata_imx); if (IS_ERR(data->usbmisc_data)) return PTR_ERR(data->usbmisc_data); - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } + if (of_id) { + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) { + dev_err(&pdev->dev, + "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); + return PTR_ERR(data->clk); + } - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "Failed to prepare or enable clock, err=%d\n", ret); - return ret; + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "Failed to prepare or enable clock, err=%d\n", ret); + return ret; + } + + data->ipgclk = NULL; + data->ahbclk = NULL; + data->perclk = NULL; + } else if (pdata_imx) { + data->ipgclk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(data->ipgclk)) { + return PTR_ERR(data->ipgclk); + } + clk_prepare_enable(data->ipgclk); + + data->ahbclk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(data->ahbclk)) { + return PTR_ERR(data->ahbclk); + } + clk_prepare_enable(data->ahbclk); + + data->perclk = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(data->perclk)) { + return PTR_ERR(data->perclk); + } + clk_prepare_enable(data->perclk); + + data->clk = NULL; } - data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); - if (!IS_ERR(data->phy)) { - ret = usb_phy_init(data->phy); - if (ret) { - dev_err(&pdev->dev, "unable to init phy: %d\n", ret); + if (of_id) { + data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); + if (IS_ERR(data->phy)) { + ret = PTR_ERR(data->phy); goto err_clk; } - } else if (PTR_ERR(data->phy) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_clk; + } else if (pdata_imx) { + data->phy = (struct usb_phy *)pdata_imx->phy; } pdata.phy = data->phy; @@ -147,17 +203,16 @@ if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - if (!pdev->dev.coherent_dma_mask) - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + goto err_clk; if (data->usbmisc_data) { ret = imx_usbmisc_init(data->usbmisc_data); if (ret) { dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); - goto err_phy; + goto err_clk; } } @@ -169,7 +224,7 @@ dev_err(&pdev->dev, "Can't register ci_hdrc platform device, err=%d\n", ret); - goto err_phy; + goto err_clk; } if (data->usbmisc_data) { @@ -190,11 +245,19 @@ disable_device: ci_hdrc_remove_device(data->ci_pdev); -err_phy: - if (data->phy) - usb_phy_shutdown(data->phy); err_clk: - clk_disable_unprepare(data->clk); + if (NULL != data->clk) + clk_disable_unprepare(data->clk); + + if (NULL != data->perclk) + clk_disable_unprepare(data->perclk); + + if (NULL != data->ahbclk) + clk_disable_unprepare(data->ahbclk); + + if (NULL != data->ipgclk) + clk_disable_unprepare(data->ipgclk); + return ret; } @@ -204,11 +267,17 @@ pm_runtime_disable(&pdev->dev); ci_hdrc_remove_device(data->ci_pdev); + if (NULL != data->clk) + clk_disable_unprepare(data->clk); + + if (NULL != data->perclk) + clk_disable_unprepare(data->perclk); - if (data->phy) - usb_phy_shutdown(data->phy); + if (NULL != data->ahbclk) + clk_disable_unprepare(data->ahbclk); - clk_disable_unprepare(data->clk); + if (NULL != data->ipgclk) + clk_disable_unprepare(data->ipgclk); return 0; } diff -urN linux-3.12.orig/drivers/usb/chipidea/ci_hdrc_imx.h linux-3.12.work/drivers/usb/chipidea/ci_hdrc_imx.h --- linux-3.12.orig/drivers/usb/chipidea/ci_hdrc_imx.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/ci_hdrc_imx.h 2014-08-03 17:17:27.819882091 +0200 @@ -9,6 +9,9 @@ * http://www.gnu.org/copyleft/gpl.html */ +#ifndef __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H +#define __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H + struct imx_usbmisc_data { int index; @@ -18,3 +21,5 @@ int imx_usbmisc_init(struct imx_usbmisc_data *); int imx_usbmisc_init_post(struct imx_usbmisc_data *); + +#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff -urN linux-3.12.orig/drivers/usb/chipidea/core.c linux-3.12.work/drivers/usb/chipidea/core.c --- linux-3.12.orig/drivers/usb/chipidea/core.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/core.c 2014-08-03 17:17:27.819882091 +0200 @@ -23,7 +23,7 @@ * - BUS: bus glue code, bus abstraction layer * * Compile Options - * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities + * - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities * - STALL_IN: non-empty bulk-in pipes cannot be halted * if defined mass storage compliance succeeds but with warnings * => case 4: Hi > Dn @@ -42,10 +42,6 @@ * - Not Supported: 15 & 16 (ISO) * * TODO List - * - OTG - * - Interrupt Traffic - * - GET_STATUS(device) - always reports 0 - * - Gadget API (majority of optional features) * - Suspend & Remote Wakeup */ #include @@ -64,6 +60,7 @@ #include #include #include +#include #include #include @@ -73,63 +70,57 @@ #include "host.h" #include "debug.h" #include "otg.h" +#include "otg_fsm.h" /* Controller register map */ -static uintptr_t ci_regs_nolpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x038UL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_OTGSC] = 0x064UL, - [OP_USBMODE] = 0x068UL, - [OP_ENDPTSETUPSTAT] = 0x06CUL, - [OP_ENDPTPRIME] = 0x070UL, - [OP_ENDPTFLUSH] = 0x074UL, - [OP_ENDPTSTAT] = 0x078UL, - [OP_ENDPTCOMPLETE] = 0x07CUL, - [OP_ENDPTCTRL] = 0x080UL, +static const u8 ci_regs_nolpm[] = { + [CAP_CAPLENGTH] = 0x00U, + [CAP_HCCPARAMS] = 0x08U, + [CAP_DCCPARAMS] = 0x24U, + [CAP_TESTMODE] = 0x38U, + [OP_USBCMD] = 0x00U, + [OP_USBSTS] = 0x04U, + [OP_USBINTR] = 0x08U, + [OP_DEVICEADDR] = 0x14U, + [OP_ENDPTLISTADDR] = 0x18U, + [OP_PORTSC] = 0x44U, + [OP_DEVLC] = 0x84U, + [OP_OTGSC] = 0x64U, + [OP_USBMODE] = 0x68U, + [OP_ENDPTSETUPSTAT] = 0x6CU, + [OP_ENDPTPRIME] = 0x70U, + [OP_ENDPTFLUSH] = 0x74U, + [OP_ENDPTSTAT] = 0x78U, + [OP_ENDPTCOMPLETE] = 0x7CU, + [OP_ENDPTCTRL] = 0x80U, }; -static uintptr_t ci_regs_lpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x0FCUL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_OTGSC] = 0x0C4UL, - [OP_USBMODE] = 0x0C8UL, - [OP_ENDPTSETUPSTAT] = 0x0D8UL, - [OP_ENDPTPRIME] = 0x0DCUL, - [OP_ENDPTFLUSH] = 0x0E0UL, - [OP_ENDPTSTAT] = 0x0E4UL, - [OP_ENDPTCOMPLETE] = 0x0E8UL, - [OP_ENDPTCTRL] = 0x0ECUL, +static const u8 ci_regs_lpm[] = { + [CAP_CAPLENGTH] = 0x00U, + [CAP_HCCPARAMS] = 0x08U, + [CAP_DCCPARAMS] = 0x24U, + [CAP_TESTMODE] = 0xFCU, + [OP_USBCMD] = 0x00U, + [OP_USBSTS] = 0x04U, + [OP_USBINTR] = 0x08U, + [OP_DEVICEADDR] = 0x14U, + [OP_ENDPTLISTADDR] = 0x18U, + [OP_PORTSC] = 0x44U, + [OP_DEVLC] = 0x84U, + [OP_OTGSC] = 0xC4U, + [OP_USBMODE] = 0xC8U, + [OP_ENDPTSETUPSTAT] = 0xD8U, + [OP_ENDPTPRIME] = 0xDCU, + [OP_ENDPTFLUSH] = 0xE0U, + [OP_ENDPTSTAT] = 0xE4U, + [OP_ENDPTCOMPLETE] = 0xE8U, + [OP_ENDPTCTRL] = 0xECU, }; static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) { int i; - kfree(ci->hw_bank.regmap); - - ci->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), - GFP_KERNEL); - if (!ci->hw_bank.regmap) - return -ENOMEM; - for (i = 0; i < OP_ENDPTCTRL; i++) ci->hw_bank.regmap[i] = (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + @@ -146,6 +137,26 @@ } /** + * hw_read_intr_enable: returns interrupt enable register + * + * This function returns register data + */ +u32 hw_read_intr_enable(struct ci_hdrc *ci) +{ + return hw_read(ci, OP_USBINTR, ~0); +} + +/** + * hw_read_intr_status: returns interrupt status register + * + * This function returns register data + */ +u32 hw_read_intr_status(struct ci_hdrc *ci) +{ + return hw_read(ci, OP_USBSTS, ~0); +} + +/** * hw_port_test_set: writes port test mode (execute without interruption) * @mode: new value * @@ -172,6 +183,26 @@ return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); } +/* The PHY enters/leaves low power mode */ +static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) +{ + enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; + bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); + + if (enable && !lpm) { + hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), + PORTSC_PHCD(ci->hw_bank.lpm)); + } else if (!enable && lpm) { + hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), + 0); + /* + * the PHY needs some time (less + * than 1ms) to leave low power mode. + */ + usleep_range(1000, 1100); + } +} + static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) { u32 reg; @@ -187,7 +218,8 @@ reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> __ffs(HCCPARAMS_LEN); ci->hw_bank.lpm = reg; - hw_alloc_regmap(ci, !!reg); + if (reg) + hw_alloc_regmap(ci, !!reg); ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; ci->hw_bank.size += OP_LAST; ci->hw_bank.size /= sizeof(u32); @@ -199,6 +231,8 @@ if (ci->hw_ep_max > ENDPT_MAX) return -ENODEV; + ci_hdrc_enter_lpm(ci, false); + /* Disable all interrupts bits */ hw_write(ci, OP_USBINTR, 0xffffffff, 0); @@ -219,7 +253,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci) { - u32 portsc, lpm, sts; + u32 portsc, lpm, sts = 0; switch (ci->platdata->phy_mode) { case USBPHY_INTERFACE_MODE_UTMI: @@ -249,14 +283,49 @@ if (ci->hw_bank.lpm) { hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); - hw_write(ci, OP_DEVLC, DEVLC_STS, sts); + if (sts) + hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); } else { hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); - hw_write(ci, OP_PORTSC, PORTSC_STS, sts); + if (sts) + hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); } } /** + * ci_usb_phy_init: initialize phy according to different phy type + * @ci: the controller + * + * This function returns an error code if usb_phy_init has failed + */ +static int ci_usb_phy_init(struct ci_hdrc *ci) +{ + int ret; + + switch (ci->platdata->phy_mode) { + case USBPHY_INTERFACE_MODE_UTMI: + case USBPHY_INTERFACE_MODE_UTMIW: + case USBPHY_INTERFACE_MODE_HSIC: + ret = usb_phy_init(ci->transceiver); + if (ret) + return ret; + hw_phymode_configure(ci); + break; + case USBPHY_INTERFACE_MODE_ULPI: + case USBPHY_INTERFACE_MODE_SERIAL: + hw_phymode_configure(ci); + ret = usb_phy_init(ci->transceiver); + if (ret) + return ret; + break; + default: + ret = usb_phy_init(ci->transceiver); + } + + return ret; +} + +/** * hw_device_reset: resets chip (execute without interruption) * @ci: the controller * @@ -279,6 +348,13 @@ if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { + if (ci->hw_bank.lpm) + hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); + else + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + } + /* USBMODE should be configured step by step */ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, mode); @@ -332,8 +408,14 @@ irqreturn_t ret = IRQ_NONE; u32 otgsc = 0; - if (ci->is_otg) - otgsc = hw_read(ci, OP_OTGSC, ~0); + if (ci->is_otg) { + otgsc = hw_read_otgsc(ci, ~0); + if (ci_otg_is_fsm_mode(ci)) { + ret = ci_otg_fsm_irq(ci); + if (ret == IRQ_HANDLED) + return ret; + } + } /* * Handle id change interrupt, it indicates device/host function @@ -341,9 +423,9 @@ */ if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { ci->id_event = true; - ci_clear_otg_interrupt(ci, OTGSC_IDIS); - disable_irq_nosync(ci->irq); - queue_work(ci->wq, &ci->work); + /* Clear ID change irq status */ + hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + ci_otg_queue_work(ci); return IRQ_HANDLED; } @@ -353,9 +435,9 @@ */ if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { ci->b_sess_valid_event = true; - ci_clear_otg_interrupt(ci, OTGSC_BSVIS); - disable_irq_nosync(ci->irq); - queue_work(ci->wq, &ci->work); + /* Clear BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + ci_otg_queue_work(ci); return IRQ_HANDLED; } @@ -369,18 +451,33 @@ static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { - /* Get the vbus regulator */ - platdata->reg_vbus = devm_regulator_get(dev, "vbus"); - if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { - platdata->reg_vbus = NULL; /* no vbus regualator is needed */ - } else if (IS_ERR(platdata->reg_vbus)) { - dev_err(dev, "Getting regulator error: %ld\n", - PTR_ERR(platdata->reg_vbus)); - return PTR_ERR(platdata->reg_vbus); + if (!platdata->phy_mode) + platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); + + if (!platdata->dr_mode) + platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); + + if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) + platdata->dr_mode = USB_DR_MODE_OTG; + + if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { + /* Get the vbus regulator */ + platdata->reg_vbus = devm_regulator_get(dev, "vbus"); + if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { + /* no vbus regualator is needed */ + platdata->reg_vbus = NULL; + } else if (IS_ERR(platdata->reg_vbus)) { + dev_err(dev, "Getting regulator error: %ld\n", + PTR_ERR(platdata->reg_vbus)); + return PTR_ERR(platdata->reg_vbus); + } } + if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) + platdata->flags |= CI_HDRC_FORCE_FULLSPEED; + return 0; } @@ -458,11 +555,8 @@ ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC | DCCPARAMS_HC) == (DCCPARAMS_DC | DCCPARAMS_HC)); - if (ci->is_otg) { + if (ci->is_otg) dev_dbg(ci->dev, "It is OTG capable controller\n"); - ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); - ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); - } } static int ci_hdrc_probe(struct platform_device *pdev) @@ -473,9 +567,8 @@ void __iomem *base; int ret; enum usb_dr_mode dr_mode; - struct device_node *of_node = dev->of_node ?: dev->parent->of_node; - if (!dev->platform_data) { + if (!dev_get_platdata(dev)) { dev_err(dev, "platform data missing\n"); return -ENODEV; } @@ -492,11 +585,7 @@ } ci->dev = dev; - ci->platdata = dev->platform_data; - if (ci->platdata->phy) - ci->transceiver = ci->platdata->phy; - else - ci->global_phy = true; + ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & CI_HDRC_IMX28_WRITE_FIX); @@ -506,27 +595,49 @@ return -ENODEV; } + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + + if (IS_ERR(ci->transceiver)) { + ret = PTR_ERR(ci->transceiver); + /* + * if -ENXIO is returned, it means PHY layer wasn't + * enabled, so it makes no sense to return -EPROBE_DEFER + * in that case, since no PHY driver will ever probe. + */ + if (ret == -ENXIO) + return ret; + + dev_err(dev, "no usb2 phy configured\n"); + return -EPROBE_DEFER; + } + + ret = ci_usb_phy_init(ci); + if (ret) { + dev_err(dev, "unable to init phy: %d\n", ret); + return ret; + } else { + /* + * The delay to sync PHY's status, the maximum delay is + * 2ms since the otgsc uses 1ms timer to debounce the + * PHY's input + */ + usleep_range(2000, 2500); + } + ci->hw_bank.phys = res->start; ci->irq = platform_get_irq(pdev, 0); if (ci->irq < 0) { dev_err(dev, "missing IRQ\n"); - return -ENODEV; + ret = ci->irq; + goto deinit_phy; } ci_get_otg_capable(ci); - if (!ci->platdata->phy_mode) - ci->platdata->phy_mode = of_usb_get_phy_mode(of_node); - - hw_phymode_configure(ci); - - if (!ci->platdata->dr_mode) - ci->platdata->dr_mode = of_usb_get_dr_mode(of_node); - - if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN) - ci->platdata->dr_mode = USB_DR_MODE_OTG; - dr_mode = ci->platdata->dr_mode; /* initialize role(s) before the interrupt is requested */ if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { @@ -543,10 +654,14 @@ if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { dev_err(dev, "no supported roles\n"); - return -ENODEV; + ret = -ENODEV; + goto deinit_phy; } if (ci->is_otg) { + /* Disable and clear all OTG irq */ + hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + OTGSC_INT_STATUS_BITS); ret = ci_hdrc_otg_init(ci); if (ret) { dev_err(dev, "init otg fails, ret = %d\n", ret); @@ -556,13 +671,9 @@ if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { if (ci->is_otg) { - /* - * ID pin needs 1ms debouce time, - * we delay 2ms for safe. - */ - mdelay(2); ci->role = ci_otg_role(ci); - ci_enable_otg_interrupt(ci, OTGSC_IDIE); + /* Enable ID change irq */ + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); } else { /* * If the controller is not OTG capable, but support @@ -581,10 +692,13 @@ if (ci->role == CI_ROLE_GADGET) ci_handle_vbus_change(ci); - ret = ci_role_start(ci, ci->role); - if (ret) { - dev_err(dev, "can't start %s role\n", ci_role(ci)->name); - goto stop; + if (!ci_otg_is_fsm_mode(ci)) { + ret = ci_role_start(ci, ci->role); + if (ret) { + dev_err(dev, "can't start %s role\n", + ci_role(ci)->name); + goto stop; + } } platform_set_drvdata(pdev, ci); @@ -593,6 +707,9 @@ if (ret) goto stop; + if (ci_otg_is_fsm_mode(ci)) + ci_hdrc_otg_fsm_start(ci); + ret = dbg_create_files(ci); if (!ret) return 0; @@ -600,6 +717,8 @@ free_irq(ci->irq, ci); stop: ci_role_destroy(ci); +deinit_phy: + usb_phy_shutdown(ci->transceiver); return ret; } @@ -611,6 +730,8 @@ dbg_remove_files(ci); free_irq(ci->irq, ci); ci_role_destroy(ci); + ci_hdrc_enter_lpm(ci, true); + usb_phy_shutdown(ci->transceiver); kfree(ci->hw_bank.regmap); return 0; @@ -621,6 +742,7 @@ .remove = ci_hdrc_remove, .driver = { .name = "ci_hdrc", + .owner = THIS_MODULE, }, }; diff -urN linux-3.12.orig/drivers/usb/chipidea/debug.c linux-3.12.work/drivers/usb/chipidea/debug.c --- linux-3.12.orig/drivers/usb/chipidea/debug.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/debug.c 2014-08-03 17:17:27.819882091 +0200 @@ -7,11 +7,15 @@ #include #include #include +#include +#include +#include #include "ci.h" #include "udc.h" #include "bits.h" #include "debug.h" +#include "otg.h" /** * ci_device_show: prints information about device capabilities and status @@ -204,6 +208,80 @@ .release = single_release, }; +int ci_otg_show(struct seq_file *s, void *unused) +{ + struct ci_hdrc *ci = s->private; + struct otg_fsm *fsm; + + if (!ci || !ci_otg_is_fsm_mode(ci)) + return 0; + + fsm = &ci->fsm; + + /* ------ State ----- */ + seq_printf(s, "OTG state: %s\n\n", + usb_otg_state_string(ci->transceiver->state)); + + /* ------ State Machine Variables ----- */ + seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop); + + seq_printf(s, "a_bus_req: %d\n", fsm->a_bus_req); + + seq_printf(s, "a_srp_det: %d\n", fsm->a_srp_det); + + seq_printf(s, "a_vbus_vld: %d\n", fsm->a_vbus_vld); + + seq_printf(s, "b_conn: %d\n", fsm->b_conn); + + seq_printf(s, "adp_change: %d\n", fsm->adp_change); + + seq_printf(s, "power_up: %d\n", fsm->power_up); + + seq_printf(s, "a_bus_resume: %d\n", fsm->a_bus_resume); + + seq_printf(s, "a_bus_suspend: %d\n", fsm->a_bus_suspend); + + seq_printf(s, "a_conn: %d\n", fsm->a_conn); + + seq_printf(s, "b_bus_req: %d\n", fsm->b_bus_req); + + seq_printf(s, "b_bus_suspend: %d\n", fsm->b_bus_suspend); + + seq_printf(s, "b_se0_srp: %d\n", fsm->b_se0_srp); + + seq_printf(s, "b_ssend_srp: %d\n", fsm->b_ssend_srp); + + seq_printf(s, "b_sess_vld: %d\n", fsm->b_sess_vld); + + seq_printf(s, "b_srp_done: %d\n", fsm->b_srp_done); + + seq_printf(s, "drv_vbus: %d\n", fsm->drv_vbus); + + seq_printf(s, "loc_conn: %d\n", fsm->loc_conn); + + seq_printf(s, "loc_sof: %d\n", fsm->loc_sof); + + seq_printf(s, "adp_prb: %d\n", fsm->adp_prb); + + seq_printf(s, "id: %d\n", fsm->id); + + seq_printf(s, "protocol: %d\n", fsm->protocol); + + return 0; +} + +static int ci_otg_open(struct inode *inode, struct file *file) +{ + return single_open(file, ci_otg_show, inode->i_private); +} + +static const struct file_operations ci_otg_fops = { + .open = ci_otg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int ci_role_show(struct seq_file *s, void *data) { struct ci_hdrc *ci = s->private; @@ -253,6 +331,50 @@ .release = single_release, }; +int ci_registers_show(struct seq_file *s, void *unused) +{ + struct ci_hdrc *ci = s->private; + u32 tmp_reg; + + if (!ci) + return 0; + + /* ------ Registers ----- */ + tmp_reg = hw_read_intr_enable(ci); + seq_printf(s, "USBINTR reg: %08x\n", tmp_reg); + + tmp_reg = hw_read_intr_status(ci); + seq_printf(s, "USBSTS reg: %08x\n", tmp_reg); + + tmp_reg = hw_read(ci, OP_USBMODE, ~0); + seq_printf(s, "USBMODE reg: %08x\n", tmp_reg); + + tmp_reg = hw_read(ci, OP_USBCMD, ~0); + seq_printf(s, "USBCMD reg: %08x\n", tmp_reg); + + tmp_reg = hw_read(ci, OP_PORTSC, ~0); + seq_printf(s, "PORTSC reg: %08x\n", tmp_reg); + + if (ci->is_otg) { + tmp_reg = hw_read_otgsc(ci, ~0); + seq_printf(s, "OTGSC reg: %08x\n", tmp_reg); + } + + return 0; +} + +static int ci_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ci_registers_show, inode->i_private); +} + +static const struct file_operations ci_registers_fops = { + .open = ci_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** * dbg_create_files: initializes the attribute interface * @ci: device @@ -287,8 +409,21 @@ if (!dent) goto err; + if (ci_otg_is_fsm_mode(ci)) { + dent = debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci, + &ci_otg_fops); + if (!dent) + goto err; + } + dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci, &ci_role_fops); + if (!dent) + goto err; + + dent = debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci, + &ci_registers_fops); + if (dent) return 0; err: diff -urN linux-3.12.orig/drivers/usb/chipidea/host.c linux-3.12.work/drivers/usb/chipidea/host.c --- linux-3.12.orig/drivers/usb/chipidea/host.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/host.c 2014-08-03 17:17:27.819882091 +0200 @@ -67,7 +67,11 @@ ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; ehci->imx28_write_fix = ci->imx28_write_fix; - if (ci->platdata->reg_vbus) { + /* + * vbus is always on if host is not in OTG FSM mode, + * otherwise should be controlled by OTG FSM + */ + if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) { ret = regulator_enable(ci->platdata->reg_vbus); if (ret) { dev_err(ci->dev, @@ -78,10 +82,17 @@ } ret = usb_add_hcd(hcd, 0, 0); - if (ret) + if (ret) { goto disable_reg; - else + } else { + struct usb_otg *otg = ci->transceiver->otg; + ci->hcd = hcd; + if (otg) { + otg->host = &hcd->self; + hcd->self.otg_port = 1; + } + } if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); @@ -89,7 +100,7 @@ return ret; disable_reg: - if (ci->platdata->reg_vbus) + if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) regulator_disable(ci->platdata->reg_vbus); put_hcd: @@ -105,15 +116,15 @@ if (hcd) { usb_remove_hcd(hcd); usb_put_hcd(hcd); + if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) + regulator_disable(ci->platdata->reg_vbus); } - if (ci->platdata->reg_vbus) - regulator_disable(ci->platdata->reg_vbus); } void ci_hdrc_host_destroy(struct ci_hdrc *ci) { - if (ci->role == CI_ROLE_HOST) + if (ci->role == CI_ROLE_HOST && ci->hcd) host_stop(ci); } diff -urN linux-3.12.orig/drivers/usb/chipidea/Makefile linux-3.12.work/drivers/usb/chipidea/Makefile --- linux-3.12.orig/drivers/usb/chipidea/Makefile 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/Makefile 2014-08-03 17:17:27.819882091 +0200 @@ -6,6 +6,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o +ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o # Glue/Bridge layers go here @@ -17,5 +18,5 @@ endif ifneq ($(CONFIG_OF),) - obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_imx.o usbmisc_imx.o + obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o endif diff -urN linux-3.12.orig/drivers/usb/chipidea/otg.c linux-3.12.work/drivers/usb/chipidea/otg.c --- linux-3.12.orig/drivers/usb/chipidea/otg.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/otg.c 2014-08-03 17:17:27.819882091 +0200 @@ -11,8 +11,8 @@ */ /* - * This file mainly handles otgsc register, it may include OTG operation - * in the future. + * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP + * are also included. */ #include @@ -22,6 +22,26 @@ #include "ci.h" #include "bits.h" #include "otg.h" +#include "otg_fsm.h" + +/** + * hw_read_otgsc returns otgsc register bits value. + * @mask: bitfield mask + */ +u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) +{ + return hw_read(ci, OP_OTGSC, mask); +} + +/** + * hw_write_otgsc updates target bits of OTGSC register. + * @mask: bitfield mask + * @data: to be written + */ +void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) +{ + hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data); +} /** * ci_otg_role - pick role based on ID pin state @@ -29,8 +49,7 @@ */ enum ci_role ci_otg_role(struct ci_hdrc *ci) { - u32 sts = hw_read(ci, OP_OTGSC, ~0); - enum ci_role role = sts & OTGSC_ID + enum ci_role role = hw_read_otgsc(ci, OTGSC_ID) ? CI_ROLE_GADGET : CI_ROLE_HOST; @@ -39,14 +58,10 @@ void ci_handle_vbus_change(struct ci_hdrc *ci) { - u32 otgsc; - if (!ci->is_otg) return; - otgsc = hw_read(ci, OP_OTGSC, ~0); - - if (otgsc & OTGSC_BSV) + if (hw_read_otgsc(ci, OTGSC_BSV)) usb_gadget_vbus_connect(&ci->gadget); else usb_gadget_vbus_disconnect(&ci->gadget); @@ -76,6 +91,11 @@ { struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) { + enable_irq(ci->irq); + return; + } + if (ci->id_event) { ci->id_event = false; ci_handle_id_switch(ci); @@ -102,6 +122,9 @@ return -ENODEV; } + if (ci_otg_is_fsm_mode(ci)) + return ci_hdrc_otg_fsm_init(ci); + return 0; } @@ -115,6 +138,9 @@ flush_workqueue(ci->wq); destroy_workqueue(ci->wq); } - ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); - ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); + /* Disable all OTG irq and clear status */ + hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + OTGSC_INT_STATUS_BITS); + if (ci_otg_is_fsm_mode(ci)) + ci_hdrc_otg_fsm_remove(ci); } diff -urN linux-3.12.orig/drivers/usb/chipidea/otg_fsm.c linux-3.12.work/drivers/usb/chipidea/otg_fsm.c --- linux-3.12.orig/drivers/usb/chipidea/otg_fsm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/otg_fsm.c 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,842 @@ +/* + * otg_fsm.c - ChipIdea USB IP core OTG FSM driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Jun Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This file mainly handles OTG fsm, it includes OTG fsm operations + * for HNP and SRP. + * + * TODO List + * - ADP + * - OTG test device + */ + +#include +#include +#include +#include +#include + +#include "ci.h" +#include "bits.h" +#include "otg.h" +#include "otg_fsm.h" + +static struct ci_otg_fsm_timer *otg_timer_initializer +(struct ci_hdrc *ci, void (*function)(void *, unsigned long), + unsigned long expires, unsigned long data) +{ + struct ci_otg_fsm_timer *timer; + + timer = devm_kzalloc(ci->dev, sizeof(struct ci_otg_fsm_timer), + GFP_KERNEL); + if (!timer) + return NULL; + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +/* Add for otg: interact with user space app */ +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') { + ci->fsm.a_bus_req = 0; + } else if (buf[0] == '1') { + /* If a_bus_drop is TRUE, a_bus_req can't be set */ + if (ci->fsm.a_bus_drop) { + mutex_unlock(&ci->fsm.lock); + return count; + } + ci->fsm.a_bus_req = 1; + } + + ci_otg_queue_work(ci); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') { + ci->fsm.a_bus_drop = 0; + } else if (buf[0] == '1') { + ci->fsm.a_bus_drop = 1; + ci->fsm.a_bus_req = 0; + } + + ci_otg_queue_work(ci); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, + set_a_bus_drop); + +static ssize_t +get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_b_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') + ci->fsm.b_bus_req = 0; + else if (buf[0] == '1') + ci->fsm.b_bus_req = 1; + + ci_otg_queue_work(ci); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '1') + ci->fsm.a_clr_err = 1; + + ci_otg_queue_work(ci); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_bus_drop.attr, + &dev_attr_b_bus_req.attr, + &dev_attr_a_clr_err.attr, + NULL, +}; + +static struct attribute_group inputs_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +/* + * Add timer to active timer list + */ +static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) +{ + struct ci_otg_fsm_timer *tmp_timer; + struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; + struct list_head *active_timers = &ci->fsm_timer->active_timers; + + if (t >= NUM_CI_OTG_FSM_TIMERS) + return; + + /* + * Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + + timer->count = timer->expires; + list_add_tail(&timer->list, active_timers); + + /* Enable 1ms irq */ + if (!(hw_read_otgsc(ci, OTGSC_1MSIE))) + hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE); +} + +/* + * Remove timer from active timer list + */ +static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) +{ + struct ci_otg_fsm_timer *tmp_timer, *del_tmp; + struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; + struct list_head *active_timers = &ci->fsm_timer->active_timers; + + if (t >= NUM_CI_OTG_FSM_TIMERS) + return; + + list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); + + /* Disable 1ms irq if there is no any active timer */ + if (list_empty(active_timers)) + hw_write_otgsc(ci, OTGSC_1MSIE, 0); +} + +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by otg 1ms timer interrupt + */ +static inline int ci_otg_tick_timer(struct ci_hdrc *ci) +{ + struct ci_otg_fsm_timer *tmp_timer, *del_tmp; + struct list_head *active_timers = &ci->fsm_timer->active_timers; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(ci, tmp_timer->data); + expired = 1; + } + } + + /* disable 1ms irq if there is no any timer active */ + if ((expired == 1) && list_empty(active_timers)) + hw_write_otgsc(ci, OTGSC_1MSIE, 0); + + return expired; +} + +/* The timeout callback function to set time out bit */ +static void set_tmout(void *ptr, unsigned long indicator) +{ + *(int *)indicator = 1; +} + +static void set_tmout_and_fsm(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + set_tmout(ci, indicator); + + ci_otg_queue_work(ci); +} + +static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + set_tmout(ci, indicator); + /* Disable port power */ + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0); + /* Clear exsiting DP irq */ + hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS); + /* Enable data pulse irq */ + hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE); + ci_otg_queue_work(ci); +} + +static void b_ase0_brst_tmout_func(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + set_tmout(ci, indicator); + if (!hw_read_otgsc(ci, OTGSC_BSV)) + ci->fsm.b_sess_vld = 0; + + ci_otg_queue_work(ci); +} + +static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + set_tmout(ci, indicator); + + /* only vbus fall below B_sess_vld in b_idle state */ + if (ci->transceiver->state == OTG_STATE_B_IDLE) + ci_otg_queue_work(ci); +} + +static void b_sess_vld_tmout_func(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + /* Check if A detached */ + if (!(hw_read_otgsc(ci, OTGSC_BSV))) { + ci->fsm.b_sess_vld = 0; + ci_otg_add_timer(ci, B_SSEND_SRP); + ci_otg_queue_work(ci); + } +} + +static void b_data_pulse_end(void *ptr, unsigned long indicator) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)ptr; + + ci->fsm.b_srp_done = 1; + ci->fsm.b_bus_req = 0; + if (ci->fsm.power_up) + ci->fsm.power_up = 0; + + hw_write_otgsc(ci, OTGSC_HABA, 0); + + ci_otg_queue_work(ci); +} + +/* Initialize timers */ +static int ci_otg_init_timers(struct ci_hdrc *ci) +{ + struct otg_fsm *fsm = &ci->fsm; + + /* FSM used timers */ + ci->fsm_timer->timer_list[A_WAIT_VRISE] = + otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + if (ci->fsm_timer->timer_list[A_WAIT_VRISE] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[A_WAIT_VFALL] = + otg_timer_initializer(ci, &a_wait_vfall_tmout_func, + TA_WAIT_VFALL, (unsigned long)&fsm->a_wait_vfall_tmout); + if (ci->fsm_timer->timer_list[A_WAIT_VFALL] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[A_WAIT_BCON] = + otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + if (ci->fsm_timer->timer_list[A_WAIT_BCON] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[A_AIDL_BDIS] = + otg_timer_initializer(ci, &set_tmout_and_fsm, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + if (ci->fsm_timer->timer_list[A_AIDL_BDIS] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[A_BIDL_ADIS] = + otg_timer_initializer(ci, &set_tmout_and_fsm, TA_BIDL_ADIS, + (unsigned long)&fsm->a_bidl_adis_tmout); + if (ci->fsm_timer->timer_list[A_BIDL_ADIS] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_ASE0_BRST] = + otg_timer_initializer(ci, &b_ase0_brst_tmout_func, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + if (ci->fsm_timer->timer_list[B_ASE0_BRST] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_SE0_SRP] = + otg_timer_initializer(ci, &set_tmout_and_fsm, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + if (ci->fsm_timer->timer_list[B_SE0_SRP] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_SSEND_SRP] = + otg_timer_initializer(ci, &b_ssend_srp_tmout_func, TB_SSEND_SRP, + (unsigned long)&fsm->b_ssend_srp); + if (ci->fsm_timer->timer_list[B_SSEND_SRP] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_SRP_FAIL] = + otg_timer_initializer(ci, &set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + if (ci->fsm_timer->timer_list[B_SRP_FAIL] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_DATA_PLS] = + otg_timer_initializer(ci, &b_data_pulse_end, TB_DATA_PLS, 0); + if (ci->fsm_timer->timer_list[B_DATA_PLS] == NULL) + return -ENOMEM; + + ci->fsm_timer->timer_list[B_SESS_VLD] = otg_timer_initializer(ci, + &b_sess_vld_tmout_func, TB_SESS_VLD, 0); + if (ci->fsm_timer->timer_list[B_SESS_VLD] == NULL) + return -ENOMEM; + + return 0; +} + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ +/* -------------------------------------------------------------*/ +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); + return; +} + +static void ci_otg_fsm_del_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_del_timer(ci, t); + return; +} + +/* + * A-device drive vbus: turn on vbus regulator and enable port power + * Data pulse irq should be disabled while vbus is on. + */ +static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + int ret; + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + if (on) { + /* Enable power power */ + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, + PORTSC_PP); + if (ci->platdata->reg_vbus) { + ret = regulator_enable(ci->platdata->reg_vbus); + if (ret) { + dev_err(ci->dev, + "Failed to enable vbus regulator, ret=%d\n", + ret); + return; + } + } + /* Disable data pulse irq */ + hw_write_otgsc(ci, OTGSC_DPIE, 0); + + fsm->a_srp_det = 0; + fsm->power_up = 0; + } else { + if (ci->platdata->reg_vbus) + regulator_disable(ci->platdata->reg_vbus); + + fsm->a_bus_drop = 1; + fsm->a_bus_req = 0; + } +} + +/* + * Control data line by Run Stop bit. + */ +static void ci_otg_loc_conn(struct otg_fsm *fsm, int on) +{ + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + if (on) + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); + else + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); +} + +/* + * Generate SOF by host. + * This is controlled through suspend/resume the port. + * In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +static void ci_otg_loc_sof(struct otg_fsm *fsm, int on) +{ + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + if (on) + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR, + PORTSC_FPR); + else + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP, + PORTSC_SUSP); +} + +/* + * Start SRP pulsing by data-line pulsing, + * no v-bus pulsing followed + */ +static void ci_otg_start_pulse(struct otg_fsm *fsm) +{ + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + /* Hardware Assistant Data pulse */ + hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP); + + ci_otg_add_timer(ci, B_DATA_PLS); +} + +static int ci_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + mutex_unlock(&fsm->lock); + if (on) { + ci_role_stop(ci); + ci_role_start(ci, CI_ROLE_HOST); + } else { + ci_role_stop(ci); + hw_device_reset(ci, USBMODE_CM_DC); + ci_role_start(ci, CI_ROLE_GADGET); + } + mutex_lock(&fsm->lock); + return 0; +} + +static int ci_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); + + mutex_unlock(&fsm->lock); + if (on) + usb_gadget_vbus_connect(&ci->gadget); + else + usb_gadget_vbus_disconnect(&ci->gadget); + mutex_lock(&fsm->lock); + + return 0; +} + +static struct otg_fsm_ops ci_otg_ops = { + .drv_vbus = ci_otg_drv_vbus, + .loc_conn = ci_otg_loc_conn, + .loc_sof = ci_otg_loc_sof, + .start_pulse = ci_otg_start_pulse, + .add_timer = ci_otg_fsm_add_timer, + .del_timer = ci_otg_fsm_del_timer, + .start_host = ci_otg_start_host, + .start_gadget = ci_otg_start_gadget, +}; + +int ci_otg_fsm_work(struct ci_hdrc *ci) +{ + /* + * Don't do fsm transition for B device + * when there is no gadget class driver + */ + if (ci->fsm.id && !(ci->driver) && + ci->transceiver->state < OTG_STATE_A_IDLE) + return 0; + + if (otg_statemachine(&ci->fsm)) { + if (ci->transceiver->state == OTG_STATE_A_IDLE) { + /* + * Further state change for cases: + * a_idle to b_idle; or + * a_idle to a_wait_vrise due to ID change(1->0), so + * B-dev becomes A-dev can try to start new session + * consequently; or + * a_idle to a_wait_vrise when power up + */ + if ((ci->fsm.id) || (ci->id_event) || + (ci->fsm.power_up)) + ci_otg_queue_work(ci); + if (ci->id_event) + ci->id_event = false; + } else if (ci->transceiver->state == OTG_STATE_B_IDLE) { + if (ci->fsm.b_sess_vld) { + ci->fsm.power_up = 0; + /* + * Further transite to b_periphearl state + * when register gadget driver with vbus on + */ + ci_otg_queue_work(ci); + } + } + } + return 0; +} + +/* + * Update fsm variables in each state if catching expected interrupts, + * called by otg fsm isr. + */ +static void ci_otg_fsm_event(struct ci_hdrc *ci) +{ + u32 intr_sts, otg_bsess_vld, port_conn; + struct otg_fsm *fsm = &ci->fsm; + + intr_sts = hw_read_intr_status(ci); + otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV); + port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS); + + switch (ci->transceiver->state) { + case OTG_STATE_A_WAIT_BCON: + if (port_conn) { + fsm->b_conn = 1; + fsm->a_bus_req = 1; + ci_otg_queue_work(ci); + } + break; + case OTG_STATE_B_IDLE: + if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) { + fsm->b_sess_vld = 1; + ci_otg_queue_work(ci); + } + break; + case OTG_STATE_B_PERIPHERAL: + if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) { + fsm->a_bus_suspend = 1; + ci_otg_queue_work(ci); + } else if (intr_sts & USBi_PCI) { + if (fsm->a_bus_suspend == 1) + fsm->a_bus_suspend = 0; + } + break; + case OTG_STATE_B_HOST: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->a_conn = 0; + fsm->b_bus_req = 0; + ci_otg_queue_work(ci); + ci_otg_add_timer(ci, B_SESS_VLD); + } + break; + case OTG_STATE_A_PERIPHERAL: + if (intr_sts & USBi_SLI) { + fsm->b_bus_suspend = 1; + /* + * Init a timer to know how long this suspend + * will contine, if time out, indicates B no longer + * wants to be host role + */ + ci_otg_add_timer(ci, A_BIDL_ADIS); + } + + if (intr_sts & USBi_URI) + ci_otg_del_timer(ci, A_BIDL_ADIS); + + if (intr_sts & USBi_PCI) { + if (fsm->b_bus_suspend == 1) { + ci_otg_del_timer(ci, A_BIDL_ADIS); + fsm->b_bus_suspend = 0; + } + } + break; + case OTG_STATE_A_SUSPEND: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->b_conn = 0; + + /* if gadget driver is binded */ + if (ci->driver) { + /* A device to be peripheral mode */ + ci->gadget.is_a_peripheral = 1; + } + ci_otg_queue_work(ci); + } + break; + case OTG_STATE_A_HOST: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->b_conn = 0; + ci_otg_queue_work(ci); + } + break; + case OTG_STATE_B_WAIT_ACON: + if ((intr_sts & USBi_PCI) && port_conn) { + fsm->a_conn = 1; + ci_otg_queue_work(ci); + } + break; + default: + break; + } +} + +/* + * ci_otg_irq - otg fsm related irq handling + * and also update otg fsm variable by monitoring usb host and udc + * state change interrupts. + * @ci: ci_hdrc + */ +irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) +{ + irqreturn_t retval = IRQ_NONE; + u32 otgsc, otg_int_src = 0; + struct otg_fsm *fsm = &ci->fsm; + + otgsc = hw_read_otgsc(ci, ~0); + otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8); + fsm->id = (otgsc & OTGSC_ID) ? 1 : 0; + + if (otg_int_src) { + if (otg_int_src & OTGSC_1MSIS) { + hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS); + retval = ci_otg_tick_timer(ci); + return IRQ_HANDLED; + } else if (otg_int_src & OTGSC_DPIS) { + hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS); + fsm->a_srp_det = 1; + fsm->a_bus_drop = 0; + } else if (otg_int_src & OTGSC_IDIS) { + hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + if (fsm->id == 0) { + fsm->a_bus_drop = 0; + fsm->a_bus_req = 1; + ci->id_event = true; + } + } else if (otg_int_src & OTGSC_BSVIS) { + hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + 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 { + fsm->b_sess_vld = 0; + if (fsm->id) + ci_otg_add_timer(ci, B_SSEND_SRP); + } + } else if (otg_int_src & OTGSC_AVVIS) { + hw_write_otgsc(ci, OTGSC_AVVIS, OTGSC_AVVIS); + if (otgsc & OTGSC_AVV) { + fsm->a_vbus_vld = 1; + } else { + fsm->a_vbus_vld = 0; + fsm->b_conn = 0; + } + } + ci_otg_queue_work(ci); + return IRQ_HANDLED; + } + + ci_otg_fsm_event(ci); + + return retval; +} + +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) +{ + ci_otg_queue_work(ci); +} + +int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) +{ + int retval = 0; + struct usb_otg *otg; + + otg = devm_kzalloc(ci->dev, + sizeof(struct usb_otg), GFP_KERNEL); + if (!otg) { + dev_err(ci->dev, + "Failed to allocate usb_otg structure for ci hdrc otg!\n"); + return -ENOMEM; + } + + otg->phy = ci->transceiver; + otg->gadget = &ci->gadget; + ci->fsm.otg = otg; + ci->transceiver->otg = ci->fsm.otg; + ci->fsm.power_up = 1; + ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0; + ci->transceiver->state = OTG_STATE_UNDEFINED; + ci->fsm.ops = &ci_otg_ops; + + mutex_init(&ci->fsm.lock); + + ci->fsm_timer = devm_kzalloc(ci->dev, + sizeof(struct ci_otg_fsm_timer_list), GFP_KERNEL); + if (!ci->fsm_timer) { + dev_err(ci->dev, + "Failed to allocate timer structure for ci hdrc otg!\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&ci->fsm_timer->active_timers); + retval = ci_otg_init_timers(ci); + if (retval) { + dev_err(ci->dev, "Couldn't init OTG timers\n"); + return retval; + } + + retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group); + if (retval < 0) { + dev_dbg(ci->dev, + "Can't register sysfs attr group: %d\n", retval); + return retval; + } + + /* Enable A vbus valid irq */ + hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE); + + if (ci->fsm.id) { + ci->fsm.b_ssend_srp = + hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1; + ci->fsm.b_sess_vld = + hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0; + /* Enable BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE); + } + + return 0; +} + +void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) +{ + sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group); +} diff -urN linux-3.12.orig/drivers/usb/chipidea/otg_fsm.h linux-3.12.work/drivers/usb/chipidea/otg_fsm.h --- linux-3.12.orig/drivers/usb/chipidea/otg_fsm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/otg_fsm.h 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Jun Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_USB_CHIPIDEA_OTG_FSM_H +#define __DRIVERS_USB_CHIPIDEA_OTG_FSM_H + +#include + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise: section 7.1.2 + * a_wait_vrise_tmr: section 7.4.5.1 + * TA_VBUS_RISE <= 100ms, section 4.4 + * Table 4-1: Electrical Characteristics + * ->DC Electrical Timing + */ +/* Wait for VBUS Fall */ +#define TA_WAIT_VFALL (1000) /* a_wait_vfall: section 7.1.7 + * a_wait_vfall_tmr: section: 7.4.5.2 + */ +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon: section 7.1.3 + * TA_WAIT_BCON: should be between 1100 + * and 30000 ms, section 5.5, Table 5-1 + */ +/* A-Idle to B-Disconnect */ +#define TA_AIDL_BDIS (5000) /* a_suspend min 200 ms, section 5.2.1 + * TA_AIDL_BDIS: section 5.5, Table 5-1 + */ +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (500) /* TA_BIDL_ADIS: section 5.2.1 + * 500ms is used for B switch to host + * for safe + */ + +/* + * B-device timing constants + */ + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms + * section:5.1.3 + */ +/* SRP Fail Time */ +#define TB_SRP_FAIL (6000) /* b_srp_init,fail time 5~6s + * section:5.1.6 + */ +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (155) /* minimum 155 ms, section:5.3.1 */ +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (1000) /* b_idle,minimum 1s, section:5.1.2 */ +/* SSEND time before SRP */ +#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */ + +#define TB_SESS_VLD (1000) + +enum ci_otg_fsm_timer_index { + /* + * CI specific timers, start from the end + * of standard and auxiliary OTG timers + */ + B_DATA_PLS = NUM_OTG_FSM_TIMERS, + B_SSEND_SRP, + B_SESS_VLD, + + NUM_CI_OTG_FSM_TIMERS, +}; + +struct ci_otg_fsm_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(void *, unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +struct ci_otg_fsm_timer_list { + struct ci_otg_fsm_timer *timer_list[NUM_CI_OTG_FSM_TIMERS]; + struct list_head active_timers; +}; + +#ifdef CONFIG_USB_OTG_FSM + +int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); +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); + +#else + +static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) +{ + return 0; +} + +static inline int ci_otg_fsm_work(struct ci_hdrc *ci) +{ + return -ENXIO; +} + +static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) +{ + return IRQ_NONE; +} + +static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) +{ + +} + +static inline void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) +{ + +} + +#endif + +#endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ diff -urN linux-3.12.orig/drivers/usb/chipidea/otg.h linux-3.12.work/drivers/usb/chipidea/otg.h --- linux-3.12.orig/drivers/usb/chipidea/otg.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/otg.h 2014-08-03 17:17:27.819882091 +0200 @@ -11,25 +11,16 @@ #ifndef __DRIVERS_USB_CHIPIDEA_OTG_H #define __DRIVERS_USB_CHIPIDEA_OTG_H -static inline void ci_clear_otg_interrupt(struct ci_hdrc *ci, u32 bits) -{ - /* Only clear request bits */ - hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits); -} - -static inline void ci_enable_otg_interrupt(struct ci_hdrc *ci, u32 bits) -{ - hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, bits); -} - -static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) -{ - hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, 0); -} - +u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask); +void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data); 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); +static inline void ci_otg_queue_work(struct ci_hdrc *ci) +{ + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); +} #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ diff -urN linux-3.12.orig/drivers/usb/chipidea/udc.c linux-3.12.work/drivers/usb/chipidea/udc.c --- linux-3.12.orig/drivers/usb/chipidea/udc.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/udc.c 2014-08-03 17:17:27.819882091 +0200 @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include "ci.h" @@ -28,6 +28,7 @@ #include "bits.h" #include "debug.h" #include "otg.h" +#include "otg_fsm.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -106,7 +107,7 @@ do { /* flush any pending transfer */ - hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTFLUSH, ~0, BIT(n)); while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); @@ -179,19 +180,6 @@ } /** - * hw_test_and_clear_setup_status: test & clear setup status (execute without - * interruption) - * @n: endpoint number - * - * This function returns setup status - */ -static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n) -{ - n = ep_to_bit(ci, n); - return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); -} - -/** * hw_ep_prime: primes endpoint (execute without interruption) * @num: endpoint number * @dir: endpoint direction @@ -206,7 +194,7 @@ if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, ~0, BIT(n)); while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); @@ -256,26 +244,6 @@ } /** - * hw_read_intr_enable: returns interrupt enable register - * - * This function returns register data - */ -static u32 hw_read_intr_enable(struct ci_hdrc *ci) -{ - return hw_read(ci, OP_USBINTR, ~0); -} - -/** - * hw_read_intr_status: returns interrupt status register - * - * This function returns register data - */ -static u32 hw_read_intr_status(struct ci_hdrc *ci) -{ - return hw_read(ci, OP_USBSTS, ~0); -} - -/** * hw_test_and_clear_complete: test & clear complete status (execute without * interruption) * @n: endpoint number @@ -695,9 +663,6 @@ usb_ep_fifo_flush(&ci->ep0out->ep); usb_ep_fifo_flush(&ci->ep0in->ep); - if (ci->driver) - ci->driver->disconnect(gadget); - /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); @@ -727,6 +692,11 @@ int retval; spin_unlock(&ci->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN) { + if (ci->driver) + ci->driver->disconnect(&ci->gadget); + } + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; @@ -739,6 +709,8 @@ if (ci->status == NULL) retval = -ENOMEM; + usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT); + done: spin_lock(&ci->lock); @@ -853,7 +825,6 @@ if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ *(u16 *)req->buf = ci->remote_wakeup << 1; - retval = 0; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? @@ -895,6 +866,8 @@ if (ci->setaddr) { hw_usb_set_address(ci, ci->address); ci->setaddr = false; + if (ci->address) + usb_gadget_set_state(&ci->gadget, USB_STATE_ADDRESS); } spin_lock_irqsave(&ci->lock, flags); @@ -961,6 +934,164 @@ } /** + * isr_setup_packet_handler: setup packet handler + * @ci: UDC descriptor + * + * This function handles setup packet + */ +static void isr_setup_packet_handler(struct ci_hdrc *ci) +__releases(ci->lock) +__acquires(ci->lock) +{ + struct ci_hw_ep *hwep = &ci->ci_hw_ep[0]; + struct usb_ctrlrequest req; + int type, num, dir, err = -EINVAL; + u8 tmode = 0; + + /* + * Flush data and handshake transactions of previous + * setup packet. + */ + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); + + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(ci); + memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard(ci)); + + type = req.bRequestType; + + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + if (!ci->ci_hw_ep[num].wedge) { + spin_unlock(&ci->lock); + err = usb_ep_clear_halt( + &ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (err) + break; + } + err = isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && + le16_to_cpu(req.wValue) == + USB_DEVICE_REMOTE_WAKEUP) { + if (req.wLength != 0) + break; + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); + } else { + goto delegate; + } + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + 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) + break; + err = isr_get_status_response(ci, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); + break; + case USB_REQ_SET_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (!err) + isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { + if (req.wLength != 0) + break; + switch (le16_to_cpu(req.wValue)) { + case USB_DEVICE_REMOTE_WAKEUP: + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); + break; + case USB_DEVICE_TEST_MODE: + tmode = le16_to_cpu(req.wIndex) >> 8; + switch (tmode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + ci->test_mode = tmode; + err = isr_setup_status_phase( + ci); + break; + default: + break; + } + break; + case USB_DEVICE_B_HNP_ENABLE: + if (ci_otg_is_fsm_mode(ci)) { + ci->gadget.b_hnp_enable = 1; + err = isr_setup_status_phase( + ci); + } + break; + default: + goto delegate; + } + } else { + goto delegate; + } + break; + default: +delegate: + if (req.wLength == 0) /* no data phase */ + ci->ep0_dir = TX; + + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); + break; + } + + if (err < 0) { + spin_unlock(&ci->lock); + if (usb_ep_set_halt(&hwep->ep)) + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); + } +} + +/** * isr_tr_complete_handler: transaction complete interrupt handler * @ci: UDC descriptor * @@ -971,12 +1102,10 @@ __acquires(ci->lock) { unsigned i; - u8 tmode = 0; + int err; for (i = 0; i < ci->hw_ep_max; i++) { struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; - int type, num, dir, err = -EINVAL; - struct usb_ctrlrequest req; if (hwep->ep.desc == NULL) continue; /* not configured */ @@ -996,148 +1125,10 @@ } } - if (hwep->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(ci, i)) - continue; - - if (i != 0) { - dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); - continue; - } - - /* - * Flush data and handshake transactions of previous - * setup packet. - */ - _ep_nuke(ci->ep0out); - _ep_nuke(ci->ep0in); - - /* read_setup_packet */ - do { - hw_test_and_set_setup_guard(ci); - memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(ci)); - - type = req.bRequestType; - - ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; - - switch (req.bRequest) { - case USB_REQ_CLEAR_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - if (!ci->ci_hw_ep[num].wedge) { - spin_unlock(&ci->lock); - err = usb_ep_clear_halt( - &ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (err) - break; - } - err = isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && - le16_to_cpu(req.wValue) == - USB_DEVICE_REMOTE_WAKEUP) { - if (req.wLength != 0) - break; - ci->remote_wakeup = 0; - err = isr_setup_status_phase(ci); - } else { - goto delegate; - } - break; - case USB_REQ_GET_STATUS: - if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && - 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) - break; - err = isr_get_status_response(ci, &req); - break; - case USB_REQ_SET_ADDRESS: - if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 0 || - le16_to_cpu(req.wIndex) != 0) - break; - ci->address = (u8)le16_to_cpu(req.wValue); - ci->setaddr = true; - err = isr_setup_status_phase(ci); - break; - case USB_REQ_SET_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - - spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (!err) - isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { - if (req.wLength != 0) - break; - switch (le16_to_cpu(req.wValue)) { - case USB_DEVICE_REMOTE_WAKEUP: - ci->remote_wakeup = 1; - err = isr_setup_status_phase(ci); - break; - case USB_DEVICE_TEST_MODE: - tmode = le16_to_cpu(req.wIndex) >> 8; - switch (tmode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: - ci->test_mode = tmode; - err = isr_setup_status_phase( - ci); - break; - default: - break; - } - default: - goto delegate; - } - } else { - goto delegate; - } - break; - default: -delegate: - if (req.wLength == 0) /* no data phase */ - ci->ep0_dir = TX; - - spin_unlock(&ci->lock); - err = ci->driver->setup(&ci->gadget, &req); - spin_lock(&ci->lock); - break; - } - - if (err < 0) { - spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&ci->lock); - } + /* Only handle setup packet below */ + if (i == 0 && + hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) + isr_setup_packet_handler(ci); } } @@ -1178,8 +1169,8 @@ if (hwep->type == USB_ENDPOINT_XFER_CONTROL) cap |= QH_IOS; - if (hwep->num) - cap |= QH_ZLT; + + cap |= QH_ZLT; cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; /* * For ISO-TX, we set mult at QH as the largest value, and use @@ -1192,6 +1183,11 @@ hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ + if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) { + dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n"); + retval = -EINVAL; + } + /* * Enable endpoints in the HW other than ep0 as ep0 * is always enabled @@ -1325,6 +1321,7 @@ struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req); unsigned long flags; + struct td_node *node, *tmpnode; if (ep == NULL || req == NULL || hwreq->req.status != -EALREADY || hwep->ep.desc == NULL || list_empty(&hwreq->queue) || @@ -1335,6 +1332,12 @@ hw_ep_flush(hwep->ci, hwep->num, hwep->dir); + list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { + dma_pool_free(hwep->td_pool, node->ptr, node->dma); + list_del(&node->td); + kfree(node); + } + /* pop request */ list_del_init(&hwreq->queue); @@ -1474,15 +1477,17 @@ pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci, USBMODE_CM_DC); hw_device_state(ci, ci->ep0out->qh.dma); - dev_dbg(ci->dev, "Connected to host\n"); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); } else { + if (ci->driver) + ci->driver->disconnect(&ci->gadget); hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); - dev_dbg(ci->dev, "Disconnected from host\n"); + usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED); } } @@ -1578,7 +1583,7 @@ * eps, maxP is set by epautoconfig() called * by gadget layer */ - hwep->ep.maxpacket = (unsigned short)~0; + usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0); INIT_LIST_HEAD(&hwep->qh.queue); hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, @@ -1598,7 +1603,7 @@ else ci->ep0in = hwep; - hwep->ep.maxpacket = CTRL_PAYLOAD_MAX; + usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX); continue; } @@ -1648,23 +1653,29 @@ retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&ci->lock, flags); ci->driver = driver; + + /* Start otg fsm for B-device */ + if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) { + ci_hdrc_otg_fsm_start(ci); + return retval; + } + pm_runtime_get_sync(&ci->gadget.dev); if (ci->vbus_active) { + spin_lock_irqsave(&ci->lock, flags); hw_device_reset(ci, USBMODE_CM_DC); } else { pm_runtime_put_sync(&ci->gadget.dev); - goto done; + return retval; } retval = hw_device_state(ci, ci->ep0out->qh.dma); + spin_unlock_irqrestore(&ci->lock, flags); if (retval) pm_runtime_put_sync(&ci->gadget.dev); - done: - spin_unlock_irqrestore(&ci->lock, flags); return retval; } @@ -1749,6 +1760,8 @@ ci->suspended = 1; spin_unlock(&ci->lock); ci->driver->suspend(&ci->gadget); + usb_gadget_set_state(&ci->gadget, + USB_STATE_SUSPENDED); spin_lock(&ci->lock); } } @@ -1775,7 +1788,7 @@ ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; - ci->gadget.is_otg = 0; + ci->gadget.is_otg = ci->is_otg ? 1 : 0; ci->gadget.name = ci->platdata->name; INIT_LIST_HEAD(&ci->gadget.ep_list); @@ -1801,51 +1814,15 @@ ci->gadget.ep0 = &ci->ep0in->ep; - if (ci->global_phy) { - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (IS_ERR(ci->transceiver)) - ci->transceiver = NULL; - } - - if (ci->platdata->flags & CI_HDRC_REQUIRE_TRANSCEIVER) { - if (ci->transceiver == NULL) { - retval = -ENODEV; - goto destroy_eps; - } - } - - if (ci->transceiver) { - retval = otg_set_peripheral(ci->transceiver->otg, - &ci->gadget); - /* - * If we implement all USB functions using chipidea drivers, - * it doesn't need to call above API, meanwhile, if we only - * use gadget function, calling above API is useless. - */ - if (retval && retval != -ENOTSUPP) - goto put_transceiver; - } - retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) - goto remove_trans; + goto destroy_eps; pm_runtime_no_callbacks(&ci->gadget.dev); pm_runtime_enable(&ci->gadget.dev); return retval; -remove_trans: - if (ci->transceiver) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } - - dev_err(dev, "error = %i\n", retval); -put_transceiver: - if (ci->transceiver && ci->global_phy) - usb_put_phy(ci->transceiver); destroy_eps: destroy_eps(ci); free_pools: @@ -1871,31 +1848,26 @@ dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); - - if (ci->transceiver) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } } static int udc_id_switch_for_device(struct ci_hdrc *ci) { - if (ci->is_otg) { - ci_clear_otg_interrupt(ci, OTGSC_BSVIS); - ci_enable_otg_interrupt(ci, OTGSC_BSVIE); - } + if (ci->is_otg) + /* Clear and enable BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, + OTGSC_BSVIS | OTGSC_BSVIE); return 0; } static void udc_id_switch_for_host(struct ci_hdrc *ci) { - if (ci->is_otg) { - /* host doesn't care B_SESSION_VALID event */ - ci_clear_otg_interrupt(ci, OTGSC_BSVIS); - ci_disable_otg_interrupt(ci, OTGSC_BSVIE); - } + /* + * host doesn't care B_SESSION_VALID event + * so clear and disbale BSV irq + */ + if (ci->is_otg) + hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); } /** diff -urN linux-3.12.orig/drivers/usb/chipidea/usbmisc_imx.c linux-3.12.work/drivers/usb/chipidea/usbmisc_imx.c --- linux-3.12.orig/drivers/usb/chipidea/usbmisc_imx.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/chipidea/usbmisc_imx.c 2014-08-03 17:17:27.819882091 +0200 @@ -15,18 +15,46 @@ #include #include #include +#include #include "ci_hdrc_imx.h" #define MX25_USB_PHY_CTRL_OFFSET 0x08 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) +#define MX25_EHCI_INTERFACE_SINGLE_UNI (2 << 0) +#define MX25_EHCI_INTERFACE_DIFF_UNI (0 << 0) +#define MX25_EHCI_INTERFACE_MASK (0xf) + +#define MX25_OTG_SIC_SHIFT 29 +#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) +#define MX25_OTG_PM_BIT BIT(24) +#define MX25_OTG_PP_BIT BIT(11) +#define MX25_OTG_OCPOL_BIT BIT(3) + +#define MX25_H1_SIC_SHIFT 21 +#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT) +#define MX25_H1_PP_BIT BIT(18) +#define MX25_H1_PM_BIT BIT(16) +#define MX25_H1_IPPUE_UP_BIT BIT(7) +#define MX25_H1_IPPUE_DOWN_BIT BIT(6) +#define MX25_H1_TLL_BIT BIT(5) +#define MX25_H1_USBTE_BIT BIT(4) +#define MX25_H1_OCPOL_BIT BIT(2) + +#define MX27_H1_PM_BIT BIT(8) +#define MX27_H2_PM_BIT BIT(16) +#define MX27_OTG_PM_BIT BIT(24) + #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 +#define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c #define MX53_USB_UH2_CTRL_OFFSET 0x14 #define MX53_USB_UH3_CTRL_OFFSET 0x18 #define MX53_BM_OVER_CUR_DIS_H1 BIT(5) #define MX53_BM_OVER_CUR_DIS_OTG BIT(8) #define MX53_BM_OVER_CUR_DIS_UHx BIT(30) +#define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3 +#define MX53_USB_PLL_DIV_24_MHZ 0x01 #define MX6_BM_OVER_CUR_DIS BIT(7) @@ -41,11 +69,45 @@ void __iomem *base; spinlock_t lock; struct clk *clk; + struct clk *ipgclk, *ahbclk, *perclk; const struct usbmisc_ops *ops; }; static struct imx_usbmisc *usbmisc; +static int usbmisc_imx25_init(struct imx_usbmisc_data *data) +{ + unsigned long flags; + u32 val = 0; + + if (data->index > 1) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + switch (data->index) { + case 0: + val = readl(usbmisc->base); + val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_OCPOL_BIT); + val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; + val |= (MX25_OTG_PM_BIT | MX25_OTG_PP_BIT); + writel(val, usbmisc->base); + break; + case 1: + val = readl(usbmisc->base); + val &= ~(MX25_H1_SIC_MASK | MX25_H1_OCPOL_BIT | MX25_H1_IPPUE_UP_BIT); + val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT; + val |= (MX25_H1_PM_BIT | MX25_H1_PP_BIT | MX25_H1_TLL_BIT | + MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT); + + writel(val, usbmisc->base); + + break; + } + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + static int usbmisc_imx25_post(struct imx_usbmisc_data *data) { void __iomem *reg; @@ -68,6 +130,36 @@ return 0; } +static int usbmisc_imx27_init(struct imx_usbmisc_data *data) +{ + unsigned long flags; + u32 val; + + switch (data->index) { + case 0: + val = MX27_OTG_PM_BIT; + break; + case 1: + val = MX27_H1_PM_BIT; + break; + case 2: + val = MX27_H2_PM_BIT; + break; + default: + return -EINVAL; + }; + + spin_lock_irqsave(&usbmisc->lock, flags); + if (data->disable_oc) + val = readl(usbmisc->base) | val; + else + val = readl(usbmisc->base) & ~val; + writel(val, usbmisc->base); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + static int usbmisc_imx53_init(struct imx_usbmisc_data *data) { void __iomem *reg = NULL; @@ -77,6 +169,13 @@ if (data->index > 3) return -EINVAL; + /* Select a 24 MHz reference clock for the PHY */ + reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET; + val = readl(reg); + val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK; + val |= MX53_USB_PLL_DIV_24_MHZ; + writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET); + if (data->disable_oc) { spin_lock_irqsave(&usbmisc->lock, flags); switch (data->index) { @@ -125,9 +224,14 @@ } static const struct usbmisc_ops imx25_usbmisc_ops = { + .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, }; +static const struct usbmisc_ops imx27_usbmisc_ops = { + .init = usbmisc_imx27_init, +}; + static const struct usbmisc_ops imx53_usbmisc_ops = { .init = usbmisc_imx53_init, }; @@ -162,6 +266,18 @@ .data = &imx25_usbmisc_ops, }, { + .compatible = "fsl,imx35-usbmisc", + .data = &imx25_usbmisc_ops, + }, + { + .compatible = "fsl,imx27-usbmisc", + .data = &imx27_usbmisc_ops, + }, + { + .compatible = "fsl,imx51-usbmisc", + .data = &imx53_usbmisc_ops, + }, + { .compatible = "fsl,imx53-usbmisc", .data = &imx53_usbmisc_ops, }, @@ -178,7 +294,8 @@ struct resource *res; struct imx_usbmisc *data; int ret; - struct of_device_id *tmp_dev; + const struct of_device_id *tmp_dev; + struct imx_usbmisc_platform_data *pdata = dev_get_platdata(&pdev->dev); if (usbmisc) return -EBUSY; @@ -194,23 +311,67 @@ if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } + data->ops = NULL; + + tmp_dev = of_match_device(usbmisc_imx_dt_ids, &pdev->dev); + if (tmp_dev) { + /* enable clocks */ + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) { + dev_err(&pdev->dev, + "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); + return PTR_ERR(data->clk); + } + + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed, err=%d\n", ret); + return ret; + } - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "clk_prepare_enable failed, err=%d\n", ret); - return ret; + data->ipgclk = NULL; + data->ahbclk = NULL; + data->perclk = NULL; + + data->ops = tmp_dev->data; + } else if (pdata) { + int i; + + /* enable clocks */ + data->ipgclk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(data->ipgclk)) { + return PTR_ERR(data->ipgclk); + } + clk_prepare_enable(data->ipgclk); + + data->ahbclk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(data->ahbclk)) { + return PTR_ERR(data->ahbclk); + } + clk_prepare_enable(data->ahbclk); + + data->perclk = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(data->perclk)) { + return PTR_ERR(data->perclk); + } + clk_prepare_enable(data->perclk); + + data->clk = NULL; + + for (i = 0; i < ARRAY_SIZE(usbmisc_imx_dt_ids); i++) { + if (!strcmp(usbmisc_imx_dt_ids[i].compatible, + pdata->name)) { + data->ops = usbmisc_imx_dt_ids[i].data; + break; + } + } + } + + if (!data->ops) { + return -ENODEV; } - tmp_dev = (struct of_device_id *) - of_match_device(usbmisc_imx_dt_ids, &pdev->dev); - data->ops = (const struct usbmisc_ops *)tmp_dev->data; usbmisc = data; return 0; @@ -218,7 +379,18 @@ static int usbmisc_imx_remove(struct platform_device *pdev) { - clk_disable_unprepare(usbmisc->clk); + if (NULL != usbmisc->clk) + clk_disable_unprepare(usbmisc->clk); + + if (NULL != usbmisc->perclk) + clk_disable_unprepare(usbmisc->perclk); + + if (NULL != usbmisc->ahbclk) + clk_disable_unprepare(usbmisc->ahbclk); + + if (NULL != usbmisc->ipgclk) + clk_disable_unprepare(usbmisc->ipgclk); + usbmisc = NULL; return 0; } diff -urN linux-3.12.orig/drivers/usb/common/Makefile linux-3.12.work/drivers/usb/common/Makefile --- linux-3.12.orig/drivers/usb/common/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/drivers/usb/common/Makefile 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,6 @@ +# +# Makefile for the usb common parts. +# + +obj-$(CONFIG_USB_COMMON) += usb-common.o +obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o diff -urN linux-3.12.orig/drivers/usb/common/usb-common.c linux-3.12.work/drivers/usb/common/usb-common.c --- linux-3.12.orig/drivers/usb/common/usb-common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/drivers/usb/common/usb-common.c 2014-08-03 17:17:27.819882091 +0200 @@ -0,0 +1,144 @@ +/* + * Provides code common for host and device side USB. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * If either host side (ie. CONFIG_USB=y) or device side USB stack + * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is + * compiled-in as well. Otherwise, if either of the two stacks is + * compiled as module, this file is compiled as module as well. + */ + +#include +#include +#include +#include +#include +#include + +const char *usb_otg_state_string(enum usb_otg_state state) +{ + static const char *const names[] = { + [OTG_STATE_A_IDLE] = "a_idle", + [OTG_STATE_A_WAIT_VRISE] = "a_wait_vrise", + [OTG_STATE_A_WAIT_BCON] = "a_wait_bcon", + [OTG_STATE_A_HOST] = "a_host", + [OTG_STATE_A_SUSPEND] = "a_suspend", + [OTG_STATE_A_PERIPHERAL] = "a_peripheral", + [OTG_STATE_A_WAIT_VFALL] = "a_wait_vfall", + [OTG_STATE_A_VBUS_ERR] = "a_vbus_err", + [OTG_STATE_B_IDLE] = "b_idle", + [OTG_STATE_B_SRP_INIT] = "b_srp_init", + [OTG_STATE_B_PERIPHERAL] = "b_peripheral", + [OTG_STATE_B_WAIT_ACON] = "b_wait_acon", + [OTG_STATE_B_HOST] = "b_host", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNDEFINED"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_otg_state_string); + +static const char *const speed_names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", +}; + +const char *usb_speed_string(enum usb_device_speed speed) +{ + if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) + speed = USB_SPEED_UNKNOWN; + return speed_names[speed]; +} +EXPORT_SYMBOL_GPL(usb_speed_string); + +const char *usb_state_string(enum usb_device_state state) +{ + static const char *const names[] = { + [USB_STATE_NOTATTACHED] = "not attached", + [USB_STATE_ATTACHED] = "attached", + [USB_STATE_POWERED] = "powered", + [USB_STATE_RECONNECTING] = "reconnecting", + [USB_STATE_UNAUTHENTICATED] = "unauthenticated", + [USB_STATE_DEFAULT] = "default", + [USB_STATE_ADDRESS] = "addressed", + [USB_STATE_CONFIGURED] = "configured", + [USB_STATE_SUSPENDED] = "suspended", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_state_string); + +#ifdef CONFIG_OF +static const char *const usb_dr_modes[] = { + [USB_DR_MODE_UNKNOWN] = "", + [USB_DR_MODE_HOST] = "host", + [USB_DR_MODE_PERIPHERAL] = "peripheral", + [USB_DR_MODE_OTG] = "otg", +}; + +/** + * of_usb_get_dr_mode - Get dual role mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets phy interface string from property 'dr_mode', + * and returns the correspondig enum usb_dr_mode + */ +enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) +{ + const char *dr_mode; + int err, i; + + err = of_property_read_string(np, "dr_mode", &dr_mode); + if (err < 0) + return USB_DR_MODE_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++) + if (!strcmp(dr_mode, usb_dr_modes[i])) + return i; + + return USB_DR_MODE_UNKNOWN; +} +EXPORT_SYMBOL_GPL(of_usb_get_dr_mode); + +/** + * of_usb_get_maximum_speed - Get maximum requested speed for a given USB + * controller. + * @np: Pointer to the given device_node + * + * The function gets the maximum speed string from property "maximum-speed", + * and returns the corresponding enum usb_device_speed. + */ +enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) +{ + const char *maximum_speed; + int err; + int i; + + err = of_property_read_string(np, "maximum-speed", &maximum_speed); + if (err < 0) + return USB_SPEED_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(speed_names); i++) + if (strcmp(maximum_speed, speed_names[i]) == 0) + return i; + + return USB_SPEED_UNKNOWN; +} +EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); + +#endif + +MODULE_LICENSE("GPL"); diff -urN linux-3.12.orig/drivers/usb/common/usb-otg-fsm.c linux-3.12.work/drivers/usb/common/usb-otg-fsm.c --- linux-3.12.orig/drivers/usb/common/usb-otg-fsm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/drivers/usb/common/usb-otg-fsm.c 2014-08-03 17:17:27.823882107 +0200 @@ -0,0 +1,367 @@ +/* + * OTG Finite State Machine from OTG spec + * + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", + fsm->protocol, protocol); + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = otg_start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = otg_start_gadget(fsm, 0); + if (ret) + return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = otg_start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = otg_start_gadget(fsm, 1); + if (ret) + return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed; + +/* Called when leaving a state. Do state clean up jobs here */ +static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, B_SE0_SRP); + fsm->b_se0_srp = 0; + fsm->adp_sns = 0; + fsm->adp_prb = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->data_pulse = 0; + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, B_ASE0_BRST); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + fsm->adp_prb = 0; + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, A_WAIT_VRISE); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, A_WAIT_BCON); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, A_WAIT_ENUM); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, A_AIDL_BDIS); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req_inf = 0; + break; + case OTG_STATE_A_PERIPHERAL: + otg_del_timer(fsm, A_BIDL_ADIS); + fsm->a_bidl_adis_tmout = 0; + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, A_WAIT_VFALL); + fsm->a_wait_vfall_tmout = 0; + otg_del_timer(fsm, A_WAIT_VRISE); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + +/* Called when entering a state */ +static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->otg->phy->state == new_state) + return 0; + VDBG("Set state: %s\n", usb_otg_state_string(new_state)); + otg_leave_state(fsm, fsm->otg->phy->state); + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + /* + * Driver is responsible for starting ADP probing + * if ADP sensing times out. + */ + otg_start_adp_sns(fsm); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, B_SE0_SRP); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, B_SRP_FAIL); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, B_ASE0_BRST); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->otg->host, + fsm->otg->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_start_adp_prb(fsm); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, A_WAIT_BCON); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* + * When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req_inf) + otg_add_timer(fsm, A_WAIT_ENUM); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, A_AIDL_BDIS); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + otg_add_timer(fsm, A_BIDL_ADIS); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, A_WAIT_VFALL); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->otg->phy->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + + mutex_lock(&fsm->lock); + + state = fsm->otg->phy->state; + state_changed = 0; + /* State machine state change judgement */ + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d\n", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->otg->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) && + fsm->b_ssend_srp && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && fsm->otg-> + gadget->b_hnp_enable && fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn || fsm->test_device) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || + fsm->a_srp_det || fsm->adp_change || fsm->power_up)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->id || fsm->a_bus_drop || + fsm->a_wait_vrise_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && + fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (!fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->a_bidl_adis_tmout || fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->a_wait_vfall_tmout) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + mutex_unlock(&fsm->lock); + + VDBG("quit statemachine, changed = %d\n", state_changed); + return state_changed; +} +EXPORT_SYMBOL_GPL(otg_statemachine); diff -urN linux-3.12.orig/drivers/usb/core/Kconfig linux-3.12.work/drivers/usb/core/Kconfig --- linux-3.12.orig/drivers/usb/core/Kconfig 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/core/Kconfig 2014-08-03 17:17:27.823882107 +0200 @@ -89,3 +89,12 @@ and software costs by not supporting external hubs. So are "Embedded Hosts" that don't offer OTG support. +config USB_OTG_FSM + tristate "USB 2.0 OTG FSM implementation" + depends on USB + select USB_OTG + select USB_PHY + help + Implements OTG Finite State Machine as specified in On-The-Go + and Embedded Host Supplement to the USB Revision 2.0 Specification. + diff -urN linux-3.12.orig/drivers/usb/Makefile linux-3.12.work/drivers/usb/Makefile --- linux-3.12.orig/drivers/usb/Makefile 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/Makefile 2014-08-03 17:17:27.823882107 +0200 @@ -57,4 +57,4 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_GADGET) += gadget/ -obj-$(CONFIG_USB_COMMON) += usb-common.o +obj-$(CONFIG_USB_COMMON) += common/ diff -urN linux-3.12.orig/drivers/usb/phy/Kconfig linux-3.12.work/drivers/usb/phy/Kconfig --- linux-3.12.orig/drivers/usb/phy/Kconfig 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/phy/Kconfig 2014-08-03 17:17:27.823882107 +0200 @@ -20,7 +20,7 @@ config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_FSL_USB2 && PM_RUNTIME + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME select USB_OTG select USB_PHY help diff -urN linux-3.12.orig/drivers/usb/usb-common.c linux-3.12.work/drivers/usb/usb-common.c --- linux-3.12.orig/drivers/usb/usb-common.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/usb/usb-common.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,144 +0,0 @@ -/* - * Provides code common for host and device side USB. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - * If either host side (ie. CONFIG_USB=y) or device side USB stack - * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is - * compiled-in as well. Otherwise, if either of the two stacks is - * compiled as module, this file is compiled as module as well. - */ - -#include -#include -#include -#include -#include -#include - -const char *usb_otg_state_string(enum usb_otg_state state) -{ - static const char *const names[] = { - [OTG_STATE_A_IDLE] = "a_idle", - [OTG_STATE_A_WAIT_VRISE] = "a_wait_vrise", - [OTG_STATE_A_WAIT_BCON] = "a_wait_bcon", - [OTG_STATE_A_HOST] = "a_host", - [OTG_STATE_A_SUSPEND] = "a_suspend", - [OTG_STATE_A_PERIPHERAL] = "a_peripheral", - [OTG_STATE_A_WAIT_VFALL] = "a_wait_vfall", - [OTG_STATE_A_VBUS_ERR] = "a_vbus_err", - [OTG_STATE_B_IDLE] = "b_idle", - [OTG_STATE_B_SRP_INIT] = "b_srp_init", - [OTG_STATE_B_PERIPHERAL] = "b_peripheral", - [OTG_STATE_B_WAIT_ACON] = "b_wait_acon", - [OTG_STATE_B_HOST] = "b_host", - }; - - if (state < 0 || state >= ARRAY_SIZE(names)) - return "UNDEFINED"; - - return names[state]; -} -EXPORT_SYMBOL_GPL(usb_otg_state_string); - -static const char *const speed_names[] = { - [USB_SPEED_UNKNOWN] = "UNKNOWN", - [USB_SPEED_LOW] = "low-speed", - [USB_SPEED_FULL] = "full-speed", - [USB_SPEED_HIGH] = "high-speed", - [USB_SPEED_WIRELESS] = "wireless", - [USB_SPEED_SUPER] = "super-speed", -}; - -const char *usb_speed_string(enum usb_device_speed speed) -{ - if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) - speed = USB_SPEED_UNKNOWN; - return speed_names[speed]; -} -EXPORT_SYMBOL_GPL(usb_speed_string); - -const char *usb_state_string(enum usb_device_state state) -{ - static const char *const names[] = { - [USB_STATE_NOTATTACHED] = "not attached", - [USB_STATE_ATTACHED] = "attached", - [USB_STATE_POWERED] = "powered", - [USB_STATE_RECONNECTING] = "reconnecting", - [USB_STATE_UNAUTHENTICATED] = "unauthenticated", - [USB_STATE_DEFAULT] = "default", - [USB_STATE_ADDRESS] = "addresssed", - [USB_STATE_CONFIGURED] = "configured", - [USB_STATE_SUSPENDED] = "suspended", - }; - - if (state < 0 || state >= ARRAY_SIZE(names)) - return "UNKNOWN"; - - return names[state]; -} -EXPORT_SYMBOL_GPL(usb_state_string); - -#ifdef CONFIG_OF -static const char *const usb_dr_modes[] = { - [USB_DR_MODE_UNKNOWN] = "", - [USB_DR_MODE_HOST] = "host", - [USB_DR_MODE_PERIPHERAL] = "peripheral", - [USB_DR_MODE_OTG] = "otg", -}; - -/** - * of_usb_get_dr_mode - Get dual role mode for given device_node - * @np: Pointer to the given device_node - * - * The function gets phy interface string from property 'dr_mode', - * and returns the correspondig enum usb_dr_mode - */ -enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) -{ - const char *dr_mode; - int err, i; - - err = of_property_read_string(np, "dr_mode", &dr_mode); - if (err < 0) - return USB_DR_MODE_UNKNOWN; - - for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++) - if (!strcmp(dr_mode, usb_dr_modes[i])) - return i; - - return USB_DR_MODE_UNKNOWN; -} -EXPORT_SYMBOL_GPL(of_usb_get_dr_mode); - -/** - * of_usb_get_maximum_speed - Get maximum requested speed for a given USB - * controller. - * @np: Pointer to the given device_node - * - * The function gets the maximum speed string from property "maximum-speed", - * and returns the corresponding enum usb_device_speed. - */ -enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) -{ - const char *maximum_speed; - int err; - int i; - - err = of_property_read_string(np, "maximum-speed", &maximum_speed); - if (err < 0) - return USB_SPEED_UNKNOWN; - - for (i = 0; i < ARRAY_SIZE(speed_names); i++) - if (strcmp(maximum_speed, speed_names[i]) == 0) - return i; - - return USB_SPEED_UNKNOWN; -} -EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); - -#endif - -MODULE_LICENSE("GPL"); diff -urN linux-3.12.orig/drivers/video/imxfb.c linux-3.12.work/drivers/video/imxfb.c --- linux-3.12.orig/drivers/video/imxfb.c 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/drivers/video/imxfb.c 2014-08-03 17:17:27.823882107 +0200 @@ -608,8 +608,8 @@ fbi->lcd_power(0); clk_disable_unprepare(fbi->clk_per); - clk_disable_unprepare(fbi->clk_ipg); clk_disable_unprepare(fbi->clk_ahb); + clk_disable_unprepare(fbi->clk_ipg); fbi->enabled = false; writel(0, fbi->regs + LCDC_RMCR); diff -urN linux-3.12.orig/include/linux/dma-mapping.h linux-3.12.work/include/linux/dma-mapping.h --- linux-3.12.orig/include/linux/dma-mapping.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/include/linux/dma-mapping.h 2014-08-03 17:17:27.823882107 +0200 @@ -8,6 +8,12 @@ #include #include +/* + * A dma_addr_t can hold any valid DMA or bus address for the platform. + * It can be given to a device to use as a DMA source or target. A CPU cannot + * reference a dma_addr_t directly because there may be translation between + * its physical address space and the bus address space. + */ struct dma_map_ops { void* (*alloc)(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp, @@ -97,6 +103,30 @@ } #endif +/* + * Set both the DMA mask and the coherent DMA mask to the same thing. + * Note that we don't check the return value from dma_set_coherent_mask() + * as the DMA API guarantees that the coherent DMA mask can be set to + * the same or smaller than the streaming DMA mask. + */ +static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) +{ + int rc = dma_set_mask(dev, mask); + if (rc == 0) + dma_set_coherent_mask(dev, mask); + return rc; +} + +/* + * Similar to the above, except it deals with the case where the device + * does not have dev->dma_mask appropriately setup. + */ +static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) +{ + dev->dma_mask = &dev->coherent_dma_mask; + return dma_set_mask_and_coherent(dev, mask); +} + extern u64 dma_get_required_mask(struct device *dev); static inline unsigned int dma_get_max_seg_size(struct device *dev) diff -urN linux-3.12.orig/include/linux/usb/chipidea.h linux-3.12.work/include/linux/usb/chipidea.h --- linux-3.12.orig/include/linux/usb/chipidea.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/include/linux/usb/chipidea.h 2014-08-03 17:17:27.823882107 +0200 @@ -25,6 +25,7 @@ */ #define CI_HDRC_DUAL_ROLE_NOT_OTG BIT(4) #define CI_HDRC_IMX28_WRITE_FIX BIT(5) +#define CI_HDRC_FORCE_FULLSPEED BIT(6) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 @@ -32,6 +33,20 @@ struct regulator *reg_vbus; }; +struct imx_usbmisc_platform_data { + char name[32]; +}; + +struct imx_usb_platform_data { + char name[32]; + enum usb_phy_interface phy_mode; + enum usb_dr_mode dr_mode; + const struct usb_phy * phy; + int usbmisc_index; + unsigned int usbmisc_disable_oc:1; /* over current detect disabled */ + unsigned int usbmisc_evdo:1; /* set external vbus divider option */ +}; + /* Default offset of capability registers */ #define DEF_CAPOFFSET 0x100 diff -urN linux-3.12.orig/include/linux/usb/gadget.h linux-3.12.work/include/linux/usb/gadget.h --- linux-3.12.orig/include/linux/usb/gadget.h 2014-02-22 22:32:50.000000000 +0100 +++ linux-3.12.work/include/linux/usb/gadget.h 2014-08-03 17:17:27.823882107 +0200 @@ -148,6 +148,9 @@ * @maxpacket:The maximum packet size used on this endpoint. The initial * value can sometimes be reduced (hardware allowing), according to * the endpoint descriptor used to configure the endpoint. + * @maxpacket_limit:The maximum packet size value which can be handled by this + * endpoint. It's set once by UDC driver when endpoint is initialized, and + * should not be changed. Should not be confused with maxpacket. * @max_streams: The maximum number of streams supported * by this EP (0 - 16, actual number is 2^n) * @mult: multiplier, 'mult' value for SS Isoc EPs @@ -171,6 +174,7 @@ const struct usb_ep_ops *ops; struct list_head ep_list; unsigned maxpacket:16; + unsigned maxpacket_limit:16; unsigned max_streams:16; unsigned mult:2; unsigned maxburst:5; @@ -182,6 +186,21 @@ /*-------------------------------------------------------------------------*/ /** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function shoud be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} + +/** * usb_ep_enable - configure endpoint, making it usable * @ep:the endpoint being configured. may not be the endpoint named "ep0". * drivers discover endpoints through the ep_list of a usb_gadget. diff -urN linux-3.12.orig/include/linux/usb/otg-fsm.h linux-3.12.work/include/linux/usb/otg-fsm.h --- linux-3.12.orig/include/linux/usb/otg-fsm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.12.work/include/linux/usb/otg-fsm.h 2014-08-03 17:17:27.823882107 +0200 @@ -0,0 +1,244 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_USB_OTG_FSM_H +#define __LINUX_USB_OTG_FSM_H + +#include +#include + +#undef VERBOSE + +#ifdef VERBOSE +#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \ + __func__, ## args) +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do {} while (0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + +enum otg_fsm_timer { + /* Standard OTG timers */ + A_WAIT_VRISE, + A_WAIT_VFALL, + A_WAIT_BCON, + A_AIDL_BDIS, + B_ASE0_BRST, + A_BIDL_ADIS, + + /* Auxiliary timers */ + B_SE0_SRP, + B_SRP_FAIL, + A_WAIT_ENUM, + + NUM_OTG_FSM_TIMERS, +}; + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* Input */ + int id; + int adp_change; + int power_up; + int test_device; + int a_bus_drop; + int a_bus_req; + int a_srp_det; + int a_vbus_vld; + int b_conn; + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int b_bus_req; + int b_se0_srp; + int b_ssend_srp; + int b_sess_vld; + /* Auxilary inputs */ + int a_sess_vld; + int b_bus_resume; + int b_bus_suspend; + + /* Output */ + int data_pulse; + int drv_vbus; + int loc_conn; + int loc_sof; + int adp_prb; + int adp_sns; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + int a_clr_err; + + /* Informative variables */ + int a_bus_drop_inf; + int a_bus_req_inf; + int a_clr_err_inf; + int b_bus_req_inf; + /* Auxilary informative variables */ + int a_suspend_req_inf; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_vfall_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + int a_bidl_adis_tmout; + + struct otg_fsm_ops *ops; + struct usb_otg *otg; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + struct mutex lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)(struct otg_fsm *fsm, int on); + void (*drv_vbus)(struct otg_fsm *fsm, int on); + void (*loc_conn)(struct otg_fsm *fsm, int on); + void (*loc_sof)(struct otg_fsm *fsm, int on); + void (*start_pulse)(struct otg_fsm *fsm); + void (*start_adp_prb)(struct otg_fsm *fsm); + void (*start_adp_sns)(struct otg_fsm *fsm); + void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); + void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->chrg_vbus) + return -EOPNOTSUPP; + fsm->ops->chrg_vbus(fsm, on); + return 0; +} + +static inline int otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->drv_vbus) + return -EOPNOTSUPP; + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(fsm, on); + } + return 0; +} + +static inline int otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->loc_conn) + return -EOPNOTSUPP; + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(fsm, on); + } + return 0; +} + +static inline int otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->loc_sof) + return -EOPNOTSUPP; + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(fsm, on); + } + return 0; +} + +static inline int otg_start_pulse(struct otg_fsm *fsm) +{ + if (!fsm->ops->start_pulse) + return -EOPNOTSUPP; + if (!fsm->data_pulse) { + fsm->data_pulse = 1; + fsm->ops->start_pulse(fsm); + } + return 0; +} + +static inline int otg_start_adp_prb(struct otg_fsm *fsm) +{ + if (!fsm->ops->start_adp_prb) + return -EOPNOTSUPP; + if (!fsm->adp_prb) { + fsm->adp_sns = 0; + fsm->adp_prb = 1; + fsm->ops->start_adp_prb(fsm); + } + return 0; +} + +static inline int otg_start_adp_sns(struct otg_fsm *fsm) +{ + if (!fsm->ops->start_adp_sns) + return -EOPNOTSUPP; + if (!fsm->adp_sns) { + fsm->adp_sns = 1; + fsm->ops->start_adp_sns(fsm); + } + return 0; +} + +static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) +{ + if (!fsm->ops->add_timer) + return -EOPNOTSUPP; + fsm->ops->add_timer(fsm, timer); + return 0; +} + +static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) +{ + if (!fsm->ops->del_timer) + return -EOPNOTSUPP; + fsm->ops->del_timer(fsm, timer); + return 0; +} + +static inline int otg_start_host(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->start_host) + return -EOPNOTSUPP; + return fsm->ops->start_host(fsm, on); +} + +static inline int otg_start_gadget(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->start_gadget) + return -EOPNOTSUPP; + return fsm->ops->start_gadget(fsm, on); +} + +int otg_statemachine(struct otg_fsm *fsm); + +#endif /* __LINUX_USB_OTG_FSM_H */