1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * mtu3_dr.c - dual role switch and host glue layer |
4 | * |
5 | * Copyright (C) 2016 MediaTek Inc. |
6 | * |
7 | * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/irq.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mfd/syscon.h> |
14 | #include <linux/of.h> |
15 | #include <linux/of_platform.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | #include "mtu3.h" |
19 | #include "mtu3_dr.h" |
20 | |
21 | /* mt8173 etc */ |
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 */ |
26 | |
27 | /* mt8183 */ |
28 | #define PERI_WK_CTRL0 0x0 |
29 | #define WC0_IS_C(x) ((u32)(((x) & 0xf) << 28)) /* cycle debounce */ |
30 | #define WC0_IS_P BIT(12) /* polarity */ |
31 | #define WC0_IS_EN BIT(6) |
32 | |
33 | /* mt8192 */ |
34 | #define WC0_SSUSB0_CDEN BIT(6) |
35 | #define WC0_IS_SPM_EN BIT(1) |
36 | |
37 | /* mt8195 */ |
38 | #define PERI_WK_CTRL0_8195 0x04 |
39 | #define WC0_IS_P_95 BIT(30) /* polarity */ |
40 | #define WC0_IS_C_95(x) ((u32)(((x) & 0x7) << 27)) |
41 | #define WC0_IS_EN_P3_95 BIT(26) |
42 | #define WC0_IS_EN_P2_95 BIT(25) |
43 | |
44 | #define PERI_WK_CTRL1_8195 0x20 |
45 | #define WC1_IS_C_95(x) ((u32)(((x) & 0xf) << 28)) |
46 | #define WC1_IS_P_95 BIT(12) |
47 | #define WC1_IS_EN_P0_95 BIT(6) |
48 | |
49 | /* mt2712 etc */ |
50 | #define PERI_SSUSB_SPM_CTRL 0x0 |
51 | #define SSC_IP_SLEEP_EN BIT(4) |
52 | #define SSC_SPM_INT_EN BIT(1) |
53 | |
54 | enum ssusb_uwk_vers { |
55 | SSUSB_UWK_V1 = 1, |
56 | SSUSB_UWK_V2, |
57 | SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */ |
58 | SSUSB_UWK_V1_2, /* specific revision 1.02 */ |
59 | SSUSB_UWK_V1_3, /* mt8195 IP0 */ |
60 | SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */ |
61 | SSUSB_UWK_V1_6, /* mt8195 IP3 */ |
62 | }; |
63 | |
64 | /* |
65 | * ip-sleep wakeup mode: |
66 | * all clocks can be turn off, but power domain should be kept on |
67 | */ |
68 | static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) |
69 | { |
70 | u32 reg, msk, val; |
71 | |
72 | switch (ssusb->uwk_vers) { |
73 | case SSUSB_UWK_V1: |
74 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL1; |
75 | msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; |
76 | val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; |
77 | break; |
78 | case SSUSB_UWK_V1_1: |
79 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; |
80 | msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P; |
81 | val = enable ? (WC0_IS_EN | WC0_IS_C(0x1)) : 0; |
82 | break; |
83 | case SSUSB_UWK_V1_2: |
84 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; |
85 | msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN; |
86 | val = enable ? msk : 0; |
87 | break; |
88 | case SSUSB_UWK_V1_3: |
89 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8195; |
90 | msk = WC1_IS_EN_P0_95 | WC1_IS_C_95(0xf) | WC1_IS_P_95; |
91 | val = enable ? (WC1_IS_EN_P0_95 | WC1_IS_C_95(0x1)) : 0; |
92 | break; |
93 | case SSUSB_UWK_V1_5: |
94 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195; |
95 | msk = WC0_IS_EN_P2_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; |
96 | val = enable ? (WC0_IS_EN_P2_95 | WC0_IS_C_95(0x1)) : 0; |
97 | break; |
98 | case SSUSB_UWK_V1_6: |
99 | reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8195; |
100 | msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; |
101 | val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0; |
102 | break; |
103 | case SSUSB_UWK_V2: |
104 | reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; |
105 | msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; |
106 | val = enable ? msk : 0; |
107 | break; |
108 | default: |
109 | return; |
110 | } |
111 | regmap_update_bits(map: ssusb->uwk, reg, mask: msk, val); |
112 | } |
113 | |
114 | int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, |
115 | struct device_node *dn) |
116 | { |
117 | struct of_phandle_args args; |
118 | int ret; |
119 | |
120 | /* wakeup function is optional */ |
121 | ssusb->uwk_en = of_property_read_bool(np: dn, propname: "wakeup-source" ); |
122 | if (!ssusb->uwk_en) |
123 | return 0; |
124 | |
125 | ret = of_parse_phandle_with_fixed_args(np: dn, |
126 | list_name: "mediatek,syscon-wakeup" , cell_count: 2, index: 0, out_args: &args); |
127 | if (ret) |
128 | return ret; |
129 | |
130 | ssusb->uwk_reg_base = args.args[0]; |
131 | ssusb->uwk_vers = args.args[1]; |
132 | ssusb->uwk = syscon_node_to_regmap(np: args.np); |
133 | of_node_put(node: args.np); |
134 | dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n" , |
135 | ssusb->uwk_reg_base, ssusb->uwk_vers); |
136 | |
137 | return PTR_ERR_OR_ZERO(ptr: ssusb->uwk); |
138 | } |
139 | |
140 | void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) |
141 | { |
142 | if (ssusb->uwk_en) |
143 | ssusb_wakeup_ip_sleep_set(ssusb, enable); |
144 | } |
145 | |
146 | static void host_ports_num_get(struct ssusb_mtk *ssusb) |
147 | { |
148 | u32 xhci_cap; |
149 | |
150 | xhci_cap = mtu3_readl(base: ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP); |
151 | ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap); |
152 | ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap); |
153 | |
154 | dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n" , |
155 | ssusb->u2_ports, ssusb->u3_ports); |
156 | } |
157 | |
158 | /* only configure ports will be used later */ |
159 | static int ssusb_host_enable(struct ssusb_mtk *ssusb) |
160 | { |
161 | void __iomem *ibase = ssusb->ippc_base; |
162 | int num_u3p = ssusb->u3_ports; |
163 | int num_u2p = ssusb->u2_ports; |
164 | int u3_ports_disabled; |
165 | u32 check_clk; |
166 | u32 value; |
167 | int i; |
168 | |
169 | /* power on host ip */ |
170 | mtu3_clrbits(base: ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
171 | |
172 | /* power on and enable u3 ports except skipped ones */ |
173 | u3_ports_disabled = 0; |
174 | for (i = 0; i < num_u3p; i++) { |
175 | if ((0x1 << i) & ssusb->u3p_dis_msk) { |
176 | u3_ports_disabled++; |
177 | continue; |
178 | } |
179 | |
180 | value = mtu3_readl(base: ibase, SSUSB_U3_CTRL(i)); |
181 | value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS); |
182 | value |= SSUSB_U3_PORT_HOST_SEL; |
183 | mtu3_writel(base: ibase, SSUSB_U3_CTRL(i), data: value); |
184 | } |
185 | |
186 | /* power on and enable all u2 ports */ |
187 | for (i = 0; i < num_u2p; i++) { |
188 | if ((0x1 << i) & ssusb->u2p_dis_msk) |
189 | continue; |
190 | |
191 | value = mtu3_readl(base: ibase, SSUSB_U2_CTRL(i)); |
192 | value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); |
193 | value |= SSUSB_U2_PORT_HOST_SEL; |
194 | mtu3_writel(base: ibase, SSUSB_U2_CTRL(i), data: value); |
195 | } |
196 | |
197 | check_clk = SSUSB_XHCI_RST_B_STS; |
198 | if (num_u3p > u3_ports_disabled) |
199 | check_clk = SSUSB_U3_MAC_RST_B_STS; |
200 | |
201 | return ssusb_check_clocks(ssusb, ex_clks: check_clk); |
202 | } |
203 | |
204 | static int ssusb_host_disable(struct ssusb_mtk *ssusb) |
205 | { |
206 | void __iomem *ibase = ssusb->ippc_base; |
207 | int num_u3p = ssusb->u3_ports; |
208 | int num_u2p = ssusb->u2_ports; |
209 | u32 value; |
210 | int i; |
211 | |
212 | /* power down and disable u3 ports except skipped ones */ |
213 | for (i = 0; i < num_u3p; i++) { |
214 | if ((0x1 << i) & ssusb->u3p_dis_msk) |
215 | continue; |
216 | |
217 | value = mtu3_readl(base: ibase, SSUSB_U3_CTRL(i)); |
218 | value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; |
219 | mtu3_writel(base: ibase, SSUSB_U3_CTRL(i), data: value); |
220 | } |
221 | |
222 | /* power down and disable u2 ports except skipped ones */ |
223 | for (i = 0; i < num_u2p; i++) { |
224 | if ((0x1 << i) & ssusb->u2p_dis_msk) |
225 | continue; |
226 | |
227 | value = mtu3_readl(base: ibase, SSUSB_U2_CTRL(i)); |
228 | value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; |
229 | mtu3_writel(base: ibase, SSUSB_U2_CTRL(i), data: value); |
230 | } |
231 | |
232 | /* power down host ip */ |
233 | mtu3_setbits(base: ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped) |
239 | { |
240 | void __iomem *ibase = ssusb->ippc_base; |
241 | int u3p_skip_msk = ssusb->u3p_dis_msk; |
242 | int u2p_skip_msk = ssusb->u2p_dis_msk; |
243 | int num_u3p = ssusb->u3_ports; |
244 | int num_u2p = ssusb->u2_ports; |
245 | u32 value; |
246 | int i; |
247 | |
248 | if (p0_skipped) { |
249 | u2p_skip_msk |= 0x1; |
250 | if (ssusb->otg_switch.is_u3_drd) |
251 | u3p_skip_msk |= 0x1; |
252 | } |
253 | |
254 | /* power on host ip */ |
255 | mtu3_clrbits(base: ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
256 | |
257 | /* power on u3 ports except skipped ones */ |
258 | for (i = 0; i < num_u3p; i++) { |
259 | if ((0x1 << i) & u3p_skip_msk) |
260 | continue; |
261 | |
262 | value = mtu3_readl(base: ibase, SSUSB_U3_CTRL(i)); |
263 | value &= ~SSUSB_U3_PORT_PDN; |
264 | mtu3_writel(base: ibase, SSUSB_U3_CTRL(i), data: value); |
265 | } |
266 | |
267 | /* power on all u2 ports except skipped ones */ |
268 | for (i = 0; i < num_u2p; i++) { |
269 | if ((0x1 << i) & u2p_skip_msk) |
270 | continue; |
271 | |
272 | value = mtu3_readl(base: ibase, SSUSB_U2_CTRL(i)); |
273 | value &= ~SSUSB_U2_PORT_PDN; |
274 | mtu3_writel(base: ibase, SSUSB_U2_CTRL(i), data: value); |
275 | } |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | /* here not skip port0 due to PDN can be set repeatedly */ |
281 | int ssusb_host_suspend(struct ssusb_mtk *ssusb) |
282 | { |
283 | void __iomem *ibase = ssusb->ippc_base; |
284 | int num_u3p = ssusb->u3_ports; |
285 | int num_u2p = ssusb->u2_ports; |
286 | u32 value; |
287 | int i; |
288 | |
289 | /* power down u3 ports except skipped ones */ |
290 | for (i = 0; i < num_u3p; i++) { |
291 | if ((0x1 << i) & ssusb->u3p_dis_msk) |
292 | continue; |
293 | |
294 | value = mtu3_readl(base: ibase, SSUSB_U3_CTRL(i)); |
295 | value |= SSUSB_U3_PORT_PDN; |
296 | mtu3_writel(base: ibase, SSUSB_U3_CTRL(i), data: value); |
297 | } |
298 | |
299 | /* power down u2 ports except skipped ones */ |
300 | for (i = 0; i < num_u2p; i++) { |
301 | if ((0x1 << i) & ssusb->u2p_dis_msk) |
302 | continue; |
303 | |
304 | value = mtu3_readl(base: ibase, SSUSB_U2_CTRL(i)); |
305 | value |= SSUSB_U2_PORT_PDN; |
306 | mtu3_writel(base: ibase, SSUSB_U2_CTRL(i), data: value); |
307 | } |
308 | |
309 | /* power down host ip */ |
310 | mtu3_setbits(base: ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | static void ssusb_host_setup(struct ssusb_mtk *ssusb) |
316 | { |
317 | host_ports_num_get(ssusb); |
318 | |
319 | /* |
320 | * power on host and power on/enable all ports |
321 | * if support OTG, gadget driver will switch port0 to device mode |
322 | */ |
323 | ssusb_host_enable(ssusb); |
324 | ssusb_set_force_mode(ssusb, mode: MTU3_DR_FORCE_HOST); |
325 | |
326 | /* if port0 supports dual-role, works as host mode by default */ |
327 | ssusb_set_vbus(otg_sx: &ssusb->otg_switch, is_on: 1); |
328 | } |
329 | |
330 | static void ssusb_host_cleanup(struct ssusb_mtk *ssusb) |
331 | { |
332 | if (ssusb->is_host) |
333 | ssusb_set_vbus(otg_sx: &ssusb->otg_switch, is_on: 0); |
334 | |
335 | ssusb_host_disable(ssusb); |
336 | } |
337 | |
338 | /* |
339 | * If host supports multiple ports, the VBUSes(5V) of ports except port0 |
340 | * which supports OTG are better to be enabled by default in DTS. |
341 | * Because the host driver will keep link with devices attached when system |
342 | * enters suspend mode, so no need to control VBUSes after initialization. |
343 | */ |
344 | int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn) |
345 | { |
346 | struct device *parent_dev = ssusb->dev; |
347 | int ret; |
348 | |
349 | ssusb_host_setup(ssusb); |
350 | |
351 | ret = of_platform_populate(root: parent_dn, NULL, NULL, parent: parent_dev); |
352 | if (ret) { |
353 | dev_dbg(parent_dev, "failed to create child devices at %pOF\n" , |
354 | parent_dn); |
355 | return ret; |
356 | } |
357 | |
358 | dev_info(parent_dev, "xHCI platform device register success...\n" ); |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | void ssusb_host_exit(struct ssusb_mtk *ssusb) |
364 | { |
365 | of_platform_depopulate(parent: ssusb->dev); |
366 | ssusb_host_cleanup(ssusb); |
367 | } |
368 | |