Commit 49d08cfc authored by Thinh Nguyen's avatar Thinh Nguyen Committed by Greg Kroah-Hartman

usb: udc: core: Introduce started state

For some UDCs, the initialization sequence by udc_start() should not be
repeated until it is properly cleaned up with udc_stop() and vise versa.
We may run into some cleanup failure as seen with the DWC3 driver during
the irq cleanup. This issue can occur when the user triggers
soft-connect/soft-disconnect from the soft_connect sysfs. To avoid
adding checks to every UDC driver, at the UDC framework, introduce a
"started" state to track and prevent the UDC from repeating the
udc_start() and udc_stop() if it had already started/stopped.
Acked-by: default avatarFelipe Balbi <balbi@kernel.org>
Signed-off-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/a7c4112fcd4dc2f0169af94a24f5685ca77f09fd.1610395599.git.Thinh.Nguyen@synopsys.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent babbdfc9
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
* @list: for use by the udc class driver * @list: for use by the udc class driver
* @vbus: for udcs who care about vbus status, this value is real vbus status; * @vbus: for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true * for udcs who do not care about vbus status, this value is always true
* @started: the UDC's started state. True if the UDC had started.
* *
* This represents the internal data structure which is used by the UDC-class * This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together. * to hold information about udc driver and gadget together.
...@@ -39,6 +40,7 @@ struct usb_udc { ...@@ -39,6 +40,7 @@ struct usb_udc {
struct device dev; struct device dev;
struct list_head list; struct list_head list;
bool vbus; bool vbus;
bool started;
}; };
static struct class *udc_class; static struct class *udc_class;
...@@ -1082,7 +1084,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); ...@@ -1082,7 +1084,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
*/ */
static inline int usb_gadget_udc_start(struct usb_udc *udc) static inline int usb_gadget_udc_start(struct usb_udc *udc)
{ {
return udc->gadget->ops->udc_start(udc->gadget, udc->driver); int ret;
if (udc->started) {
dev_err(&udc->dev, "UDC had already started\n");
return -EBUSY;
}
ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver);
if (!ret)
udc->started = true;
return ret;
} }
/** /**
...@@ -1098,7 +1111,13 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc) ...@@ -1098,7 +1111,13 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
*/ */
static inline void usb_gadget_udc_stop(struct usb_udc *udc) static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{ {
if (!udc->started) {
dev_err(&udc->dev, "UDC had already stopped\n");
return;
}
udc->gadget->ops->udc_stop(udc->gadget); udc->gadget->ops->udc_stop(udc->gadget);
udc->started = false;
} }
/** /**
...@@ -1222,6 +1241,8 @@ int usb_add_gadget(struct usb_gadget *gadget) ...@@ -1222,6 +1241,8 @@ int usb_add_gadget(struct usb_gadget *gadget)
udc->gadget = gadget; udc->gadget = gadget;
gadget->udc = udc; gadget->udc = udc;
udc->started = false;
mutex_lock(&udc_lock); mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list); list_add_tail(&udc->list, &udc_list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment