1 // SPDX-License-Identifier: GPL-2.0
3 * mtu3_dr.c - dual role switch and host glue layer
5 * Copyright (C) 2016 MediaTek Inc.
7 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
10 #include <linux/clk.h>
11 #include <linux/iopoll.h>
12 #include <linux/irq.h>
13 #include <linux/kernel.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/of_device.h>
16 #include <linux/regmap.h>
22 #define PERI_WK_CTRL1 0x4
23 #define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
24 #define WC1_IS_EN BIT(25)
25 #define WC1_IS_P BIT(6) /* polarity for ip sleep */
28 #define PERI_SSUSB_SPM_CTRL 0x0
29 #define SSC_IP_SLEEP_EN BIT(4)
30 #define SSC_SPM_INT_EN BIT(1)
38 * ip-sleep wakeup mode:
39 * all clocks can be turn off, but power domain should be kept on
41 static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk
*ssusb
, bool enable
)
45 switch (ssusb
->uwk_vers
) {
47 reg
= ssusb
->uwk_reg_base
+ PERI_WK_CTRL1
;
48 msk
= WC1_IS_EN
| WC1_IS_C(0xf) | WC1_IS_P
;
49 val
= enable
? (WC1_IS_EN
| WC1_IS_C(0x8)) : 0;
52 reg
= ssusb
->uwk_reg_base
+ PERI_SSUSB_SPM_CTRL
;
53 msk
= SSC_IP_SLEEP_EN
| SSC_SPM_INT_EN
;
54 val
= enable
? msk
: 0;
59 regmap_update_bits(ssusb
->uwk
, reg
, msk
, val
);
62 int ssusb_wakeup_of_property_parse(struct ssusb_mtk
*ssusb
,
63 struct device_node
*dn
)
65 struct of_phandle_args args
;
68 /* wakeup function is optional */
69 ssusb
->uwk_en
= of_property_read_bool(dn
, "wakeup-source");
73 ret
= of_parse_phandle_with_fixed_args(dn
,
74 "mediatek,syscon-wakeup", 2, 0, &args
);
78 ssusb
->uwk_reg_base
= args
.args
[0];
79 ssusb
->uwk_vers
= args
.args
[1];
80 ssusb
->uwk
= syscon_node_to_regmap(args
.np
);
82 dev_info(ssusb
->dev
, "uwk - reg:0x%x, version:%d\n",
83 ssusb
->uwk_reg_base
, ssusb
->uwk_vers
);
85 return PTR_ERR_OR_ZERO(ssusb
->uwk
);
88 void ssusb_wakeup_set(struct ssusb_mtk
*ssusb
, bool enable
)
91 ssusb_wakeup_ip_sleep_set(ssusb
, enable
);
94 static void host_ports_num_get(struct ssusb_mtk
*ssusb
)
98 xhci_cap
= mtu3_readl(ssusb
->ippc_base
, U3D_SSUSB_IP_XHCI_CAP
);
99 ssusb
->u2_ports
= SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap
);
100 ssusb
->u3_ports
= SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap
);
102 dev_dbg(ssusb
->dev
, "host - u2_ports:%d, u3_ports:%d\n",
103 ssusb
->u2_ports
, ssusb
->u3_ports
);
106 /* only configure ports will be used later */
107 int ssusb_host_enable(struct ssusb_mtk
*ssusb
)
109 void __iomem
*ibase
= ssusb
->ippc_base
;
110 int num_u3p
= ssusb
->u3_ports
;
111 int num_u2p
= ssusb
->u2_ports
;
112 int u3_ports_disabed
;
117 /* power on host ip */
118 mtu3_clrbits(ibase
, U3D_SSUSB_IP_PW_CTRL1
, SSUSB_IP_HOST_PDN
);
120 /* power on and enable u3 ports except skipped ones */
121 u3_ports_disabed
= 0;
122 for (i
= 0; i
< num_u3p
; i
++) {
123 if ((0x1 << i
) & ssusb
->u3p_dis_msk
) {
128 value
= mtu3_readl(ibase
, SSUSB_U3_CTRL(i
));
129 value
&= ~(SSUSB_U3_PORT_PDN
| SSUSB_U3_PORT_DIS
);
130 value
|= SSUSB_U3_PORT_HOST_SEL
;
131 mtu3_writel(ibase
, SSUSB_U3_CTRL(i
), value
);
134 /* power on and enable all u2 ports */
135 for (i
= 0; i
< num_u2p
; i
++) {
136 value
= mtu3_readl(ibase
, SSUSB_U2_CTRL(i
));
137 value
&= ~(SSUSB_U2_PORT_PDN
| SSUSB_U2_PORT_DIS
);
138 value
|= SSUSB_U2_PORT_HOST_SEL
;
139 mtu3_writel(ibase
, SSUSB_U2_CTRL(i
), value
);
142 check_clk
= SSUSB_XHCI_RST_B_STS
;
143 if (num_u3p
> u3_ports_disabed
)
144 check_clk
= SSUSB_U3_MAC_RST_B_STS
;
146 return ssusb_check_clocks(ssusb
, check_clk
);
149 int ssusb_host_disable(struct ssusb_mtk
*ssusb
, bool suspend
)
151 void __iomem
*ibase
= ssusb
->ippc_base
;
152 int num_u3p
= ssusb
->u3_ports
;
153 int num_u2p
= ssusb
->u2_ports
;
158 /* power down and disable u3 ports except skipped ones */
159 for (i
= 0; i
< num_u3p
; i
++) {
160 if ((0x1 << i
) & ssusb
->u3p_dis_msk
)
163 value
= mtu3_readl(ibase
, SSUSB_U3_CTRL(i
));
164 value
|= SSUSB_U3_PORT_PDN
;
165 value
|= suspend
? 0 : SSUSB_U3_PORT_DIS
;
166 mtu3_writel(ibase
, SSUSB_U3_CTRL(i
), value
);
169 /* power down and disable all u2 ports */
170 for (i
= 0; i
< num_u2p
; i
++) {
171 value
= mtu3_readl(ibase
, SSUSB_U2_CTRL(i
));
172 value
|= SSUSB_U2_PORT_PDN
;
173 value
|= suspend
? 0 : SSUSB_U2_PORT_DIS
;
174 mtu3_writel(ibase
, SSUSB_U2_CTRL(i
), value
);
177 /* power down host ip */
178 mtu3_setbits(ibase
, U3D_SSUSB_IP_PW_CTRL1
, SSUSB_IP_HOST_PDN
);
183 /* wait for host ip to sleep */
184 ret
= readl_poll_timeout(ibase
+ U3D_SSUSB_IP_PW_STS1
, value
,
185 (value
& SSUSB_IP_SLEEP_STS
), 100, 100000);
187 dev_err(ssusb
->dev
, "ip sleep failed!!!\n");
192 static void ssusb_host_setup(struct ssusb_mtk
*ssusb
)
194 struct otg_switch_mtk
*otg_sx
= &ssusb
->otg_switch
;
196 host_ports_num_get(ssusb
);
199 * power on host and power on/enable all ports
200 * if support OTG, gadget driver will switch port0 to device mode
202 ssusb_host_enable(ssusb
);
204 if (otg_sx
->manual_drd_enabled
)
205 ssusb_set_force_mode(ssusb
, MTU3_DR_FORCE_HOST
);
207 /* if port0 supports dual-role, works as host mode by default */
208 ssusb_set_vbus(&ssusb
->otg_switch
, 1);
211 static void ssusb_host_cleanup(struct ssusb_mtk
*ssusb
)
214 ssusb_set_vbus(&ssusb
->otg_switch
, 0);
216 ssusb_host_disable(ssusb
, false);
220 * If host supports multiple ports, the VBUSes(5V) of ports except port0
221 * which supports OTG are better to be enabled by default in DTS.
222 * Because the host driver will keep link with devices attached when system
223 * enters suspend mode, so no need to control VBUSes after initialization.
225 int ssusb_host_init(struct ssusb_mtk
*ssusb
, struct device_node
*parent_dn
)
227 struct device
*parent_dev
= ssusb
->dev
;
230 ssusb_host_setup(ssusb
);
232 ret
= of_platform_populate(parent_dn
, NULL
, NULL
, parent_dev
);
234 dev_dbg(parent_dev
, "failed to create child devices at %pOF\n",
239 dev_info(parent_dev
, "xHCI platform device register success...\n");
244 void ssusb_host_exit(struct ssusb_mtk
*ssusb
)
246 of_platform_depopulate(ssusb
->dev
);
247 ssusb_host_cleanup(ssusb
);