Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (97 commits) USB: qcserial: add device id for HP devices USB: isp1760: Add a delay before reading the SKIPMAP registers in isp1760-hcd.c USB: allow malformed LANGID descriptors USB: pxa27x_udc: typo fixes and code cleanups USB: gadget: gadget zero uses new suspend/resume hooks USB: gadget: composite device-level suspend/resume hooks USB: r8a66597-hcd: suspend/resume support USB: more u32 conversion after transfer_buffer_length and actual_length USB: Fix cp2101 USB serial device driver termios functions for console use USB: CP2101 New Device ID USB: ipaq: handle 4 endpoint devices USB: S3C: Move usb-control.h to platform include USB: ohci-hcd: Add ARCH_S3C24XX to the ohci-s3c2410.c glue USB: pedantic: spelling correction in comment for ch9.h USB: host: fix sparse warning: Using plain integer as NULL pointer USB: ohci-s3c2410: fix name of bus clock USB: ohci-s3c2410: remove <mach/hardware.h> include USB: serial: rename cp2101 driver to cp210x USB: CP2101 Reduce Error Logging USB: CP2101 Support AN205 baud rates ...
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* driver/usb/gadget/imx_udc.c
|
||||
*
|
||||
* Copyright (C) 2005 Mike Lee(eemike@gmail.com)
|
||||
* Copyright (C) 2005 Mike Lee <eemike@gmail.com>
|
||||
* Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
@@ -51,7 +52,8 @@ void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb,
|
||||
void imx_udc_enable(struct imx_udc_struct *imx_usb)
|
||||
{
|
||||
int temp = __raw_readl(imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA,
|
||||
imx_usb->base + USB_CTRL);
|
||||
imx_usb->gadget.speed = USB_SPEED_FULL;
|
||||
}
|
||||
|
||||
@@ -126,7 +128,8 @@ void imx_udc_config(struct imx_udc_struct *imx_usb)
|
||||
for (j = 0; j < 5; j++) {
|
||||
__raw_writeb(ep_conf[j],
|
||||
imx_usb->base + USB_DDAT);
|
||||
do {} while (__raw_readl(imx_usb->base + USB_DADR)
|
||||
do {} while (__raw_readl(imx_usb->base
|
||||
+ USB_DADR)
|
||||
& DADR_BSY);
|
||||
}
|
||||
}
|
||||
@@ -183,7 +186,8 @@ void imx_udc_init_ep(struct imx_udc_struct *imx_usb)
|
||||
temp = (EP_DIR(imx_ep) << 7) | (max << 5)
|
||||
| (imx_ep->bmAttributes << 3);
|
||||
__raw_writel(temp, imx_usb->base + USB_EP_STAT(i));
|
||||
__raw_writel(temp | EPSTAT_FLUSH, imx_usb->base + USB_EP_STAT(i));
|
||||
__raw_writel(temp | EPSTAT_FLUSH,
|
||||
imx_usb->base + USB_EP_STAT(i));
|
||||
D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i,
|
||||
__raw_readl(imx_usb->base + USB_EP_STAT(i)));
|
||||
}
|
||||
@@ -278,15 +282,18 @@ void imx_ep_stall(struct imx_ep_struct *imx_ep)
|
||||
struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
|
||||
int temp, i;
|
||||
|
||||
D_ERR(imx_usb->dev, "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name);
|
||||
D_ERR(imx_usb->dev,
|
||||
"<%s> Forced stall on %s\n", __func__, imx_ep->ep.name);
|
||||
|
||||
imx_flush(imx_ep);
|
||||
|
||||
/* Special care for ep0 */
|
||||
if (EP_NO(imx_ep)) {
|
||||
if (!EP_NO(imx_ep)) {
|
||||
temp = __raw_readl(imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, imx_usb->base + USB_CTRL);
|
||||
do { } while (__raw_readl(imx_usb->base + USB_CTRL) & CTRL_CMDOVER);
|
||||
__raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR,
|
||||
imx_usb->base + USB_CTRL);
|
||||
do { } while (__raw_readl(imx_usb->base + USB_CTRL)
|
||||
& CTRL_CMDOVER);
|
||||
temp = __raw_readl(imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL);
|
||||
}
|
||||
@@ -296,12 +303,13 @@ void imx_ep_stall(struct imx_ep_struct *imx_ep)
|
||||
imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
|
||||
|
||||
for (i = 0; i < 100; i ++) {
|
||||
temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
|
||||
temp = __raw_readl(imx_usb->base
|
||||
+ USB_EP_STAT(EP_NO(imx_ep)));
|
||||
if (!(temp & EPSTAT_STALL))
|
||||
break;
|
||||
udelay(20);
|
||||
}
|
||||
if (i == 50)
|
||||
if (i == 100)
|
||||
D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n",
|
||||
__func__, imx_ep->ep.name);
|
||||
}
|
||||
@@ -325,7 +333,8 @@ static int imx_udc_wakeup(struct usb_gadget *_gadget)
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
|
||||
static void ep_add_request(struct imx_ep_struct *imx_ep,
|
||||
struct imx_request *req)
|
||||
{
|
||||
if (unlikely(!req))
|
||||
return;
|
||||
@@ -334,7 +343,8 @@ static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req
|
||||
list_add_tail(&req->queue, &imx_ep->queue);
|
||||
}
|
||||
|
||||
static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
|
||||
static void ep_del_request(struct imx_ep_struct *imx_ep,
|
||||
struct imx_request *req)
|
||||
{
|
||||
if (unlikely(!req))
|
||||
return;
|
||||
@@ -343,7 +353,8 @@ static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req
|
||||
req->in_use = 0;
|
||||
}
|
||||
|
||||
static void done(struct imx_ep_struct *imx_ep, struct imx_request *req, int status)
|
||||
static void done(struct imx_ep_struct *imx_ep,
|
||||
struct imx_request *req, int status)
|
||||
{
|
||||
ep_del_request(imx_ep, req);
|
||||
|
||||
@@ -494,7 +505,8 @@ static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req)
|
||||
__func__, imx_ep->ep.name, req,
|
||||
completed ? "completed" : "not completed");
|
||||
if (!EP_NO(imx_ep))
|
||||
ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE);
|
||||
ep0_chg_stat(__func__,
|
||||
imx_ep->imx_usb, EP0_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,10 +551,9 @@ static int handle_ep0(struct imx_ep_struct *imx_ep)
|
||||
struct imx_request *req = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!list_empty(&imx_ep->queue))
|
||||
if (!list_empty(&imx_ep->queue)) {
|
||||
req = list_entry(imx_ep->queue.next, struct imx_request, queue);
|
||||
|
||||
if (req) {
|
||||
switch (imx_ep->imx_usb->ep0state) {
|
||||
|
||||
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */
|
||||
@@ -561,6 +572,10 @@ static int handle_ep0(struct imx_ep_struct *imx_ep)
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n",
|
||||
__func__, imx_ep->ep.name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -583,7 +598,8 @@ static void handle_ep0_devreq(struct imx_udc_struct *imx_usb)
|
||||
"<%s> no setup packet received\n", __func__);
|
||||
goto stall;
|
||||
}
|
||||
u.word[i] = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep)));
|
||||
u.word[i] = __raw_readl(imx_usb->base
|
||||
+ USB_EP_FDAT(EP_NO(imx_ep)));
|
||||
}
|
||||
|
||||
temp = imx_ep_empty(imx_ep);
|
||||
@@ -759,7 +775,7 @@ static int imx_ep_queue
|
||||
*/
|
||||
if (imx_usb->set_config && !EP_NO(imx_ep)) {
|
||||
imx_usb->set_config = 0;
|
||||
D_EPX(imx_usb->dev,
|
||||
D_ERR(imx_usb->dev,
|
||||
"<%s> gadget reply set config\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
@@ -779,28 +795,29 @@ static int imx_ep_queue
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Debug */
|
||||
D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n",
|
||||
__func__, EP_NO(imx_ep),
|
||||
((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
|
||||
|| (EP_NO(imx_ep) && EP_DIR(imx_ep))) ? "IN" : "OUT", usb_req->length);
|
||||
((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state
|
||||
== EP0_IN_DATA_PHASE)
|
||||
|| (EP_NO(imx_ep) && EP_DIR(imx_ep)))
|
||||
? "IN" : "OUT", usb_req->length);
|
||||
dump_req(__func__, imx_ep, usb_req);
|
||||
|
||||
if (imx_ep->stopped) {
|
||||
usb_req->status = -ESHUTDOWN;
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
if (req->in_use) {
|
||||
D_ERR(imx_usb->dev,
|
||||
"<%s> refusing to queue req %p (already queued)\n",
|
||||
__func__, req);
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
usb_req->status = -EINPROGRESS;
|
||||
usb_req->actual = 0;
|
||||
|
||||
@@ -810,7 +827,7 @@ static int imx_ep_queue
|
||||
ret = handle_ep0(imx_ep);
|
||||
else
|
||||
ret = handle_ep(imx_ep);
|
||||
out:
|
||||
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
}
|
||||
@@ -997,71 +1014,32 @@ static void udc_stop_activity(struct imx_udc_struct *imx_usb,
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
static irqreturn_t imx_udc_irq(int irq, void *dev)
|
||||
/*
|
||||
* Called when timer expires.
|
||||
* Timer is started when CFG_CHG is received.
|
||||
*/
|
||||
static void handle_config(unsigned long data)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = dev;
|
||||
struct imx_udc_struct *imx_usb = (void *)data;
|
||||
struct usb_ctrlrequest u;
|
||||
int temp, cfg, intf, alt;
|
||||
int intr = __raw_readl(imx_usb->base + USB_INTR);
|
||||
|
||||
if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
|
||||
| INTR_RESET_STOP | INTR_CFG_CHG)) {
|
||||
dump_intr(__func__, intr, imx_usb->dev);
|
||||
dump_usb_stat(__func__, imx_usb);
|
||||
}
|
||||
local_irq_disable();
|
||||
|
||||
if (!imx_usb->driver) {
|
||||
/*imx_udc_disable(imx_usb);*/
|
||||
goto end_irq;
|
||||
}
|
||||
temp = __raw_readl(imx_usb->base + USB_STAT);
|
||||
cfg = (temp & STAT_CFG) >> 5;
|
||||
intf = (temp & STAT_INTF) >> 3;
|
||||
alt = temp & STAT_ALTSET;
|
||||
|
||||
if (intr & INTR_WAKEUP) {
|
||||
if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
|
||||
&& imx_usb->driver && imx_usb->driver->resume)
|
||||
imx_usb->driver->resume(&imx_usb->gadget);
|
||||
imx_usb->set_config = 0;
|
||||
imx_usb->gadget.speed = USB_SPEED_FULL;
|
||||
}
|
||||
D_REQ(imx_usb->dev,
|
||||
"<%s> orig config C=%d, I=%d, A=%d / "
|
||||
"req config C=%d, I=%d, A=%d\n",
|
||||
__func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
|
||||
cfg, intf, alt);
|
||||
|
||||
if (intr & INTR_SUSPEND) {
|
||||
if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
|
||||
&& imx_usb->driver && imx_usb->driver->suspend)
|
||||
imx_usb->driver->suspend(&imx_usb->gadget);
|
||||
imx_usb->set_config = 0;
|
||||
imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
}
|
||||
if (cfg == 1 || cfg == 2) {
|
||||
|
||||
if (intr & INTR_RESET_START) {
|
||||
__raw_writel(intr, imx_usb->base + USB_INTR);
|
||||
udc_stop_activity(imx_usb, imx_usb->driver);
|
||||
imx_usb->set_config = 0;
|
||||
imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
if (intr & INTR_RESET_STOP)
|
||||
imx_usb->gadget.speed = USB_SPEED_FULL;
|
||||
|
||||
if (intr & INTR_CFG_CHG) {
|
||||
__raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
|
||||
temp = __raw_readl(imx_usb->base + USB_STAT);
|
||||
cfg = (temp & STAT_CFG) >> 5;
|
||||
intf = (temp & STAT_INTF) >> 3;
|
||||
alt = temp & STAT_ALTSET;
|
||||
|
||||
D_REQ(imx_usb->dev,
|
||||
"<%s> orig config C=%d, I=%d, A=%d / "
|
||||
"req config C=%d, I=%d, A=%d\n",
|
||||
__func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
|
||||
cfg, intf, alt);
|
||||
|
||||
if (cfg != 1 && cfg != 2)
|
||||
goto end_irq;
|
||||
|
||||
imx_usb->set_config = 0;
|
||||
|
||||
/* Config setup */
|
||||
if (imx_usb->cfg != cfg) {
|
||||
D_REQ(imx_usb->dev, "<%s> Change config start\n",__func__);
|
||||
u.bRequest = USB_REQ_SET_CONFIGURATION;
|
||||
u.bRequestType = USB_DIR_OUT |
|
||||
USB_TYPE_STANDARD |
|
||||
@@ -1070,14 +1048,10 @@ static irqreturn_t imx_udc_irq(int irq, void *dev)
|
||||
u.wIndex = 0;
|
||||
u.wLength = 0;
|
||||
imx_usb->cfg = cfg;
|
||||
imx_usb->set_config = 1;
|
||||
imx_usb->driver->setup(&imx_usb->gadget, &u);
|
||||
imx_usb->set_config = 0;
|
||||
D_REQ(imx_usb->dev, "<%s> Change config done\n",__func__);
|
||||
|
||||
}
|
||||
if (imx_usb->intf != intf || imx_usb->alt != alt) {
|
||||
D_REQ(imx_usb->dev, "<%s> Change interface start\n",__func__);
|
||||
u.bRequest = USB_REQ_SET_INTERFACE;
|
||||
u.bRequestType = USB_DIR_OUT |
|
||||
USB_TYPE_STANDARD |
|
||||
@@ -1087,20 +1061,92 @@ static irqreturn_t imx_udc_irq(int irq, void *dev)
|
||||
u.wLength = 0;
|
||||
imx_usb->intf = intf;
|
||||
imx_usb->alt = alt;
|
||||
imx_usb->set_config = 1;
|
||||
imx_usb->driver->setup(&imx_usb->gadget, &u);
|
||||
imx_usb->set_config = 0;
|
||||
D_REQ(imx_usb->dev, "<%s> Change interface done\n",__func__);
|
||||
}
|
||||
}
|
||||
|
||||
imx_usb->set_config = 0;
|
||||
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static irqreturn_t imx_udc_irq(int irq, void *dev)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = dev;
|
||||
int intr = __raw_readl(imx_usb->base + USB_INTR);
|
||||
int temp;
|
||||
|
||||
if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
|
||||
| INTR_RESET_STOP | INTR_CFG_CHG)) {
|
||||
dump_intr(__func__, intr, imx_usb->dev);
|
||||
dump_usb_stat(__func__, imx_usb);
|
||||
}
|
||||
|
||||
if (!imx_usb->driver)
|
||||
goto end_irq;
|
||||
|
||||
if (intr & INTR_SOF) {
|
||||
/* Copy from Freescale BSP.
|
||||
We must enable SOF intr and set CMDOVER.
|
||||
Datasheet don't specifiy this action, but it
|
||||
is done in Freescale BSP, so just copy it.
|
||||
*/
|
||||
if (imx_usb->ep0state == EP0_IDLE) {
|
||||
temp = __raw_readl(imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
|
||||
__raw_writel(temp | CTRL_CMDOVER,
|
||||
imx_usb->base + USB_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
if (intr & INTR_CFG_CHG) {
|
||||
/* A workaround of serious IMX UDC bug.
|
||||
Handling of CFG_CHG should be delayed for some time, because
|
||||
IMX does not NACK the host when CFG_CHG interrupt is pending.
|
||||
There is no time to handle current CFG_CHG
|
||||
if next CFG_CHG or SETUP packed is send immediately.
|
||||
We have to clear CFG_CHG, start the timer and
|
||||
NACK the host by setting CTRL_CMDOVER
|
||||
if it sends any SETUP packet.
|
||||
When timer expires, handler is called to handle configuration
|
||||
changes. While CFG_CHG is not handled (set_config=1),
|
||||
we must NACK the host to every SETUP packed.
|
||||
This delay prevents from going out of sync with host.
|
||||
*/
|
||||
__raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
|
||||
imx_usb->set_config = 1;
|
||||
mod_timer(&imx_usb->timer, jiffies + 5);
|
||||
goto end_irq;
|
||||
}
|
||||
|
||||
if (intr & INTR_WAKEUP) {
|
||||
if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
|
||||
&& imx_usb->driver && imx_usb->driver->resume)
|
||||
imx_usb->driver->resume(&imx_usb->gadget);
|
||||
imx_usb->set_config = 0;
|
||||
del_timer(&imx_usb->timer);
|
||||
imx_usb->gadget.speed = USB_SPEED_FULL;
|
||||
}
|
||||
|
||||
if (intr & INTR_SUSPEND) {
|
||||
if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
|
||||
&& imx_usb->driver && imx_usb->driver->suspend)
|
||||
imx_usb->driver->suspend(&imx_usb->gadget);
|
||||
imx_usb->set_config = 0;
|
||||
del_timer(&imx_usb->timer);
|
||||
imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
if (intr & INTR_RESET_START) {
|
||||
__raw_writel(intr, imx_usb->base + USB_INTR);
|
||||
udc_stop_activity(imx_usb, imx_usb->driver);
|
||||
imx_usb->set_config = 0;
|
||||
del_timer(&imx_usb->timer);
|
||||
imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
if (intr & INTR_RESET_STOP)
|
||||
imx_usb->gadget.speed = USB_SPEED_FULL;
|
||||
|
||||
end_irq:
|
||||
__raw_writel(intr, imx_usb->base + USB_INTR);
|
||||
return IRQ_HANDLED;
|
||||
@@ -1109,6 +1155,7 @@ end_irq:
|
||||
static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = dev;
|
||||
struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0];
|
||||
int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0));
|
||||
|
||||
dump_ep_intr(__func__, 0, intr, imx_usb->dev);
|
||||
@@ -1118,16 +1165,15 @@ static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* DEVREQ IRQ has highest priority */
|
||||
/* DEVREQ has highest priority */
|
||||
if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ))
|
||||
handle_ep0_devreq(imx_usb);
|
||||
/* Seem i.MX is missing EOF interrupt sometimes.
|
||||
* Therefore we monitor both EOF and FIFO_EMPTY interrups
|
||||
* when transmiting, and both EOF and FIFO_FULL when
|
||||
* receiving data.
|
||||
* Therefore we don't monitor EOF.
|
||||
* We call handle_ep0() only if a request is queued for ep0.
|
||||
*/
|
||||
else if (intr & (EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL))
|
||||
handle_ep0(&imx_usb->imx_ep[0]);
|
||||
else if (!list_empty(&imx_ep->queue))
|
||||
handle_ep0(imx_ep);
|
||||
|
||||
__raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
|
||||
|
||||
@@ -1318,6 +1364,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
|
||||
udc_stop_activity(imx_usb, driver);
|
||||
imx_udc_disable(imx_usb);
|
||||
del_timer(&imx_usb->timer);
|
||||
|
||||
driver->unbind(&imx_usb->gadget);
|
||||
imx_usb->gadget.dev.driver = NULL;
|
||||
@@ -1435,6 +1482,10 @@ static int __init imx_udc_probe(struct platform_device *pdev)
|
||||
usb_init_data(imx_usb);
|
||||
imx_udc_init(imx_usb);
|
||||
|
||||
init_timer(&imx_usb->timer);
|
||||
imx_usb->timer.function = handle_config;
|
||||
imx_usb->timer.data = (unsigned long)imx_usb;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
@@ -1457,6 +1508,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
imx_udc_disable(imx_usb);
|
||||
del_timer(&imx_usb->timer);
|
||||
|
||||
for (i = 0; i < IMX_USB_NB_EP + 1; i++)
|
||||
free_irq(imx_usb->usbd_int[i], imx_usb);
|
||||
|
Reference in New Issue
Block a user