From 9d9d7526c1fa8137da6f744412747e4510e25b76 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Mon, 6 Nov 2017 16:11:08 +0900 Subject: [PATCH] usb: dwc2: gadget: Prevent wrong extcon notification In sometimes, USB disconnection triggers enumeration-done interrupt even though it is false state. Actually, this interrupt is ignored by dwc2 irq handler, but extcon notfication can be mis-delivered to user-space. Thus, this patch make worker determined the current state by itself, and it also prevent some cases which have chance for wrong notification such as repeatedly connect/disconnecting usb cable. Change-Id: Iff88cf2c030b627f3d2da23930b10e9f3a84c8e1 Signed-off-by: Dongwoo Lee --- drivers/usb/dwc2/core.h | 1 - drivers/usb/dwc2/gadget.c | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index cbe1b56..faa9ce0 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -876,7 +876,6 @@ struct dwc2_hsotg { #if IS_ENABLED(CONFIG_EXTCON) struct extcon_dev *edev; struct work_struct extcon_work; - unsigned int extcon_state; #endif /* CONFIG_EXTCON */ #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c232f2d..bb26bcc 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -41,18 +41,26 @@ static const unsigned int supported_cable[] = { EXTCON_NONE, }; -static void dwc2_gadget_extcon_notify(struct dwc2_hsotg *hsotg, bool on) +static inline void dwc2_gadget_extcon_notify(struct dwc2_hsotg *hsotg) { - hsotg->extcon_state = on; queue_work(system_power_efficient_wq, &hsotg->extcon_work); } +static inline void dwc2_gadget_cancel_extcon_work(struct dwc2_hsotg *hsotg) +{ + cancel_work_sync(&hsotg->extcon_work); +} + static void dwc2_gadget_extcon_work(struct work_struct *work) { struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, extcon_work); + u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL); - extcon_set_cable_state_(hsotg->edev, EXTCON_USB, hsotg->extcon_state); + if (usb_status & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) + extcon_set_cable_state_(hsotg->edev, EXTCON_USB, true); + else + extcon_set_cable_state_(hsotg->edev, EXTCON_USB, false); } static int dwc2_gadget_extcon_init(struct dwc2_hsotg *hsotg) @@ -79,7 +87,11 @@ static int dwc2_gadget_extcon_init(struct dwc2_hsotg *hsotg) return 0; } #else -static inline void dwc2_gadget_extcon_notify(struct dwc2_hsotg *hsotg, bool on) +static inline void dwc2_gadget_extcon_notify(struct dwc2_hsotg *hsotg) +{ +} + +static inline void dwc2_gadget_cancel_extcon_work(struct dwc2_hsotg *hsotg) { } @@ -2573,7 +2585,7 @@ irq_retry: u32 flags = GOTGCTL_ASESVLD | GOTGCTL_BSESVLD; if (!(usb_status & flags)) - dwc2_gadget_extcon_notify(hsotg, false); + dwc2_gadget_extcon_notify(hsotg); } } @@ -2583,7 +2595,7 @@ irq_retry: dwc2_hsotg_irq_enumdone(hsotg); if (hsotg->g_extcon_notify) - dwc2_gadget_extcon_notify(hsotg, true); + dwc2_gadget_extcon_notify(hsotg); } if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { @@ -3329,11 +3341,22 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) dwc2_hsotg_core_init_disconnected(hsotg, false); dwc2_hsotg_core_connect(hsotg); } else { + /* + * If gadget disconnection makes also PHY pullup + * disconnection, usb controller cannot generate + * interrupt for usb connection and thus extcon + * notification won't be working. In order to prevent + * this situation, we have to keep PHY pullup alive + * when extcon notification is activated. + */ + if (hsotg->g_extcon_notify) + goto skip_change; dwc2_hsotg_core_disconnect(hsotg); dwc2_hsotg_disconnect(hsotg); hsotg->enabled = 0; } +skip_change: hsotg->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3757,6 +3780,9 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) */ int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) { + if (hsotg->g_extcon_notify) + dwc2_gadget_cancel_extcon_work(hsotg); + usb_del_gadget_udc(&hsotg->gadget); dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req); @@ -3780,6 +3806,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) } } + if (hsotg->g_extcon_notify) + dwc2_gadget_cancel_extcon_work(hsotg); + if (hsotg->driver) { int ep; -- 2.7.4