summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSang-Hun Lee <sanlee@nvidia.com>2012-05-15 16:04:41 -0700
committerSimone Willett <swillett@nvidia.com>2012-05-25 09:56:22 -0700
commit168971ab0977d04e958671651c0be4be116fee01 (patch)
tree24c12f96cd2a0d52ad980e26d87e1a423e9f2dad /drivers
parenteff5dd61e05e1b01c396609e2129a8ab433d2666 (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')
-rw-r--r--drivers/usb/gadget/tegra_udc.c22
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);