diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2015-01-06 13:46:59 (GMT) |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2015-01-12 18:13:29 (GMT) |
commit | 258e2ddd634c20065d1c41290d10d2e5cf2f56e2 (patch) | |
tree | 92fe7b3861717a0696c185a9c8c661b1bea52e2f | |
parent | 3280e67536f8a4d4adf8dcde10cb4c4b577c34f4 (diff) | |
download | linux-258e2ddd634c20065d1c41290d10d2e5cf2f56e2.tar.xz |
usb: atmel_usba_udc: Add at91sam9g45 and at91sam9x5 errata handling
at91sam9g45 and at91sam9x5 SoCs have an hardware bug forcing us to
generate a pulse on the BIAS signal on "USB end of reset” and
“USB end of resume" events.
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Reported-by: Patrice VILCHEZ <patrice.vilchez@atmel.com>
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.c | 28 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.h | 2 |
2 files changed, 29 insertions, 1 deletions
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 36fd34b..55c8dde 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -331,6 +331,17 @@ static void toggle_bias(struct usba_udc *udc, int is_on) udc->errata->toggle_bias(udc, is_on); } +static void generate_bias_pulse(struct usba_udc *udc) +{ + if (!udc->bias_pulse_needed) + return; + + if (udc->errata && udc->errata->pulse_bias) + udc->errata->pulse_bias(udc); + + udc->bias_pulse_needed = false; +} + static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) { unsigned int transaction_len; @@ -1607,6 +1618,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_DET_SUSPEND) { toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); + udc->bias_pulse_needed = true; DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->suspend) { @@ -1624,6 +1636,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_END_OF_RESUME) { usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); + generate_bias_pulse(udc); DBG(DBG_BUS, "Resume detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->resume) { @@ -1659,6 +1672,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) struct usba_ep *ep0; usba_writel(udc, INT_CLR, USBA_END_OF_RESET); + generate_bias_pulse(udc); reset_all_endpoints(udc); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) { @@ -1818,13 +1832,25 @@ static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); } +static void at91sam9g45_pulse_bias(struct usba_udc *udc) +{ + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); + + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); +} + static const struct usba_udc_errata at91sam9rl_errata = { .toggle_bias = at91sam9rl_toggle_bias, }; +static const struct usba_udc_errata at91sam9g45_errata = { + .pulse_bias = at91sam9g45_pulse_bias, +}; + static const struct of_device_id atmel_udc_dt_ids[] = { { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata }, - { .compatible = "atmel,at91sam9g45-udc" }, + { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata }, { .compatible = "atmel,sama5d3-udc" }, { /* sentinel */ } }; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 456899e..72b3537 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -306,6 +306,7 @@ struct usba_request { struct usba_udc_errata { void (*toggle_bias)(struct usba_udc *udc, int is_on); + void (*pulse_bias)(struct usba_udc *udc); }; struct usba_udc { @@ -326,6 +327,7 @@ struct usba_udc { struct clk *pclk; struct clk *hclk; struct usba_ep *usba_ep; + bool bias_pulse_needed; u16 devstatus; |