xref: /linux/drivers/usb/mtu3/mtu3_host.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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