From: Benoit Goby Date: Wed, 12 Jan 2011 23:38:24 +0000 (-0800) Subject: mdm6600: Resume modem synchronously X-Git-Tag: firefly_0821_release~9834^2~173 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=dda0b352a7dac2c7eb188ffe2091bd509ce3853b;p=firefly-linux-kernel-4.4.55.git 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. Resumes the modem synchronously and make sure there are no pending wakeup requests before disconnect returns. Change-Id: Ie5fbbe819324018564502245a53e4fb5a663adb3 Signed-off-by: Benoit Goby --- diff --git a/drivers/usb/serial/mdm6600.c b/drivers/usb/serial/mdm6600.c index caa8164f360a..cc6556f6ae44 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,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);