diff options
author | Sang-Hun Lee <sanlee@nvidia.com> | 2012-05-15 16:04:41 -0700 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-05-25 09:56:22 -0700 |
commit | 168971ab0977d04e958671651c0be4be116fee01 (patch) | |
tree | 24c12f96cd2a0d52ad980e26d87e1a423e9f2dad /drivers/usb/gadget | |
parent | eff5dd61e05e1b01c396609e2129a8ab433d2666 (diff) |
tegra: usb: disable interrupts when locking
Problem description:
- tegra_udc_irq uses udc->lock
- Some functions running in the process context was not disabling
interrupts when locking udc->lock
- If a function gets interrupted by tegra_udc_irq after locking
udc->lock, a deadlock occurs, as tegra_udc_irq would also try to
lock
Fix description:
- Use an interruption disabling variant of spin_lock
Bug 983958
Change-Id: Ib774847212da64f1f727a207a4821860ffa7b4a8
Signed-off-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-on: http://git-master/r/102693
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/tegra_udc.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c index dc14c612b38f..f978f0f2d1e7 100644 --- a/drivers/usb/gadget/tegra_udc.c +++ b/drivers/usb/gadget/tegra_udc.c @@ -130,7 +130,7 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status) unsigned char stopped = ep->stopped; struct ep_td_struct *curr_td, *next_td; int j; - + BUG_ON(!(in_irq() || irqs_disabled())); udc = (struct tegra_udc *)ep->udc; /* Removed the req from tegra_ep->queue */ list_del_init(&req->queue); @@ -180,19 +180,20 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status) } #endif - spin_unlock(&ep->udc->lock); /* complete() is from gadget layer, * eg fsg->bulk_in_complete() */ - if (req->req.complete) + if (req->req.complete) { + spin_unlock(&ep->udc->lock); req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->udc->lock); + } - spin_lock(&ep->udc->lock); ep->stopped = stopped; } /* * nuke(): delete all requests related to this ep - * called with spinlock held + * Must be called with spinlock held and interrupt disabled */ static void nuke(struct tegra_ep *ep, int status) { @@ -1221,14 +1222,14 @@ static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on) static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) { struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); - + unsigned long flags; DBG("%s(%d) turn VBUS state from %s to %s", __func__, __LINE__, udc->vbus_active ? "on" : "off", is_active ? "on" : "off"); if (udc->vbus_active && !is_active) { /* If cable disconnected, cancel any delayed work */ cancel_delayed_work(&udc->work); - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); /* reset all internal Queues and inform client driver */ reset_queues(udc); /* stop the controller and turn off the clocks */ @@ -1236,7 +1237,7 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) dr_controller_reset(udc); udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock,flags); tegra_usb_phy_power_off(udc->phy); if (udc->vbus_reg) { /* set the current limit to 0mA */ @@ -2704,6 +2705,7 @@ static int __exit tegra_udc_remove(struct platform_device *pdev) static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) { struct tegra_udc *udc = platform_get_drvdata(pdev); + unsigned long flags; DBG("%s(%d) BEGIN\n", __func__, __LINE__); /* If the controller is in otg mode, return */ @@ -2711,12 +2713,12 @@ static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) return 0; if (udc->vbus_active) { - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); /* Reset all internal Queues and inform client driver */ reset_queues(udc); udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); } /* Stop the controller and turn off the clocks */ dr_controller_stop(udc); |