1 // SPDX-License-Identifier: GPL-2.0
3 * drd.c - DesignWare USB2 DRD Controller Dual-role support
5 * Copyright (C) 2020 STMicroelectronics
7 * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
10 #include <linux/iopoll.h>
11 #include <linux/platform_device.h>
12 #include <linux/usb/role.h>
15 static void dwc2_ovr_init(struct dwc2_hsotg
*hsotg
)
20 spin_lock_irqsave(&hsotg
->lock
, flags
);
22 gotgctl
= dwc2_readl(hsotg
, GOTGCTL
);
23 gotgctl
|= GOTGCTL_BVALOEN
| GOTGCTL_AVALOEN
| GOTGCTL_VBVALOEN
;
24 gotgctl
|= GOTGCTL_DBNCE_FLTR_BYPASS
;
25 gotgctl
&= ~(GOTGCTL_BVALOVAL
| GOTGCTL_AVALOVAL
| GOTGCTL_VBVALOVAL
);
26 dwc2_writel(hsotg
, gotgctl
, GOTGCTL
);
28 dwc2_force_mode(hsotg
, false);
30 spin_unlock_irqrestore(&hsotg
->lock
, flags
);
33 static int dwc2_ovr_avalid(struct dwc2_hsotg
*hsotg
, bool valid
)
35 u32 gotgctl
= dwc2_readl(hsotg
, GOTGCTL
);
37 /* Check if A-Session is already in the right state */
38 if ((valid
&& (gotgctl
& GOTGCTL_ASESVLD
)) ||
39 (!valid
&& !(gotgctl
& GOTGCTL_ASESVLD
)))
43 gotgctl
|= GOTGCTL_AVALOVAL
| GOTGCTL_VBVALOVAL
;
45 gotgctl
&= ~(GOTGCTL_AVALOVAL
| GOTGCTL_VBVALOVAL
);
46 dwc2_writel(hsotg
, gotgctl
, GOTGCTL
);
51 static int dwc2_ovr_bvalid(struct dwc2_hsotg
*hsotg
, bool valid
)
53 u32 gotgctl
= dwc2_readl(hsotg
, GOTGCTL
);
55 /* Check if B-Session is already in the right state */
56 if ((valid
&& (gotgctl
& GOTGCTL_BSESVLD
)) ||
57 (!valid
&& !(gotgctl
& GOTGCTL_BSESVLD
)))
61 gotgctl
|= GOTGCTL_BVALOVAL
| GOTGCTL_VBVALOVAL
;
63 gotgctl
&= ~(GOTGCTL_BVALOVAL
| GOTGCTL_VBVALOVAL
);
64 dwc2_writel(hsotg
, gotgctl
, GOTGCTL
);
69 static int dwc2_drd_role_sw_set(struct usb_role_switch
*sw
, enum usb_role role
)
71 struct dwc2_hsotg
*hsotg
= usb_role_switch_get_drvdata(sw
);
74 /* Skip session not in line with dr_mode */
75 if ((role
== USB_ROLE_DEVICE
&& hsotg
->dr_mode
== USB_DR_MODE_HOST
) ||
76 (role
== USB_ROLE_HOST
&& hsotg
->dr_mode
== USB_DR_MODE_PERIPHERAL
))
79 /* Skip session if core is in test mode */
80 if (role
== USB_ROLE_NONE
&& hsotg
->test_mode
) {
81 dev_dbg(hsotg
->dev
, "Core is in test mode\n");
85 spin_lock_irqsave(&hsotg
->lock
, flags
);
87 if (role
== USB_ROLE_HOST
) {
88 if (dwc2_ovr_avalid(hsotg
, true))
91 if (hsotg
->dr_mode
== USB_DR_MODE_OTG
)
93 * This will raise a Connector ID Status Change
94 * Interrupt - connID A
96 dwc2_force_mode(hsotg
, true);
97 } else if (role
== USB_ROLE_DEVICE
) {
98 if (dwc2_ovr_bvalid(hsotg
, true))
101 if (hsotg
->dr_mode
== USB_DR_MODE_OTG
)
103 * This will raise a Connector ID Status Change
104 * Interrupt - connID B
106 dwc2_force_mode(hsotg
, false);
108 /* This clear DCTL.SFTDISCON bit */
109 dwc2_hsotg_core_connect(hsotg
);
111 if (dwc2_is_device_mode(hsotg
)) {
112 if (!dwc2_ovr_bvalid(hsotg
, false))
113 /* This set DCTL.SFTDISCON bit */
114 dwc2_hsotg_core_disconnect(hsotg
);
116 dwc2_ovr_avalid(hsotg
, false);
121 spin_unlock_irqrestore(&hsotg
->lock
, flags
);
123 dev_dbg(hsotg
->dev
, "%s-session valid\n",
124 role
== USB_ROLE_NONE
? "No" :
125 role
== USB_ROLE_HOST
? "A" : "B");
130 int dwc2_drd_init(struct dwc2_hsotg
*hsotg
)
132 struct usb_role_switch_desc role_sw_desc
= {0};
133 struct usb_role_switch
*role_sw
;
136 if (!device_property_read_bool(hsotg
->dev
, "usb-role-switch"))
139 role_sw_desc
.driver_data
= hsotg
;
140 role_sw_desc
.fwnode
= dev_fwnode(hsotg
->dev
);
141 role_sw_desc
.set
= dwc2_drd_role_sw_set
;
142 role_sw_desc
.allow_userspace_control
= true;
144 role_sw
= usb_role_switch_register(hsotg
->dev
, &role_sw_desc
);
145 if (IS_ERR(role_sw
)) {
146 ret
= PTR_ERR(role_sw
);
148 "failed to register role switch: %d\n", ret
);
152 hsotg
->role_sw
= role_sw
;
154 /* Enable override and initialize values */
155 dwc2_ovr_init(hsotg
);
160 void dwc2_drd_suspend(struct dwc2_hsotg
*hsotg
)
162 u32 gintsts
, gintmsk
;
164 if (hsotg
->role_sw
&& !hsotg
->params
.external_id_pin_ctl
) {
165 gintmsk
= dwc2_readl(hsotg
, GINTMSK
);
166 gintmsk
&= ~GINTSTS_CONIDSTSCHNG
;
167 dwc2_writel(hsotg
, gintmsk
, GINTMSK
);
168 gintsts
= dwc2_readl(hsotg
, GINTSTS
);
169 dwc2_writel(hsotg
, gintsts
| GINTSTS_CONIDSTSCHNG
, GINTSTS
);
173 void dwc2_drd_resume(struct dwc2_hsotg
*hsotg
)
175 u32 gintsts
, gintmsk
;
177 if (hsotg
->role_sw
&& !hsotg
->params
.external_id_pin_ctl
) {
178 gintsts
= dwc2_readl(hsotg
, GINTSTS
);
179 dwc2_writel(hsotg
, gintsts
| GINTSTS_CONIDSTSCHNG
, GINTSTS
);
180 gintmsk
= dwc2_readl(hsotg
, GINTMSK
);
181 gintmsk
|= GINTSTS_CONIDSTSCHNG
;
182 dwc2_writel(hsotg
, gintmsk
, GINTMSK
);
186 void dwc2_drd_exit(struct dwc2_hsotg
*hsotg
)
189 usb_role_switch_unregister(hsotg
->role_sw
);