Commit c2344f13 authored by Robert Jarzmik's avatar Robert Jarzmik Committed by Greg Kroah-Hartman

USB: gpio_vbus: add delayed vbus_session calls

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: default avatarRobert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent dd44be6b
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/workqueue.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -34,6 +35,7 @@ struct gpio_vbus_data { ...@@ -34,6 +35,7 @@ struct gpio_vbus_data {
struct regulator *vbus_draw; struct regulator *vbus_draw;
int vbus_draw_enabled; int vbus_draw_enabled;
unsigned mA; unsigned mA;
struct work_struct work;
}; };
...@@ -76,24 +78,26 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) ...@@ -76,24 +78,26 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
gpio_vbus->mA = mA; gpio_vbus->mA = mA;
} }
/* VBUS change IRQ handler */ static int is_vbus_powered(struct gpio_vbus_mach_info *pdata)
static irqreturn_t gpio_vbus_irq(int irq, void *data)
{ {
struct platform_device *pdev = data; int vbus;
struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
int gpio, vbus;
vbus = gpio_get_value(pdata->gpio_vbus); vbus = gpio_get_value(pdata->gpio_vbus);
if (pdata->gpio_vbus_inverted) if (pdata->gpio_vbus_inverted)
vbus = !vbus; vbus = !vbus;
dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", return vbus;
vbus ? "supplied" : "inactive", }
gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none");
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) if (!gpio_vbus->otg.gadget)
return IRQ_HANDLED; return;
/* Peripheral controllers which manage the pullup themselves won't have /* Peripheral controllers which manage the pullup themselves won't have
* gpio_pullup configured here. If it's configured here, we'll do what * 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) ...@@ -101,7 +105,7 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data)
* that may complicate usb_gadget_{,dis}connect() support. * that may complicate usb_gadget_{,dis}connect() support.
*/ */
gpio = pdata->gpio_pullup; gpio = pdata->gpio_pullup;
if (vbus) { if (is_vbus_powered(pdata)) {
gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL; gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL;
usb_gadget_vbus_connect(gpio_vbus->otg.gadget); usb_gadget_vbus_connect(gpio_vbus->otg.gadget);
...@@ -121,6 +125,21 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data) ...@@ -121,6 +125,21 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data)
usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget); usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget);
gpio_vbus->otg.state = OTG_STATE_B_IDLE; 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; return IRQ_HANDLED;
} }
...@@ -257,6 +276,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) ...@@ -257,6 +276,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev)
irq, err); irq, err);
goto err_irq; goto err_irq;
} }
INIT_WORK(&gpio_vbus->work, gpio_vbus_work);
/* only active when a gadget is registered */ /* only active when a gadget is registered */
err = otg_set_transceiver(&gpio_vbus->otg); err = otg_set_transceiver(&gpio_vbus->otg);
......
...@@ -598,6 +598,7 @@ static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) ...@@ -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 * usb_gadget_vbus_connect - Notify controller that VBUS is powered
* @gadget:The device which now has VBUS power. * @gadget:The device which now has VBUS power.
* Context: can sleep
* *
* This call is used by a driver for an external transceiver (or GPIO) * This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session starting. Common responses include * 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) ...@@ -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 * usb_gadget_vbus_disconnect - notify controller about VBUS session end
* @gadget:the device whose VBUS supply is being described * @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) * This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session ending. Common responses include * that detects a VBUS power session ending. Common responses include
...@@ -792,19 +794,20 @@ struct usb_gadget_driver { ...@@ -792,19 +794,20 @@ struct usb_gadget_driver {
/** /**
* usb_gadget_register_driver - register a gadget driver * usb_gadget_register_driver - register a gadget driver
* @driver:the driver being registered * @driver:the driver being registered
* Context: can sleep
* *
* Call this in your gadget driver's module initialization function, * Call this in your gadget driver's module initialization function,
* to tell the underlying usb controller driver about your driver. * to tell the underlying usb controller driver about your driver.
* The driver's bind() function will be called to bind it to a * The driver's bind() function will be called to bind it to a
* gadget before this registration call returns. It's expected that * gadget before this registration call returns. It's expected that
* the bind() functions will be in init sections. * 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); int usb_gadget_register_driver(struct usb_gadget_driver *driver);
/** /**
* usb_gadget_unregister_driver - unregister a gadget driver * usb_gadget_unregister_driver - unregister a gadget driver
* @driver:the driver being unregistered * @driver:the driver being unregistered
* Context: can sleep
* *
* Call this in your gadget driver's module cleanup function, * Call this in your gadget driver's module cleanup function,
* to tell the underlying usb controller that your driver is * to tell the underlying usb controller that your driver is
...@@ -813,7 +816,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver); ...@@ -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 * to unbind() and clean up any device state, before this procedure
* finally returns. It's expected that the unbind() functions * finally returns. It's expected that the unbind() functions
* will in in exit sections, so may not be linked in some kernels. * 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); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
......
...@@ -86,6 +86,7 @@ extern int otg_set_transceiver(struct otg_transceiver *); ...@@ -86,6 +86,7 @@ extern int otg_set_transceiver(struct otg_transceiver *);
extern struct otg_transceiver *otg_get_transceiver(void); extern struct otg_transceiver *otg_get_transceiver(void);
extern void otg_put_transceiver(struct otg_transceiver *); extern void otg_put_transceiver(struct otg_transceiver *);
/* Context: can sleep */
static inline int static inline int
otg_start_hnp(struct otg_transceiver *otg) otg_start_hnp(struct otg_transceiver *otg)
{ {
...@@ -102,6 +103,8 @@ otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) ...@@ -102,6 +103,8 @@ otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
/* for usb peripheral controller drivers */ /* for usb peripheral controller drivers */
/* Context: can sleep */
static inline int static inline int
otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph) otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph)
{ {
...@@ -114,6 +117,7 @@ otg_set_power(struct otg_transceiver *otg, unsigned mA) ...@@ -114,6 +117,7 @@ otg_set_power(struct otg_transceiver *otg, unsigned mA)
return otg->set_power(otg, mA); return otg->set_power(otg, mA);
} }
/* Context: can sleep */
static inline int static inline int
otg_set_suspend(struct otg_transceiver *otg, int suspend) otg_set_suspend(struct otg_transceiver *otg, int suspend)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment