USB: MXC: Add i.MX21 specific USB host controller driver.

This driver is a Full / Low speed only USB host for the i.MX21.

Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Martin Fuzzey 2009-11-21 12:14:48 +01:00 committed by Greg Kroah-Hartman
parent 13dda80e48
commit 23d3e7a659
7 changed files with 2804 additions and 0 deletions

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2009 Martin Fuzzey <mfuzzey@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __ASM_ARCH_MX21_USBH
#define __ASM_ARCH_MX21_USBH
enum mx21_usbh_xcvr {
/* Values below as used by hardware (HWMODE register) */
MX21_USBXCVR_TXDIF_RXDIF = 0,
MX21_USBXCVR_TXDIF_RXSE = 1,
MX21_USBXCVR_TXSE_RXDIF = 2,
MX21_USBXCVR_TXSE_RXSE = 3,
};
struct mx21_usbh_platform_data {
enum mx21_usbh_xcvr host_xcvr; /* tranceiver mode host 1,2 ports */
enum mx21_usbh_xcvr otg_xcvr; /* tranceiver mode otg (as host) port */
u16 enable_host1:1,
enable_host2:1,
enable_otg_host:1, /* enable "OTG" port (as host) */
host1_xcverless:1, /* traceiverless host1 port */
host1_txenoe:1, /* output enable host1 transmit enable */
otg_ext_xcvr:1, /* external tranceiver for OTG port */
unused:10;
};
#endif /* __ASM_ARCH_MX21_USBH */

View File

@ -21,6 +21,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_HWA_HCD) += host/
obj-$(CONFIG_USB_ISP1760_HCD) += host/
obj-$(CONFIG_USB_IMX21_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/

View File

@ -399,3 +399,14 @@ config USB_HWA_HCD
To compile this driver a module, choose M here: the module
will be called "hwa-hc".
config USB_IMX21_HCD
tristate "iMX21 HCD support"
depends on USB && ARM && MACH_MX21
help
This driver enables support for the on-chip USB host in the
iMX21 processor.
To compile this driver as a module, choose M here: the
module will be called "imx21-hcd".

View File

@ -32,3 +32,5 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o

View File

@ -0,0 +1,527 @@
/*
* Copyright (c) 2009 by Martin Fuzzey
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this file is part of imx21-hcd.c */
#ifndef DEBUG
static inline void create_debug_files(struct imx21 *imx21) { }
static inline void remove_debug_files(struct imx21 *imx21) { }
static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {}
static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb,
int status) {}
static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {}
static inline void debug_urb_queued_for_etd(struct imx21 *imx21,
struct urb *urb) {}
static inline void debug_urb_queued_for_dmem(struct imx21 *imx21,
struct urb *urb) {}
static inline void debug_etd_allocated(struct imx21 *imx21) {}
static inline void debug_etd_freed(struct imx21 *imx21) {}
static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {}
static inline void debug_dmem_freed(struct imx21 *imx21, int size) {}
static inline void debug_isoc_submitted(struct imx21 *imx21,
int frame, struct td *td) {}
static inline void debug_isoc_completed(struct imx21 *imx21,
int frame, struct td *td, int cc, int len) {}
#else
#include <linux/debugfs.h>
#include <linux/seq_file.h>
static const char *dir_labels[] = {
"TD 0",
"OUT",
"IN",
"TD 1"
};
static const char *speed_labels[] = {
"Full",
"Low"
};
static const char *format_labels[] = {
"Control",
"ISO",
"Bulk",
"Interrupt"
};
static inline struct debug_stats *stats_for_urb(struct imx21 *imx21,
struct urb *urb)
{
return usb_pipeisoc(urb->pipe) ?
&imx21->isoc_stats : &imx21->nonisoc_stats;
}
static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb)
{
stats_for_urb(imx21, urb)->submitted++;
}
static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st)
{
if (st)
stats_for_urb(imx21, urb)->completed_failed++;
else
stats_for_urb(imx21, urb)->completed_ok++;
}
static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb)
{
stats_for_urb(imx21, urb)->unlinked++;
}
static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb)
{
stats_for_urb(imx21, urb)->queue_etd++;
}
static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb)
{
stats_for_urb(imx21, urb)->queue_dmem++;
}
static inline void debug_etd_allocated(struct imx21 *imx21)
{
imx21->etd_usage.maximum = max(
++(imx21->etd_usage.value),
imx21->etd_usage.maximum);
}
static inline void debug_etd_freed(struct imx21 *imx21)
{
imx21->etd_usage.value--;
}
static inline void debug_dmem_allocated(struct imx21 *imx21, int size)
{
imx21->dmem_usage.value += size;
imx21->dmem_usage.maximum = max(
imx21->dmem_usage.value,
imx21->dmem_usage.maximum);
}
static inline void debug_dmem_freed(struct imx21 *imx21, int size)
{
imx21->dmem_usage.value -= size;
}
static void debug_isoc_submitted(struct imx21 *imx21,
int frame, struct td *td)
{
struct debug_isoc_trace *trace = &imx21->isoc_trace[
imx21->isoc_trace_index++];
imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace);
trace->schedule_frame = td->frame;
trace->submit_frame = frame;
trace->request_len = td->len;
trace->td = td;
}
static inline void debug_isoc_completed(struct imx21 *imx21,
int frame, struct td *td, int cc, int len)
{
struct debug_isoc_trace *trace, *trace_failed;
int i;
int found = 0;
trace = imx21->isoc_trace;
for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) {
if (trace->td == td) {
trace->done_frame = frame;
trace->done_len = len;
trace->cc = cc;
trace->td = NULL;
found = 1;
break;
}
}
if (found && cc) {
trace_failed = &imx21->isoc_trace_failed[
imx21->isoc_trace_index_failed++];
imx21->isoc_trace_index_failed %= ARRAY_SIZE(
imx21->isoc_trace_failed);
*trace_failed = *trace;
}
}
static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize)
{
if (ep)
snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)",
ep->desc.bEndpointAddress,
usb_endpoint_type(&ep->desc),
ep);
else
snprintf(buf, bufsize, "none");
return buf;
}
static char *format_etd_dword0(u32 value, char *buf, int bufsize)
{
snprintf(buf, bufsize,
"addr=%d ep=%d dir=%s speed=%s format=%s halted=%d",
value & 0x7F,
(value >> DW0_ENDPNT) & 0x0F,
dir_labels[(value >> DW0_DIRECT) & 0x03],
speed_labels[(value >> DW0_SPEED) & 0x01],
format_labels[(value >> DW0_FORMAT) & 0x03],
(value >> DW0_HALTED) & 0x01);
return buf;
}
static int debug_status_show(struct seq_file *s, void *v)
{
struct imx21 *imx21 = s->private;
int etds_allocated = 0;
int etds_sw_busy = 0;
int etds_hw_busy = 0;
int dmem_blocks = 0;
int queued_for_etd = 0;
int queued_for_dmem = 0;
unsigned int dmem_bytes = 0;
int i;
struct etd_priv *etd;
u32 etd_enable_mask;
unsigned long flags;
struct imx21_dmem_area *dmem;
struct ep_priv *ep_priv;
spin_lock_irqsave(&imx21->lock, flags);
etd_enable_mask = readl(imx21->regs + USBH_ETDENSET);
for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
if (etd->alloc)
etds_allocated++;
if (etd->urb)
etds_sw_busy++;
if (etd_enable_mask & (1<<i))
etds_hw_busy++;
}
list_for_each_entry(dmem, &imx21->dmem_list, list) {
dmem_bytes += dmem->size;
dmem_blocks++;
}
list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue)
queued_for_etd++;
list_for_each_entry(etd, &imx21->queue_for_dmem, queue)
queued_for_dmem++;
spin_unlock_irqrestore(&imx21->lock, flags);
seq_printf(s,
"Frame: %d\n"
"ETDs allocated: %d/%d (max=%d)\n"
"ETDs in use sw: %d\n"
"ETDs in use hw: %d\n"
"DMEM alocated: %d/%d (max=%d)\n"
"DMEM blocks: %d\n"
"Queued waiting for ETD: %d\n"
"Queued waiting for DMEM: %d\n",
readl(imx21->regs + USBH_FRMNUB) & 0xFFFF,
etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum,
etds_sw_busy,
etds_hw_busy,
dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum,
dmem_blocks,
queued_for_etd,
queued_for_dmem);
return 0;
}
static int debug_dmem_show(struct seq_file *s, void *v)
{
struct imx21 *imx21 = s->private;
struct imx21_dmem_area *dmem;
unsigned long flags;
char ep_text[40];
spin_lock_irqsave(&imx21->lock, flags);
list_for_each_entry(dmem, &imx21->dmem_list, list)
seq_printf(s,
"%04X: size=0x%X "
"ep=%s\n",
dmem->offset, dmem->size,
format_ep(dmem->ep, ep_text, sizeof(ep_text)));
spin_unlock_irqrestore(&imx21->lock, flags);
return 0;
}
static int debug_etd_show(struct seq_file *s, void *v)
{
struct imx21 *imx21 = s->private;
struct etd_priv *etd;
char buf[60];
u32 dword;
int i, j;
unsigned long flags;
spin_lock_irqsave(&imx21->lock, flags);
for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
int state = -1;
struct urb_priv *urb_priv;
if (etd->urb) {
urb_priv = etd->urb->hcpriv;
if (urb_priv)
state = urb_priv->state;
}
seq_printf(s,
"etd_num: %d\n"
"ep: %s\n"
"alloc: %d\n"
"len: %d\n"
"busy sw: %d\n"
"busy hw: %d\n"
"urb state: %d\n"
"current urb: %p\n",
i,
format_ep(etd->ep, buf, sizeof(buf)),
etd->alloc,
etd->len,
etd->urb != NULL,
(readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0,
state,
etd->urb);
for (j = 0; j < 4; j++) {
dword = etd_readl(imx21, i, j);
switch (j) {
case 0:
format_etd_dword0(dword, buf, sizeof(buf));
break;
case 2:
snprintf(buf, sizeof(buf),
"cc=0X%02X", dword >> DW2_COMPCODE);
break;
default:
*buf = 0;
break;
}
seq_printf(s,
"dword %d: submitted=%08X cur=%08X [%s]\n",
j,
etd->submitted_dwords[j],
dword,
buf);
}
seq_printf(s, "\n");
}
spin_unlock_irqrestore(&imx21->lock, flags);
return 0;
}
static void debug_statistics_show_one(struct seq_file *s,
const char *name, struct debug_stats *stats)
{
seq_printf(s, "%s:\n"
"submitted URBs: %lu\n"
"completed OK: %lu\n"
"completed failed: %lu\n"
"unlinked: %lu\n"
"queued for ETD: %lu\n"
"queued for DMEM: %lu\n\n",
name,
stats->submitted,
stats->completed_ok,
stats->completed_failed,
stats->unlinked,
stats->queue_etd,
stats->queue_dmem);
}
static int debug_statistics_show(struct seq_file *s, void *v)
{
struct imx21 *imx21 = s->private;
unsigned long flags;
spin_lock_irqsave(&imx21->lock, flags);
debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats);
debug_statistics_show_one(s, "isoc", &imx21->isoc_stats);
seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks);
spin_unlock_irqrestore(&imx21->lock, flags);
return 0;
}
static void debug_isoc_show_one(struct seq_file *s,
const char *name, int index, struct debug_isoc_trace *trace)
{
seq_printf(s, "%s %d:\n"
"cc=0X%02X\n"
"scheduled frame %d (%d)\n"
"submittted frame %d (%d)\n"
"completed frame %d (%d)\n"
"requested length=%d\n"
"completed length=%d\n\n",
name, index,
trace->cc,
trace->schedule_frame, trace->schedule_frame & 0xFFFF,
trace->submit_frame, trace->submit_frame & 0xFFFF,
trace->done_frame, trace->done_frame & 0xFFFF,
trace->request_len,
trace->done_len);
}
static int debug_isoc_show(struct seq_file *s, void *v)
{
struct imx21 *imx21 = s->private;
struct debug_isoc_trace *trace;
unsigned long flags;
int i;
spin_lock_irqsave(&imx21->lock, flags);
trace = imx21->isoc_trace_failed;
for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++)
debug_isoc_show_one(s, "isoc failed", i, trace);
trace = imx21->isoc_trace;
for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++)
debug_isoc_show_one(s, "isoc", i, trace);
spin_unlock_irqrestore(&imx21->lock, flags);
return 0;
}
static int debug_status_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_status_show, inode->i_private);
}
static int debug_dmem_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_dmem_show, inode->i_private);
}
static int debug_etd_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_etd_show, inode->i_private);
}
static int debug_statistics_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_statistics_show, inode->i_private);
}
static int debug_isoc_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_isoc_show, inode->i_private);
}
static const struct file_operations debug_status_fops = {
.open = debug_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations debug_dmem_fops = {
.open = debug_dmem_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations debug_etd_fops = {
.open = debug_etd_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations debug_statistics_fops = {
.open = debug_statistics_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations debug_isoc_fops = {
.open = debug_isoc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void create_debug_files(struct imx21 *imx21)
{
imx21->debug_root = debugfs_create_dir(dev_name(imx21->dev), NULL);
if (!imx21->debug_root)
goto failed_create_rootdir;
if (!debugfs_create_file("status", S_IRUGO,
imx21->debug_root, imx21, &debug_status_fops))
goto failed_create;
if (!debugfs_create_file("dmem", S_IRUGO,
imx21->debug_root, imx21, &debug_dmem_fops))
goto failed_create;
if (!debugfs_create_file("etd", S_IRUGO,
imx21->debug_root, imx21, &debug_etd_fops))
goto failed_create;
if (!debugfs_create_file("statistics", S_IRUGO,
imx21->debug_root, imx21, &debug_statistics_fops))
goto failed_create;
if (!debugfs_create_file("isoc", S_IRUGO,
imx21->debug_root, imx21, &debug_isoc_fops))
goto failed_create;
return;
failed_create:
debugfs_remove_recursive(imx21->debug_root);
failed_create_rootdir:
imx21->debug_root = NULL;
}
static void remove_debug_files(struct imx21 *imx21)
{
if (imx21->debug_root) {
debugfs_remove_recursive(imx21->debug_root);
imx21->debug_root = NULL;
}
}
#endif

1789
drivers/usb/host/imx21-hcd.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,436 @@
/*
* Macros and prototypes for i.MX21
*
* Copyright (C) 2006 Loping Dog Embedded Systems
* Copyright (C) 2009 Martin Fuzzey
* Originally written by Jay Monkman <jtm@lopingdog.com>
* Ported to 2.6.30, debugged and enhanced by Martin Fuzzey
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __LINUX_IMX21_HCD_H__
#define __LINUX_IMX21_HCD_H__
#include <mach/mx21-usbhost.h>
#define NUM_ISO_ETDS 2
#define USB_NUM_ETD 32
#define DMEM_SIZE 4096
/* Register definitions */
#define USBOTG_HWMODE 0x00
#define USBOTG_HWMODE_ANASDBEN (1 << 14)
#define USBOTG_HWMODE_OTGXCVR_SHIFT 6
#define USBOTG_HWMODE_OTGXCVR_MASK (3 << 6)
#define USBOTG_HWMODE_OTGXCVR_TD_RD (0 << 6)
#define USBOTG_HWMODE_OTGXCVR_TS_RD (2 << 6)
#define USBOTG_HWMODE_OTGXCVR_TD_RS (1 << 6)
#define USBOTG_HWMODE_OTGXCVR_TS_RS (3 << 6)
#define USBOTG_HWMODE_HOSTXCVR_SHIFT 4
#define USBOTG_HWMODE_HOSTXCVR_MASK (3 << 4)
#define USBOTG_HWMODE_HOSTXCVR_TD_RD (0 << 4)
#define USBOTG_HWMODE_HOSTXCVR_TS_RD (2 << 4)
#define USBOTG_HWMODE_HOSTXCVR_TD_RS (1 << 4)
#define USBOTG_HWMODE_HOSTXCVR_TS_RS (3 << 4)
#define USBOTG_HWMODE_CRECFG_MASK (3 << 0)
#define USBOTG_HWMODE_CRECFG_HOST (1 << 0)
#define USBOTG_HWMODE_CRECFG_FUNC (2 << 0)
#define USBOTG_HWMODE_CRECFG_HNP (3 << 0)
#define USBOTG_CINT_STAT 0x04
#define USBOTG_CINT_STEN 0x08
#define USBOTG_ASHNPINT (1 << 5)
#define USBOTG_ASFCINT (1 << 4)
#define USBOTG_ASHCINT (1 << 3)
#define USBOTG_SHNPINT (1 << 2)
#define USBOTG_FCINT (1 << 1)
#define USBOTG_HCINT (1 << 0)
#define USBOTG_CLK_CTRL 0x0c
#define USBOTG_CLK_CTRL_FUNC (1 << 2)
#define USBOTG_CLK_CTRL_HST (1 << 1)
#define USBOTG_CLK_CTRL_MAIN (1 << 0)
#define USBOTG_RST_CTRL 0x10
#define USBOTG_RST_RSTI2C (1 << 15)
#define USBOTG_RST_RSTCTRL (1 << 5)
#define USBOTG_RST_RSTFC (1 << 4)
#define USBOTG_RST_RSTFSKE (1 << 3)
#define USBOTG_RST_RSTRH (1 << 2)
#define USBOTG_RST_RSTHSIE (1 << 1)
#define USBOTG_RST_RSTHC (1 << 0)
#define USBOTG_FRM_INTVL 0x14
#define USBOTG_FRM_REMAIN 0x18
#define USBOTG_HNP_CSR 0x1c
#define USBOTG_HNP_ISR 0x2c
#define USBOTG_HNP_IEN 0x30
#define USBOTG_I2C_TXCVR_REG(x) (0x100 + (x))
#define USBOTG_I2C_XCVR_DEVAD 0x118
#define USBOTG_I2C_SEQ_OP_REG 0x119
#define USBOTG_I2C_SEQ_RD_STARTAD 0x11a
#define USBOTG_I2C_OP_CTRL_REG 0x11b
#define USBOTG_I2C_SCLK_TO_SCK_HPER 0x11e
#define USBOTG_I2C_MASTER_INT_REG 0x11f
#define USBH_HOST_CTRL 0x80
#define USBH_HOST_CTRL_HCRESET (1 << 31)
#define USBH_HOST_CTRL_SCHDOVR(x) ((x) << 16)
#define USBH_HOST_CTRL_RMTWUEN (1 << 4)
#define USBH_HOST_CTRL_HCUSBSTE_RESET (0 << 2)
#define USBH_HOST_CTRL_HCUSBSTE_RESUME (1 << 2)
#define USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL (2 << 2)
#define USBH_HOST_CTRL_HCUSBSTE_SUSPEND (3 << 2)
#define USBH_HOST_CTRL_CTLBLKSR_1 (0 << 0)
#define USBH_HOST_CTRL_CTLBLKSR_2 (1 << 0)
#define USBH_HOST_CTRL_CTLBLKSR_3 (2 << 0)
#define USBH_HOST_CTRL_CTLBLKSR_4 (3 << 0)
#define USBH_SYSISR 0x88
#define USBH_SYSISR_PSCINT (1 << 6)
#define USBH_SYSISR_FMOFINT (1 << 5)
#define USBH_SYSISR_HERRINT (1 << 4)
#define USBH_SYSISR_RESDETINT (1 << 3)
#define USBH_SYSISR_SOFINT (1 << 2)
#define USBH_SYSISR_DONEINT (1 << 1)
#define USBH_SYSISR_SORINT (1 << 0)
#define USBH_SYSIEN 0x8c
#define USBH_SYSIEN_PSCINT (1 << 6)
#define USBH_SYSIEN_FMOFINT (1 << 5)
#define USBH_SYSIEN_HERRINT (1 << 4)
#define USBH_SYSIEN_RESDETINT (1 << 3)
#define USBH_SYSIEN_SOFINT (1 << 2)
#define USBH_SYSIEN_DONEINT (1 << 1)
#define USBH_SYSIEN_SORINT (1 << 0)
#define USBH_XBUFSTAT 0x98
#define USBH_YBUFSTAT 0x9c
#define USBH_XYINTEN 0xa0
#define USBH_XFILLSTAT 0xa8
#define USBH_YFILLSTAT 0xac
#define USBH_ETDENSET 0xc0
#define USBH_ETDENCLR 0xc4
#define USBH_IMMEDINT 0xcc
#define USBH_ETDDONESTAT 0xd0
#define USBH_ETDDONEEN 0xd4
#define USBH_FRMNUB 0xe0
#define USBH_LSTHRESH 0xe4
#define USBH_ROOTHUBA 0xe8
#define USBH_ROOTHUBA_PWRTOGOOD_MASK (0xff)
#define USBH_ROOTHUBA_PWRTOGOOD_SHIFT (24)
#define USBH_ROOTHUBA_NOOVRCURP (1 << 12)
#define USBH_ROOTHUBA_OVRCURPM (1 << 11)
#define USBH_ROOTHUBA_DEVTYPE (1 << 10)
#define USBH_ROOTHUBA_PWRSWTMD (1 << 9)
#define USBH_ROOTHUBA_NOPWRSWT (1 << 8)
#define USBH_ROOTHUBA_NDNSTMPRT_MASK (0xff)
#define USBH_ROOTHUBB 0xec
#define USBH_ROOTHUBB_PRTPWRCM(x) (1 << ((x) + 16))
#define USBH_ROOTHUBB_DEVREMOVE(x) (1 << (x))
#define USBH_ROOTSTAT 0xf0
#define USBH_ROOTSTAT_CLRRMTWUE (1 << 31)
#define USBH_ROOTSTAT_OVRCURCHG (1 << 17)
#define USBH_ROOTSTAT_DEVCONWUE (1 << 15)
#define USBH_ROOTSTAT_OVRCURI (1 << 1)
#define USBH_ROOTSTAT_LOCPWRS (1 << 0)
#define USBH_PORTSTAT(x) (0xf4 + ((x) * 4))
#define USBH_PORTSTAT_PRTRSTSC (1 << 20)
#define USBH_PORTSTAT_OVRCURIC (1 << 19)
#define USBH_PORTSTAT_PRTSTATSC (1 << 18)
#define USBH_PORTSTAT_PRTENBLSC (1 << 17)
#define USBH_PORTSTAT_CONNECTSC (1 << 16)
#define USBH_PORTSTAT_LSDEVCON (1 << 9)
#define USBH_PORTSTAT_PRTPWRST (1 << 8)
#define USBH_PORTSTAT_PRTRSTST (1 << 4)
#define USBH_PORTSTAT_PRTOVRCURI (1 << 3)
#define USBH_PORTSTAT_PRTSUSPST (1 << 2)
#define USBH_PORTSTAT_PRTENABST (1 << 1)
#define USBH_PORTSTAT_CURCONST (1 << 0)
#define USB_DMAREV 0x800
#define USB_DMAINTSTAT 0x804
#define USB_DMAINTSTAT_EPERR (1 << 1)
#define USB_DMAINTSTAT_ETDERR (1 << 0)
#define USB_DMAINTEN 0x808
#define USB_DMAINTEN_EPERRINTEN (1 << 1)
#define USB_DMAINTEN_ETDERRINTEN (1 << 0)
#define USB_ETDDMAERSTAT 0x80c
#define USB_EPDMAERSTAT 0x810
#define USB_ETDDMAEN 0x820
#define USB_EPDMAEN 0x824
#define USB_ETDDMAXTEN 0x828
#define USB_EPDMAXTEN 0x82c
#define USB_ETDDMAENXYT 0x830
#define USB_EPDMAENXYT 0x834
#define USB_ETDDMABST4EN 0x838
#define USB_EPDMABST4EN 0x83c
#define USB_MISCCONTROL 0x840
#define USB_MISCCONTROL_ISOPREVFRM (1 << 3)
#define USB_MISCCONTROL_SKPRTRY (1 << 2)
#define USB_MISCCONTROL_ARBMODE (1 << 1)
#define USB_MISCCONTROL_FILTCC (1 << 0)
#define USB_ETDDMACHANLCLR 0x848
#define USB_EPDMACHANLCLR 0x84c
#define USB_ETDSMSA(x) (0x900 + ((x) * 4))
#define USB_EPSMSA(x) (0x980 + ((x) * 4))
#define USB_ETDDMABUFPTR(x) (0xa00 + ((x) * 4))
#define USB_EPDMABUFPTR(x) (0xa80 + ((x) * 4))
#define USB_ETD_DWORD(x, w) (0x200 + ((x) * 16) + ((w) * 4))
#define DW0_ADDRESS 0
#define DW0_ENDPNT 7
#define DW0_DIRECT 11
#define DW0_SPEED 13
#define DW0_FORMAT 14
#define DW0_MAXPKTSIZ 16
#define DW0_HALTED 27
#define DW0_TOGCRY 28
#define DW0_SNDNAK 30
#define DW1_XBUFSRTAD 0
#define DW1_YBUFSRTAD 16
#define DW2_RTRYDELAY 0
#define DW2_POLINTERV 0
#define DW2_STARTFRM 0
#define DW2_RELPOLPOS 8
#define DW2_DIRPID 16
#define DW2_BUFROUND 18
#define DW2_DELAYINT 19
#define DW2_DATATOG 22
#define DW2_ERRORCNT 24
#define DW2_COMPCODE 28
#define DW3_TOTBYECNT 0
#define DW3_PKTLEN0 0
#define DW3_COMPCODE0 12
#define DW3_PKTLEN1 16
#define DW3_BUFSIZE 21
#define DW3_COMPCODE1 28
#define USBCTRL 0x600
#define USBCTRL_I2C_WU_INT_STAT (1 << 27)
#define USBCTRL_OTG_WU_INT_STAT (1 << 26)
#define USBCTRL_HOST_WU_INT_STAT (1 << 25)
#define USBCTRL_FNT_WU_INT_STAT (1 << 24)
#define USBCTRL_I2C_WU_INT_EN (1 << 19)
#define USBCTRL_OTG_WU_INT_EN (1 << 18)
#define USBCTRL_HOST_WU_INT_EN (1 << 17)
#define USBCTRL_FNT_WU_INT_EN (1 << 16)
#define USBCTRL_OTC_RCV_RXDP (1 << 13)
#define USBCTRL_HOST1_BYP_TLL (1 << 12)
#define USBCTRL_OTG_BYP_VAL(x) ((x) << 10)
#define USBCTRL_HOST1_BYP_VAL(x) ((x) << 8)
#define USBCTRL_OTG_PWR_MASK (1 << 6)
#define USBCTRL_HOST1_PWR_MASK (1 << 5)
#define USBCTRL_HOST2_PWR_MASK (1 << 4)
#define USBCTRL_USB_BYP (1 << 2)
#define USBCTRL_HOST1_TXEN_OE (1 << 1)
/* Values in TD blocks */
#define TD_DIR_SETUP 0
#define TD_DIR_OUT 1
#define TD_DIR_IN 2
#define TD_FORMAT_CONTROL 0
#define TD_FORMAT_ISO 1
#define TD_FORMAT_BULK 2
#define TD_FORMAT_INT 3
#define TD_TOGGLE_CARRY 0
#define TD_TOGGLE_DATA0 2
#define TD_TOGGLE_DATA1 3
/* control transfer states */
#define US_CTRL_SETUP 2
#define US_CTRL_DATA 1
#define US_CTRL_ACK 0
/* bulk transfer main state and 0-length packet */
#define US_BULK 1
#define US_BULK0 0
/*ETD format description*/
#define IMX_FMT_CTRL 0x0
#define IMX_FMT_ISO 0x1
#define IMX_FMT_BULK 0x2
#define IMX_FMT_INT 0x3
static char fmt_urb_to_etd[4] = {
/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO,
/*PIPE_INTERRUPT*/ IMX_FMT_INT,
/*PIPE_CONTROL*/ IMX_FMT_CTRL,
/*PIPE_BULK*/ IMX_FMT_BULK
};
/* condition (error) CC codes and mapping (OHCI like) */
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
/*#define TD_UNEXPECTEDPID 0x07 - reserved, not active on MX2*/
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
#define TD_SCHEDULEOVERRUN 0x0E
#define TD_NOTACCESSED 0x0F
static const int cc_to_error[16] = {
/* No Error */ 0,
/* CRC Error */ -EILSEQ,
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
/* DevNotResp */ -ETIMEDOUT,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
/* DataUnder */ -EREMOTEIO,
/* (for hw) */ -EIO,
/* (for hw) */ -EIO,
/* BufferOver */ -ECOMM,
/* BuffUnder */ -ENOSR,
/* (for HCD) */ -ENOSPC,
/* (for HCD) */ -EALREADY
};
/* HCD data associated with a usb core URB */
struct urb_priv {
struct urb *urb;
struct usb_host_endpoint *ep;
int active;
int state;
struct td *isoc_td;
int isoc_remaining;
int isoc_status;
};
/* HCD data associated with a usb core endpoint */
struct ep_priv {
struct usb_host_endpoint *ep;
struct list_head td_list;
struct list_head queue;
int etd[NUM_ISO_ETDS];
int waiting_etd;
};
/* isoc packet */
struct td {
struct list_head list;
struct urb *urb;
struct usb_host_endpoint *ep;
dma_addr_t data;
unsigned long buf_addr;
int len;
int frame;
int isoc_index;
};
/* HCD data associated with a hardware ETD */
struct etd_priv {
struct usb_host_endpoint *ep;
struct urb *urb;
struct td *td;
struct list_head queue;
dma_addr_t dma_handle;
int alloc;
int len;
int dmem_size;
int dmem_offset;
int active_count;
#ifdef DEBUG
int activated_frame;
int disactivated_frame;
int last_int_frame;
int last_req_frame;
u32 submitted_dwords[4];
#endif
};
/* Hardware data memory info */
struct imx21_dmem_area {
struct usb_host_endpoint *ep;
unsigned int offset;
unsigned int size;
struct list_head list;
};
#ifdef DEBUG
struct debug_usage_stats {
unsigned int value;
unsigned int maximum;
};
struct debug_stats {
unsigned long submitted;
unsigned long completed_ok;
unsigned long completed_failed;
unsigned long unlinked;
unsigned long queue_etd;
unsigned long queue_dmem;
};
struct debug_isoc_trace {
int schedule_frame;
int submit_frame;
int request_len;
int done_frame;
int done_len;
int cc;
struct td *td;
};
#endif
/* HCD data structure */
struct imx21 {
spinlock_t lock;
struct device *dev;
struct mx21_usbh_platform_data *pdata;
struct list_head dmem_list;
struct list_head queue_for_etd; /* eps queued due to etd shortage */
struct list_head queue_for_dmem; /* etds queued due to dmem shortage */
struct etd_priv etd[USB_NUM_ETD];
struct clk *clk;
void __iomem *regs;
#ifdef DEBUG
struct dentry *debug_root;
struct debug_stats nonisoc_stats;
struct debug_stats isoc_stats;
struct debug_usage_stats etd_usage;
struct debug_usage_stats dmem_usage;
struct debug_isoc_trace isoc_trace[20];
struct debug_isoc_trace isoc_trace_failed[20];
unsigned long debug_unblocks;
int isoc_trace_index;
int isoc_trace_index_failed;
#endif
};
#endif