mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 19:26:25 +00:00
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: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
dd44be6b17
commit
c2344f13b5
3 changed files with 39 additions and 13 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue