From bc22a7ecb2bf24324d914fb3463ebc5e8756c0c5 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 12 Jan 2011 15:38:24 -0800 Subject: [PATCH] mdm6600: Resume modem synchronously If runtime pm auto-resumes the modem while the modem is being disconnected, runtime pm might use the modem power spinlock after free. Auto-resumes the modem synchronously and make sure there are no pending wakeup requests before disconnect returns. Don't request an auto-resume if the modem is suspended or this may cause usb devices to be resumed in the wrong order. Change-Id: I38bf4830d30bb8b7619e093e0ba1b3861d240aec Signed-off-by: Benoit Goby --- drivers/usb/serial/mdm6600.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/mdm6600.c b/drivers/usb/serial/mdm6600.c index caa8164f360a..fe2d2530d62d 100644 --- a/drivers/usb/serial/mdm6600.c +++ b/drivers/usb/serial/mdm6600.c @@ -98,6 +98,7 @@ struct mdm6600_port { int opened; int number; u16 tiocm_status; + struct work_struct wake_work; }; static int mdm6600_wake_irq; @@ -112,16 +113,36 @@ static void mdm6600_read_bulk_cb(struct urb *urb); static void mdm6600_write_bulk_cb(struct urb *urb); static void mdm6600_release(struct usb_serial *serial); -static irqreturn_t mdm6600_irq_handler(int irq, void *ptr) +static void mdm6600_wake_work(struct work_struct *work) { - struct mdm6600_port *modem = ptr; + struct mdm6600_port *modem = container_of(work, struct mdm6600_port, + wake_work); + struct usb_interface *intf = modem->serial->interface; - wake_lock_timeout(&modem->readlock, MODEM_WAKELOCK_TIME); + dbg("%s: port %d", __func__, modem->number); + + device_lock(&intf->dev); + + if (intf->dev.power.status >= DPM_OFF || + intf->dev.power.status == DPM_RESUMING) { + device_unlock(&intf->dev); + return; + } /* let usbcore auto-resume the modem */ - if (usb_autopm_get_interface_async(modem->serial->interface) == 0) + if (usb_autopm_get_interface(intf) == 0) /* set usage count back to 0 */ - usb_autopm_put_interface_no_suspend(modem->serial->interface); + usb_autopm_put_interface_no_suspend(intf); + + device_unlock(&intf->dev); +} + +static irqreturn_t mdm6600_irq_handler(int irq, void *ptr) +{ + struct mdm6600_port *modem = ptr; + + wake_lock_timeout(&modem->readlock, MODEM_WAKELOCK_TIME); + queue_work(system_nrt_wq, &modem->wake_work); return IRQ_HANDLED; } @@ -242,6 +263,7 @@ static int mdm6600_attach(struct usb_serial *serial) serial->dev->parent->autosuspend_delay = 0; if (modem->number == MODEM_INTERFACE_NUM) { + INIT_WORK(&modem->wake_work, mdm6600_wake_work); status = request_irq(mdm6600_wake_irq, mdm6600_irq_handler, IRQ_TYPE_EDGE_FALLING, "usb_wake_host", modem); if (status) { @@ -299,6 +321,7 @@ static void mdm6600_disconnect(struct usb_serial *serial) if (modem->number == MODEM_INTERFACE_NUM) { disable_irq_wake(mdm6600_wake_irq); free_irq(mdm6600_wake_irq, modem); + cancel_work_sync(&modem->wake_work); } mdm6600_kill_urbs(modem); -- 2.34.1