15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b3f4e727SChunfeng Yun /*
3b3f4e727SChunfeng Yun * mtu3_dr.c - dual role switch and host glue layer
4b3f4e727SChunfeng Yun *
5b3f4e727SChunfeng Yun * Copyright (C) 2016 MediaTek Inc.
6b3f4e727SChunfeng Yun *
7b3f4e727SChunfeng Yun * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
8b3f4e727SChunfeng Yun */
9b3f4e727SChunfeng Yun
10b3f4e727SChunfeng Yun #include <linux/clk.h>
11b3f4e727SChunfeng Yun #include <linux/irq.h>
12b3f4e727SChunfeng Yun #include <linux/kernel.h>
13b3f4e727SChunfeng Yun #include <linux/mfd/syscon.h>
14484468fbSRob Herring #include <linux/of.h>
15091b813dSRob Herring #include <linux/of_platform.h>
16b3f4e727SChunfeng Yun #include <linux/regmap.h>
17b3f4e727SChunfeng Yun
18b3f4e727SChunfeng Yun #include "mtu3.h"
19b3f4e727SChunfeng Yun #include "mtu3_dr.h"
20b3f4e727SChunfeng Yun
21f0ede2c6SChunfeng Yun /* mt8173 etc */
22f0ede2c6SChunfeng Yun #define PERI_WK_CTRL1 0x4
23f0ede2c6SChunfeng Yun #define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
24f0ede2c6SChunfeng Yun #define WC1_IS_EN BIT(25)
25f0ede2c6SChunfeng Yun #define WC1_IS_P BIT(6) /* polarity for ip sleep */
26f0ede2c6SChunfeng Yun
27b1a34458SChunfeng Yun /* mt8183 */
28b1a34458SChunfeng Yun #define PERI_WK_CTRL0 0x0
29b1a34458SChunfeng Yun #define WC0_IS_C(x) ((u32)(((x) & 0xf) << 28)) /* cycle debounce */
30b1a34458SChunfeng Yun #define WC0_IS_P BIT(12) /* polarity */
31b1a34458SChunfeng Yun #define WC0_IS_EN BIT(6)
32b1a34458SChunfeng Yun
33a099d368SChunfeng Yun /* mt8192 */
34a099d368SChunfeng Yun #define WC0_SSUSB0_CDEN BIT(6)
35a099d368SChunfeng Yun #define WC0_IS_SPM_EN BIT(1)
36a099d368SChunfeng Yun
37*4158af05SAngeloGioacchino Del Regno /* mt8195 */
38*4158af05SAngeloGioacchino Del Regno #define PERI_WK_CTRL0_8195 0x04
39*4158af05SAngeloGioacchino Del Regno #define WC0_IS_P_95 BIT(30) /* polarity */
40*4158af05SAngeloGioacchino Del Regno #define WC0_IS_C_95(x) ((u32)(((x) & 0x7) << 27))
41*4158af05SAngeloGioacchino Del Regno #define WC0_IS_EN_P3_95 BIT(26)
42*4158af05SAngeloGioacchino Del Regno #define WC0_IS_EN_P2_95 BIT(25)
43*4158af05SAngeloGioacchino Del Regno
44*4158af05SAngeloGioacchino Del Regno #define PERI_WK_CTRL1_8195 0x20
45*4158af05SAngeloGioacchino Del Regno #define WC1_IS_C_95(x) ((u32)(((x) & 0xf) << 28))
46*4158af05SAngeloGioacchino Del Regno #define WC1_IS_P_95 BIT(12)
47*4158af05SAngeloGioacchino Del Regno #define WC1_IS_EN_P0_95 BIT(6)
48*4158af05SAngeloGioacchino Del Regno
49f0ede2c6SChunfeng Yun /* mt2712 etc */
50f0ede2c6SChunfeng Yun #define PERI_SSUSB_SPM_CTRL 0x0
51f0ede2c6SChunfeng Yun #define SSC_IP_SLEEP_EN BIT(4)
52f0ede2c6SChunfeng Yun #define SSC_SPM_INT_EN BIT(1)
53f0ede2c6SChunfeng Yun
54f0ede2c6SChunfeng Yun enum ssusb_uwk_vers {
55f0ede2c6SChunfeng Yun SSUSB_UWK_V1 = 1,
56f0ede2c6SChunfeng Yun SSUSB_UWK_V2,
57b1a34458SChunfeng Yun SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */
58a099d368SChunfeng Yun SSUSB_UWK_V1_2, /* specific revision 1.02 */
59*4158af05SAngeloGioacchino Del Regno SSUSB_UWK_V1_3, /* mt8195 IP0 */
60*4158af05SAngeloGioacchino Del Regno SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */
61*4158af05SAngeloGioacchino Del Regno SSUSB_UWK_V1_6, /* mt8195 IP3 */
62f0ede2c6SChunfeng Yun };
63b3f4e727SChunfeng Yun
64b3f4e727SChunfeng Yun /*
65b3f4e727SChunfeng Yun * ip-sleep wakeup mode:
66b3f4e727SChunfeng Yun * all clocks can be turn off, but power domain should be kept on
67b3f4e727SChunfeng Yun */
ssusb_wakeup_ip_sleep_set(struct ssusb_mtk * ssusb,bool enable)68f0ede2c6SChunfeng Yun static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable)
69b3f4e727SChunfeng Yun {
70f0ede2c6SChunfeng Yun u32 reg, msk, val;
71b3f4e727SChunfeng Yun
72f0ede2c6SChunfeng Yun switch (ssusb->uwk_vers) {
73f0ede2c6SChunfeng Yun case SSUSB_UWK_V1:
74f0ede2c6SChunfeng Yun reg = ssusb->uwk_reg_base + PERI_WK_CTRL1;
75f0ede2c6SChunfeng Yun msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
76f0ede2c6SChunfeng Yun val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
77f0ede2c6SChunfeng Yun break;
78b1a34458SChunfeng Yun case SSUSB_UWK_V1_1:
79b1a34458SChunfeng Yun reg = ssusb->uwk_reg_base + PERI_WK_CTRL0;
80b1a34458SChunfeng Yun msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P;
81d98a30ccSChunfeng Yun val = enable ? (WC0_IS_EN | WC0_IS_C(0x1)) : 0;
82b1a34458SChunfeng Yun break;
83a099d368SChunfeng Yun case SSUSB_UWK_V1_2:
84a099d368SChunfeng Yun reg = ssusb->uwk_reg_base + PERI_WK_CTRL0;
85a099d368SChunfeng Yun msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN;
86a099d368SChunfeng Yun val = enable ? msk : 0;
87a099d368SChunfeng Yun break;
88*4158af05SAngeloGioacchino Del Regno case SSUSB_UWK_V1_3:
89*4158af05SAngeloGioacchino Del Regno reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8195;
90*4158af05SAngeloGioacchino Del Regno msk = WC1_IS_EN_P0_95 | WC1_IS_C_95(0xf) | WC1_IS_P_95;
91*4158af05SAngeloGioacchino Del Regno val = enable ? (WC1_IS_EN_P0_95 | WC1_IS_C_95(0x1)) : 0;
92*4158af05SAngeloGioacchino Del Regno break;
93*4158af05SAngeloGioacchino Del Regno case SSUSB_UWK_V1_5:
94*4158af05SAngeloGioacchino Del Regno reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195;
95*4158af05SAngeloGioacchino Del Regno msk = WC0_IS_EN_P2_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
96*4158af05SAngeloGioacchino Del Regno val = enable ? (WC0_IS_EN_P2_95 | WC0_IS_C_95(0x1)) : 0;
97*4158af05SAngeloGioacchino Del Regno break;
98*4158af05SAngeloGioacchino Del Regno case SSUSB_UWK_V1_6:
99*4158af05SAngeloGioacchino Del Regno reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195;
100*4158af05SAngeloGioacchino Del Regno msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
101*4158af05SAngeloGioacchino Del Regno val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0;
102*4158af05SAngeloGioacchino Del Regno break;
103f0ede2c6SChunfeng Yun case SSUSB_UWK_V2:
104f0ede2c6SChunfeng Yun reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
105f0ede2c6SChunfeng Yun msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
106f0ede2c6SChunfeng Yun val = enable ? msk : 0;
107f0ede2c6SChunfeng Yun break;
108f0ede2c6SChunfeng Yun default:
109f0ede2c6SChunfeng Yun return;
110b90c6d10SFengguang Wu }
111f0ede2c6SChunfeng Yun regmap_update_bits(ssusb->uwk, reg, msk, val);
112b3f4e727SChunfeng Yun }
113b3f4e727SChunfeng Yun
ssusb_wakeup_of_property_parse(struct ssusb_mtk * ssusb,struct device_node * dn)114b3f4e727SChunfeng Yun int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
115b3f4e727SChunfeng Yun struct device_node *dn)
116b3f4e727SChunfeng Yun {
117f0ede2c6SChunfeng Yun struct of_phandle_args args;
118f0ede2c6SChunfeng Yun int ret;
119b3f4e727SChunfeng Yun
120f0ede2c6SChunfeng Yun /* wakeup function is optional */
121f0ede2c6SChunfeng Yun ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source");
122f0ede2c6SChunfeng Yun if (!ssusb->uwk_en)
123b3f4e727SChunfeng Yun return 0;
124b3f4e727SChunfeng Yun
125f0ede2c6SChunfeng Yun ret = of_parse_phandle_with_fixed_args(dn,
126f0ede2c6SChunfeng Yun "mediatek,syscon-wakeup", 2, 0, &args);
127f0ede2c6SChunfeng Yun if (ret)
128f0ede2c6SChunfeng Yun return ret;
129f0ede2c6SChunfeng Yun
130f0ede2c6SChunfeng Yun ssusb->uwk_reg_base = args.args[0];
131f0ede2c6SChunfeng Yun ssusb->uwk_vers = args.args[1];
132f0ede2c6SChunfeng Yun ssusb->uwk = syscon_node_to_regmap(args.np);
133f0ede2c6SChunfeng Yun of_node_put(args.np);
134f0ede2c6SChunfeng Yun dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n",
135f0ede2c6SChunfeng Yun ssusb->uwk_reg_base, ssusb->uwk_vers);
136f0ede2c6SChunfeng Yun
137f0ede2c6SChunfeng Yun return PTR_ERR_OR_ZERO(ssusb->uwk);
138b3f4e727SChunfeng Yun }
139b3f4e727SChunfeng Yun
ssusb_wakeup_set(struct ssusb_mtk * ssusb,bool enable)140f0ede2c6SChunfeng Yun void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
141f0ede2c6SChunfeng Yun {
142f0ede2c6SChunfeng Yun if (ssusb->uwk_en)
143f0ede2c6SChunfeng Yun ssusb_wakeup_ip_sleep_set(ssusb, enable);
144b3f4e727SChunfeng Yun }
145b3f4e727SChunfeng Yun
host_ports_num_get(struct ssusb_mtk * ssusb)146b3f4e727SChunfeng Yun static void host_ports_num_get(struct ssusb_mtk *ssusb)
147b3f4e727SChunfeng Yun {
148b3f4e727SChunfeng Yun u32 xhci_cap;
149b3f4e727SChunfeng Yun
150b3f4e727SChunfeng Yun xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
151b3f4e727SChunfeng Yun ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
152b3f4e727SChunfeng Yun ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
153b3f4e727SChunfeng Yun
154b3f4e727SChunfeng Yun dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
155b3f4e727SChunfeng Yun ssusb->u2_ports, ssusb->u3_ports);
156b3f4e727SChunfeng Yun }
157b3f4e727SChunfeng Yun
158b3f4e727SChunfeng Yun /* only configure ports will be used later */
ssusb_host_enable(struct ssusb_mtk * ssusb)1590609c1aaSChunfeng Yun static int ssusb_host_enable(struct ssusb_mtk *ssusb)
160b3f4e727SChunfeng Yun {
161b3f4e727SChunfeng Yun void __iomem *ibase = ssusb->ippc_base;
162b3f4e727SChunfeng Yun int num_u3p = ssusb->u3_ports;
163b3f4e727SChunfeng Yun int num_u2p = ssusb->u2_ports;
16418106234SColin Ian King int u3_ports_disabled;
165b3f4e727SChunfeng Yun u32 check_clk;
166b3f4e727SChunfeng Yun u32 value;
167b3f4e727SChunfeng Yun int i;
168b3f4e727SChunfeng Yun
169b3f4e727SChunfeng Yun /* power on host ip */
170b3f4e727SChunfeng Yun mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
171b3f4e727SChunfeng Yun
172076f1a89SChunfeng Yun /* power on and enable u3 ports except skipped ones */
17318106234SColin Ian King u3_ports_disabled = 0;
174b3f4e727SChunfeng Yun for (i = 0; i < num_u3p; i++) {
175076f1a89SChunfeng Yun if ((0x1 << i) & ssusb->u3p_dis_msk) {
17618106234SColin Ian King u3_ports_disabled++;
177076f1a89SChunfeng Yun continue;
178076f1a89SChunfeng Yun }
179076f1a89SChunfeng Yun
180b3f4e727SChunfeng Yun value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
181b3f4e727SChunfeng Yun value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
182b3f4e727SChunfeng Yun value |= SSUSB_U3_PORT_HOST_SEL;
183b3f4e727SChunfeng Yun mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
184b3f4e727SChunfeng Yun }
185b3f4e727SChunfeng Yun
186b3f4e727SChunfeng Yun /* power on and enable all u2 ports */
187b3f4e727SChunfeng Yun for (i = 0; i < num_u2p; i++) {
188d7e12724SChunfeng Yun if ((0x1 << i) & ssusb->u2p_dis_msk)
189d7e12724SChunfeng Yun continue;
190d7e12724SChunfeng Yun
191b3f4e727SChunfeng Yun value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
192b3f4e727SChunfeng Yun value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
193b3f4e727SChunfeng Yun value |= SSUSB_U2_PORT_HOST_SEL;
194b3f4e727SChunfeng Yun mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
195b3f4e727SChunfeng Yun }
196b3f4e727SChunfeng Yun
197b3f4e727SChunfeng Yun check_clk = SSUSB_XHCI_RST_B_STS;
19818106234SColin Ian King if (num_u3p > u3_ports_disabled)
199b3f4e727SChunfeng Yun check_clk = SSUSB_U3_MAC_RST_B_STS;
200b3f4e727SChunfeng Yun
201b3f4e727SChunfeng Yun return ssusb_check_clocks(ssusb, check_clk);
202b3f4e727SChunfeng Yun }
203b3f4e727SChunfeng Yun
ssusb_host_disable(struct ssusb_mtk * ssusb)2040609c1aaSChunfeng Yun static int ssusb_host_disable(struct ssusb_mtk *ssusb)
205b3f4e727SChunfeng Yun {
206b3f4e727SChunfeng Yun void __iomem *ibase = ssusb->ippc_base;
207b3f4e727SChunfeng Yun int num_u3p = ssusb->u3_ports;
208b3f4e727SChunfeng Yun int num_u2p = ssusb->u2_ports;
209b3f4e727SChunfeng Yun u32 value;
210b3f4e727SChunfeng Yun int i;
211b3f4e727SChunfeng Yun
212076f1a89SChunfeng Yun /* power down and disable u3 ports except skipped ones */
213b3f4e727SChunfeng Yun for (i = 0; i < num_u3p; i++) {
214076f1a89SChunfeng Yun if ((0x1 << i) & ssusb->u3p_dis_msk)
215076f1a89SChunfeng Yun continue;
216076f1a89SChunfeng Yun
217b3f4e727SChunfeng Yun value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
2180609c1aaSChunfeng Yun value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
219b3f4e727SChunfeng Yun mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
220b3f4e727SChunfeng Yun }
221b3f4e727SChunfeng Yun
222d7e12724SChunfeng Yun /* power down and disable u2 ports except skipped ones */
223b3f4e727SChunfeng Yun for (i = 0; i < num_u2p; i++) {
224d7e12724SChunfeng Yun if ((0x1 << i) & ssusb->u2p_dis_msk)
225d7e12724SChunfeng Yun continue;
226d7e12724SChunfeng Yun
227b3f4e727SChunfeng Yun value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
2280609c1aaSChunfeng Yun value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
229b3f4e727SChunfeng Yun mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
230b3f4e727SChunfeng Yun }
231b3f4e727SChunfeng Yun
232b3f4e727SChunfeng Yun /* power down host ip */
233b3f4e727SChunfeng Yun mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
234b3f4e727SChunfeng Yun
235b3f4e727SChunfeng Yun return 0;
2360609c1aaSChunfeng Yun }
2370609c1aaSChunfeng Yun
ssusb_host_resume(struct ssusb_mtk * ssusb,bool p0_skipped)2380609c1aaSChunfeng Yun int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped)
2390609c1aaSChunfeng Yun {
2400609c1aaSChunfeng Yun void __iomem *ibase = ssusb->ippc_base;
2410609c1aaSChunfeng Yun int u3p_skip_msk = ssusb->u3p_dis_msk;
2420609c1aaSChunfeng Yun int u2p_skip_msk = ssusb->u2p_dis_msk;
2430609c1aaSChunfeng Yun int num_u3p = ssusb->u3_ports;
2440609c1aaSChunfeng Yun int num_u2p = ssusb->u2_ports;
2450609c1aaSChunfeng Yun u32 value;
2460609c1aaSChunfeng Yun int i;
2470609c1aaSChunfeng Yun
2480609c1aaSChunfeng Yun if (p0_skipped) {
2490609c1aaSChunfeng Yun u2p_skip_msk |= 0x1;
2500609c1aaSChunfeng Yun if (ssusb->otg_switch.is_u3_drd)
2510609c1aaSChunfeng Yun u3p_skip_msk |= 0x1;
2520609c1aaSChunfeng Yun }
2530609c1aaSChunfeng Yun
2540609c1aaSChunfeng Yun /* power on host ip */
2550609c1aaSChunfeng Yun mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
2560609c1aaSChunfeng Yun
2570609c1aaSChunfeng Yun /* power on u3 ports except skipped ones */
2580609c1aaSChunfeng Yun for (i = 0; i < num_u3p; i++) {
2590609c1aaSChunfeng Yun if ((0x1 << i) & u3p_skip_msk)
2600609c1aaSChunfeng Yun continue;
2610609c1aaSChunfeng Yun
2620609c1aaSChunfeng Yun value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
2630609c1aaSChunfeng Yun value &= ~SSUSB_U3_PORT_PDN;
2640609c1aaSChunfeng Yun mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
2650609c1aaSChunfeng Yun }
2660609c1aaSChunfeng Yun
2670609c1aaSChunfeng Yun /* power on all u2 ports except skipped ones */
2680609c1aaSChunfeng Yun for (i = 0; i < num_u2p; i++) {
2690609c1aaSChunfeng Yun if ((0x1 << i) & u2p_skip_msk)
2700609c1aaSChunfeng Yun continue;
2710609c1aaSChunfeng Yun
2720609c1aaSChunfeng Yun value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
2730609c1aaSChunfeng Yun value &= ~SSUSB_U2_PORT_PDN;
2740609c1aaSChunfeng Yun mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
2750609c1aaSChunfeng Yun }
2760609c1aaSChunfeng Yun
2770609c1aaSChunfeng Yun return 0;
2780609c1aaSChunfeng Yun }
2790609c1aaSChunfeng Yun
2800609c1aaSChunfeng Yun /* here not skip port0 due to PDN can be set repeatedly */
ssusb_host_suspend(struct ssusb_mtk * ssusb)2810609c1aaSChunfeng Yun int ssusb_host_suspend(struct ssusb_mtk *ssusb)
2820609c1aaSChunfeng Yun {
2830609c1aaSChunfeng Yun void __iomem *ibase = ssusb->ippc_base;
2840609c1aaSChunfeng Yun int num_u3p = ssusb->u3_ports;
2850609c1aaSChunfeng Yun int num_u2p = ssusb->u2_ports;
2860609c1aaSChunfeng Yun u32 value;
2870609c1aaSChunfeng Yun int i;
2880609c1aaSChunfeng Yun
2890609c1aaSChunfeng Yun /* power down u3 ports except skipped ones */
2900609c1aaSChunfeng Yun for (i = 0; i < num_u3p; i++) {
2910609c1aaSChunfeng Yun if ((0x1 << i) & ssusb->u3p_dis_msk)
2920609c1aaSChunfeng Yun continue;
2930609c1aaSChunfeng Yun
2940609c1aaSChunfeng Yun value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
2950609c1aaSChunfeng Yun value |= SSUSB_U3_PORT_PDN;
2960609c1aaSChunfeng Yun mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
2970609c1aaSChunfeng Yun }
2980609c1aaSChunfeng Yun
2990609c1aaSChunfeng Yun /* power down u2 ports except skipped ones */
3000609c1aaSChunfeng Yun for (i = 0; i < num_u2p; i++) {
3010609c1aaSChunfeng Yun if ((0x1 << i) & ssusb->u2p_dis_msk)
3020609c1aaSChunfeng Yun continue;
3030609c1aaSChunfeng Yun
3040609c1aaSChunfeng Yun value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
3050609c1aaSChunfeng Yun value |= SSUSB_U2_PORT_PDN;
3060609c1aaSChunfeng Yun mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
3070609c1aaSChunfeng Yun }
3080609c1aaSChunfeng Yun
3090609c1aaSChunfeng Yun /* power down host ip */
3100609c1aaSChunfeng Yun mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
311b3f4e727SChunfeng Yun
3126b587394SChunfeng Yun return 0;
313b3f4e727SChunfeng Yun }
314b3f4e727SChunfeng Yun
ssusb_host_setup(struct ssusb_mtk * ssusb)315b3f4e727SChunfeng Yun static void ssusb_host_setup(struct ssusb_mtk *ssusb)
316b3f4e727SChunfeng Yun {
317b3f4e727SChunfeng Yun host_ports_num_get(ssusb);
318b3f4e727SChunfeng Yun
319b3f4e727SChunfeng Yun /*
320b3f4e727SChunfeng Yun * power on host and power on/enable all ports
321b3f4e727SChunfeng Yun * if support OTG, gadget driver will switch port0 to device mode
322b3f4e727SChunfeng Yun */
323b3f4e727SChunfeng Yun ssusb_host_enable(ssusb);
324c776f2c3SChunfeng Yun ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
325c776f2c3SChunfeng Yun
326d0ed062aSChunfeng Yun /* if port0 supports dual-role, works as host mode by default */
327d0ed062aSChunfeng Yun ssusb_set_vbus(&ssusb->otg_switch, 1);
328b3f4e727SChunfeng Yun }
329b3f4e727SChunfeng Yun
ssusb_host_cleanup(struct ssusb_mtk * ssusb)330b3f4e727SChunfeng Yun static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
331b3f4e727SChunfeng Yun {
332d0ed062aSChunfeng Yun if (ssusb->is_host)
333d0ed062aSChunfeng Yun ssusb_set_vbus(&ssusb->otg_switch, 0);
334d0ed062aSChunfeng Yun
3350609c1aaSChunfeng Yun ssusb_host_disable(ssusb);
336b3f4e727SChunfeng Yun }
337b3f4e727SChunfeng Yun
338b3f4e727SChunfeng Yun /*
339b3f4e727SChunfeng Yun * If host supports multiple ports, the VBUSes(5V) of ports except port0
340b3f4e727SChunfeng Yun * which supports OTG are better to be enabled by default in DTS.
341b3f4e727SChunfeng Yun * Because the host driver will keep link with devices attached when system
342b3f4e727SChunfeng Yun * enters suspend mode, so no need to control VBUSes after initialization.
343b3f4e727SChunfeng Yun */
ssusb_host_init(struct ssusb_mtk * ssusb,struct device_node * parent_dn)344b3f4e727SChunfeng Yun int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
345b3f4e727SChunfeng Yun {
346b3f4e727SChunfeng Yun struct device *parent_dev = ssusb->dev;
347b3f4e727SChunfeng Yun int ret;
348b3f4e727SChunfeng Yun
349b3f4e727SChunfeng Yun ssusb_host_setup(ssusb);
350b3f4e727SChunfeng Yun
351b3f4e727SChunfeng Yun ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
352b3f4e727SChunfeng Yun if (ret) {
353d9241ff2SRob Herring dev_dbg(parent_dev, "failed to create child devices at %pOF\n",
354d9241ff2SRob Herring parent_dn);
355b3f4e727SChunfeng Yun return ret;
356b3f4e727SChunfeng Yun }
357b3f4e727SChunfeng Yun
358b3f4e727SChunfeng Yun dev_info(parent_dev, "xHCI platform device register success...\n");
359b3f4e727SChunfeng Yun
360b3f4e727SChunfeng Yun return 0;
361b3f4e727SChunfeng Yun }
362b3f4e727SChunfeng Yun
ssusb_host_exit(struct ssusb_mtk * ssusb)363b3f4e727SChunfeng Yun void ssusb_host_exit(struct ssusb_mtk *ssusb)
364b3f4e727SChunfeng Yun {
365b3f4e727SChunfeng Yun of_platform_depopulate(ssusb->dev);
366b3f4e727SChunfeng Yun ssusb_host_cleanup(ssusb);
367b3f4e727SChunfeng Yun }
368