GNU Linux-libre 5.16.19-gnu
[releases.git] / drivers / usb / dwc2 / drd.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * drd.c - DesignWare USB2 DRD Controller Dual-role support
4  *
5  * Copyright (C) 2020 STMicroelectronics
6  *
7  * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
8  */
9
10 #include <linux/clk.h>
11 #include <linux/iopoll.h>
12 #include <linux/platform_device.h>
13 #include <linux/usb/role.h>
14 #include "core.h"
15
16 static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
17 {
18         unsigned long flags;
19         u32 gotgctl;
20
21         spin_lock_irqsave(&hsotg->lock, flags);
22
23         gotgctl = dwc2_readl(hsotg, GOTGCTL);
24         gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN;
25         gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
26         gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
27         dwc2_writel(hsotg, gotgctl, GOTGCTL);
28
29         spin_unlock_irqrestore(&hsotg->lock, flags);
30
31         dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
32 }
33
34 static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
35 {
36         u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
37
38         /* Check if A-Session is already in the right state */
39         if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
40             (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
41                 return -EALREADY;
42
43         gotgctl &= ~GOTGCTL_BVALOVAL;
44         if (valid)
45                 gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
46         else
47                 gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
48         dwc2_writel(hsotg, gotgctl, GOTGCTL);
49
50         return 0;
51 }
52
53 static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
54 {
55         u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
56
57         /* Check if B-Session is already in the right state */
58         if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
59             (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
60                 return -EALREADY;
61
62         gotgctl &= ~GOTGCTL_AVALOVAL;
63         if (valid)
64                 gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
65         else
66                 gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
67         dwc2_writel(hsotg, gotgctl, GOTGCTL);
68
69         return 0;
70 }
71
72 static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
73 {
74         struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
75         unsigned long flags;
76         int already = 0;
77
78         /* Skip session not in line with dr_mode */
79         if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
80             (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
81                 return -EINVAL;
82
83 #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
84         IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
85         /* Skip session if core is in test mode */
86         if (role == USB_ROLE_NONE && hsotg->test_mode) {
87                 dev_dbg(hsotg->dev, "Core is in test mode\n");
88                 return -EBUSY;
89         }
90 #endif
91
92         /*
93          * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
94          * the probe and enabled on udc_start.
95          * If role-switch set is called before the udc_start, we need to enable
96          * the clock to read/write GOTGCTL and GUSBCFG registers to override
97          * mode and sessions. It is the case if cable is plugged at boot.
98          */
99         if (!hsotg->ll_hw_enabled && hsotg->clk) {
100                 int ret = clk_prepare_enable(hsotg->clk);
101
102                 if (ret)
103                         return ret;
104         }
105
106         spin_lock_irqsave(&hsotg->lock, flags);
107
108         if (role == USB_ROLE_HOST) {
109                 already = dwc2_ovr_avalid(hsotg, true);
110         } else if (role == USB_ROLE_DEVICE) {
111                 already = dwc2_ovr_bvalid(hsotg, true);
112                 if (dwc2_is_device_enabled(hsotg)) {
113                         /* This clear DCTL.SFTDISCON bit */
114                         dwc2_hsotg_core_connect(hsotg);
115                 }
116         } else {
117                 if (dwc2_is_device_mode(hsotg)) {
118                         if (!dwc2_ovr_bvalid(hsotg, false))
119                                 /* This set DCTL.SFTDISCON bit */
120                                 dwc2_hsotg_core_disconnect(hsotg);
121                 } else {
122                         dwc2_ovr_avalid(hsotg, false);
123                 }
124         }
125
126         spin_unlock_irqrestore(&hsotg->lock, flags);
127
128         if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
129                 /* This will raise a Connector ID Status Change Interrupt */
130                 dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
131
132         if (!hsotg->ll_hw_enabled && hsotg->clk)
133                 clk_disable_unprepare(hsotg->clk);
134
135         dev_dbg(hsotg->dev, "%s-session valid\n",
136                 role == USB_ROLE_NONE ? "No" :
137                 role == USB_ROLE_HOST ? "A" : "B");
138
139         return 0;
140 }
141
142 int dwc2_drd_init(struct dwc2_hsotg *hsotg)
143 {
144         struct usb_role_switch_desc role_sw_desc = {0};
145         struct usb_role_switch *role_sw;
146         int ret;
147
148         if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
149                 return 0;
150
151         role_sw_desc.driver_data = hsotg;
152         role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
153         role_sw_desc.set = dwc2_drd_role_sw_set;
154         role_sw_desc.allow_userspace_control = true;
155
156         role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
157         if (IS_ERR(role_sw)) {
158                 ret = PTR_ERR(role_sw);
159                 dev_err(hsotg->dev,
160                         "failed to register role switch: %d\n", ret);
161                 return ret;
162         }
163
164         hsotg->role_sw = role_sw;
165
166         /* Enable override and initialize values */
167         dwc2_ovr_init(hsotg);
168
169         return 0;
170 }
171
172 void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
173 {
174         u32 gintsts, gintmsk;
175
176         if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
177                 gintmsk = dwc2_readl(hsotg, GINTMSK);
178                 gintmsk &= ~GINTSTS_CONIDSTSCHNG;
179                 dwc2_writel(hsotg, gintmsk, GINTMSK);
180                 gintsts = dwc2_readl(hsotg, GINTSTS);
181                 dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
182         }
183 }
184
185 void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
186 {
187         u32 gintsts, gintmsk;
188
189         if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
190                 gintsts = dwc2_readl(hsotg, GINTSTS);
191                 dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
192                 gintmsk = dwc2_readl(hsotg, GINTMSK);
193                 gintmsk |= GINTSTS_CONIDSTSCHNG;
194                 dwc2_writel(hsotg, gintmsk, GINTMSK);
195         }
196 }
197
198 void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
199 {
200         if (hsotg->role_sw)
201                 usb_role_switch_unregister(hsotg->role_sw);
202 }