Linux Audio

Check our new training course

Loading...
v6.2
  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#define dwc2_ovr_gotgctl(gotgctl) \
 17	((gotgctl) |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN | \
 18	 GOTGCTL_DBNCE_FLTR_BYPASS)
 19
 20static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
 21{
 22	unsigned long flags;
 23	u32 gotgctl;
 24
 25	spin_lock_irqsave(&hsotg->lock, flags);
 26
 27	gotgctl = dwc2_readl(hsotg, GOTGCTL);
 28	dwc2_ovr_gotgctl(gotgctl);
 
 29	gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
 30	if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
 31		gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
 32	else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
 33		gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
 34	dwc2_writel(hsotg, gotgctl, GOTGCTL);
 35
 36	spin_unlock_irqrestore(&hsotg->lock, flags);
 37
 38	dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
 39}
 40
 41static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
 42{
 43	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
 44
 45	/* Check if A-Session is already in the right state */
 46	if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
 47	    (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
 48		return -EALREADY;
 49
 50	/* Always enable overrides to handle the resume case */
 51	dwc2_ovr_gotgctl(gotgctl);
 52
 53	gotgctl &= ~GOTGCTL_BVALOVAL;
 54	if (valid)
 55		gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
 56	else
 57		gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
 58	dwc2_writel(hsotg, gotgctl, GOTGCTL);
 59
 60	return 0;
 61}
 62
 63static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
 64{
 65	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
 66
 67	/* Check if B-Session is already in the right state */
 68	if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
 69	    (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
 70		return -EALREADY;
 71
 72	/* Always enable overrides to handle the resume case */
 73	dwc2_ovr_gotgctl(gotgctl);
 74
 75	gotgctl &= ~GOTGCTL_AVALOVAL;
 76	if (valid)
 77		gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
 78	else
 79		gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
 80	dwc2_writel(hsotg, gotgctl, GOTGCTL);
 81
 82	return 0;
 83}
 84
 85static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
 86{
 87	struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
 88	unsigned long flags;
 89	int already = 0;
 90
 91	/* Skip session not in line with dr_mode */
 92	if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
 93	    (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
 94		return -EINVAL;
 95
 96#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
 97	IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 98	/* Skip session if core is in test mode */
 99	if (role == USB_ROLE_NONE && hsotg->test_mode) {
100		dev_dbg(hsotg->dev, "Core is in test mode\n");
101		return -EBUSY;
102	}
103#endif
104
105	/*
106	 * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
107	 * the probe and enabled on udc_start.
108	 * If role-switch set is called before the udc_start, we need to enable
109	 * the clock to read/write GOTGCTL and GUSBCFG registers to override
110	 * mode and sessions. It is the case if cable is plugged at boot.
111	 */
112	if (!hsotg->ll_hw_enabled && hsotg->clk) {
113		int ret = clk_prepare_enable(hsotg->clk);
114
115		if (ret)
116			return ret;
117	}
118
119	spin_lock_irqsave(&hsotg->lock, flags);
120
121	if (role == USB_ROLE_NONE) {
122		/* default operation mode when usb role is USB_ROLE_NONE */
123		if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
124			role = USB_ROLE_HOST;
125		else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
126			role = USB_ROLE_DEVICE;
127	}
128
129	if (role == USB_ROLE_HOST) {
130		already = dwc2_ovr_avalid(hsotg, true);
131	} else if (role == USB_ROLE_DEVICE) {
132		already = dwc2_ovr_bvalid(hsotg, true);
133		if (dwc2_is_device_enabled(hsotg)) {
134			/* This clear DCTL.SFTDISCON bit */
135			dwc2_hsotg_core_connect(hsotg);
136		}
137	} else {
138		if (dwc2_is_device_mode(hsotg)) {
139			if (!dwc2_ovr_bvalid(hsotg, false))
140				/* This set DCTL.SFTDISCON bit */
141				dwc2_hsotg_core_disconnect(hsotg);
142		} else {
143			dwc2_ovr_avalid(hsotg, false);
144		}
145	}
146
147	spin_unlock_irqrestore(&hsotg->lock, flags);
148
149	if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
150		/* This will raise a Connector ID Status Change Interrupt */
151		dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
152
153	if (!hsotg->ll_hw_enabled && hsotg->clk)
154		clk_disable_unprepare(hsotg->clk);
155
156	dev_dbg(hsotg->dev, "%s-session valid\n",
157		role == USB_ROLE_NONE ? "No" :
158		role == USB_ROLE_HOST ? "A" : "B");
159
160	return 0;
161}
162
163int dwc2_drd_init(struct dwc2_hsotg *hsotg)
164{
165	struct usb_role_switch_desc role_sw_desc = {0};
166	struct usb_role_switch *role_sw;
167	int ret;
168
169	if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
170		return 0;
171
172	hsotg->role_sw_default_mode = usb_get_role_switch_default_mode(hsotg->dev);
173	role_sw_desc.driver_data = hsotg;
174	role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
175	role_sw_desc.set = dwc2_drd_role_sw_set;
176	role_sw_desc.allow_userspace_control = true;
177
178	role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
179	if (IS_ERR(role_sw)) {
180		ret = PTR_ERR(role_sw);
181		dev_err(hsotg->dev,
182			"failed to register role switch: %d\n", ret);
183		return ret;
184	}
185
186	hsotg->role_sw = role_sw;
187
188	/* Enable override and initialize values */
189	dwc2_ovr_init(hsotg);
190
191	return 0;
192}
193
194void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
195{
196	u32 gintsts, gintmsk;
197
198	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
199		gintmsk = dwc2_readl(hsotg, GINTMSK);
200		gintmsk &= ~GINTSTS_CONIDSTSCHNG;
201		dwc2_writel(hsotg, gintmsk, GINTMSK);
202		gintsts = dwc2_readl(hsotg, GINTSTS);
203		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
204	}
205}
206
207void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
208{
209	u32 gintsts, gintmsk;
210	enum usb_role role;
211
212	if (hsotg->role_sw) {
213		/* get last known role (as the get ops isn't implemented by this driver) */
214		role = usb_role_switch_get_role(hsotg->role_sw);
215
216		if (role == USB_ROLE_NONE) {
217			if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
218				role = USB_ROLE_HOST;
219			else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
220				role = USB_ROLE_DEVICE;
221		}
222
223		/* restore last role that may have been lost */
224		if (role == USB_ROLE_HOST)
225			dwc2_ovr_avalid(hsotg, true);
226		else if (role == USB_ROLE_DEVICE)
227			dwc2_ovr_bvalid(hsotg, true);
228
229		dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
230
231		dev_dbg(hsotg->dev, "resuming %s-session valid\n",
232			role == USB_ROLE_NONE ? "No" :
233			role == USB_ROLE_HOST ? "A" : "B");
234	}
235
236	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
237		gintsts = dwc2_readl(hsotg, GINTSTS);
238		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
239		gintmsk = dwc2_readl(hsotg, GINTMSK);
240		gintmsk |= GINTSTS_CONIDSTSCHNG;
241		dwc2_writel(hsotg, gintmsk, GINTMSK);
242	}
243}
244
245void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
246{
247	if (hsotg->role_sw)
248		usb_role_switch_unregister(hsotg->role_sw);
249}
v5.14.15
  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/iopoll.h>
 11#include <linux/platform_device.h>
 12#include <linux/usb/role.h>
 13#include "core.h"
 14
 
 
 
 
 15static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
 16{
 17	unsigned long flags;
 18	u32 gotgctl;
 19
 20	spin_lock_irqsave(&hsotg->lock, flags);
 21
 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);
 27
 28	dwc2_force_mode(hsotg, false);
 29
 30	spin_unlock_irqrestore(&hsotg->lock, flags);
 31}
 32
 33static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
 34{
 35	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
 36
 37	/* Check if A-Session is already in the right state */
 38	if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
 39	    (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
 40		return -EALREADY;
 41
 
 
 
 
 42	if (valid)
 43		gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
 44	else
 45		gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
 46	dwc2_writel(hsotg, gotgctl, GOTGCTL);
 47
 48	return 0;
 49}
 50
 51static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
 52{
 53	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
 54
 55	/* Check if B-Session is already in the right state */
 56	if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
 57	    (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
 58		return -EALREADY;
 59
 
 
 
 
 60	if (valid)
 61		gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
 62	else
 63		gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
 64	dwc2_writel(hsotg, gotgctl, GOTGCTL);
 65
 66	return 0;
 67}
 68
 69static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
 70{
 71	struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
 72	unsigned long flags;
 73	int already = 0;
 74
 75	/* Skip session not in line with dr_mode */
 76	if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
 77	    (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
 78		return -EINVAL;
 79
 80#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
 81	IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 82	/* Skip session if core is in test mode */
 83	if (role == USB_ROLE_NONE && hsotg->test_mode) {
 84		dev_dbg(hsotg->dev, "Core is in test mode\n");
 85		return -EBUSY;
 86	}
 87#endif
 88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 89	spin_lock_irqsave(&hsotg->lock, flags);
 90
 
 
 
 
 
 
 
 
 91	if (role == USB_ROLE_HOST) {
 92		already = dwc2_ovr_avalid(hsotg, true);
 93	} else if (role == USB_ROLE_DEVICE) {
 94		already = dwc2_ovr_bvalid(hsotg, true);
 95		/* This clear DCTL.SFTDISCON bit */
 96		dwc2_hsotg_core_connect(hsotg);
 
 
 97	} else {
 98		if (dwc2_is_device_mode(hsotg)) {
 99			if (!dwc2_ovr_bvalid(hsotg, false))
100				/* This set DCTL.SFTDISCON bit */
101				dwc2_hsotg_core_disconnect(hsotg);
102		} else {
103			dwc2_ovr_avalid(hsotg, false);
104		}
105	}
106
107	spin_unlock_irqrestore(&hsotg->lock, flags);
108
109	if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
110		/* This will raise a Connector ID Status Change Interrupt */
111		dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
112
 
 
 
113	dev_dbg(hsotg->dev, "%s-session valid\n",
114		role == USB_ROLE_NONE ? "No" :
115		role == USB_ROLE_HOST ? "A" : "B");
116
117	return 0;
118}
119
120int dwc2_drd_init(struct dwc2_hsotg *hsotg)
121{
122	struct usb_role_switch_desc role_sw_desc = {0};
123	struct usb_role_switch *role_sw;
124	int ret;
125
126	if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
127		return 0;
128
 
129	role_sw_desc.driver_data = hsotg;
130	role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
131	role_sw_desc.set = dwc2_drd_role_sw_set;
132	role_sw_desc.allow_userspace_control = true;
133
134	role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
135	if (IS_ERR(role_sw)) {
136		ret = PTR_ERR(role_sw);
137		dev_err(hsotg->dev,
138			"failed to register role switch: %d\n", ret);
139		return ret;
140	}
141
142	hsotg->role_sw = role_sw;
143
144	/* Enable override and initialize values */
145	dwc2_ovr_init(hsotg);
146
147	return 0;
148}
149
150void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
151{
152	u32 gintsts, gintmsk;
153
154	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
155		gintmsk = dwc2_readl(hsotg, GINTMSK);
156		gintmsk &= ~GINTSTS_CONIDSTSCHNG;
157		dwc2_writel(hsotg, gintmsk, GINTMSK);
158		gintsts = dwc2_readl(hsotg, GINTSTS);
159		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
160	}
161}
162
163void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
164{
165	u32 gintsts, gintmsk;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
167	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
168		gintsts = dwc2_readl(hsotg, GINTSTS);
169		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
170		gintmsk = dwc2_readl(hsotg, GINTMSK);
171		gintmsk |= GINTSTS_CONIDSTSCHNG;
172		dwc2_writel(hsotg, gintmsk, GINTMSK);
173	}
174}
175
176void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
177{
178	if (hsotg->role_sw)
179		usb_role_switch_unregister(hsotg->role_sw);
180}