USB: gpio_vbus: add delayed vbus_session calls
authorRobert Jarzmik <robert.jarzmik@free.fr>
Sun, 25 Jan 2009 07:54:31 +0000 (23:54 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 24 Mar 2009 23:20:26 +0000 (16:20 -0700)
Call usb_gadget_vbus_connect() and ...disconnect() from a
workqueue rather than from an irq handler, allowing msleep()
calls in vbus_session.  Update kerneldoc to match.

[ dbrownell@users.sourceforge.net: more kerneldoc updates ]

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/otg/gpio_vbus.c
include/linux/usb/gadget.h
include/linux/usb/otg.h

index 63a6036f04befd51f84b5e1e8802274524208da3..1c26c94513e9e5bd14299d1ae046125a6d1cce07 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/usb.h>
+#include <linux/workqueue.h>
 
 #include <linux/regulator/consumer.h>
 
@@ -34,6 +35,7 @@ struct gpio_vbus_data {
        struct regulator       *vbus_draw;
        int                     vbus_draw_enabled;
        unsigned                mA;
+       struct work_struct      work;
 };
 
 
@@ -76,24 +78,26 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
        gpio_vbus->mA = mA;
 }
 
-/* VBUS change IRQ handler */
-static irqreturn_t gpio_vbus_irq(int irq, void *data)
+static int is_vbus_powered(struct gpio_vbus_mach_info *pdata)
 {
-       struct platform_device *pdev = data;
-       struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
-       struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
-       int gpio, vbus;
+       int vbus;
 
        vbus = gpio_get_value(pdata->gpio_vbus);
        if (pdata->gpio_vbus_inverted)
                vbus = !vbus;
 
-       dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
-               vbus ? "supplied" : "inactive",
-               gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none");
+       return vbus;
+}
+
+static void gpio_vbus_work(struct work_struct *work)
+{
+       struct gpio_vbus_data *gpio_vbus =
+               container_of(work, struct gpio_vbus_data, work);
+       struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data;
+       int gpio;
 
        if (!gpio_vbus->otg.gadget)
-               return IRQ_HANDLED;
+               return;
 
        /* Peripheral controllers which manage the pullup themselves won't have
         * gpio_pullup configured here.  If it's configured here, we'll do what
@@ -101,7 +105,7 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data)
         * that may complicate usb_gadget_{,dis}connect() support.
         */
        gpio = pdata->gpio_pullup;
-       if (vbus) {
+       if (is_vbus_powered(pdata)) {
                gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL;
                usb_gadget_vbus_connect(gpio_vbus->otg.gadget);
 
@@ -121,6 +125,21 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data)
                usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget);
                gpio_vbus->otg.state = OTG_STATE_B_IDLE;
        }
+}
+
+/* VBUS change IRQ handler */
+static irqreturn_t gpio_vbus_irq(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+       struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
+       struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
+               is_vbus_powered(pdata) ? "supplied" : "inactive",
+               gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none");
+
+       if (gpio_vbus->otg.gadget)
+               schedule_work(&gpio_vbus->work);
 
        return IRQ_HANDLED;
 }
@@ -257,6 +276,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev)
                        irq, err);
                goto err_irq;
        }
+       INIT_WORK(&gpio_vbus->work, gpio_vbus_work);
 
        /* only active when a gadget is registered */
        err = otg_set_transceiver(&gpio_vbus->otg);
index 0460a746480ccf338e4f1a714099968e86e52b49..bbf45d500b6dd547535bd2c6523ac84a0438e110 100644 (file)
@@ -598,6 +598,7 @@ static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
 /**
  * usb_gadget_vbus_connect - Notify controller that VBUS is powered
  * @gadget:The device which now has VBUS power.
+ * Context: can sleep
  *
  * This call is used by a driver for an external transceiver (or GPIO)
  * that detects a VBUS power session starting.  Common responses include
@@ -636,6 +637,7 @@ static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
 /**
  * usb_gadget_vbus_disconnect - notify controller about VBUS session end
  * @gadget:the device whose VBUS supply is being described
+ * Context: can sleep
  *
  * This call is used by a driver for an external transceiver (or GPIO)
  * that detects a VBUS power session ending.  Common responses include
@@ -792,19 +794,20 @@ struct usb_gadget_driver {
 /**
  * usb_gadget_register_driver - register a gadget driver
  * @driver:the driver being registered
+ * Context: can sleep
  *
  * Call this in your gadget driver's module initialization function,
  * to tell the underlying usb controller driver about your driver.
  * The driver's bind() function will be called to bind it to a
  * gadget before this registration call returns.  It's expected that
  * the bind() functions will be in init sections.
- * This function must be called in a context that can sleep.
  */
 int usb_gadget_register_driver(struct usb_gadget_driver *driver);
 
 /**
  * usb_gadget_unregister_driver - unregister a gadget driver
  * @driver:the driver being unregistered
+ * Context: can sleep
  *
  * Call this in your gadget driver's module cleanup function,
  * to tell the underlying usb controller that your driver is
@@ -813,7 +816,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver);
  * to unbind() and clean up any device state, before this procedure
  * finally returns.  It's expected that the unbind() functions
  * will in in exit sections, so may not be linked in some kernels.
- * This function must be called in a context that can sleep.
  */
 int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
 
index 94df4fe6c6c0feb1b6a559a8719a8b7195a5d551..60a52576fd5c1c90436af8c2e8a5eaa6530c324b 100644 (file)
@@ -86,6 +86,7 @@ extern int otg_set_transceiver(struct otg_transceiver *);
 extern struct otg_transceiver *otg_get_transceiver(void);
 extern void otg_put_transceiver(struct otg_transceiver *);
 
+/* Context: can sleep */
 static inline int
 otg_start_hnp(struct otg_transceiver *otg)
 {
@@ -102,6 +103,8 @@ otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
 
 
 /* for usb peripheral controller drivers */
+
+/* Context: can sleep */
 static inline int
 otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph)
 {
@@ -114,6 +117,7 @@ otg_set_power(struct otg_transceiver *otg, unsigned mA)
        return otg->set_power(otg, mA);
 }
 
+/* Context: can sleep */
 static inline int
 otg_set_suspend(struct otg_transceiver *otg, int suspend)
 {