mdm6600: Resume modem synchronously
authorBenoit Goby <benoit@android.com>
Wed, 12 Jan 2011 23:38:24 +0000 (15:38 -0800)
committerBenoit Goby <benoit@android.com>
Thu, 13 Jan 2011 02:11:29 +0000 (18:11 -0800)
If runtime pm auto-resumes the modem while the modem is being
disconnected, runtime pm might use the modem power spinlock after free.

Resumes the modem synchronously and make sure there are no pending
wakeup requests before disconnect returns.

Change-Id: Ie5fbbe819324018564502245a53e4fb5a663adb3
Signed-off-by: Benoit Goby <benoit@android.com>
drivers/usb/serial/mdm6600.c

index caa8164f360a210237299d06a023ca5675b5332f..cc6556f6ae44c1b76db92935c26f218437fb1fc3 100644 (file)
@@ -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,24 @@ 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;
-
-       wake_lock_timeout(&modem->readlock, MODEM_WAKELOCK_TIME);
+       struct mdm6600_port *modem = container_of(work, struct mdm6600_port,
+               wake_work);
 
+       dbg("%s: port %d", __func__, modem->number);
        /* let usbcore auto-resume the modem */
-       if (usb_autopm_get_interface_async(modem->serial->interface) == 0)
+       if (usb_autopm_get_interface(modem->serial->interface) == 0)
                /* set usage count back to 0 */
                usb_autopm_put_interface_no_suspend(modem->serial->interface);
+}
+
+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 +251,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 +309,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);