Commit b8b9c974 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Felipe Balbi

usb: renesas_usbhs: gadget: disable all eps when the driver stops

A gadget driver will not disable eps immediately when ->disconnect()
is called. But, since this driver assumes all eps stop after
the ->disconnect(), unexpected behavior happens (especially in system
suspend).
So, this patch disables all eps in usbhsg_try_stop(). After disabling
eps by renesas_usbhs driver, since some functions will be called by
both a gadget and renesas_usbhs driver, renesas_usbhs driver should
protect uep->pipe. To protect uep->pipe easily, this patch adds a new
lock in struct usbhsg_uep.

Fixes: 2f98382d ("usb: renesas_usbhs: Add Renesas USBHS Gadget")
Cc: <stable@vger.kernel.org> # v3.0+
Signed-off-by: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 59a0879a
...@@ -37,6 +37,7 @@ struct usbhsg_gpriv; ...@@ -37,6 +37,7 @@ struct usbhsg_gpriv;
struct usbhsg_uep { struct usbhsg_uep {
struct usb_ep ep; struct usb_ep ep;
struct usbhs_pipe *pipe; struct usbhs_pipe *pipe;
spinlock_t lock; /* protect the pipe */
char ep_name[EP_NAME_SIZE]; char ep_name[EP_NAME_SIZE];
...@@ -636,10 +637,16 @@ static int usbhsg_ep_enable(struct usb_ep *ep, ...@@ -636,10 +637,16 @@ static int usbhsg_ep_enable(struct usb_ep *ep,
static int usbhsg_ep_disable(struct usb_ep *ep) static int usbhsg_ep_disable(struct usb_ep *ep)
{ {
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pipe *pipe;
unsigned long flags;
int ret = 0;
if (!pipe) spin_lock_irqsave(&uep->lock, flags);
return -EINVAL; pipe = usbhsg_uep_to_pipe(uep);
if (!pipe) {
ret = -EINVAL;
goto out;
}
usbhsg_pipe_disable(uep); usbhsg_pipe_disable(uep);
usbhs_pipe_free(pipe); usbhs_pipe_free(pipe);
...@@ -647,6 +654,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep) ...@@ -647,6 +654,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep)
uep->pipe->mod_private = NULL; uep->pipe->mod_private = NULL;
uep->pipe = NULL; uep->pipe = NULL;
out:
spin_unlock_irqrestore(&uep->lock, flags);
return 0; return 0;
} }
...@@ -696,8 +706,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) ...@@ -696,8 +706,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{ {
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pipe *pipe;
unsigned long flags;
spin_lock_irqsave(&uep->lock, flags);
pipe = usbhsg_uep_to_pipe(uep);
if (pipe) if (pipe)
usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq)); usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
...@@ -706,6 +719,7 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) ...@@ -706,6 +719,7 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
* even if the pipe is NULL. * even if the pipe is NULL.
*/ */
usbhsg_queue_pop(uep, ureq, -ECONNRESET); usbhsg_queue_pop(uep, ureq, -ECONNRESET);
spin_unlock_irqrestore(&uep->lock, flags);
return 0; return 0;
} }
...@@ -852,10 +866,10 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) ...@@ -852,10 +866,10 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
{ {
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); struct usbhsg_uep *uep;
struct device *dev = usbhs_priv_to_dev(priv); struct device *dev = usbhs_priv_to_dev(priv);
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0, i;
/******************** spin lock ********************/ /******************** spin lock ********************/
usbhs_lock(priv, flags); usbhs_lock(priv, flags);
...@@ -887,7 +901,9 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) ...@@ -887,7 +901,9 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
usbhs_sys_set_test_mode(priv, 0); usbhs_sys_set_test_mode(priv, 0);
usbhs_sys_function_ctrl(priv, 0); usbhs_sys_function_ctrl(priv, 0);
usbhsg_ep_disable(&dcp->ep); /* disable all eps */
usbhsg_for_each_uep_with_dcp(uep, gpriv, i)
usbhsg_ep_disable(&uep->ep);
dev_dbg(dev, "stop gadget\n"); dev_dbg(dev, "stop gadget\n");
...@@ -1069,6 +1085,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ...@@ -1069,6 +1085,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
ret = -ENOMEM; ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv; goto usbhs_mod_gadget_probe_err_gpriv;
} }
spin_lock_init(&uep->lock);
gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n", dev_info(dev, "%stransceiver found\n",
......
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