From stanley_chang at realtek.com Wed Oct 7 04:50:33 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:33 +0800 Subject: [PATCH 13/15] arm64: dts: realtek: rtd139x-usb: Add rtd139x type c module nodes In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-14-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi index 112f733bf277..89a89b61117f 100644 --- a/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi @@ -52,6 +52,46 @@ dwc3_drd at 98020000 { dr_mode = "host"; /*otg, host, peripheral*/ snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend }; + + dwc3_type_c: rtk_dwc3_drd_type_c at 98007220 { + compatible = "Realtek,dwc3-type_c"; + reg = <0x98007220 0x20>; + interrupts = <0 60 4>; + //debug; /*to enable debug log*/ + delay_probe_work; /*To delay probe work*/ + boot_check_time = <30000>; /*ms (At boot Device switch Host time)*/ + filter_config_channel_signal; /* A sw debounce to filter cc signal */ + status ="okay"; + + default_revision = <0xA00>; + A00 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0xC>; + cc1_rp_36k_code = <0xB>; + cc1_rp_12k_code = <0xE>; + cc1_rd_code = <0x10>; + cc1_vref_ufp = /bits/ 8 + <0x1 0x0 0x0>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x7 0x0 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x0>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x0 0x0 0x0>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0xB>; + cc2_rp_36k_code = <0x9>; + cc2_rp_12k_code = <0xE>; + cc2_rd_code = <0xF>; + cc2_vref_ufp = /bits/ 8 + <0x3 0x2 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x1 0x3 0x2>; /*<2p6v,0p8v,0p2v>*/ + }; + }; }; dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:21 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:21 +0800 Subject: [PATCH 01/15] usb: dwc3: core: remove the static property of function dwc3_core_soft_reset In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-2-stanley_chang@realtek.com> dwc3-rtk driver used the function to reset dwc3 core. Signed-off-by: Stanley Chang --- drivers/usb/dwc3/core.c | 2 +- drivers/usb/dwc3/core.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e842e926b535..f50324df38ed 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -221,7 +221,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static int dwc3_core_soft_reset(struct dwc3 *dwc) +int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; int retries = 1000; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6b9eb777085f..2782c2930370 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1386,6 +1386,7 @@ struct dwc3_gadget_ep_cmd_params { void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); +int dwc3_core_soft_reset(struct dwc3 *dwc); /* check whether we are on the DWC_usb3 core */ static inline bool dwc3_is_usb3(struct dwc3 *dwc) -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:35 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:35 +0800 Subject: [PATCH 15/15] arm64: dts: realtek: rtd13xx-usb: Add rtd13xx type c module nodes In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-16-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi index d4419cad4eab..d296b4e8c197 100644 --- a/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi @@ -222,5 +222,45 @@ dwc3_u3drd at 981f0000 { status = "okay"; }; + + rtk_dwc3_type_c at 98007220 { + compatible = "realtek,dwc3-type_c"; + reg = <0x98007220 0x20>; + interrupts = <0 60 4>; + //debug; /*to enable debug log*/ + delay_probe_work; /*To delay probe work*/ + //boot_check_time = <(-1)>; /*ms (At boot Device switch Host time)*/ + boot_check_time = <30000>; /*ms (At boot Device switch Host time)*/ + status = "okay"; + + default_revision = <0xA00>; + A00 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0xC>; + cc1_rp_36k_code = <0xF>; + cc1_rp_12k_code = <0xE>; + cc1_rd_code = <0x11>; + cc1_vref_ufp = /bits/ 8 + <0x7 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0xC>; + cc2_rp_36k_code = <0xF>; + cc2_rp_12k_code = <0xE>; + cc2_rd_code = <0xF>; + cc2_vref_ufp = /bits/ 8 + <0x8 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + }; + }; }; }; -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:20 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:20 +0800 Subject: [PATCH 00/15] Realtek DHC SoCs USB DWC3 DRD and type c drivers Message-ID: <20201007085035.30477-1-stanley_chang@realtek.com> Hi Andreas, This series is continuous of the series of Realtek DHC SoCs USB module driver. The following details: (1) A driver dwc3-rtk-drd is used to dynamic switch host/device mode of dwc3 drd module. (2) The type c driver realizes the cc pin status detection. The driver can switch the USB host/device mode according to the type c state. These drivers support the DHC SoC series included 129x, 139x, 16xx, 13xx. Note: Realtek DHC SoCs USB module driver http://lists.infradead.org/pipermail/linux-realtek-soc/2020-September/000095.html Thanks, Stanley Stanley Chang (15): usb: dwc3: core: remove the static property of function dwc3_core_soft_reset usb: dwc3: host: clear xhci point when host be removed. usb: dwc3: gadget: clear gadget.udc point when gadget be removed usb: dwc3: dwc3-rtk: support the dual role dynamic switch doc: dt: bindings: usb: dwc3-rtk: Add the property drd_mode arm64: dts: realtek: rtd13xx-usb: add the property of drd mode arm64: dts: realtek: rtd16xx-usb: add the property of drd mode arm64: dts: realtek: rtd139x-usb: add the property of drd mode arm64: dts: realtek: rtd129x-usb: add the property of drd mode usb: dwc3: dwc3-rtk-type_c: Add a rtk type c driver. doc: dt: bindings: usb: dwc3-rtk-type_c: Add the dwc3-rtk-type_c driver doc. arm64: dts: realtek: rtd129x-usb: Add rtd129x type c module nodes arm64: dts: realtek: rtd139x-usb: Add rtd139x type c module nodes arm64: dts: realtek: rtd16xx-usb: Add rtd16xx type c module nodes arm64: dts: realtek: rtd13xx-usb: Add rtd13xx type c module nodes .../bindings/usb/dwc3-rtk-type_c.yaml | 90 + .../devicetree/bindings/usb/dwc3-rtk.yaml | 4 + arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi | 68 + arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi | 41 + arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi | 42 + arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi | 40 + drivers/usb/dwc3/Kconfig | 15 + drivers/usb/dwc3/Makefile | 2 + drivers/usb/dwc3/core.c | 2 +- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/dwc3-rtk-drd.c | 207 ++ drivers/usb/dwc3/dwc3-rtk-drd.h | 26 + drivers/usb/dwc3/dwc3-rtk-type_c.c | 2243 +++++++++++++++++ drivers/usb/dwc3/dwc3-rtk.c | 168 ++ drivers/usb/dwc3/dwc3-rtk.h | 1 + drivers/usb/dwc3/gadget.c | 2 + drivers/usb/dwc3/host.c | 1 + 17 files changed, 2952 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/usb/dwc3-rtk-type_c.yaml create mode 100644 drivers/usb/dwc3/dwc3-rtk-drd.c create mode 100644 drivers/usb/dwc3/dwc3-rtk-drd.h create mode 100644 drivers/usb/dwc3/dwc3-rtk-type_c.c -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:23 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:23 +0800 Subject: [PATCH 03/15] usb: dwc3: gadget: clear gadget.udc point when gadget be removed In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-4-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- drivers/usb/dwc3/gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a9aba716bf80..a1a1ea336408 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3415,6 +3415,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc) kfree(dwc->setup_buf); dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); + + dwc->gadget.udc = NULL; } int dwc3_gadget_suspend(struct dwc3 *dwc) -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:25 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:25 +0800 Subject: [PATCH 05/15] doc: dt: bindings: usb: dwc3-rtk: Add the property drd_mode In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-6-stanley_chang@realtek.com> This property is about enabled dwc3 drd_mode dynamic switch. Signed-off-by: Stanley Chang --- Documentation/devicetree/bindings/usb/dwc3-rtk.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml b/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml index ba079c17f65c..c2f495049ed6 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml +++ b/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml @@ -28,6 +28,9 @@ properties: reg: maxItems: 1 + drd_mode: + A flag to support drd mode for dynamic switch host or device mode. + required: - compatible - "#address-cells" @@ -44,6 +47,7 @@ examples: #size-cells = <1>; ranges; delay_probe_work; //To delay probe work + drd_mode; status = "okay"; dwc3_u3drd at 981f0000 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:31 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:31 +0800 Subject: [PATCH 11/15] doc: dt: bindings: usb: dwc3-rtk-type_c: Add the dwc3-rtk-type_c driver doc. In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-12-stanley_chang@realtek.com> Realtek DHC SoCs embeds a simple type c module to detect cc pin status. Signed-off-by: Stanley Chang --- .../bindings/usb/dwc3-rtk-type_c.yaml | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/dwc3-rtk-type_c.yaml diff --git a/Documentation/devicetree/bindings/usb/dwc3-rtk-type_c.yaml b/Documentation/devicetree/bindings/usb/dwc3-rtk-type_c.yaml new file mode 100644 index 000000000000..f211d0b52ea0 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/dwc3-rtk-type_c.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020 Realtek Semiconductor Corporation +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/dwc3-rtk-type_c.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Realtek DWC3 USB Type C module driver + +maintainers: + - Stanley Chang + +description: | + The Realtek DHC Soc embeds a simple type c module to detect cc pin status. + This driver can switch USB host/device mode dynamically based on the type c + status. + +properties: + compatible: + enum: + - realtek,dwc3-type_c + + ranges: true + + reg: + maxItems: 1 + + boot_check_time: + To set a time for checking the device mode on boot. + + debug: + A debug flag to print debug log. + + default_revision: + Set the default version of used parameter. + + + A00: + The parameter node for type c module. + +required: + - compatible + - "#address-cells" + - "#size-cells" + - ranges + - reg + - default_revision + - A00 + +examples: + - | + + rtk_dwc3_type_c at 98007220 { + compatible = "realtek,dwc3-type_c"; + reg = <0x98007220 0x20>; + interrupts = <0 60 4>; + //debug; /*to enable debug log*/ + delay_probe_work; /*To delay probe work*/ + boot_check_time = <30000>; /*ms (At boot Device switch Host time)*/ + status = "okay"; + + default_revision = <0xA00>; + A00 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0xC>; + cc1_rp_36k_code = <0xF>; + cc1_rp_12k_code = <0xE>; + cc1_rd_code = <0x11>; + cc1_vref_ufp = /bits/ 8 + <0x7 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0xC>; + cc2_rp_36k_code = <0xF>; + cc2_rp_12k_code = <0xE>; + cc2_rd_code = <0xF>; + cc2_vref_ufp = /bits/ 8 + <0x8 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + }; + }; -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:22 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:22 +0800 Subject: [PATCH 02/15] usb: dwc3: host: clear xhci point when host be removed. In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-3-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- drivers/usb/dwc3/host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 5567ed2cddbe..78f470a2cddf 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -128,4 +128,5 @@ int dwc3_host_init(struct dwc3 *dwc) void dwc3_host_exit(struct dwc3 *dwc) { platform_device_unregister(dwc->xhci); + dwc->xhci = NULL; } -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:26 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:26 +0800 Subject: [PATCH 06/15] arm64: dts: realtek: rtd13xx-usb: add the property of drd mode In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-7-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi index 6b55a873c941..d4419cad4eab 100644 --- a/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi @@ -50,6 +50,7 @@ dwc3_u2drd: rtk_dwc3_u2drd at 98013200 { #size-cells = <1>; ranges; delay_probe_work; //To delay probe work + drd_mode; status = "okay"; dwc3_u2drd at 98020000 { @@ -206,6 +207,7 @@ dwc3_u3drd: rtk_dwc3_u3drd at 98013E00 { #size-cells = <1>; ranges; delay_probe_work; //To delay probe work + drd_mode; status = "okay"; dwc3_u3drd at 981f0000 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:24 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:24 +0800 Subject: [PATCH 04/15] usb: dwc3: dwc3-rtk: support the dual role dynamic switch In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-5-stanley_chang@realtek.com> Realtek DHC SoC embeds a dual role DWC3 USB core IP. The function will support dynamic switch host or device mode. Signed-off-by: Stanley Chang --- drivers/usb/dwc3/Kconfig | 8 ++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-rtk-drd.c | 207 ++++++++++++++++++++++++++++++++ drivers/usb/dwc3/dwc3-rtk-drd.h | 26 ++++ drivers/usb/dwc3/dwc3-rtk.c | 168 ++++++++++++++++++++++++++ drivers/usb/dwc3/dwc3-rtk.h | 1 + 6 files changed, 411 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-rtk-drd.c create mode 100644 drivers/usb/dwc3/dwc3-rtk-drd.h diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 5353d05d9f6c..a04d280c9835 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -61,6 +61,14 @@ config USB_DWC3_RTK RTK SoCs with DesignWare Core USB3 IP inside, say 'Y' or 'M' if you have such device. +config USB_DWC3_RTK_DUAL_ROLE + tristate "Realtek DWC3 Platform Dual Role Driver" + default USB_DWC3_RTK if (USB_DWC3_DUAL_ROLE) + depends on USB_DWC3_DUAL_ROLE + help + RTK SoCs with DesignWare Core USB3 IP to suport drd mode, + Support Realtek dwc3 drd mode to dynamical host/device switch. + say 'Y' or 'M' if you have such device. config USB_DWC3_OMAP tristate "Texas Instruments OMAP5 and similar Platforms" diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index d57cb12d000a..25f8823e8811 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o dwc3-rtk-debugfs.o +obj-$(CONFIG_USB_DWC3_RTK_DUAL_ROLE) += dwc3-rtk-drd.o diff --git a/drivers/usb/dwc3/dwc3-rtk-drd.c b/drivers/usb/dwc3/dwc3-rtk-drd.c new file mode 100644 index 000000000000..9e18bfadb96a --- /dev/null +++ b/drivers/usb/dwc3/dwc3-rtk-drd.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * dwc3-rtk-drd.c - Realtek DWC3 Specific Glue layer + * + * Copyright (C) 2017 Realtek Semiconductor Corporation + * + */ + +#include "core.h" +#include "gadget.h" +#include "io.h" + +static int dwc3_check_drd_mode(struct dwc3 *dwc) +{ + int mode = USB_DR_MODE_UNKNOWN; + + if (dwc->xhci) { + mode = USB_DR_MODE_HOST; + dev_dbg(dwc->dev, "%s Now is host\n", __func__); + } else if (dwc->gadget.udc) { + mode = USB_DR_MODE_PERIPHERAL; + dev_dbg(dwc->dev, "%s Now is gadget\n", __func__); + } + + return mode; +} + +static int rtk_dwc3_drd_core_soft_reset(struct dwc3 *dwc) +{ + int ret; + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + dwc3_writel(dwc->regs, DWC3_GCTL, reg | DWC3_GCTL_DSBLCLKGTNG); + + ret = dwc3_core_soft_reset(dwc); + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + return ret; +} + +static int rtk_dwc3_drd_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + + evt = dwc->ev_buf; + evt->lpos = 0; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_SIZE(evt->length)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + + return 0; +} + +static void rtk_dwc3_set_mode(struct dwc3 *dwc, u32 mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + reg |= DWC3_GCTL_PRTCAPDIR(mode); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +int dwc3_drd_to_host(struct dwc3 *dwc) +{ + int ret; + unsigned long timeout; + u32 reg; + + dev_info(dwc->dev, "%s START....", __func__); + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_PERIPHERAL) { + dwc3_gadget_exit(dwc); + } + /* Do wmb */ + wmb(); + + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_HOST) { + dev_info(dwc->dev, "%s Now is host", __func__); + return 0; + } + + /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); + + ret = rtk_dwc3_drd_core_soft_reset(dwc); + if (ret) { + dev_err(dwc->dev, "soft reset failed\n"); + goto err0; + } + + ret = rtk_dwc3_drd_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err0; + } + + rtk_dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); + + ret = dwc3_host_init(dwc); + if (ret) + dev_err(dwc->dev, "failed to init host\n"); + +err0: + dev_info(dwc->dev, "%s END....", __func__); + return ret; +} + +int dwc3_drd_to_device(struct dwc3 *dwc) +{ + int ret; + unsigned long timeout, flags = 0; + u32 reg; + + dev_info(dwc->dev, "%s START....", __func__); + + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_HOST) { + dev_info(dwc->dev, "%s dwc3_host_exit", __func__); + dwc3_host_exit(dwc); + } + /* Do wmb */ + wmb(); + + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_PERIPHERAL) { + dev_info(dwc->dev, "%s Now is gadget", __func__); + return 0; + } + + /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); + + ret = rtk_dwc3_drd_core_soft_reset(dwc); + if (ret) { + dev_err(dwc->dev, "soft reset failed\n"); + goto err0; + } + + spin_lock_irqsave(&dwc->lock, flags); + + ret = rtk_dwc3_drd_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + spin_unlock_irqrestore(&dwc->lock, flags); + goto err0; + } + + rtk_dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + + spin_unlock_irqrestore(&dwc->lock, flags); + + ret = dwc3_gadget_init(dwc); + if (ret) + dev_err(dwc->dev, "failed to init gadget\n"); + +err0: + dev_info(dwc->dev, "%s END....", __func__); + return ret; +} + +int dwc3_drd_to_stop_all(struct dwc3 *dwc) +{ + int ret = 0; + + dev_info(dwc->dev, "%s START....", __func__); + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_HOST) + dwc3_host_exit(dwc); + if (dwc3_check_drd_mode(dwc) == USB_DR_MODE_PERIPHERAL) + dwc3_gadget_exit(dwc); + + /* Do wmb */ + wmb(); + dev_info(dwc->dev, "%s END....", __func__); + return ret; +} diff --git a/drivers/usb/dwc3/dwc3-rtk-drd.h b/drivers/usb/dwc3/dwc3-rtk-drd.h new file mode 100644 index 000000000000..997d5c3fc301 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-rtk-drd.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * dwc3-rtk-drd.h - Realtek DWC3 Specific Glue layer + * + * Copyright (C) 2017 Realtek Semiconductor Corporation + * + */ + +#ifndef __DRIVERS_USB_DWC3_RTK_DRD_H +#define __DRIVERS_USB_DWC3_RTK_DRD_H + +struct dwc3; + +int dwc3_drd_to_host(struct dwc3 *dwc); +int dwc3_drd_to_device(struct dwc3 *dwc); +int dwc3_drd_to_stop_all(struct dwc3 *dwc); + +struct dwc3_rtk; + +int dwc3_rtk_get_dr_mode(struct dwc3_rtk *rtk); +int dwc3_rtk_set_dr_mode(struct dwc3_rtk *rtk, int dr_mode); +bool dwc3_rtk_is_connected_on_device_mode(struct dwc3_rtk *dwc3_rtk); +bool dwc3_rtk_is_support_drd_mode(struct dwc3_rtk *dwc3_rtk); + +#endif /* __DRIVERS_USB_DWC3_RTK_CORE_H */ + diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index 3291ee401a64..3fc31cfe6f57 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -19,6 +19,7 @@ #include #include "dwc3-rtk.h" +#include "dwc3-rtk-drd.h" #include "core.h" #include "io.h" @@ -41,6 +42,48 @@ static const struct soc_device_attribute rtk_soc_hercules[] = { } }; +bool dwc3_rtk_is_support_drd_mode(struct dwc3_rtk *dwc3_rtk) +{ + if (!dwc3_rtk) { + pr_err("%s: ERROR: dwc3_rtk is NULL!", __func__); + return false; + } + + return dwc3_rtk->support_drd_mode; +} + +bool dwc3_rtk_is_connected_on_device_mode(struct dwc3_rtk *dwc3_rtk) +{ + bool connected = true; + int no_host_connect = 0; + int no_run_gadget = 0; + u32 dsts, dctl; + + if (!dwc3_rtk) { + pr_err("%s: ERROR: dwc3_rtk is NULL!", __func__); + return connected; + } + if (dwc3_rtk->cur_dr_mode != USB_DR_MODE_PERIPHERAL) { + dev_info(dwc3_rtk->dev, + "%s: Error: not in device mode (cur_dr_mode=%x)\n", + __func__, dwc3_rtk->cur_dr_mode); + return connected; + } + + dsts = dwc3_readl(dwc3_rtk->dwc->regs, DWC3_DSTS); + dctl = dwc3_readl(dwc3_rtk->dwc->regs, DWC3_DCTL); + + dev_info(dwc3_rtk->dev, "%s: Device mode check DSTS=%x DCTL=%x\n", + __func__, + dsts, dctl); + no_host_connect = DWC3_DSTS_USBLNKST(dsts) >= DWC3_LINK_STATE_SS_DIS; + no_run_gadget = (dctl & BIT(31)) == 0x0; + if (no_host_connect || no_run_gadget) + connected = false; + + return connected; +} + static void switch_u2_dr_mode(struct dwc3_rtk *rtk, int dr_mode) { switch (dr_mode) { @@ -63,6 +106,121 @@ static void switch_u2_dr_mode(struct dwc3_rtk *rtk, int dr_mode) } } +static void switch_dwc3_dr_mode(struct dwc3_rtk *rtk, int dr_mode) +{ +#ifdef CONFIG_USB_DWC3_RTK_DUAL_ROLE + switch (dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dev_info(rtk->dev, "%s dr_mode=USB_DR_MODE_PERIPHERAL\n", + __func__); + dwc3_drd_to_device(rtk->dwc); + break; + case USB_DR_MODE_HOST: + dev_info(rtk->dev, "%s dr_mode=USB_DR_MODE_HOST\n", + __func__); + dwc3_drd_to_host(rtk->dwc); + break; + default: + dev_info(rtk->dev, "%s dr_mode=%d\n", __func__, dr_mode); + dwc3_drd_to_stop_all(rtk->dwc); + } +#else + dev_info(rtk->dev, "Not support CONFIG_USB_DWC3_RTK_DUAL_ROLE\n"); +#endif /* CONFIG_USB_DWC3_RTK_DUAL_ROLE */ +} + +int dwc3_rtk_get_dr_mode(struct dwc3_rtk *rtk) +{ + return rtk->cur_dr_mode; +} + +int dwc3_rtk_set_dr_mode(struct dwc3_rtk *rtk, int dr_mode) +{ + if (!rtk->support_drd_mode) + return rtk->cur_dr_mode; + + if (!rtk->dwc) { + dev_err(rtk->dev, "%s Error! dwc3 is NULL", __func__); + return rtk->cur_dr_mode; + } + + dev_dbg(rtk->dev, "%s START....", __func__); + + rtk->cur_dr_mode = dr_mode; + rtk->dwc->dr_mode = dr_mode; + + switch_dwc3_dr_mode(rtk, dr_mode); + mdelay(10); + switch_u2_dr_mode(rtk, dr_mode); + + dev_dbg(rtk->dev, "%s END....", __func__); + + return rtk->cur_dr_mode; +} + +static ssize_t set_dr_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dwc3_rtk *rtk = dev_get_drvdata(dev); + char *ptr = buf; + int count = PAGE_SIZE; + int n; + + n = scnprintf(ptr, count, + "Now cur_dr_mode is %s (default dwc3 dr_mode is %s)\n", + ({ char *tmp; + switch (rtk->cur_dr_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; }), + ({ char *tmp; + switch (rtk->default_dwc3_dr_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; })); + + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write host -> switch to Host mode\n"); + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write device -> switch to Device mode\n"); + ptr += n; + count -= n; + + return ptr - buf; +} + +static ssize_t set_dr_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dwc3_rtk *rtk = dev_get_drvdata(dev); + + if (!strncmp(buf, "host", 4)) + dwc3_rtk_set_dr_mode(rtk, USB_DR_MODE_HOST); + else if (!strncmp(buf, "device", 6)) + dwc3_rtk_set_dr_mode(rtk, USB_DR_MODE_PERIPHERAL); + else + dwc3_rtk_set_dr_mode(rtk, 0); + + return count; +} +static DEVICE_ATTR_RW(set_dr_mode); + static int dwc3_rtk_init(struct dwc3_rtk *rtk) { struct device *dev = rtk->dev; @@ -122,6 +280,8 @@ static int dwc3_rtk_probe_dwc3core(struct dwc3_rtk *rtk) } else { dev_err(dev, "dwc3_rtk node is NULL\n"); } + if (rtk->support_drd_mode) + device_create_file(dev, &dev_attr_set_dr_mode); return ret; } @@ -207,6 +367,11 @@ static int dwc3_rtk_probe(struct platform_device *pdev) __func__, readl(usb_hmac_ctr0)); } + rtk->support_drd_mode = false; + if (of_property_read_bool(node, "drd_mode")) { + dev_info(rtk->dev, "%s: support drd_mode\n", __func__); + rtk->support_drd_mode = true; + } } if (node) { @@ -240,6 +405,9 @@ static int dwc3_rtk_remove(struct platform_device *pdev) { struct dwc3_rtk *rtk = platform_get_drvdata(pdev); + if (rtk->support_drd_mode) + device_remove_file(rtk->dev, &dev_attr_set_dr_mode); + dwc3_rtk_debugfs_exit(rtk); of_platform_depopulate(rtk->dev); diff --git a/drivers/usb/dwc3/dwc3-rtk.h b/drivers/usb/dwc3/dwc3-rtk.h index 9b4e7683def7..eeefcecd6f22 100644 --- a/drivers/usb/dwc3/dwc3-rtk.h +++ b/drivers/usb/dwc3/dwc3-rtk.h @@ -34,6 +34,7 @@ struct dwc3_rtk { int default_dwc3_dr_mode; /* define by dwc3 driver, and it is fixed */ int cur_dr_mode; /* current dr mode */ + bool support_drd_mode; /* if support Host/device switch */ /* For debugfs */ struct dentry *debug_dir; -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:27 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:27 +0800 Subject: [PATCH 07/15] arm64: dts: realtek: rtd16xx-usb: add the property of drd mode In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-8-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi index 9770885fb13c..ee3f61f9e2f5 100644 --- a/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi @@ -71,6 +71,7 @@ dwc3_drd: rtk_dwc3_drd at 98013200 { #size-cells = <1>; ranges; delay_probe_work; //To delay probe work + drd_mode; status = "okay"; dwc3_drd at 98020000 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:32 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:32 +0800 Subject: [PATCH 12/15] arm64: dts: realtek: rtd129x-usb: Add rtd129x type c module nodes In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-13-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi | 67 ++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi index 00a1bc6d9138..7f9d90238372 100644 --- a/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi @@ -78,6 +78,73 @@ dwc3_drd at 98020000 { snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode }; + + dwc3_type_c: rtk_dwc3_drd_type_c at 9801334c { + compatible = "Realtek,dwc3-type_c"; + reg = <0x9801334C 0x20>; + interrupts = <0 60 4>; + //debug; /*to enable debug log*/ + delay_probe_work; //To delay probe work + boot_check_time = <(-1)>; /*ms (At boot Device switch Host time)*/ + status = "okay"; + + default_revision = <0xA01>; + A00 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0x9>; + cc1_rp_36k_code = <0x14>; + cc1_rp_12k_code = <0xf>; + cc1_rd_code = <0>; + cc1_vref_ufp = /bits/ 8 + <0x0 0x0 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x0 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x0 0x0 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x0 0x1 0x2>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0x2>; + cc2_rp_36k_code = <0x15>; + cc2_rp_12k_code = <0xe>; + cc2_rd_code = <0>; + cc2_vref_ufp = /bits/ 8 + <0x0 0x0 0x3>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x0 0x3 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x0 0x0 0x3>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x0 0x3 0x3>; /*<2p6v,0p8v,0p2v>*/ + }; + + A01 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0xb>; + cc1_rp_36k_code = <0x17>; + cc1_rp_12k_code = <0x10>; + cc1_rd_code = <0>; + cc1_vref_ufp = /bits/ 8 + <0x0 0x0 0x4>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x0 0x4 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x0 0x0 0x4>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x0 0x3 0x4>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0xc>; + cc2_rp_36k_code = <0x17>; + cc2_rp_12k_code = <0x12>; + cc2_rd_code = <0>; + cc2_vref_ufp = /bits/ 8 + <0x0 0x0 0x5>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x0 0x5 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x0 0x0 0x5>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x2 0x3 0x5>; /*<2p6v,0p8v,0p2v>*/ + }; + }; }; dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:34 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:34 +0800 Subject: [PATCH 14/15] arm64: dts: realtek: rtd16xx-usb: Add rtd16xx type c module nodes In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-15-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi index ee3f61f9e2f5..8211185da0a1 100644 --- a/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi @@ -85,6 +85,45 @@ dwc3_drd at 98020000 { snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode status = "okay"; }; + + dwc3_type_c: rtk_dwc3_drd_type_c at 98007220 { + compatible = "Realtek,dwc3-type_c"; + reg = <0x98007220 0x20>; + interrupts = <0 60 4>; + //debug; /*to enable debug log*/ + delay_probe_work; /*To delay probe work*/ + boot_check_time = <(-1)>; /*ms (At boot Device switch Host time)*/ + status = "okay"; + + default_revision = <0xA00>; + A00 { + cc_dfp_mode = "dfp_3_0"; /*dfp_3_0, dfp_1_5, dfp_usb*/ + cc1_rp_4p7k_code = <0xC>; + cc1_rp_36k_code = <0xF>; + cc1_rp_12k_code = <0xE>; + cc1_rd_code = <0x11>; + cc1_vref_ufp = /bits/ 8 + <0x7 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc1_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc1_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc1_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + cc2_rp_4p7k_code = <0xC>; + cc2_rp_36k_code = <0xF>; + cc2_rp_12k_code = <0xE>; + cc2_rd_code = <0xF>; + cc2_vref_ufp = /bits/ 8 + <0x8 0xA 0x2>; /*<1p23v,0p66v,0p2v>*/ + cc2_vref_dfp_usb = /bits/ 8 + <0x7 0x2 0x0>; /*<0_1p6v,0p2v,unused>*/ + cc2_vref_dfp_1_5 = /bits/ 8 + <0x7 0x3 0x2>; /*<1_1p6v,0p4v,0p2v>*/ + cc2_vref_dfp_3_0 = /bits/ 8 + <0x5 0xA 0x2>; /*<2p6v,0p8v,0p2v>*/ + }; + }; }; dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:28 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:28 +0800 Subject: [PATCH 08/15] arm64: dts: realtek: rtd139x-usb: add the property of drd mode In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-9-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi index a98cf134aa52..112f733bf277 100644 --- a/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi @@ -40,6 +40,7 @@ dwc3_drd: rtk_dwc3_drd at 98013200 { #size-cells = <1>; ranges; delay_probe_work; //To delay probe work + drd_mode; status = "okay"; dwc3_drd at 98020000 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 04:50:29 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 7 Oct 2020 16:50:29 +0800 Subject: [PATCH 09/15] arm64: dts: realtek: rtd129x-usb: add the property of drd mode In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-10-stanley_chang@realtek.com> Signed-off-by: Stanley Chang --- arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi index 82537da8f4fb..00a1bc6d9138 100644 --- a/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi +++ b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi @@ -65,6 +65,7 @@ dwc3_drd: rtk_dwc3_drd at 98013200 { #address-cells = <1>; #size-cells = <1>; ranges; + drd_mode; delay_probe_work; //To delay probe work dwc3_drd at 98020000 { -- 2.28.0 From stanley_chang at realtek.com Wed Oct 7 09:51:02 2020 From: stanley_chang at realtek.com (Stanley Chang) Date: Wed, 07 Oct 2020 08:51:02 -0000 Subject: [PATCH 10/15] usb: dwc3: dwc3-rtk-type_c: Add a rtk type c driver. In-Reply-To: <20201007085035.30477-1-stanley_chang@realtek.com> References: <20201007085035.30477-1-stanley_chang@realtek.com> Message-ID: <20201007085035.30477-11-stanley_chang@realtek.com> Realtek SoCs embeds a simple type c module to detect cc pin status. This driver can switch usb host/device mode based on type c status. Signed-off-by: Stanley Chang --- drivers/usb/dwc3/Kconfig | 7 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-rtk-type_c.c | 2243 ++++++++++++++++++++++++++++ 3 files changed, 2251 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-rtk-type_c.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index a04d280c9835..a59803f0d7f9 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -70,6 +70,13 @@ config USB_DWC3_RTK_DUAL_ROLE Support Realtek dwc3 drd mode to dynamical host/device switch. say 'Y' or 'M' if you have such device. +config USB_DWC3_RTK_TYPE_C + bool "RTK DWC3 Type C Driver (dynamical host/device mode switch)" + default USB_DWC3_RTK if (USB_DWC3_DUAL_ROLE) + depends on USB_DWC3_DUAL_ROLE + help + Support Realtek dwc3 drd type c port + config USB_DWC3_OMAP tristate "Texas Instruments OMAP5 and similar Platforms" depends on ARCH_OMAP2PLUS || COMPILE_TEST diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 25f8823e8811..19fcbc840982 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o dwc3-rtk-debugfs.o obj-$(CONFIG_USB_DWC3_RTK_DUAL_ROLE) += dwc3-rtk-drd.o +obj-$(CONFIG_USB_DWC3_RTK_TYPE_C) += dwc3-rtk-type_c.o diff --git a/drivers/usb/dwc3/dwc3-rtk-type_c.c b/drivers/usb/dwc3/dwc3-rtk-type_c.c new file mode 100644 index 000000000000..fd20255a9565 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-rtk-type_c.c @@ -0,0 +1,2243 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * * dwc3-rtk-type_c.c - Realtek DWC3 Type C driver + * + * Copyright (C) 2017 Realtek Semiconductor Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dwc3-rtk-drd.h" + +static const struct soc_device_attribute rtk_soc_kylin[] = { + { + .family = "Realtek Kylin", + }, + { + /* empty */ + } +}; + +struct dwc3_rtk; + +struct type_c_data { + void __iomem *type_c_reg_base; + struct device *dev; + + int chip_revision; + + /* Parameters */ + u32 cc1_rp; + u32 cc1_rp_code; + u32 cc1_rd_code; + u32 cc1_vref_ufp; + u32 cc1_vref_dfp_usb; + u32 cc1_vref_dfp_1_5; + u32 cc1_vref_dfp_3_0; + u32 cc2_rp; + u32 cc2_rp_code; + u32 cc2_rd_code; + u32 cc2_vref_ufp; + u32 cc2_vref_dfp_usb; + u32 cc2_vref_dfp_1_5; + u32 cc2_vref_dfp_3_0; + u32 debounce_val; /* 1b,1us 7f,4.7us */ + int cc_dfp_mode; + + /* Host/Device mode status */ + struct dwc3_rtk *dwc3_rtk; + int dr_mode; /* current mode in type c cc status */ + + /* type_c state */ + int connect_change; +#define CONNECT_CHANGE 1 +#define CONNECT_NO_CHANGE 0 + int cc_mode; /* cc is host or device */ +#define IN_HOST_MODE 1 +#define IN_DEVICE_MODE 0 + int is_attach; +#define IN_ATTACH 1 +#define TO_ATTACH 1 +#define IN_DETACH 0 +#define TO_DETACH 0 + int at_cc1; +#define AT_CC1 1 +#define AT_CC2 0 + bool is_role_swap; /* if in role swap */ +#define ROLE_SWAP 1 +#define NO_ROLE_SWAP 0 +#define TO_SWAP_ROLE 1 +#define TO_RESTORE_ROLE 0 + + u32 int_status; + u32 cc_status; + spinlock_t lock; + struct delayed_work delayed_work; + + struct work_struct start_work; + + /* boot time check device mode transfer to host mode*/ + bool check_at_boot; + int boot_check_time; + struct delayed_work boot_check_work; + + bool rd_en_at_first; + + /* A sw debounce to filter cc signal */ + bool filter_config_channel_signal; + + bool debug; + +#ifdef CONFIG_DYNAMIC_DEBUG + struct dentry *debug_dir; +#endif +}; + +/* Type C register offset */ +#define USB_TYPEC_CTRL_CC1_0 0x0 +#define USB_TYPEC_CTRL_CC1_1 0x4 +#define USB_TYPEC_CTRL_CC2_0 0x8 +#define USB_TYPEC_CTRL_CC2_1 0xC +#define USB_TYPEC_STS 0x10 +#define USB_TYPEC_CTRL 0x14 +#define USB_DBUS_PWR_CTRL 0x18 + +#define enable_cc1 0x1 +#define enable_cc2 0x2 +#define disable_cc 0x0 + +/* Bit mapping USB_TYPEC_CTRL_CC1_0 and USB_TYPEC_CTRL_CC2_0 */ +#define PLR_EN BIT(29) +#define rp4pk_code(val) ((0x1f & val) << 22) +#define code_rp4pk(val) ((val >> 22) & 0x1f) +#define rp36k_code(val) ((0x1f & val) << 17) +#define code_rp36k(val) ((val >> 17) & 0x1f) +#define rp12k_code(val) ((0x1f & val) << 12) +#define code_rp12k(val) ((val >> 12) & 0x1f) +#define rd_code(val) ((0x1f & val) << 7) +#define code_rd(val) ((val >> 7) & 0x1f) +#define cc_mode(val) ((0x3 & val) << 5) +#define En_rp4p7k BIT(4) +#define En_rp36k BIT(3) +#define En_rp12k BIT(2) +#define En_rd BIT(1) +#define En_cc_det BIT(0) + +#define CC_MODE_UFP 0x0 +#define CC_MODE_DFP_USB 0x1 +#define CC_MODE_DFP_1_5 0x2 +#define CC_MODE_DFP_3_0 0x3 + +/* Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */ +#define vref_2p6v(val) ((0x7 & val) << 26) +#define vref_1p23v(val) ((0xf & val) << 22) +#define vref_0p8v(val) ((0xf & val) << 18) +#define vref_0p66v(val) ((0xf & val) << 14) +#define vref_0p4v(val) ((0x7 & val) << 11) +#define vref_0p2v(val) ((0x7 & val) << 8) +#define vref_1_1p6v(val) ((0xf & val) << 4) +#define vref_0_1p6v(val) ((0xf & val) << 0) + +#define decode_2p6v(val) ((val >> 26) & 0x7) +#define decode_1p23v(val) ((val >> 22) & 0xf) +#define decode_0p8v(val) ((val >> 18) & 0xf) +#define decode_0p66v(val) ((val >> 14) & 0xf) +#define decode_0p4v(val) ((val >> 11) & 0x7) +#define decode_0p2v(val) ((val >> 8) & 0x7) +#define decode_1_1p6v(val) ((val >> 4) & 0xf) +#define decode_0_1p6v(val) ((val >> 0) & 0xf) + +/* Bit mapping USB_TYPEC_STS */ +#define det_sts 0x7 +#define cc1_det_sts (det_sts) +#define cc2_det_sts (det_sts << 3) +#define det_sts_ra 0x1 +#define det_sts_rd 0x3 +#define det_sts_rp 0x1 +#define cc1_det_sts_ra (det_sts_ra) +#define cc1_det_sts_rd (det_sts_rd) +#define cc1_det_sts_rp (det_sts_rp) +#define cc2_det_sts_ra (det_sts_ra << 3) +#define cc2_det_sts_rd (det_sts_rd << 3) +#define cc2_det_sts_rp (det_sts_rp << 3) + +/* Bit mapping USB_TYPEC_CTRL */ +#define cc2_int_en BIT(11) +#define cc1_int_en BIT(10) +#define cc2_int_sts BIT(9) +#define cc1_int_sts BIT(8) +#define debounce_time_MASK 0xff +#define ENABLE_TYPE_C_DETECT (cc1_int_en | cc2_int_en) +#define all_cc_int_sts (cc1_int_sts | cc2_int_sts) + +/* Parameter */ +#define DETECT_TIME 50 /* ms */ + +static void enable_writel(int value, void __iomem *addr) +{ + writel(value | readl(addr), addr); +} + +static void disable_writel(int value, void __iomem *addr) +{ + writel(~value & readl(addr), addr); +} + +static inline int rtk_type_c_init(struct type_c_data *type_c) +{ + dev_info(type_c->dev, "%s\n", __func__); + + /* TODO */ + + return 0; +} + +static int rtd129x_switch_type_c_plug_config(struct type_c_data *type_c, + int dr_mode, int cc) +{ + void __iomem *usb_typec_ctrl_cc1_0; + int val_cc; + +#define TYPE_C_EN_SWITCH BIT(29) +#define TYPE_C_TxRX_sel (BIT(28) | BIT(27)) +#define TYPE_C_SWITCH_MASK (TYPE_C_EN_SWITCH | TYPE_C_TxRX_sel) +#define TYPE_C_enable_cc1 TYPE_C_EN_SWITCH +#define TYPE_C_enable_cc2 (TYPE_C_EN_SWITCH | TYPE_C_TxRX_sel) +#define TYPE_C_disable_cc ~TYPE_C_SWITCH_MASK + + usb_typec_ctrl_cc1_0 = type_c->type_c_reg_base + USB_TYPEC_CTRL_CC1_0; + val_cc = readl(usb_typec_ctrl_cc1_0); + val_cc &= ~TYPE_C_SWITCH_MASK; + + if (cc == disable_cc) { + val_cc &= TYPE_C_disable_cc; + } else if (cc == enable_cc1) { + val_cc |= TYPE_C_enable_cc1; + } else if (cc == enable_cc2) { + val_cc |= TYPE_C_enable_cc2; + } else { + pr_err("%s: Error cc setting cc=0x%x\n", __func__, cc); + return -1; + } + writel(val_cc, usb_typec_ctrl_cc1_0); + + mdelay(1); + + pr_info("%s: cc=0x%x val_cc=0x%x usb_typec_ctrl_cc1_0=0x%x\n", + __func__, cc, val_cc, readl(usb_typec_ctrl_cc1_0)); + + return 0; +} + +static inline void switch_type_c_plug_config(struct type_c_data *type_c, + int dr_mode, int cc) +{ + int ret = 0; + + dev_info(type_c->dev, "%s dr_mode=%d cc=0x%x\n", __func__, dr_mode, cc); + + if (soc_device_match(rtk_soc_kylin)) + ret = rtd129x_switch_type_c_plug_config(type_c, dr_mode, cc); + + if (ret < 0) + dev_err(type_c->dev, "%s: Error set type c plug config\n", + __func__); + + /* TODO for gpio control switch */ +} + +static void switch_type_c_dr_mode(struct type_c_data *type_c, + int dr_mode, int cc) +{ + dev_dbg(type_c->dev, "%s START....", __func__); + + switch_type_c_plug_config(type_c, dr_mode, cc); + if (cc == disable_cc) + msleep(1000); + + type_c->dr_mode = dwc3_rtk_set_dr_mode(type_c->dwc3_rtk, dr_mode); + + dev_dbg(type_c->dev, "%s END....", __func__); +} + +/* device attached/detached */ +static int device_attached(struct type_c_data *type_c, u32 enable_cc) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + + dev_info(dev, "%s: a device attach\n", __func__); + + cancel_delayed_work(&type_c->delayed_work); + + switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc); + + enable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL); + return 0; +} + +static int device_detached(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + + dev_info(dev, "%s: a device detach\n", __func__); + + disable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL); + + switch_type_c_dr_mode(type_c, 0, disable_cc); + + schedule_delayed_work(&type_c->delayed_work, + msecs_to_jiffies(DETECT_TIME)); + + return 0; +} + +/* host connect/disconnect*/ +static int host_connected(struct type_c_data *type_c, u32 enable_cc) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + + dev_info(dev, "%s: a Host connect\n", __func__); + + cancel_delayed_work(&type_c->delayed_work); + + switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, enable_cc); + + enable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL); + return 0; +} + +static int host_disconnected(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + + dev_info(dev, "%s: a Host disconnect\n", __func__); + + disable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL); + + switch_type_c_dr_mode(type_c, 0, disable_cc); + + schedule_delayed_work(&type_c->delayed_work, + msecs_to_jiffies(DETECT_TIME)); + + return 0; +} + +/* detect host device switch */ +static int detect_device(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + u32 cc1_config, cc2_config, default_ctrl; + int cc_mode_sel = type_c->cc_dfp_mode; + + /* For kylin to disable external rd control gpio */ + if (soc_device_match(rtk_soc_kylin)) { + /* TODO add a gpio control to disable external rd */ + } + + default_ctrl = readl(type_c_reg_base + USB_TYPEC_CTRL) & + debounce_time_MASK; + writel(default_ctrl, type_c_reg_base + USB_TYPEC_CTRL); + + disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + switch (cc_mode_sel) { + case CC_MODE_DFP_USB: + writel(type_c->cc1_vref_dfp_usb, + type_c_reg_base + USB_TYPEC_CTRL_CC1_1); + writel(type_c->cc2_vref_dfp_usb, + type_c_reg_base + USB_TYPEC_CTRL_CC2_1); + break; + case CC_MODE_DFP_1_5: + writel(type_c->cc1_vref_dfp_1_5, + type_c_reg_base + USB_TYPEC_CTRL_CC1_1); + writel(type_c->cc2_vref_dfp_1_5, + type_c_reg_base + USB_TYPEC_CTRL_CC2_1); + break; + case CC_MODE_DFP_3_0: + writel(type_c->cc1_vref_dfp_3_0, + type_c_reg_base + USB_TYPEC_CTRL_CC1_1); + writel(type_c->cc2_vref_dfp_3_0, + type_c_reg_base + USB_TYPEC_CTRL_CC2_1); + break; + default: + dev_err(dev, "%s ERROR cc_mode_sel=%d\n", + __func__, cc_mode_sel); + break; + } + cc1_config = type_c->cc1_rp | type_c->cc1_rp_code | + cc_mode(cc_mode_sel); + cc2_config = type_c->cc2_rp | type_c->cc2_rp_code | + cc_mode(cc_mode_sel); + + writel(cc1_config, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + writel(cc2_config, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + /* Do wmb */ + wmb(); + + enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + return 0; +} + +static int detect_host(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + void __iomem *type_c_reg_base = type_c->type_c_reg_base; + u32 cc1_config, cc2_config, default_ctrl; + u32 cc_rd = En_rd; + + if (type_c->rd_en_at_first) { + if (soc_device_match(rtk_soc_kylin)) { + cc_rd = 0; + /* TODO add a gpio control to enable external rd */ + } else { + dev_info(dev, "%s set PLR_EN on rd_en_at_first\n", + __func__); + cc_rd = PLR_EN; + } + type_c->rd_en_at_first = false; + } else { + /* For kylin to disable external rd control gpio */ + if (soc_device_match(rtk_soc_kylin)) { + /* TODO add a gpio control to disable external rd */ + } + } + + default_ctrl = readl(type_c_reg_base + USB_TYPEC_CTRL) & + debounce_time_MASK; + writel(default_ctrl, type_c_reg_base + USB_TYPEC_CTRL); + + disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + writel(type_c->cc1_vref_ufp, type_c_reg_base + USB_TYPEC_CTRL_CC1_1); + writel(type_c->cc2_vref_ufp, type_c_reg_base + USB_TYPEC_CTRL_CC2_1); + + cc1_config = cc_rd | type_c->cc1_rd_code | cc_mode(CC_MODE_UFP); + cc2_config = cc_rd | type_c->cc2_rd_code | cc_mode(CC_MODE_UFP); + + writel(cc1_config, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + writel(cc2_config, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + /* Do wmb */ + wmb(); + + enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0); + enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0); + + return 0; +} + +static int host_device_switch_detection(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + int ret = 0; + + if (type_c->debug) + dev_dbg(dev, "ENTER %s", __func__); + if (type_c->cc_mode) { + type_c->cc_mode = IN_DEVICE_MODE; + detect_host(type_c); + if (type_c->debug) + dev_dbg(dev, "Now device mode $$$$"); + } else { + type_c->cc_mode = IN_HOST_MODE; + detect_device(type_c); + if (type_c->debug) + dev_dbg(dev, "Now host mode ####"); + } + + return ret; +} + +static int detect_type_c_state(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + u32 int_status, cc_status, cc_status_check; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + int_status = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL); + cc_status = readl(type_c->type_c_reg_base + USB_TYPEC_STS); + + type_c->connect_change = CONNECT_NO_CHANGE; + + switch (type_c->cc_mode) { + case IN_HOST_MODE: + switch (type_c->is_attach) { + case IN_ATTACH: + if (((cc_status & cc1_det_sts) == cc1_det_sts) && + (type_c->at_cc1 == AT_CC1)) { + dev_dbg(dev, "IN host mode and cc1 device detach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } else if (((cc_status & cc2_det_sts) == cc2_det_sts) && + (type_c->at_cc1 == AT_CC2)) { + dev_dbg(dev, "IN host mode and cc2 device detach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + case IN_DETACH: + cc_status_check = readl( + type_c->type_c_reg_base + USB_TYPEC_STS); + if (cc_status_check != (cc1_det_sts | cc2_det_sts)) { + if (in_interrupt()) { + mdelay(300); + } else { + spin_unlock_irqrestore(&type_c->lock, + flags); + msleep(300); + spin_lock_irqsave(&type_c->lock, flags); + } + cc_status_check = readl( + type_c->type_c_reg_base + + USB_TYPEC_STS); + } + if (cc_status != cc_status_check) { + dev_warn(dev, "IN_HOST_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n", + cc_status, cc_status_check); + cc_status = readl(type_c->type_c_reg_base + + USB_TYPEC_STS); + } + + if ((cc_status & cc1_det_sts) == cc1_det_sts_rd) { + dev_dbg(dev, "IN host mode and cc1 device attach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_ATTACH; + type_c->at_cc1 = AT_CC1; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & cc2_det_sts) == + cc2_det_sts_rd) { + dev_dbg(dev, "In host mode and cc2 device attach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_ATTACH; + type_c->at_cc1 = AT_CC2; + type_c->connect_change = CONNECT_CHANGE; + } + break; + default: + dev_err(dev, "IN host_mode and error attach state (is_attach=%d)", + type_c->is_attach); + } + break; + case IN_DEVICE_MODE: + switch (type_c->is_attach) { + case IN_ATTACH: + if (type_c->filter_config_channel_signal && + ((cc_status & cc1_det_sts) < + cc1_det_sts_rp || + (cc_status & cc2_det_sts) < + cc2_det_sts_rp)) { + /* Add a sw debounce to filter cc signal sent + * from apple pd adapter + */ + if (in_interrupt()) { + mdelay(5); + } else { + spin_unlock_irqrestore( + &type_c->lock, flags); + msleep(5); + spin_lock_irqsave(&type_c->lock, flags); + } + cc_status_check = readl( + type_c->type_c_reg_base + + USB_TYPEC_STS); + + if (cc_status != cc_status_check) { + dev_dbg(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x) maybe use a pd adapter\n", + cc_status, cc_status_check); + cc_status = cc_status_check; + } + } + + if ((cc_status & cc1_det_sts) < cc1_det_sts_rp && + type_c->at_cc1 == AT_CC1) { + dev_dbg(dev, "IN device mode and cc1 host disconnect (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & cc2_det_sts) < cc2_det_sts_rp && + type_c->at_cc1 == AT_CC2) { + dev_dbg(dev, "IN device mode and cc2 host connect (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + case IN_DETACH: + cc_status_check = readl(type_c->type_c_reg_base + + USB_TYPEC_STS); + if (cc_status_check != 0x0) { + if (in_interrupt()) { + mdelay(300); + } else { + spin_unlock_irqrestore(&type_c->lock, + flags); + msleep(300); + spin_lock_irqsave(&type_c->lock, flags); + } + cc_status_check = readl( + type_c->type_c_reg_base + + USB_TYPEC_STS); + } + if (cc_status != cc_status_check) { + dev_warn(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n", + cc_status, cc_status_check); + cc_status = readl(type_c->type_c_reg_base + + USB_TYPEC_STS); + } + + if ((cc_status & cc1_det_sts) >= cc1_det_sts_rp) { + dev_dbg(dev, "IN device mode and cc1 host connect (cc_status=0x%x)", + cc_status); + type_c->at_cc1 = AT_CC1; + type_c->is_attach = TO_ATTACH; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & cc2_det_sts) >= + cc2_det_sts_rp) { + dev_dbg(dev, "IN device mode and cc2 host connect (cc_status=0x%x)", + cc_status); + type_c->at_cc1 = AT_CC2; + type_c->is_attach = TO_ATTACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + default: + dev_err(dev, "IN device_mode and error attach state (is_attach=%d)", + type_c->is_attach); + } + break; + default: + dev_err(dev, "error host or device mode (cc_mode=%d)", + type_c->cc_mode); + } + + type_c->int_status = int_status; + type_c->cc_status = cc_status; + + spin_unlock_irqrestore(&type_c->lock, flags); + return 0; +} + +static void host_device_switch(struct work_struct *work) +{ + struct type_c_data *type_c = container_of(work, + struct type_c_data, delayed_work.work); + struct device *dev = type_c->dev; + unsigned long flags; + int connect_change = 0; + int cc_mode = 0; + int is_attach = 0; + int at_cc1 = 0; + + if (type_c->debug) + dev_dbg(type_c->dev, "ENTER %s", __func__); + + spin_lock_irqsave(&type_c->lock, flags); + if (type_c->connect_change) + connect_change = type_c->connect_change; + spin_unlock_irqrestore(&type_c->lock, flags); + + if (!connect_change) + detect_type_c_state(type_c); + + spin_lock_irqsave(&type_c->lock, flags); + if (type_c->connect_change) { + connect_change = type_c->connect_change; + cc_mode = type_c->cc_mode; + is_attach = type_c->is_attach; + at_cc1 = type_c->at_cc1; + type_c->connect_change = CONNECT_NO_CHANGE; + type_c->is_role_swap = NO_ROLE_SWAP; + } else { + host_device_switch_detection(type_c); + + schedule_delayed_work(&type_c->delayed_work, + msecs_to_jiffies(DETECT_TIME)); + } + spin_unlock_irqrestore(&type_c->lock, flags); + + if (connect_change) { + dev_info(dev, "%s: usb cable connection change\n", __func__); + if (cc_mode) { + if (is_attach && at_cc1) + device_attached(type_c, enable_cc1); + else if (is_attach && !at_cc1) + device_attached(type_c, enable_cc2); + else + device_detached(type_c); + } else { + if (is_attach && at_cc1) + host_connected(type_c, enable_cc1); + else if (is_attach && !at_cc1) + host_connected(type_c, enable_cc2); + else + host_disconnected(type_c); + } + dev_err(dev, "Connection change OK: IN %s mode to %s %s at %s (cc_status=0x%x)\n", + cc_mode?"host":"device", + cc_mode ? + (is_attach?"attach":"detach") : + (is_attach?"connect":"disconnect"), + cc_mode?"device":"host", + at_cc1?"cc1":"cc2", type_c->cc_status); + + } + + /* For special case, some boards use type c power and + * need use host mode. + * After 30s, We switch to host mode if in device mode + * but no host connect. + */ + if (type_c->check_at_boot) { + if (connect_change && + (cc_mode == IN_DEVICE_MODE) && + is_attach) { + dev_info(dev, "%s: In Device mode check connection at boot time\n", + __func__); + schedule_delayed_work(&type_c->boot_check_work, + msecs_to_jiffies(type_c->boot_check_time)); + } + type_c->check_at_boot = false; + } +} + +static int host_device_role_swap(struct type_c_data *type_c, bool swap_role) +{ + int ret = 0; + int swap_to_xx_mode = USB_DR_MODE_UNKNOWN; + int at_cc1, cc_mode, is_attach; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + at_cc1 = type_c->at_cc1; + cc_mode = type_c->cc_mode; + is_attach = type_c->is_attach; + + spin_unlock_irqrestore(&type_c->lock, flags); + + if (!is_attach) { + dev_warn(type_c->dev, "Can not swap role!! It is Not attach\n"); + return -1; + } + + dev_info(type_c->dev, "%s: In %s Mode and now is%s role swap ", + __func__, + cc_mode == IN_HOST_MODE?"Host":"Device", + type_c->is_role_swap == ROLE_SWAP?"":" Not"); + + if (swap_role == TO_RESTORE_ROLE && + type_c->is_role_swap == ROLE_SWAP) + /* Restore Role */ + if (cc_mode == IN_DEVICE_MODE) + swap_to_xx_mode = USB_DR_MODE_PERIPHERAL; + else + swap_to_xx_mode = USB_DR_MODE_HOST; + else if (swap_role == TO_SWAP_ROLE && + type_c->is_role_swap == NO_ROLE_SWAP) + /* Swap Role */ + if (cc_mode == IN_DEVICE_MODE) + swap_to_xx_mode = USB_DR_MODE_HOST; + else + swap_to_xx_mode = USB_DR_MODE_PERIPHERAL; + else + dev_info(type_c->dev, "%s: Other Case swap_role=%x is_role_swap=%x\n", + __func__, swap_role, type_c->is_role_swap); + + if (swap_to_xx_mode != USB_DR_MODE_UNKNOWN) { + u32 enable_cc = at_cc1?enable_cc1:enable_cc2; + + dev_info(type_c->dev, "%s: now is%s role swap ==> to %s role (swap_to_xx_mode=%s\n", + __func__, + type_c->is_role_swap == ROLE_SWAP?"":" No", + swap_role == TO_SWAP_ROLE?"swap":"restore", + ({ char *tmp; + switch (swap_to_xx_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; })); + + switch_type_c_plug_config(type_c, 0, disable_cc); + mdelay(100); + switch_type_c_dr_mode(type_c, swap_to_xx_mode, enable_cc); + + type_c->is_role_swap = (swap_role == TO_SWAP_ROLE ? + ROLE_SWAP : NO_ROLE_SWAP); + } else { + dev_info(type_c->dev, "%s: No swap and No restore\n", __func__); + } + + return ret; +} + +static void boot_time_check(struct work_struct *work) +{ + struct type_c_data *type_c = container_of(work, + struct type_c_data, boot_check_work.work); + struct device *dev = type_c->dev; + int at_cc1, cc_mode, is_attach; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + at_cc1 = type_c->at_cc1; + cc_mode = type_c->cc_mode; + is_attach = type_c->is_attach; + + spin_unlock_irqrestore(&type_c->lock, flags); + + if ((cc_mode == IN_DEVICE_MODE) && is_attach) { + + if (!dwc3_rtk_is_connected_on_device_mode( + type_c->dwc3_rtk)) { + dev_info(dev, "%s: In Device mode, NO host connect at boot time (After %dms), switch to Host mode\n", + __func__, type_c->boot_check_time); + + host_device_role_swap(type_c, TO_SWAP_ROLE); + } + } +} + +irqreturn_t type_c_detect_irq(int irq, void *__data) +{ + struct type_c_data *type_c = (struct type_c_data *) __data; + struct device *dev = type_c->dev; + unsigned long flags; + + detect_type_c_state(type_c); + + spin_lock_irqsave(&type_c->lock, flags); + + if (type_c->connect_change) { + dev_info(dev, "%s: IN %s mode to %s %s (at %s interrupt) int_status=0x%x, cc_status=0x%x", + __func__, + type_c->cc_mode?"host":"device", + type_c->cc_mode ? + (type_c->is_attach?"attach":"detach") : + (type_c->is_attach?"connect":"disconnect"), + type_c->cc_mode?"device":"host", + type_c->at_cc1?"cc1":"cc2", + type_c->int_status, type_c->cc_status); + + /* clear interrupt status */ + disable_writel(all_cc_int_sts, + type_c->type_c_reg_base + USB_TYPEC_CTRL); + + cancel_delayed_work(&type_c->delayed_work); + schedule_delayed_work(&type_c->delayed_work, + msecs_to_jiffies(0)); + } else { + static int local_count; + + if (local_count++ > 10) { + /* clear interrupt status */ + disable_writel(all_cc_int_sts, + type_c->type_c_reg_base + USB_TYPEC_CTRL); + local_count = 0; + } + if (type_c->debug) + dev_dbg(dev, "%s: ###NO change### Status: IN %s mode %s %s (at %s interrupt)\n", + __func__, + type_c->cc_mode?"host":"device", + type_c->cc_mode ? + (type_c->is_attach?"attach":"detach") : + (type_c->is_attach?"connect":"disconnect"), + type_c->cc_mode?"device":"host", + type_c->at_cc1?"cc1":"cc2"); + dev_dbg(dev, "%s: int_status=0x%x, cc_status=0x%x\n", + __func__, + type_c->int_status, type_c->cc_status); + } + + spin_unlock_irqrestore(&type_c->lock, flags); + + return IRQ_HANDLED; +} + +static ssize_t role_swap_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + char *ptr = buf; + int count = PAGE_SIZE; + int n; + + n = scnprintf(ptr, count, + "Now cc_mode is %s and cur_mode is %s ==> Now is%s role swap\n", + ({ char *tmp; + switch (type_c->cc_mode) { + case IN_DEVICE_MODE: + tmp = "IN_DEVICE_MODE"; break; + case IN_HOST_MODE: + tmp = "IN_HOST_MODE"; break; + default: + tmp = "UNKNOWN"; break; + } tmp; }), + ({ char *tmp; + switch (type_c->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; }), + type_c->is_role_swap == ROLE_SWAP?"":" not"); + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write 1 to swap role, ex: echo 1 > role_swap\n"); + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write 0 to restore role, ex: echo 0 > role_swap\n"); + ptr += n; + count -= n; + + return ptr - buf; +} + +static ssize_t role_swap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + + if (!strncmp(buf, "1", 1)) + host_device_role_swap(type_c, TO_SWAP_ROLE); + else if (!strncmp(buf, "0", 1)) + host_device_role_swap(type_c, TO_RESTORE_ROLE); + + return count; +} +static DEVICE_ATTR_RW(role_swap); + +static ssize_t force_set_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + char *ptr = buf; + int count = PAGE_SIZE; + int n; + + n = scnprintf(ptr, count, + "Now cc_mode is %s and type c dr_mode is %s (dwc3_rtk dr_mode=%s)\n", + ({ char *tmp; + switch (type_c->cc_mode) { + case IN_DEVICE_MODE: + tmp = "IN_DEVICE_MODE"; break; + case IN_HOST_MODE: + tmp = "IN_HOST_MODE"; break; + default: + tmp = "UNKNOWN"; break; + } tmp; }), + ({ char *tmp; + switch (type_c->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; }), + ({ char *tmp; + switch (dwc3_rtk_get_dr_mode(type_c->dwc3_rtk)) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; })); + + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write host -> switch to Host mode\n"); + ptr += n; + count -= n; + + n = scnprintf(ptr, count, + "write device -> switch to Device mode\n"); + ptr += n; + count -= n; + + return ptr - buf; +} + +static ssize_t force_set_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + unsigned long flags; + u32 enable_cc; + + spin_lock_irqsave(&type_c->lock, flags); + enable_cc = type_c->at_cc1?enable_cc1:enable_cc2; + spin_unlock_irqrestore(&type_c->lock, flags); + + + if (!strncmp(buf, "host", 4)) { + switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc); + } else if (!strncmp(buf, "device", 6)) { + switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, + enable_cc); + } else { + switch_type_c_dr_mode(type_c, 0, disable_cc); + } + return count; +} +static DEVICE_ATTR_RW(force_set_mode); + +#ifdef CONFIG_DYNAMIC_DEBUG +static int type_c_parameter_show(struct seq_file *s, void *unused) +{ + struct type_c_data *type_c = s->private; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + seq_printf(s, "cc_dfp_mode %s\n", + ({ char *tmp; + switch (type_c->cc_dfp_mode) { + case CC_MODE_DFP_USB: + tmp = "CC_MODE_DFP_USB"; break; + case CC_MODE_DFP_1_5: + tmp = "CC_MODE_DFP_1_5"; break; + case CC_MODE_DFP_3_0: + tmp = "CC_MODE_DFP_3_0"; break; + default: + tmp = "?"; break; + } tmp; })); + seq_printf(s, "cc1_rp 0x%x\n", type_c->cc1_rp); + seq_printf(s, "cc1_rp_code 0x%x\n", + ({ int tmp; + switch (type_c->cc_dfp_mode) { + case CC_MODE_DFP_USB: + tmp = code_rp12k(type_c->cc1_rp_code); break; + case CC_MODE_DFP_1_5: + tmp = code_rp36k(type_c->cc1_rp_code); break; + case CC_MODE_DFP_3_0: + tmp = code_rp4pk(type_c->cc1_rp_code); break; + default: + tmp = -1; break; + } tmp; })); + seq_printf(s, "cc1_rd_code 0x%x\n", + code_rd(type_c->cc1_rd_code)); + seq_printf(s, "cc1_vref_ufp vref_1p23v 0x%x vref_0p66v 0x%x vref_0p2v 0x%x\n", + decode_1p23v(type_c->cc1_vref_ufp), + decode_0p66v(type_c->cc1_vref_ufp), + decode_0p2v(type_c->cc1_vref_ufp)); + seq_printf(s, "cc1_vref_dfp_usb vref_0_1p6v 0x%x vref_0p2v 0x%x\n", + decode_0_1p6v(type_c->cc1_vref_dfp_usb), + decode_0p2v(type_c->cc1_vref_dfp_usb)); + seq_printf(s, "cc1_vref_dfp_1_5 vref_1_1p6v 0x%x vref_0p4v 0x%x vref_0p2v 0x%x\n", + decode_1_1p6v(type_c->cc1_vref_dfp_1_5), + decode_0p4v(type_c->cc1_vref_dfp_1_5), + decode_0p2v(type_c->cc1_vref_dfp_1_5)); + seq_printf(s, "cc1_vref_dfp_3_0 vref_2p6v 0x%x vref_0p8v 0x%x vref_0p2v 0x%x\n", + decode_2p6v(type_c->cc1_vref_dfp_3_0), + decode_0p8v(type_c->cc1_vref_dfp_3_0), + decode_0p2v(type_c->cc1_vref_dfp_3_0)); + seq_printf(s, "cc2_rp 0x%x\n", type_c->cc2_rp); + seq_printf(s, "cc2_rp_code 0x%x\n", + ({ int tmp; + switch (type_c->cc_dfp_mode) { + case CC_MODE_DFP_USB: + tmp = code_rp12k(type_c->cc2_rp_code); break; + case CC_MODE_DFP_1_5: + tmp = code_rp36k(type_c->cc2_rp_code); break; + case CC_MODE_DFP_3_0: + tmp = code_rp4pk(type_c->cc2_rp_code); break; + default: + tmp = -1; break; + } tmp; })); + seq_printf(s, "cc2_rd_code 0x%x\n", + code_rd(type_c->cc2_rd_code)); + seq_printf(s, "cc2_vref_ufp vref_1p23v 0x%x vref_0p66v 0x%x vref_0p2v 0x%x\n", + decode_1p23v(type_c->cc2_vref_ufp), + decode_0p66v(type_c->cc2_vref_ufp), + decode_0p2v(type_c->cc2_vref_ufp)); + seq_printf(s, "cc2_vref_dfp_usb vref_0_1p6v 0x%x vref_0p2v 0x%x\n", + decode_0_1p6v(type_c->cc2_vref_dfp_usb), + decode_0p2v(type_c->cc2_vref_dfp_usb)); + seq_printf(s, "cc2_vref_dfp_1_5 vref_1_1p6v 0x%x vref_0p4v 0x%x vref_0p2v 0x%x\n", + decode_1_1p6v(type_c->cc2_vref_dfp_1_5), + decode_0p4v(type_c->cc2_vref_dfp_1_5), + decode_0p2v(type_c->cc2_vref_dfp_1_5)); + seq_printf(s, "cc2_vref_dfp_3_0 vref_2p6v 0x%x vref_0p8v 0x%x vref_0p2v 0x%x\n", + decode_2p6v(type_c->cc2_vref_dfp_3_0), + decode_0p8v(type_c->cc2_vref_dfp_3_0), + decode_0p2v(type_c->cc2_vref_dfp_3_0)); + seq_printf(s, "debounce_val 0x%x\n", type_c->debounce_val); + seq_puts(s, "\n"); + + spin_unlock_irqrestore(&type_c->lock, flags); + + return 0; +} + +static int type_c_parameter_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_parameter_show, inode->i_private); +} + +static const struct file_operations type_c_parameter_fops = { + .open = type_c_parameter_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int type_c_set_parameter_show(struct seq_file *s, void *unused) +{ + seq_puts(s, "cc_dfp_mode [CC_MODE_DFP_USB|CC_MODE_DFP_1_5|CC_MODE_DFP_3_0]\n"); + seq_puts(s, "cc1_rp_code 0x_value\n"); + seq_puts(s, "cc1_rd_code 0x_value\n"); + seq_puts(s, "cc1_vref_ufp_vref_1p23v 0x_value\n"); + seq_puts(s, "cc1_vref_ufp_vref_0p66v 0x_value\n"); + seq_puts(s, "cc1_vref_ufp_vref_0p2v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_usb_vref_1p6v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_usb_vref_0p2v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_1_5_vref_1p6v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_1_5_vref_0p4v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_1_5_vref_0p2v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_3_0_vref_2p6v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_3_0_vref_0p8v 0x_value\n"); + seq_puts(s, "cc1_vref_dfp_3_0_vref_0p2v 0x_value\n"); + seq_puts(s, "cc2_rp_code 0x_value\n"); + seq_puts(s, "cc2_rd_code 0x_value\n"); + seq_puts(s, "cc2_vref_ufp_vref_1p23v 0x_value\n"); + seq_puts(s, "cc2_vref_ufp_vref_0p66v 0x_value\n"); + seq_puts(s, "cc2_vref_ufp_vref_0p2v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_usb_vref_1p6v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_usb_vref_0p2v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_1_5_vref_1p6v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_1_5_vref_0p4v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_1_5_vref_0p2v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_3_0_vref_2p6v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_3_0_vref_0p8v 0x_value\n"); + seq_puts(s, "cc2_vref_dfp_3_0_vref_0p2v 0x_value\n"); + seq_puts(s, "debounce_val value\n"); + + return 0; +} + +static int type_c_set_parameter_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_set_parameter_show, inode->i_private); +} + +static ssize_t type_c_set_parameter_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct type_c_data *type_c = s->private; + unsigned long flags; + char buffer[40]; + char *buf = buffer; + u32 value; + int ret = 0; + + if (copy_from_user(&buffer, ubuf, + min_t(size_t, sizeof(buffer) - 1, count))) + return -EFAULT; + + spin_lock_irqsave(&type_c->lock, flags); + if (!strncmp(buf, "cc_dfp_mode", 11)) { + buf = buf + 11; + buf = skip_spaces(buf); + if (!strncmp(buf, "CC_MODE_DFP_USB", 15)) { + type_c->cc_dfp_mode = CC_MODE_DFP_USB; + type_c->cc1_rp = En_rp12k; + type_c->cc2_rp = En_rp12k; + } else if (!strncmp(buf, "CC_MODE_DFP_1_5", 15)) { + type_c->cc_dfp_mode = CC_MODE_DFP_1_5; + type_c->cc1_rp = En_rp36k; + type_c->cc2_rp = En_rp36k; + } else if (!strncmp(buf, "CC_MODE_DFP_3_0", 15)) { + type_c->cc_dfp_mode = CC_MODE_DFP_3_0; + type_c->cc1_rp = En_rp4p7k; + type_c->cc2_rp = En_rp4p7k; + } else { + dev_err(type_c->dev, "cc_dfp_mode UNKNOWN (%s)", buf); + } + } else if (!strncmp(buf, "cc1_rp_code", 11)) { + buf = buf + 11; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + if (type_c->cc_dfp_mode == CC_MODE_DFP_USB) + type_c->cc1_rp_code = rp12k_code(value); + else if (type_c->cc_dfp_mode == CC_MODE_DFP_1_5) + type_c->cc1_rp_code = rp36k_code(value); + else if (type_c->cc_dfp_mode == CC_MODE_DFP_3_0) + type_c->cc1_rp_code = rp4pk_code(value); + } else if (!strncmp(buf, "cc1_rd_code", 11)) { + buf = buf + 11; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_rd_code = rd_code(value); + } else if (!strncmp(buf, "cc1_vref_ufp_vref_1p23v", 23)) { + buf = buf + 23; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp & + (~vref_1p23v(0xf))) | + vref_1p23v(value); + } else if (!strncmp(buf, "cc1_vref_ufp_vref_0p66v", 23)) { + buf = buf + 23; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp & + (~vref_0p66v(0xf))) | + vref_0p66v(value); + } else if (!strncmp(buf, "cc1_vref_ufp_vref_0p2v", 22)) { + buf = buf + 22; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp & + (~vref_0p2v(0x7))) | + vref_0p2v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_usb_vref_1p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_usb = (type_c->cc1_vref_dfp_usb & + (~vref_0_1p6v(0xf))) | vref_0_1p6v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_usb_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_usb = (type_c->cc1_vref_dfp_usb & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_1p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 & + (~vref_1_1p6v(0xf))) | vref_1_1p6v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_0p4v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 & + (~vref_0p4v(0x7))) | vref_0p4v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_2p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_ufp = (type_c->cc1_vref_dfp_1_5 & + (~vref_2p6v(0x7))) | + vref_2p6v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_0p8v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_3_0 = (type_c->cc1_vref_dfp_3_0 & + (~vref_0p8v(0xf))) | vref_0p8v(value); + } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc1_vref_dfp_3_0 = (type_c->cc1_vref_dfp_3_0 & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "cc2_rp_code", 11)) { + buf = buf + 11; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + if (type_c->cc_dfp_mode == CC_MODE_DFP_USB) + type_c->cc2_rp_code = rp12k_code(value); + else if (type_c->cc_dfp_mode == CC_MODE_DFP_1_5) + type_c->cc2_rp_code = rp36k_code(value); + else if (type_c->cc_dfp_mode == CC_MODE_DFP_3_0) + type_c->cc2_rp_code = rp4pk_code(value); + } else if (!strncmp(buf, "cc2_rd_code", 11)) { + buf = buf + 11; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_rd_code = rd_code(value); + } else if (!strncmp(buf, "cc2_vref_ufp_vref_1p23v", 23)) { + buf = buf + 23; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp & + (~vref_1p23v(0xf))) | + vref_1p23v(value); + } else if (!strncmp(buf, "cc2_vref_ufp_vref_0p66v", 23)) { + buf = buf + 23; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp & + (~vref_0p66v(0xf))) | + vref_0p66v(value); + } else if (!strncmp(buf, "cc2_vref_ufp_vref_0p2v", 22)) { + buf = buf + 22; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp & + (~vref_0p2v(0x7))) | + vref_0p2v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_usb_vref_1p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_usb = (type_c->cc2_vref_dfp_usb & + (~vref_0_1p6v(0xf))) | vref_0_1p6v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_usb_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_usb = (type_c->cc2_vref_dfp_usb & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_1p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 & + (~vref_1_1p6v(0xf))) | vref_1_1p6v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_0p4v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 & + (~vref_0p4v(0x7))) | vref_0p4v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_2p6v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 & + (~vref_2p6v(0x7))) | vref_2p6v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_0p8v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 & + (~vref_0p8v(0xf))) | vref_0p8v(value); + } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_0p2v", 26)) { + buf = buf + 26; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 & + (~vref_0p2v(0x7))) | vref_0p2v(value); + } else if (!strncmp(buf, "debounce_val", 12)) { + buf = buf + 12; + buf = skip_spaces(buf); + ret = kstrtoint(buf, 0, &value); + if (ret < 0) { + dev_err(type_c->dev, "kstrtoint ret error (%d)", ret); + return -EFAULT; + } + type_c->debounce_val = value; + } else + dev_err(type_c->dev, "UNKNOWN input (%s)", buf); + + spin_unlock_irqrestore(&type_c->lock, flags); + return count; +} + +static const struct file_operations type_c_set_parameter_fops = { + .open = type_c_set_parameter_open, + .write = type_c_set_parameter_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int type_c_status_show(struct seq_file *s, void *unused) +{ + struct type_c_data *type_c = s->private; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + seq_printf(s, "Now dr_mode is %s (Is%s role swap)\n", + ({ char *tmp; + switch (type_c->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + tmp = "USB_DR_MODE_PERIPHERAL"; break; + case USB_DR_MODE_HOST: + tmp = "USB_DR_MODE_HOST"; break; + default: + tmp = "USB_DR_MODE_UNKNOWN"; break; + } tmp; }), type_c->is_role_swap == ROLE_SWAP?"":" not"); + + seq_printf(s, "In %s mode %s %s at %s (cc_status=0x%x)\n", + type_c->cc_mode?"host":"device", + type_c->cc_mode ? + (type_c->is_attach?"attach":"detach") : + (type_c->is_attach?"connect":"disconnect"), + type_c->cc_mode?"device":"host", + type_c->at_cc1?"cc1":"cc2", type_c->cc_status); + + seq_printf(s, "Read Register (type_c_ctrl_cc1_0=0x%x)\n", + readl(type_c->type_c_reg_base + 0x0)); + seq_printf(s, "Read Register (type_c_ctrl_cc1_1=0x%x)\n", + readl(type_c->type_c_reg_base + 0x4)); + seq_printf(s, "Read Register (type_c_ctrl_cc2_0=0x%x)\n", + readl(type_c->type_c_reg_base + 0x8)); + seq_printf(s, "Read Register (type_c_ctrl_cc2_1=0x%x)\n", + readl(type_c->type_c_reg_base + 0xc)); + seq_printf(s, "Read Register (type_c_status=0x%x)\n", + readl(type_c->type_c_reg_base + 0x10)); + seq_printf(s, "Read Register (type_c_ctrl=0x%x)\n", + readl(type_c->type_c_reg_base + 0x14)); + + spin_unlock_irqrestore(&type_c->lock, flags); + + return 0; +} + +static int type_c_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_status_show, inode->i_private); +} + +static const struct file_operations type_c_status_fops = { + .open = type_c_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int type_c_debug_show(struct seq_file *s, void *unused) +{ + struct type_c_data *type_c = s->private; + + seq_printf(s, "Debug: %s\n", + type_c->debug?"Enable":"disable"); + + return 0; +} + +static int type_c_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_debug_show, inode->i_private); +} + +static ssize_t type_c_debug_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct type_c_data *type_c = s->private; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "enable", 6)) + type_c->debug = true; + else if (!strncmp(buf, "disable", 7)) + type_c->debug = false; + + return count; +} + +static const struct file_operations type_c_debug_fops = { + .open = type_c_debug_open, + .write = type_c_debug_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static inline void create_debug_files(struct type_c_data *type_c) +{ + dev_err(type_c->dev, "%s", __func__); + + type_c->debug_dir = debugfs_create_dir("type_c", usb_debug_root); + if (!type_c->debug_dir) { + dev_err(type_c->dev, "%s Error debug_dir is NULL", __func__); + return; + } + + if (!debugfs_create_file("parameter", 0444, type_c->debug_dir, type_c, + &type_c_parameter_fops)) + goto file_error; + + if (!debugfs_create_file("set_parameter", 0644, + type_c->debug_dir, type_c, + &type_c_set_parameter_fops)) + goto file_error; + + if (!debugfs_create_file("status", 0444, type_c->debug_dir, type_c, + &type_c_status_fops)) + goto file_error; + + if (!debugfs_create_file("debug", 0644, + type_c->debug_dir, type_c, + &type_c_debug_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(type_c->debug_dir); +} +#endif //CONFIG_DYNAMIC_DEBUG + +#define DEFAULT_CHIP_REVISION 0xA00 +#define MAX_CHIP_REVISION 0xC00 + +static int __get_chip_revision(void) +{ + int chip_revision = 0xFFF; + char revision[] = "FFF"; + struct soc_device_attribute soc_att[] = {{.revision = revision}, {}}; + struct soc_device_attribute *soc_att_match = NULL; + + while (soc_att_match == NULL) { + chip_revision--; + + if (chip_revision <= DEFAULT_CHIP_REVISION) + break; + if (chip_revision > MAX_CHIP_REVISION) + chip_revision = MAX_CHIP_REVISION; + else if ((chip_revision & 0xFF) > 0xF) + chip_revision = (chip_revision & 0xF00) + 0xF; + + snprintf(revision, 4, "%X", chip_revision); + + soc_att_match = (struct soc_device_attribute *) + soc_device_match(soc_att); + } + + if (soc_att_match) { + pr_debug("%s get chip_revision %x\n", __func__, chip_revision); + return chip_revision; + } + + pr_err("%s ERROR: no match chip_revision\n", __func__); + return DEFAULT_CHIP_REVISION; +} + +/* Init and probe */ +static int dwc3_rtk_type_c_init(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + u32 debounce_val = type_c->debounce_val;// 1b,1us 7f,4.7us + unsigned long flags; + + enable_writel(debounce_val<<1, + type_c->type_c_reg_base + USB_TYPEC_CTRL); + dev_info(dev, "%s set debounce = 0x%x (check--> 0x%x)\n", + __func__, debounce_val, + readl(type_c->type_c_reg_base + USB_TYPEC_CTRL)); + + rtk_type_c_init(type_c); + + switch_type_c_dr_mode(type_c, 0, disable_cc); + + spin_lock_irqsave(&type_c->lock, flags); + + dev_info(dev, "First check USB_DR_MODE_PERIPHERAL"); + type_c->cc_mode = IN_DEVICE_MODE; + type_c->is_attach = IN_DETACH; + type_c->connect_change = CONNECT_NO_CHANGE; + if (type_c->boot_check_time < 0) + type_c->check_at_boot = false; + else + type_c->check_at_boot = true; + dev_info(dev, "First time device mode check is %s", + type_c->check_at_boot?"Enable":"Diable"); + + type_c->rd_en_at_first = true; + + detect_host(type_c); + + spin_unlock_irqrestore(&type_c->lock, flags); + + schedule_delayed_work(&type_c->delayed_work, + msecs_to_jiffies(0)); + + return 0; +} + +static void dwc3_rtk_type_c_probe_work(struct work_struct *work) +{ + struct type_c_data *type_c = container_of(work, + struct type_c_data, start_work); + struct device *dev = type_c->dev; + int ret = 0; + + unsigned long probe_time = jiffies; + + dev_info(dev, "%s Start ...\n", __func__); + + ret = dwc3_rtk_type_c_init(type_c); + + if (ret) + dev_err(dev, "%s failed to init type_c\n", __func__); + + dev_info(dev, "%s End ... ok! (take %d ms)\n", __func__, + jiffies_to_msecs(jiffies - probe_time)); +} + +static int dwc3_rtk_type_c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct type_c_data *type_c; + int irq; + int ret = 0; + unsigned long probe_time = jiffies; + + dev_info(dev, "ENTER %s", __func__); + type_c = devm_kzalloc(dev, sizeof(*type_c), GFP_KERNEL); + if (!type_c) + return -ENOMEM; + + type_c->type_c_reg_base = of_iomap(pdev->dev.of_node, 0); + if (type_c->type_c_reg_base == NULL) { + dev_err(&pdev->dev, "error mapping memory for reg_base\n"); + ret = -EFAULT; + goto err1; + } + + type_c->dev = dev; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Type C driver with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + ret = -ENODEV; + goto err1; + } + + ret = request_irq(irq, type_c_detect_irq, + IRQF_SHARED, "type_c_detect", type_c); + + spin_lock_init(&type_c->lock); + type_c->chip_revision = __get_chip_revision(); + + if (node && of_device_is_available(node)) { + ret = of_property_read_u32(node, "boot_check_time", + &type_c->boot_check_time); + if (ret) + type_c->boot_check_time = -1; + + dev_info(dev, + "Set device mode boot_check_time %d ms ==> (%s to check)\n", + type_c->boot_check_time, + type_c->boot_check_time < 0?"Disable":"Enable"); + + if (of_property_read_bool(node, "filter_config_channel_signal")) + type_c->filter_config_channel_signal = true; + else + type_c->filter_config_channel_signal = false; + dev_info(dev, "Set filter_config_channel_signal is %s\n", + type_c->filter_config_channel_signal ? + "True":"False"); + + } + + if (node && of_device_is_available(node)) { + const char *str; + u32 val; + struct device_node *sub_node; + u8 array_vals[3]; + u32 u32_vals[3]; + u32 default_revision; + char revision[4] = {0}; + + ret = of_property_read_u32(node, "default_revision", + &default_revision); + if (ret) { + default_revision = 0xA00; + dev_info(dev, "%s: No set default_revision (use %x)\n", + __func__, default_revision); + } + + if (!type_c->chip_revision || + type_c->chip_revision > default_revision) + snprintf(revision, 4, "%X", default_revision); + else + snprintf(revision, 4, "%X", type_c->chip_revision); + dev_info(dev, "Chip revision is %x (support revision %x) to load %s parameter\n", + type_c->chip_revision, + default_revision, revision); + sub_node = of_get_child_by_name(node, revision); + + ret = of_property_read_string(sub_node, "cc_dfp_mode", &str); + if (ret) { + dev_err(&pdev->dev, "%s: cc_dfp_mode error(%d)\n", + __func__, ret); + goto err1; + } + if (!strcmp(str, "dfp_usb")) { + type_c->cc_dfp_mode = CC_MODE_DFP_USB; + type_c->cc1_rp = En_rp12k; + type_c->cc2_rp = En_rp12k; + } else if (!strcmp(str, "dfp_1_5")) { + type_c->cc_dfp_mode = CC_MODE_DFP_1_5; + type_c->cc1_rp = En_rp36k; + type_c->cc2_rp = En_rp36k; + } else if (!strcmp(str, "dfp_3_0")) { + type_c->cc_dfp_mode = CC_MODE_DFP_3_0; + type_c->cc1_rp = En_rp4p7k; + type_c->cc2_rp = En_rp4p7k; + } else { + dev_err(&pdev->dev, "%s: unknown cc_dfp_mode %s\n", + __func__, str); + } + + //cc1 parameters + ret = of_property_read_u32(sub_node, "cc1_rp_4p7k_code", + &u32_vals[0]); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_rp_4p7k_code error(%d)\n", + __func__, u32_vals[0]); + goto err1; + } + ret = of_property_read_u32(sub_node, "cc1_rp_36k_code", + &u32_vals[1]); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_rp_36k_code error(%d)\n", + __func__, + u32_vals[1]); + goto err1; + } + ret = of_property_read_u32(sub_node, "cc1_rp_12k_code", + &u32_vals[2]); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_rp_12k_code error(%d)\n", + __func__, u32_vals[2]); + goto err1; + } + type_c->cc1_rp_code = rp4pk_code(u32_vals[0]) + | rp36k_code(u32_vals[1]) + | rp12k_code(u32_vals[2]); + + ret = of_property_read_u32(sub_node, "cc1_rd_code", &val); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_rd_code error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc1_rd_code = rd_code(val); + + ret = of_property_read_u8_array(sub_node, "cc1_vref_ufp", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_vref_ufp error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc1_vref_ufp = vref_1p23v(array_vals[0]) | + vref_0p66v(array_vals[1]) | + vref_0p2v(array_vals[2]); + + ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_usb", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_vref_dfp_usb error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc1_vref_dfp_usb = vref_0_1p6v(array_vals[0]) | + vref_0p2v(array_vals[1]); + + ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_1_5", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_vref_dfp_1_5 error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc1_vref_dfp_1_5 = vref_1_1p6v(array_vals[0]) | + vref_0p4v(array_vals[1]) | + vref_0p2v(array_vals[2]); + + ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_3_0", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc1_vref_dfp_3_0 error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc1_vref_dfp_3_0 = vref_2p6v(array_vals[0]) | + vref_0p8v(array_vals[1]) | vref_0p2v(array_vals[2]); + + //cc2 parameters + ret = of_property_read_u32(sub_node, "cc2_rp_4p7k_code", + &u32_vals[0]); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_rp_4p7k_code error(%d)\n", + __func__, ret); + goto err1; + } + ret = of_property_read_u32(sub_node, "cc2_rp_36k_code", + &u32_vals[1]); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_rp_36k_code error(%d)\n", + __func__, ret); + goto err1; + } + ret = of_property_read_u32(sub_node, "cc2_rp_12k_code", + &u32_vals[2]); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_rp_12k_code error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_rp_code = rp4pk_code(array_vals[0]) + | rp36k_code(array_vals[1]) + | rp12k_code(array_vals[2]); + + ret = of_property_read_u32(sub_node, "cc2_rd_code", &val); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_rd_code error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_rd_code = rd_code(val); + + ret = of_property_read_u8_array(sub_node, "cc2_vref_ufp", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_vref_ufp error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_vref_ufp = vref_1p23v(array_vals[0]) | + vref_0p66v(array_vals[1]) | + vref_0p2v(array_vals[2]); + + ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_usb", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_vref_dfp_usb error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_vref_dfp_usb = vref_0_1p6v(array_vals[0]) | + vref_0p2v(array_vals[1]); + + ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_1_5", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_vref_dfp_1_5 error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_vref_dfp_1_5 = vref_1_1p6v(array_vals[0]) | + vref_0p4v(array_vals[1]) | vref_0p2v(array_vals[2]); + + ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_3_0", + array_vals, 3); + if (ret) { + dev_err(&pdev->dev, "%s: cc2_vref_dfp_3_0 error(%d)\n", + __func__, ret); + goto err1; + } + type_c->cc2_vref_dfp_3_0 = vref_2p6v(array_vals[0]) | + vref_0p8v(array_vals[1]) | vref_0p2v(array_vals[2]); + + type_c->debounce_val = 0x7f;/* 1b,1us 7f,4.7us */ + } + + if (node) { + struct device_node *parent_node; + + parent_node = of_get_parent(node); + if (parent_node != NULL) { + type_c->dwc3_rtk = platform_get_drvdata( + of_find_device_by_node(parent_node)); + } else { + dev_err(dev, "%s No find dwc3_rtk", __func__); + ret = -ENODEV; + goto err1; + } + } + + type_c->is_attach = IN_DETACH; + type_c->is_role_swap = NO_ROLE_SWAP; + + if (of_property_read_bool(node, "debug")) { + dev_info(&pdev->dev, "%s device tree set debug flag\n", + __func__); + type_c->debug = true; + } else { + type_c->debug = false; + } + + INIT_DELAYED_WORK(&type_c->delayed_work, host_device_switch); + INIT_DELAYED_WORK(&type_c->boot_check_work, boot_time_check); + + if (node) { + if (of_property_read_bool(node, "delay_probe_work")) { + INIT_WORK(&type_c->start_work, + dwc3_rtk_type_c_probe_work); + + schedule_work(&type_c->start_work); + } else { + ret = dwc3_rtk_type_c_init(type_c); + if (ret) { + dev_err(dev, "%s failed to init type_c\n", + __func__); + goto err1; + } + } + } else { + dev_err(dev, "no device node, failed to init type_c\n"); + ret = -ENODEV; + goto err1; + } + + platform_set_drvdata(pdev, type_c); + + device_create_file(type_c->dev, &dev_attr_role_swap); + device_create_file(type_c->dev, &dev_attr_force_set_mode); + +#ifdef CONFIG_DYNAMIC_DEBUG + create_debug_files(type_c); +#endif + + dev_info(&pdev->dev, "Exit %s OK (take %d ms)\n", __func__, + jiffies_to_msecs(jiffies - probe_time)); + return 0; + +err1: + dev_err(&pdev->dev, "%s: Probe fail, %d\n", __func__, ret); + + return ret; +} + +static int dwc3_rtk_type_c_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct type_c_data *type_c = dev_get_drvdata(dev); + u32 default_ctrl; + unsigned long flags; + + dev_info(dev, "[USB] Enter %s", __func__); + +#ifdef CONFIG_DYNAMIC_DEBUG + debugfs_remove_recursive(type_c->debug_dir); +#endif + + device_remove_file(type_c->dev, &dev_attr_role_swap); + device_remove_file(type_c->dev, &dev_attr_force_set_mode); + + cancel_delayed_work_sync(&type_c->delayed_work); + flush_delayed_work(&type_c->delayed_work); + BUG_ON(delayed_work_pending(&type_c->delayed_work)); + + cancel_delayed_work_sync(&type_c->boot_check_work); + flush_delayed_work(&type_c->boot_check_work); + BUG_ON(delayed_work_pending(&type_c->boot_check_work)); + + spin_lock_irqsave(&type_c->lock, flags); + /* disable interrupt */ + default_ctrl = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL) & + debounce_time_MASK; + writel(default_ctrl, type_c->type_c_reg_base + USB_TYPEC_CTRL); + + spin_unlock_irqrestore(&type_c->lock, flags); + + dev_info(&pdev->dev, "[USB] Exit %s\n", __func__); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id rtk_dwc3_type_c_match[] = { + { .compatible = "realtek,dwc3-type_c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtk_dwc3_type_c_match); +#endif + +#ifdef CONFIG_PM_SLEEP +static int dwc3_rtk_type_c_prepare(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + u32 default_ctrl; + unsigned long flags; + int ret = 0; + + dev_info(dev, "[USB] Enter %s\n", __func__); + + if (!type_c) { + dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__); + goto out; + } + + cancel_delayed_work_sync(&type_c->delayed_work); + flush_delayed_work(&type_c->delayed_work); + BUG_ON(delayed_work_pending(&type_c->delayed_work)); + + cancel_delayed_work_sync(&type_c->boot_check_work); + flush_delayed_work(&type_c->boot_check_work); + BUG_ON(delayed_work_pending(&type_c->boot_check_work)); + + spin_lock_irqsave(&type_c->lock, flags); + /* disable interrupt */ + default_ctrl = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL) & + debounce_time_MASK; + writel(default_ctrl, type_c->type_c_reg_base + USB_TYPEC_CTRL); + + spin_unlock_irqrestore(&type_c->lock, flags); + +out: + dev_info(dev, "[USB] Exit %s\n", __func__); + return ret; +} + +static void dwc3_rtk_type_c_complete(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + + dev_info(dev, "[USB] Enter %s\n", __func__); + + if (!type_c) { + dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__); + goto out; + } + +out: + dev_info(dev, "[USB] Exit %s\n", __func__); +} + +static int dwc3_rtk_type_c_suspend(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + + dev_info(dev, "[USB] Enter %s", __func__); + + if (!type_c) { + dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__); + goto out; + } + +out: + dev_info(dev, "[USB] Exit %s\n", __func__); + return 0; +} + +static int dwc3_rtk_type_c_resume(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + bool reinit = false; + + dev_info(dev, "[USB] Enter %s", __func__); + + if (!type_c) { + dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__); + goto out; + } + + dev_info(dev, "[USB] %s reinit dwc3_rtk_type_c_init\n", + __func__); + + if (type_c->is_attach == IN_ATTACH) { + u32 enable_cc; + + rtk_type_c_init(type_c); + enable_writel(ENABLE_TYPE_C_DETECT, + type_c->type_c_reg_base + USB_TYPEC_CTRL); + enable_cc = type_c->at_cc1?enable_cc1:enable_cc2; + switch_type_c_plug_config(type_c, + type_c->dr_mode, enable_cc); + } else { + reinit = true; + } + + if (reinit) + dwc3_rtk_type_c_init(type_c); + +out: + dev_info(dev, "[USB] Exit %s\n", __func__); + return 0; +} + +static const struct dev_pm_ops dwc3_rtk_type_c_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS( + dwc3_rtk_type_c_suspend, dwc3_rtk_type_c_resume) + .prepare = dwc3_rtk_type_c_prepare, + .complete = dwc3_rtk_type_c_complete, +}; + +#define DEV_PM_OPS (&dwc3_rtk_type_c_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver dwc3_rtk_type_c_driver = { + .probe = dwc3_rtk_type_c_probe, + .remove = dwc3_rtk_type_c_remove, + .driver = { + .name = "rtk-dwc3-type_c", + .of_match_table = of_match_ptr(rtk_dwc3_type_c_match), + .pm = DEV_PM_OPS, + }, +}; + +static int __init dwc3_rtk_type_c_driver_init(void) +{ + return platform_driver_register(&(dwc3_rtk_type_c_driver)); +} +module_init(dwc3_rtk_type_c_driver_init); + +static void __exit dwc3_rtk_type_c_driver_exit(void) +{ + platform_driver_unregister(&(dwc3_rtk_type_c_driver)); +} +module_exit(dwc3_rtk_type_c_driver_exit); + +MODULE_ALIAS("platform:rtk-dwc3-type_c"); +MODULE_LICENSE("GPL"); + -- 2.28.0