aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/host/imx21-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/imx21-hcd.c')
-rw-r--r--drivers/usb/host/imx21-hcd.c380
1 files changed, 269 insertions, 111 deletions
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index 213e270e1c2..207bad99301 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -27,8 +27,8 @@
* * 32 transfer descriptors (called ETDs)
* * 4Kb of Data memory
*
- * The data memory is shared between the host and fuction controlers
- * (but this driver only supports the host controler)
+ * The data memory is shared between the host and function controllers
+ * (but this driver only supports the host controller)
*
* So setting up a transfer involves:
* * Allocating a ETD
@@ -54,11 +54,18 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
-#include "../core/hcd.h"
#include "imx21-hcd.h"
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define DEBUG
+#endif
+
#ifdef DEBUG
#define DEBUG_LOG_FRAME(imx21, etd, event) \
(etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB)
@@ -135,9 +142,18 @@ static int imx21_hc_get_frame(struct usb_hcd *hcd)
return wrap_frame(readl(imx21->regs + USBH_FRMNUB));
}
+static inline bool unsuitable_for_dma(dma_addr_t addr)
+{
+ return (addr & 3) != 0;
+}
#include "imx21-dbg.c"
+static void nonisoc_urb_completed_for_etd(
+ struct imx21 *imx21, struct etd_priv *etd, int status);
+static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb);
+static void free_dmem(struct imx21 *imx21, struct etd_priv *etd);
+
/* =========================================== */
/* ETD management */
/* =========================================== */
@@ -184,7 +200,8 @@ static void reset_etd(struct imx21 *imx21, int num)
etd_writel(imx21, num, i, 0);
etd->urb = NULL;
etd->ep = NULL;
- etd->td = NULL;;
+ etd->td = NULL;
+ etd->bounce_buffer = NULL;
}
static void free_etd(struct imx21 *imx21, int num)
@@ -220,26 +237,94 @@ static void setup_etd_dword0(struct imx21 *imx21,
((u32) maxpacket << DW0_MAXPKTSIZ));
}
-static void activate_etd(struct imx21 *imx21,
- int etd_num, dma_addr_t dma, u8 dir)
+/**
+ * Copy buffer to data controller data memory.
+ * We cannot use memcpy_toio() because the hardware requires 32bit writes
+ */
+static void copy_to_dmem(
+ struct imx21 *imx21, int dmem_offset, void *src, int count)
+{
+ void __iomem *dmem = imx21->regs + USBOTG_DMEM + dmem_offset;
+ u32 word = 0;
+ u8 *p = src;
+ int byte = 0;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ byte = i % 4;
+ word += (*p++ << (byte * 8));
+ if (byte == 3) {
+ writel(word, dmem);
+ dmem += 4;
+ word = 0;
+ }
+ }
+
+ if (count && byte != 3)
+ writel(word, dmem);
+}
+
+static void activate_etd(struct imx21 *imx21, int etd_num, u8 dir)
{
u32 etd_mask = 1 << etd_num;
struct etd_priv *etd = &imx21->etd[etd_num];
+ if (etd->dma_handle && unsuitable_for_dma(etd->dma_handle)) {
+ /* For non aligned isoc the condition below is always true */
+ if (etd->len <= etd->dmem_size) {
+ /* Fits into data memory, use PIO */
+ if (dir != TD_DIR_IN) {
+ copy_to_dmem(imx21,
+ etd->dmem_offset,
+ etd->cpu_buffer, etd->len);
+ }
+ etd->dma_handle = 0;
+
+ } else {
+ /* Too big for data memory, use bounce buffer */
+ enum dma_data_direction dmadir;
+
+ if (dir == TD_DIR_IN) {
+ dmadir = DMA_FROM_DEVICE;
+ etd->bounce_buffer = kmalloc(etd->len,
+ GFP_ATOMIC);
+ } else {
+ dmadir = DMA_TO_DEVICE;
+ etd->bounce_buffer = kmemdup(etd->cpu_buffer,
+ etd->len,
+ GFP_ATOMIC);
+ }
+ if (!etd->bounce_buffer) {
+ dev_err(imx21->dev, "failed bounce alloc\n");
+ goto err_bounce_alloc;
+ }
+
+ etd->dma_handle =
+ dma_map_single(imx21->dev,
+ etd->bounce_buffer,
+ etd->len,
+ dmadir);
+ if (dma_mapping_error(imx21->dev, etd->dma_handle)) {
+ dev_err(imx21->dev, "failed bounce map\n");
+ goto err_bounce_map;
+ }
+ }
+ }
+
clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask);
set_register_bits(imx21, USBH_ETDDONEEN, etd_mask);
clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
- if (dma) {
+ if (etd->dma_handle) {
set_register_bits(imx21, USB_ETDDMACHANLCLR, etd_mask);
clear_toggle_bit(imx21, USBH_XBUFSTAT, etd_mask);
clear_toggle_bit(imx21, USBH_YBUFSTAT, etd_mask);
- writel(dma, imx21->regs + USB_ETDSMSA(etd_num));
+ writel(etd->dma_handle, imx21->regs + USB_ETDSMSA(etd_num));
set_register_bits(imx21, USB_ETDDMAEN, etd_mask);
} else {
if (dir != TD_DIR_IN) {
- /* need to set for ZLP */
+ /* need to set for ZLP and PIO */
set_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
set_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
}
@@ -262,6 +347,14 @@ static void activate_etd(struct imx21 *imx21,
etd->active_count = 1;
writel(etd_mask, imx21->regs + USBH_ETDENSET);
+ return;
+
+err_bounce_map:
+ kfree(etd->bounce_buffer);
+
+err_bounce_alloc:
+ free_dmem(imx21, etd);
+ nonisoc_urb_completed_for_etd(imx21, etd, -ENOMEM);
}
/* =========================================== */
@@ -322,16 +415,23 @@ static void activate_queued_etd(struct imx21 *imx21,
etd_writel(imx21, etd_num, 1,
((dmem_offset + maxpacket) << DW1_YBUFSRTAD) | dmem_offset);
+ etd->dmem_offset = dmem_offset;
urb_priv->active = 1;
- activate_etd(imx21, etd_num, etd->dma_handle, dir);
+ activate_etd(imx21, etd_num, dir);
}
-static void free_dmem(struct imx21 *imx21, int offset)
+static void free_dmem(struct imx21 *imx21, struct etd_priv *etd)
{
struct imx21_dmem_area *area;
- struct etd_priv *etd, *tmp;
+ struct etd_priv *tmp;
int found = 0;
+ int offset;
+ if (!etd->dmem_size)
+ return;
+ etd->dmem_size = 0;
+
+ offset = etd->dmem_offset;
list_for_each_entry(area, &imx21->dmem_list, list) {
if (area->offset == offset) {
debug_dmem_freed(imx21, area->size);
@@ -377,20 +477,23 @@ static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep)
/* =========================================== */
/* End handling */
/* =========================================== */
-static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb);
-/* Endpoint now idle - release it's ETD(s) or asssign to queued request */
+/* Endpoint now idle - release its ETD(s) or assign to queued request */
static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv)
{
- int etd_num;
int i;
for (i = 0; i < NUM_ISO_ETDS; i++) {
- etd_num = ep_priv->etd[i];
+ int etd_num = ep_priv->etd[i];
+ struct etd_priv *etd;
if (etd_num < 0)
continue;
+ etd = &imx21->etd[etd_num];
ep_priv->etd[i] = -1;
+
+ free_dmem(imx21, etd); /* for isoc */
+
if (list_empty(&imx21->queue_for_etd)) {
free_etd(imx21, etd_num);
continue;
@@ -436,6 +539,24 @@ __acquires(imx21->lock)
ep_idle(imx21, ep_priv);
}
+static void nonisoc_urb_completed_for_etd(
+ struct imx21 *imx21, struct etd_priv *etd, int status)
+{
+ struct usb_host_endpoint *ep = etd->ep;
+
+ urb_done(imx21->hcd, etd->urb, status);
+ etd->urb = NULL;
+
+ if (!list_empty(&ep->urb_list)) {
+ struct urb *urb = list_first_entry(
+ &ep->urb_list, struct urb, urb_list);
+
+ dev_vdbg(imx21->dev, "next URB %p\n", urb);
+ schedule_nonisoc_etd(imx21, urb);
+ }
+}
+
+
/* =========================================== */
/* ISOC Handling ... */
/* =========================================== */
@@ -488,6 +609,8 @@ too_late:
etd->ep = td->ep;
etd->urb = td->urb;
etd->len = td->len;
+ etd->dma_handle = td->dma_handle;
+ etd->cpu_buffer = td->cpu_buffer;
debug_isoc_submitted(imx21, cur_frame, td);
@@ -501,16 +624,17 @@ too_late:
(TD_NOTACCESSED << DW3_COMPCODE0) |
(td->len << DW3_PKTLEN0));
- activate_etd(imx21, etd_num, td->data, dir);
+ activate_etd(imx21, etd_num, dir);
}
}
-static void isoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
+static void isoc_etd_done(struct usb_hcd *hcd, int etd_num)
{
struct imx21 *imx21 = hcd_to_imx21(hcd);
int etd_mask = 1 << etd_num;
- struct urb_priv *urb_priv = urb->hcpriv;
struct etd_priv *etd = imx21->etd + etd_num;
+ struct urb *urb = etd->urb;
+ struct urb_priv *urb_priv = urb->hcpriv;
struct td *td = etd->td;
struct usb_host_endpoint *ep = etd->ep;
int isoc_index = td->isoc_index;
@@ -544,8 +668,13 @@ static void isoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
bytes_xfrd, td->len, urb, etd_num, isoc_index);
}
- if (dir_in)
+ if (dir_in) {
clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
+ if (!etd->dma_handle)
+ memcpy_fromio(etd->cpu_buffer,
+ imx21->regs + USBOTG_DMEM + etd->dmem_offset,
+ bytes_xfrd);
+ }
urb->actual_length += bytes_xfrd;
urb->iso_frame_desc[isoc_index].actual_length = bytes_xfrd;
@@ -568,30 +697,43 @@ static struct ep_priv *alloc_isoc_ep(
int i;
ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
- if (ep_priv == NULL)
+ if (!ep_priv)
return NULL;
- /* Allocate the ETDs */
- for (i = 0; i < NUM_ISO_ETDS; i++) {
- ep_priv->etd[i] = alloc_etd(imx21);
- if (ep_priv->etd[i] < 0) {
- int j;
- dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
- for (j = 0; j < i; j++)
- free_etd(imx21, ep_priv->etd[j]);
- goto alloc_etd_failed;
- }
- imx21->etd[ep_priv->etd[i]].ep = ep;
- }
+ for (i = 0; i < NUM_ISO_ETDS; i++)
+ ep_priv->etd[i] = -1;
INIT_LIST_HEAD(&ep_priv->td_list);
ep_priv->ep = ep;
ep->hcpriv = ep_priv;
return ep_priv;
+}
+
+static int alloc_isoc_etds(struct imx21 *imx21, struct ep_priv *ep_priv)
+{
+ int i, j;
+ int etd_num;
+
+ /* Allocate the ETDs if required */
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ if (ep_priv->etd[i] < 0) {
+ etd_num = alloc_etd(imx21);
+ if (etd_num < 0)
+ goto alloc_etd_failed;
+
+ ep_priv->etd[i] = etd_num;
+ imx21->etd[etd_num].ep = ep_priv->ep;
+ }
+ }
+ return 0;
alloc_etd_failed:
- kfree(ep_priv);
- return NULL;
+ dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
+ for (j = 0; j < i; j++) {
+ free_etd(imx21, ep_priv->etd[j]);
+ ep_priv->etd[j] = -1;
+ }
+ return -ENOMEM;
}
static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
@@ -631,6 +773,10 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
ep_priv = ep->hcpriv;
}
+ ret = alloc_isoc_etds(imx21, ep_priv);
+ if (ret)
+ goto alloc_etd_failed;
+
ret = usb_hcd_link_urb_to_ep(hcd, urb);
if (ret)
goto link_failed;
@@ -667,36 +813,47 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
/* calculate frame */
cur_frame = imx21_hc_get_frame(hcd);
- if (urb->transfer_flags & URB_ISO_ASAP) {
- if (list_empty(&ep_priv->td_list))
- urb->start_frame = cur_frame + 5;
- else
- urb->start_frame = list_entry(
- ep_priv->td_list.prev,
- struct td, list)->frame + urb->interval;
- }
- urb->start_frame = wrap_frame(urb->start_frame);
- if (frame_after(cur_frame, urb->start_frame)) {
- dev_dbg(imx21->dev,
- "enqueue: adjusting iso start %d (cur=%d) asap=%d\n",
- urb->start_frame, cur_frame,
- (urb->transfer_flags & URB_ISO_ASAP) != 0);
- urb->start_frame = wrap_frame(cur_frame + 1);
+ i = 0;
+ if (list_empty(&ep_priv->td_list)) {
+ urb->start_frame = wrap_frame(cur_frame + 5);
+ } else {
+ urb->start_frame = wrap_frame(list_entry(ep_priv->td_list.prev,
+ struct td, list)->frame + urb->interval);
+
+ if (frame_after(cur_frame, urb->start_frame)) {
+ dev_dbg(imx21->dev,
+ "enqueue: adjusting iso start %d (cur=%d) asap=%d\n",
+ urb->start_frame, cur_frame,
+ (urb->transfer_flags & URB_ISO_ASAP) != 0);
+ i = DIV_ROUND_UP(wrap_frame(
+ cur_frame - urb->start_frame),
+ urb->interval);
+
+ /* Treat underruns as if URB_ISO_ASAP was set */
+ if ((urb->transfer_flags & URB_ISO_ASAP) ||
+ i >= urb->number_of_packets) {
+ urb->start_frame = wrap_frame(urb->start_frame
+ + i * urb->interval);
+ i = 0;
+ }
+ }
}
/* set up transfers */
+ urb_priv->isoc_remaining = urb->number_of_packets - i;
td = urb_priv->isoc_td;
- for (i = 0; i < urb->number_of_packets; i++, td++) {
+ for (; i < urb->number_of_packets; i++, td++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
td->ep = ep;
td->urb = urb;
td->len = urb->iso_frame_desc[i].length;
td->isoc_index = i;
td->frame = wrap_frame(urb->start_frame + urb->interval * i);
- td->data = urb->transfer_dma + urb->iso_frame_desc[i].offset;
+ td->dma_handle = urb->transfer_dma + offset;
+ td->cpu_buffer = urb->transfer_buffer + offset;
list_add_tail(&td->list, &ep_priv->td_list);
}
- urb_priv->isoc_remaining = urb->number_of_packets;
dev_vdbg(imx21->dev, "setup %d packets for iso frame %d->%d\n",
urb->number_of_packets, urb->start_frame, td->frame);
@@ -710,6 +867,7 @@ alloc_dmem_failed:
usb_hcd_unlink_urb_from_ep(hcd, urb);
link_failed:
+alloc_etd_failed:
alloc_ep_failed:
spin_unlock_irqrestore(&imx21->lock, flags);
kfree(urb_priv->isoc_td);
@@ -733,9 +891,7 @@ static void dequeue_isoc_urb(struct imx21 *imx21,
struct etd_priv *etd = imx21->etd + etd_num;
reset_etd(imx21, etd_num);
- if (etd->dmem_size)
- free_dmem(imx21, etd->dmem_offset);
- etd->dmem_size = 0;
+ free_dmem(imx21, etd);
}
}
}
@@ -760,7 +916,6 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
int state = urb_priv->state;
int etd_num = ep_priv->etd[0];
struct etd_priv *etd;
- int dmem_offset;
u32 count;
u16 etd_buf_size;
u16 maxpacket;
@@ -785,13 +940,16 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) {
if (state == US_CTRL_SETUP) {
dir = TD_DIR_SETUP;
+ if (unsuitable_for_dma(urb->setup_dma))
+ usb_hcd_unmap_urb_setup_for_dma(imx21->hcd,
+ urb);
etd->dma_handle = urb->setup_dma;
+ etd->cpu_buffer = urb->setup_packet;
bufround = 0;
count = 8;
datatoggle = TD_TOGGLE_DATA0;
} else { /* US_CTRL_ACK */
dir = usb_pipeout(pipe) ? TD_DIR_IN : TD_DIR_OUT;
- etd->dma_handle = urb->transfer_dma;
bufround = 0;
count = 0;
datatoggle = TD_TOGGLE_DATA1;
@@ -799,7 +957,11 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
} else {
dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN;
bufround = (dir == TD_DIR_IN) ? 1 : 0;
+ if (unsuitable_for_dma(urb->transfer_dma))
+ usb_hcd_unmap_urb_for_dma(imx21->hcd, urb);
+
etd->dma_handle = urb->transfer_dma;
+ etd->cpu_buffer = urb->transfer_buffer;
if (usb_pipebulk(pipe) && (state == US_BULK0))
count = 0;
else
@@ -854,8 +1016,8 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
/* allocate x and y buffer space at once */
etd->dmem_size = (count > maxpacket) ? maxpacket * 2 : maxpacket;
- dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep);
- if (dmem_offset < 0) {
+ etd->dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep);
+ if (etd->dmem_offset < 0) {
/* Setup everything we can in HW and update when we get DMEM */
etd_writel(imx21, etd_num, 1, (u32)maxpacket << 16);
@@ -866,26 +1028,26 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
}
etd_writel(imx21, etd_num, 1,
- (((u32) dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) |
- (u32) dmem_offset);
+ (((u32) etd->dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) |
+ (u32) etd->dmem_offset);
urb_priv->active = 1;
/* enable the ETD to kick off transfer */
dev_vdbg(imx21->dev, "Activating etd %d for %d bytes %s\n",
etd_num, count, dir != TD_DIR_IN ? "out" : "in");
- activate_etd(imx21, etd_num, etd->dma_handle, dir);
+ activate_etd(imx21, etd_num, dir);
}
-static void nonisoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
+static void nonisoc_etd_done(struct usb_hcd *hcd, int etd_num)
{
struct imx21 *imx21 = hcd_to_imx21(hcd);
struct etd_priv *etd = &imx21->etd[etd_num];
+ struct urb *urb = etd->urb;
u32 etd_mask = 1 << etd_num;
struct urb_priv *urb_priv = urb->hcpriv;
int dir;
- u16 xbufaddr;
int cc;
u32 bytes_xfrd;
int etd_done;
@@ -893,7 +1055,6 @@ static void nonisoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
disactivate_etd(imx21, etd_num);
dir = (etd_readl(imx21, etd_num, 0) >> DW0_DIRECT) & 0x3;
- xbufaddr = etd_readl(imx21, etd_num, 1) & 0xffff;
cc = (etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE) & 0xf;
bytes_xfrd = etd->len - (etd_readl(imx21, etd_num, 3) & 0x1fffff);
@@ -905,8 +1066,21 @@ static void nonisoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
if (dir == TD_DIR_IN) {
clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
+
+ if (etd->bounce_buffer) {
+ memcpy(etd->cpu_buffer, etd->bounce_buffer, bytes_xfrd);
+ dma_unmap_single(imx21->dev,
+ etd->dma_handle, etd->len, DMA_FROM_DEVICE);
+ } else if (!etd->dma_handle && bytes_xfrd) {/* PIO */
+ memcpy_fromio(etd->cpu_buffer,
+ imx21->regs + USBOTG_DMEM + etd->dmem_offset,
+ bytes_xfrd);
+ }
}
- free_dmem(imx21, xbufaddr);
+
+ kfree(etd->bounce_buffer);
+ etd->bounce_buffer = NULL;
+ free_dmem(imx21, etd);
urb->error_count = 0;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)
@@ -963,24 +1137,15 @@ static void nonisoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
break;
}
- if (!etd_done) {
+ if (etd_done)
+ nonisoc_urb_completed_for_etd(imx21, etd, cc_to_error[cc]);
+ else {
dev_vdbg(imx21->dev, "next state=%d\n", urb_priv->state);
schedule_nonisoc_etd(imx21, urb);
- } else {
- struct usb_host_endpoint *ep = urb->ep;
-
- urb_done(hcd, urb, cc_to_error[cc]);
- etd->urb = NULL;
-
- if (!list_empty(&ep->urb_list)) {
- urb = list_first_entry(&ep->urb_list,
- struct urb, urb_list);
- dev_vdbg(imx21->dev, "next URB %p\n", urb);
- schedule_nonisoc_etd(imx21, urb);
- }
}
}
+
static struct ep_priv *alloc_ep(void)
{
int i;
@@ -1006,7 +1171,6 @@ static int imx21_hc_urb_enqueue(struct usb_hcd *hcd,
struct etd_priv *etd;
int ret;
unsigned long flags;
- int new_ep = 0;
dev_vdbg(imx21->dev,
"enqueue urb=%p ep=%p len=%d "
@@ -1034,7 +1198,6 @@ static int imx21_hc_urb_enqueue(struct usb_hcd *hcd,
}
ep->hcpriv = ep_priv;
ep_priv->ep = ep;
- new_ep = 1;
}
ret = usb_hcd_link_urb_to_ep(hcd, urb);
@@ -1123,9 +1286,13 @@ static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
} else if (urb_priv->active) {
int etd_num = ep_priv->etd[0];
if (etd_num != -1) {
+ struct etd_priv *etd = &imx21->etd[etd_num];
+
disactivate_etd(imx21, etd_num);
- free_dmem(imx21, etd_readl(imx21, etd_num, 1) & 0xffff);
- imx21->etd[etd_num].urb = NULL;
+ free_dmem(imx21, etd);
+ etd->urb = NULL;
+ kfree(etd->bounce_buffer);
+ etd->bounce_buffer = NULL;
}
}
@@ -1170,7 +1337,7 @@ static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof)
* (and hence no interrupt occurs).
* This causes the transfer in question to hang.
* The kludge below checks for this condition at each SOF and processes any
- * blocked ETDs (after an arbitary 10 frame wait)
+ * blocked ETDs (after an arbitrary 10 frame wait)
*
* With a single active transfer the usbtest test suite will run for days
* without the kludge.
@@ -1225,9 +1392,9 @@ static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof)
}
if (usb_pipeisoc(etd->urb->pipe))
- isoc_etd_done(hcd, etd->urb, etd_num);
+ isoc_etd_done(hcd, etd_num);
else
- nonisoc_etd_done(hcd, etd->urb, etd_num);
+ nonisoc_etd_done(hcd, etd_num);
}
/* only enable SOF interrupt if it may be needed for the kludge */
@@ -1319,8 +1486,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
0x0010 | /* No over current protection */
0);
- desc->bitmap[0] = 1 << 1;
- desc->bitmap[1] = ~0;
+ desc->u.hs.DeviceRemovable[0] = 1 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
return 0;
}
@@ -1506,7 +1673,7 @@ static int imx21_hc_reset(struct usb_hcd *hcd)
spin_lock_irqsave(&imx21->lock, flags);
- /* Reset the Host controler modules */
+ /* Reset the Host controller modules */
writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH |
USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC,
imx21->regs + USBOTG_RST_CTRL);
@@ -1520,14 +1687,14 @@ static int imx21_hc_reset(struct usb_hcd *hcd)
return -ETIMEDOUT;
}
spin_unlock_irq(&imx21->lock);
- schedule_timeout(1);
+ schedule_timeout_uninterruptible(1);
spin_lock_irq(&imx21->lock);
}
spin_unlock_irqrestore(&imx21->lock, flags);
return 0;
}
-static int __devinit imx21_hc_start(struct usb_hcd *hcd)
+static int imx21_hc_start(struct usb_hcd *hcd)
{
struct imx21 *imx21 = hcd_to_imx21(hcd);
unsigned long flags;
@@ -1658,7 +1825,7 @@ static int imx21_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
if (res != NULL) {
- clk_disable(imx21->clk);
+ clk_disable_unprepare(imx21->clk);
clk_put(imx21->clk);
iounmap(imx21->regs);
release_mem_region(res->start, resource_size(res));
@@ -1695,8 +1862,9 @@ static int imx21_probe(struct platform_device *pdev)
}
imx21 = hcd_to_imx21(hcd);
+ imx21->hcd = hcd;
imx21->dev = &pdev->dev;
- imx21->pdata = pdev->dev.platform_data;
+ imx21->pdata = dev_get_platdata(&pdev->dev);
if (!imx21->pdata)
imx21->pdata = &default_pdata;
@@ -1730,30 +1898,31 @@ static int imx21_probe(struct platform_device *pdev)
ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000));
if (ret)
goto failed_clock_set;
- ret = clk_enable(imx21->clk);
+ ret = clk_prepare_enable(imx21->clk);
if (ret)
goto failed_clock_enable;
dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n",
(readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret != 0) {
dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret);
goto failed_add_hcd;
}
+ device_wakeup_enable(hcd->self.controller);
return 0;
failed_add_hcd:
- clk_disable(imx21->clk);
+ clk_disable_unprepare(imx21->clk);
failed_clock_enable:
failed_clock_set:
clk_put(imx21->clk);
failed_clock_get:
iounmap(imx21->regs);
failed_ioremap:
- release_mem_region(res->start, res->end - res->start);
+ release_mem_region(res->start, resource_size(res));
failed_request_mem:
remove_debug_files(imx21);
usb_put_hcd(hcd);
@@ -1762,7 +1931,7 @@ failed_request_mem:
static struct platform_driver imx21_hcd_driver = {
.driver = {
- .name = (char *)hcd_name,
+ .name = hcd_name,
},
.probe = imx21_probe,
.remove = imx21_remove,
@@ -1770,18 +1939,7 @@ static struct platform_driver imx21_hcd_driver = {
.resume = NULL,
};
-static int __init imx21_hcd_init(void)
-{
- return platform_driver_register(&imx21_hcd_driver);
-}
-
-static void __exit imx21_hcd_cleanup(void)
-{
- platform_driver_unregister(&imx21_hcd_driver);
-}
-
-module_init(imx21_hcd_init);
-module_exit(imx21_hcd_cleanup);
+module_platform_driver(imx21_hcd_driver);
MODULE_DESCRIPTION("i.MX21 USB Host controller");
MODULE_AUTHOR("Martin Fuzzey");