Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 201406
b: refs/heads/master
c: 4037242
h: refs/heads/master
v: v3
  • Loading branch information
Ryan Mallon authored and Russell King committed Jul 18, 2010
1 parent 66840ed commit e3c55d8
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 064baaca873c64feaea0e4476d14d4c9fd978e9e
refs/heads/master: 4037242c4f5ff77afe61bf07ca1e8a99490219e5
2 changes: 2 additions & 0 deletions trunk/arch/arm/mach-at91/include/mach/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
/* USB Device */
struct at91_udc_data {
u8 vbus_pin; /* high == host powering us */
u8 vbus_active_low; /* vbus polarity */
u8 vbus_polled; /* Use polling, not interrupt */
u8 pullup_pin; /* active == D+ pulled up */
u8 pullup_active_low; /* true == pullup_pin is active low */
};
Expand Down
66 changes: 53 additions & 13 deletions trunk/drivers/usb/gadget/at91_udc.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
static const char driver_name [] = "at91_udc";
static const char ep0name[] = "ep0";

#define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000)

#define at91_udp_read(udc, reg) \
__raw_readl((udc)->udp_baseaddr + (reg))
Expand Down Expand Up @@ -1585,20 +1586,48 @@ static struct at91_udc controller = {
/* ep6 and ep7 are also reserved (custom silicon might use them) */
};

static void at91_vbus_update(struct at91_udc *udc, unsigned value)
{
value ^= udc->board.vbus_active_low;
if (value != udc->vbus)
at91_vbus_session(&udc->gadget, value);
}

static irqreturn_t at91_vbus_irq(int irq, void *_udc)
{
struct at91_udc *udc = _udc;
unsigned value;

/* vbus needs at least brief debouncing */
udelay(10);
value = gpio_get_value(udc->board.vbus_pin);
if (value != udc->vbus)
at91_vbus_session(&udc->gadget, value);
at91_vbus_update(udc, gpio_get_value(udc->board.vbus_pin));

return IRQ_HANDLED;
}

static void at91_vbus_timer_work(struct work_struct *work)
{
struct at91_udc *udc = container_of(work, struct at91_udc,
vbus_timer_work);

at91_vbus_update(udc, gpio_get_value_cansleep(udc->board.vbus_pin));

if (!timer_pending(&udc->vbus_timer))
mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
}

static void at91_vbus_timer(unsigned long data)
{
struct at91_udc *udc = (struct at91_udc *)data;

/*
* If we are polling vbus it is likely that the gpio is on an
* bus such as i2c or spi which may sleep, so schedule some work
* to read the vbus gpio
*/
if (!work_pending(&udc->vbus_timer_work))
schedule_work(&udc->vbus_timer_work);
}

int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
Expand Down Expand Up @@ -1800,13 +1829,23 @@ static int __init at91udc_probe(struct platform_device *pdev)
* Get the initial state of VBUS - we cannot expect
* a pending interrupt.
*/
udc->vbus = gpio_get_value(udc->board.vbus_pin);
if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
IRQF_DISABLED, driver_name, udc)) {
DBG("request vbus irq %d failed\n",
udc->board.vbus_pin);
retval = -EBUSY;
goto fail3;
udc->vbus = gpio_get_value_cansleep(udc->board.vbus_pin) ^
udc->board.vbus_active_low;

if (udc->board.vbus_polled) {
INIT_WORK(&udc->vbus_timer_work, at91_vbus_timer_work);
setup_timer(&udc->vbus_timer, at91_vbus_timer,
(unsigned long)udc);
mod_timer(&udc->vbus_timer,
jiffies + VBUS_POLL_TIMEOUT);
} else {
if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
IRQF_DISABLED, driver_name, udc)) {
DBG("request vbus irq %d failed\n",
udc->board.vbus_pin);
retval = -EBUSY;
goto fail3;
}
}
} else {
DBG("no VBUS detection, assuming always-on\n");
Expand Down Expand Up @@ -1898,7 +1937,7 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
enable_irq_wake(udc->udp_irq);

udc->active_suspend = wake;
if (udc->board.vbus_pin > 0 && wake)
if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled && wake)
enable_irq_wake(udc->board.vbus_pin);
return 0;
}
Expand All @@ -1908,7 +1947,8 @@ static int at91udc_resume(struct platform_device *pdev)
struct at91_udc *udc = platform_get_drvdata(pdev);
unsigned long flags;

if (udc->board.vbus_pin > 0 && udc->active_suspend)
if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled &&
udc->active_suspend)
disable_irq_wake(udc->board.vbus_pin);

/* maybe reconnect to host; if so, clocks on */
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/gadget/at91_udc.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ struct at91_udc {
void __iomem *udp_baseaddr;
int udp_irq;
spinlock_t lock;
struct timer_list vbus_timer;
struct work_struct vbus_timer_work;
};

static inline struct at91_udc *to_udc(struct usb_gadget *g)
Expand Down

0 comments on commit e3c55d8

Please sign in to comment.