aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/class/cdc-acm.c36
-rw-r--r--drivers/usb/class/cdc-wdm.c4
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/devio.c43
-rw-r--r--drivers/usb/core/driver.c7
-rw-r--r--drivers/usb/core/hcd-pci.c9
-rw-r--r--drivers/usb/core/hcd.c10
-rw-r--r--drivers/usb/core/hub.c16
-rw-r--r--drivers/usb/core/message.c3
-rw-r--r--drivers/usb/core/quirks.c7
-rw-r--r--drivers/usb/dwc/Kconfig84
-rw-r--r--drivers/usb/dwc/Makefile17
-rw-r--r--drivers/usb/dwc/apmppc.c372
-rw-r--r--drivers/usb/dwc/cil.c893
-rw-r--r--drivers/usb/dwc/cil.h1179
-rw-r--r--drivers/usb/dwc/cil_intr.c616
-rw-r--r--drivers/usb/dwc/driver.h76
-rw-r--r--drivers/usb/dwc/hcd.c2473
-rw-r--r--drivers/usb/dwc/hcd.h416
-rw-r--r--drivers/usb/dwc/hcd_intr.c1477
-rw-r--r--drivers/usb/dwc/hcd_queue.c696
-rw-r--r--drivers/usb/dwc/param.c180
-rw-r--r--drivers/usb/dwc/pcd.c1791
-rw-r--r--drivers/usb/dwc/pcd.h139
-rw-r--r--drivers/usb/dwc/pcd_intr.c2312
-rw-r--r--drivers/usb/dwc/regs.h1326
-rw-r--r--drivers/usb/early/ehci-dbgp.c2
-rw-r--r--drivers/usb/gadget/Kconfig58
-rwxr-xr-x[-rw-r--r--]drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/epautoconf.c54
-rw-r--r--drivers/usb/gadget/f_mass_storage.c2
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c2
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/gadget/s3c_udc.h164
-rw-r--r--drivers/usb/gadget/s3c_udc_otg.c1547
-rw-r--r--drivers/usb/gadget/s3c_udc_otg_xfer_dma.c1387
-rw-r--r--drivers/usb/gadget/u_ether.c37
-rw-r--r--drivers/usb/host/Kconfig16
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-pci.c12
-rw-r--r--drivers/usb/host/ehci-q.c12
-rw-r--r--drivers/usb/host/pci-quirks.c89
-rw-r--r--drivers/usb/host/pci-quirks.h2
-rw-r--r--drivers/usb/host/s3c-otg/Makefile18
-rw-r--r--drivers/usb/host/s3c-otg/debug-mem.c55
-rw-r--r--drivers/usb/host/s3c-otg/debug-mem.h9
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-common-common.h49
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-common-const.h167
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h811
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h115
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h302
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h88
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c386
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h75
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c708
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h152
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h420
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h217
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h191
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-isr.c294
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-isr.h72
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-oci.c815
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-oci.h86
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-roothub.c605
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-roothub.h65
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c369
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c264
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c424
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h100
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c810
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c140
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c128
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h144
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c720
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h88
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h66
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c238
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h79
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c698
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h99
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c574
-rw-r--r--drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h97
-rw-r--r--drivers/usb/host/xhci-mem.c8
-rw-r--r--drivers/usb/host/xhci-pci.c12
-rw-r--r--drivers/usb/host/xhci-ring.c357
-rw-r--r--drivers/usb/host/xhci.c51
-rw-r--r--drivers/usb/host/xhci.h25
-rw-r--r--drivers/usb/misc/emi62.c2
-rw-r--r--drivers/usb/misc/usbtest.c17
-rwxr-xr-xdrivers/usb/notify/Kconfig11
-rwxr-xr-xdrivers/usb/notify/Makefile4
-rwxr-xr-xdrivers/usb/notify/host_notify_class.c262
-rw-r--r--drivers/usb/serial/cp210x.c12
-rw-r--r--drivers/usb/serial/ftdi_sio.c27
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h48
-rw-r--r--drivers/usb/serial/mct_u232.c13
-rw-r--r--drivers/usb/serial/mos7840.c41
-rw-r--r--drivers/usb/serial/opticon.c11
-rw-r--r--drivers/usb/serial/option.c496
-rw-r--r--drivers/usb/serial/qcaux.c10
-rw-r--r--drivers/usb/serial/qcserial.c6
-rw-r--r--drivers/usb/serial/sierra.c30
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c6
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.h1
-rw-r--r--drivers/usb/serial/usb-serial.c6
-rw-r--r--drivers/usb/serial/whiteheat.c1
-rw-r--r--drivers/usb/storage/unusual_devs.h13
109 files changed, 29391 insertions, 403 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 48f1781..e9660f1 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -120,6 +120,8 @@ source "drivers/usb/musb/Kconfig"
source "drivers/usb/renesas_usbhs/Kconfig"
+source "drivers/usb/dwc/Kconfig"
+
source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig"
@@ -168,4 +170,6 @@ source "drivers/usb/gadget/Kconfig"
source "drivers/usb/otg/Kconfig"
+source "drivers/usb/notify/Kconfig"
+
endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 30ddf8d..5adcecf 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_PCI) += host/
+obj-$(CONFIG_USB_S3C_OTG_HOST) += host/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
@@ -50,4 +51,7 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
obj-$(CONFIG_USB_MUSB_HDRC) += musb/
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_OTG_UTILS) += otg/
+obj-$(CONFIG_USB_DWC_OTG) += dwc/
obj-$(CONFIG_USB_GADGET) += gadget/
+
+obj-$(CONFIG_USB_HOST_NOTIFY) += notify/
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 158f631..b107339 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -498,6 +498,14 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
usb_autopm_put_interface(acm->control);
+ /*
+ * Unthrottle device in case the TTY was closed while throttled.
+ */
+ spin_lock_irq(&acm->read_lock);
+ acm->throttled = 0;
+ acm->throttle_req = 0;
+ spin_unlock_irq(&acm->read_lock);
+
if (acm_submit_read_urbs(acm, GFP_KERNEL))
goto bail_out;
@@ -752,10 +760,6 @@ static const __u32 acm_tty_speed[] = {
2500000, 3000000, 3500000, 4000000
};
-static const __u8 acm_tty_size[] = {
- 5, 6, 7, 8
-};
-
static void acm_tty_set_termios(struct tty_struct *tty,
struct ktermios *termios_old)
{
@@ -772,7 +776,21 @@ static void acm_tty_set_termios(struct tty_struct *tty,
newline.bParityType = termios->c_cflag & PARENB ?
(termios->c_cflag & PARODD ? 1 : 2) +
(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
- newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ newline.bDataBits = 5;
+ break;
+ case CS6:
+ newline.bDataBits = 6;
+ break;
+ case CS7:
+ newline.bDataBits = 7;
+ break;
+ case CS8:
+ default:
+ newline.bDataBits = 8;
+ break;
+ }
/* FIXME: Needs to clear unsupported bits in the termios */
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
@@ -1035,7 +1053,8 @@ skip_normal_probe:
}
- if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
+ if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+ control_interface->cur_altsetting->desc.bNumEndpoints == 0)
return -EINVAL;
epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
@@ -1163,7 +1182,7 @@ made_compressed_probe:
if (usb_endpoint_xfer_int(epwrite))
usb_fill_int_urb(snd->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
else
usb_fill_bulk_urb(snd->urb, usb_dev,
@@ -1487,6 +1506,9 @@ static const struct usb_device_id acm_ids[] = {
Maybe we should define a new
quirk for this. */
},
+ { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
+ .driver_info = NO_UNION_NORMAL,
+ },
{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
},
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 00b7bf9..8a72e05 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -457,6 +457,8 @@ retry:
goto retry;
}
if (!desc->reslength) { /* zero length read */
+ dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
+ clear_bit(WDM_READ, &desc->flags);
spin_unlock_irq(&desc->iuspin);
goto retry;
}
@@ -511,7 +513,7 @@ static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
spin_lock_irqsave(&desc->iuspin, flags);
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
- mask = POLLERR;
+ mask = POLLHUP | POLLERR;
spin_unlock_irqrestore(&desc->iuspin, flags);
goto desc_out;
}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 0149c09..ca98341 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
- if (!bus->root_hub)
+ if (!bus_to_hcd(bus)->rh_registered)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 0ca54e2..4d1f996 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -292,17 +292,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
static struct async *async_getpending(struct dev_state *ps,
void __user *userurb)
{
- unsigned long flags;
struct async *as;
- spin_lock_irqsave(&ps->lock, flags);
list_for_each_entry(as, &ps->async_pending, asynclist)
if (as->userurb == userurb) {
list_del_init(&as->asynclist);
- spin_unlock_irqrestore(&ps->lock, flags);
return as;
}
- spin_unlock_irqrestore(&ps->lock, flags);
+
return NULL;
}
@@ -357,6 +354,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
__releases(ps->lock)
__acquires(ps->lock)
{
+ struct urb *urb;
struct async *as;
/* Mark all the pending URBs that match bulk_addr, up to but not
@@ -379,8 +377,11 @@ __acquires(ps->lock)
list_for_each_entry(as, &ps->async_pending, asynclist) {
if (as->bulk_status == AS_UNLINK) {
as->bulk_status = 0; /* Only once */
+ urb = as->urb;
+ usb_get_urb(urb);
spin_unlock(&ps->lock); /* Allow completions */
- usb_unlink_urb(as->urb);
+ usb_unlink_urb(urb);
+ usb_put_urb(urb);
spin_lock(&ps->lock);
goto rescan;
}
@@ -433,6 +434,7 @@ static void async_completed(struct urb *urb)
static void destroy_async(struct dev_state *ps, struct list_head *list)
{
+ struct urb *urb;
struct async *as;
unsigned long flags;
@@ -440,10 +442,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist);
list_del_init(&as->asynclist);
+ urb = as->urb;
+ usb_get_urb(urb);
/* drop the spinlock so the completion handler can run */
spin_unlock_irqrestore(&ps->lock, flags);
- usb_kill_urb(as->urb);
+ usb_kill_urb(urb);
+ usb_put_urb(urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
@@ -1352,12 +1357,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
{
+ struct urb *urb;
struct async *as;
+ unsigned long flags;
+ spin_lock_irqsave(&ps->lock, flags);
as = async_getpending(ps, arg);
- if (!as)
+ if (!as) {
+ spin_unlock_irqrestore(&ps->lock, flags);
return -EINVAL;
- usb_kill_urb(as->urb);
+ }
+
+ urb = as->urb;
+ usb_get_urb(urb);
+ spin_unlock_irqrestore(&ps->lock, flags);
+
+ usb_kill_urb(urb);
+ usb_put_urb(urb);
+
return 0;
}
@@ -1540,10 +1557,14 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb;
unsigned int i;
- if (as->userbuffer && urb->actual_length)
- if (copy_to_user(as->userbuffer, urb->transfer_buffer,
- urb->actual_length))
+ if (as->userbuffer && urb->actual_length) {
+ if (urb->number_of_packets > 0) /* Isochronous */
+ i = urb->transfer_buffer_length;
+ else /* Non-Isoc */
+ i = urb->actual_length;
+ if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
return -EFAULT;
+ }
if (put_user(as->status, &userurb->status))
return -EFAULT;
if (put_user(urb->actual_length, &userurb->actual_length))
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 75b4bc0..962f2b5 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1617,6 +1617,13 @@ static int autosuspend_check(struct usb_device *udev)
/* Fail if autosuspend is disabled, or any interfaces are in use, or
* any interface drivers require remote wakeup but it isn't available.
*/
+
+#if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG
+ /* temporarily disabled autosuspend for otg host */
+ if (udev->bus->busnum == 2)
+ return -EOPNOTSUPP;
+#endif
+
w = 0;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index aa7bbbc..6c1642b 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -495,15 +495,6 @@ static int hcd_pci_suspend_noirq(struct device *dev)
pci_save_state(pci_dev);
- /*
- * Some systems crash if an EHCI controller is in D3 during
- * a sleep transition. We have to leave such controllers in D0.
- */
- if (hcd->broken_pci_sleep) {
- dev_dbg(dev, "Staying in PCI D0\n");
- return retval;
- }
-
/* If the root hub is dead rather than suspended, disallow remote
* wakeup. usb_hc_died() should ensure that both hosts are marked as
* dying, so we only need to check the primary roothub.
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 45e0908..9c24bf5 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -156,7 +156,11 @@ static const u8 usb2_rh_dev_descriptor [18] = {
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
+#if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG
+ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ] */
+#else
0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
+#endif
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
@@ -977,10 +981,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
- }
- mutex_unlock(&usb_bus_list_lock);
-
- if (retval == 0) {
+ } else {
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock);
@@ -989,6 +990,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
}
+ mutex_unlock(&usb_bus_list_lock);
return retval;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 34bb059..b4688fa 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -24,6 +24,7 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/random.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -481,13 +482,16 @@ static void hub_tt_work(struct work_struct *work)
int limit = 100;
spin_lock_irqsave (&hub->tt.lock, flags);
- while (--limit && !list_empty (&hub->tt.clear_list)) {
+ while (!list_empty(&hub->tt.clear_list)) {
struct list_head *next;
struct usb_tt_clear *clear;
struct usb_device *hdev = hub->hdev;
const struct hc_driver *drv;
int status;
+ if (!hub->quiescing && --limit < 0)
+ break;
+
next = hub->tt.clear_list.next;
clear = list_entry (next, struct usb_tt_clear, clear_list);
list_del (&clear->clear_list);
@@ -951,7 +955,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
- cancel_work_sync(&hub->tt.clear_work);
+ flush_work_sync(&hub->tt.clear_work);
}
/* caller has locked the hub device */
@@ -1902,6 +1906,14 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */
announce_device(udev);
+ if (udev->serial)
+ add_device_randomness(udev->serial, strlen(udev->serial));
+ if (udev->product)
+ add_device_randomness(udev->product, strlen(udev->product));
+ if (udev->manufacturer)
+ add_device_randomness(udev->manufacturer,
+ strlen(udev->manufacturer));
+
device_enable_async_suspend(&udev->dev);
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 1eebd45..806060c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1803,7 +1803,6 @@ free_interfaces:
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
- intf->intf_assoc = find_iad(dev, cp, i);
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
@@ -1816,6 +1815,8 @@ free_interfaces:
if (!alt)
alt = &intf->altsetting[0];
+ intf->intf_assoc =
+ find_iad(dev, cp, alt->desc.bInterfaceNumber);
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 4c65eb6..8b2a9d8 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -96,6 +96,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x04b4, 0x0526), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Microchip Joss Optical infrared touchboard device */
+ { USB_DEVICE(0x04d8, 0x000c), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* Samsung Android phone modem - ID conflict with SPH-I500 */
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -123,6 +127,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Guillemot Webcam Hercules Dualpix Exchange*/
{ USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Midiman M-Audio Keystation 88es */
+ { USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/dwc/Kconfig b/drivers/usb/dwc/Kconfig
new file mode 100644
index 0000000..4439810
--- /dev/null
+++ b/drivers/usb/dwc/Kconfig
@@ -0,0 +1,84 @@
+#
+# USB Dual Role (OTG-ready) Controller Drivers
+# for silicon based on Synopsys DesignWare IP
+#
+
+comment "Enable Host or Gadget support for DesignWare OTG controller"
+ depends on !USB && USB_GADGET=n
+
+config USB_DWC_OTG
+ tristate "Synopsys DWC OTG Controller"
+ depends on USB || USB_GADGET
+ select NOP_USB_XCEIV
+ select USB_OTG_UTILS
+ default USB_GADGET
+ help
+ This driver provides USB Device Controller support for the
+ Synopsys DesignWare USB OTG Core used on the AppliedMicro PowerPC SoC.
+
+config DWC_DEBUG
+ bool "Enable DWC Debugging"
+ depends on USB_DWC_OTG
+ default n
+ help
+ Enable DWC driver debugging
+
+choice
+ prompt "DWC Mode Selection"
+ depends on USB_DWC_OTG
+ default DWC_HOST_ONLY
+ help
+ Select the DWC Core in OTG, Host only, or Device only mode.
+
+config DWC_HOST_ONLY
+ bool "DWC Host Only Mode"
+
+config DWC_OTG_MODE
+ bool "DWC OTG Mode"
+ select USB_GADGET_SELECTED
+
+config DWC_DEVICE_ONLY
+ bool "DWC Device Only Mode"
+ select USB_GADGET_SELECTED
+
+endchoice
+
+# enable peripheral support (including with OTG)
+choice
+ prompt "DWC DMA/SlaveMode Selection"
+ depends on USB_DWC_OTG
+ default DWC_DMA_MODE
+ help
+ Select the DWC DMA or Slave Mode.
+ DMA mode uses the DWC core internal DMA engines.
+ Slave mode uses the processor PIO to tranfer data.
+ In Slave mode, processor's DMA channels can be used if available.
+
+config DWC_SLAVE
+ bool "DWC Slave Mode"
+
+config DWC_DMA_MODE
+ bool "DWC DMA Mode"
+
+endchoice
+
+config DWC_OTG_REG_LE
+ bool "DWC Little Endian Register"
+ depends on USB_DWC_OTG
+ default y
+ help
+ OTG core register access is Little-Endian.
+
+config DWC_OTG_FIFO_LE
+ bool "DWC FIFO Little Endian"
+ depends on USB_DWC_OTG
+ default y
+ help
+ OTG core FIFO access is Little-Endian.
+
+config DWC_LIMITED_XFER_SIZE
+ bool "DWC Endpoint Limited Xfer Size"
+ depends on USB_GADGET_DWC_HDRC
+ default n
+ help
+ Bit fields in the Device EP Transfer Size Register is 11 bits.
diff --git a/drivers/usb/dwc/Makefile b/drivers/usb/dwc/Makefile
new file mode 100644
index 0000000..c79d6e6
--- /dev/null
+++ b/drivers/usb/dwc/Makefile
@@ -0,0 +1,17 @@
+#
+# OTG infrastructure and transceiver drivers
+#
+obj-$(CONFIG_USB_DWC_OTG) += dwc.o
+
+dwc-objs := cil.o cil_intr.o param.o
+
+dwc-objs += apmppc.o
+
+ifneq ($(CONFIG_DWC_DEVICE_ONLY),y)
+dwc-objs += hcd.o hcd_intr.o \
+ hcd_queue.o
+endif
+
+ifneq ($(CONFIG_DWC_HOST_ONLY),y)
+dwc-objs += pcd.o pcd_intr.o
+endif
diff --git a/drivers/usb/dwc/apmppc.c b/drivers/usb/dwc/apmppc.c
new file mode 100644
index 0000000..81881d0
--- /dev/null
+++ b/drivers/usb/dwc/apmppc.c
@@ -0,0 +1,372 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The dwc_otg module provides the initialization and cleanup entry
+ * points for the dwcotg driver. This module will be dynamically installed
+ * after Linux is booted using the insmod command. When the module is
+ * installed, the dwc_otg_driver_init function is called. When the module is
+ * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
+ *
+ * This module also defines a data structure for the dwc_otg driver, which is
+ * used in conjunction with the standard device structure. These
+ * structures allow the OTG driver to comply with the standard Linux driver
+ * model in which devices and drivers are registered with a bus driver. This
+ * has the benefit that Linux can expose attributes of the driver and device
+ * in its special sysfs file system. Users can then read or write files in
+ * this file system to perform diagnostics on the driver components or the
+ * device.
+ */
+
+#include <linux/of_platform.h>
+#include <mach/map.h>
+#include <linux/platform_device.h>
+#include "driver.h"
+#include <linux/delay.h>
+
+#define DWC_DRIVER_VERSION "1.05"
+#define DWC_DRIVER_DESC "HS OTG USB Controller driver"
+static const char dwc_driver_name[] = "dwc_otg";
+
+static irqreturn_t dwc_otg_common_irq(int _irq, void *dev)
+{
+ struct dwc_otg_device *dwc_dev = dev;
+ int retval;
+ struct dwc_hcd *dwc_hcd;
+
+ dwc_hcd = dwc_dev->hcd;
+ spin_lock(&dwc_hcd->lock);
+ retval = dwc_otg_handle_common_intr(dwc_dev->core_if);
+ spin_unlock(&dwc_hcd->lock);
+ return IRQ_RETVAL(retval);
+}
+
+static irqreturn_t dwc_otg_externalchgpump_irq(int _irq, void *dev)
+{
+ struct dwc_otg_device *dwc_dev = dev;
+
+ if (dwc_otg_is_host_mode(dwc_dev->core_if)) {
+ struct dwc_hcd *dwc_hcd;
+ u32 hprt0 = 0;
+
+ dwc_hcd = dwc_dev->hcd;
+ spin_lock(&dwc_hcd->lock);
+ dwc_hcd->flags.b.port_over_current_change = 1;
+
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+ dwc_reg_write(dwc_dev->core_if->host_if->hprt0, 0, hprt0);
+ spin_unlock(&dwc_hcd->lock);
+ } else {
+ /* Device mode - This int is n/a for device mode */
+ dev_dbg(dev, "DeviceMode: OTG OverCurrent Detected\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devexit dwc_otg_driver_remove(struct platform_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct dwc_otg_device *dwc_dev = dev_get_drvdata(dev);
+
+ /* Memory allocation for dwc_otg_device may have failed. */
+ if (!dwc_dev)
+ return 0;
+
+ /* Free the IRQ */
+ free_irq(dwc_dev->irq, dwc_dev);
+ /* Free external charge pump irq */
+ free_irq(dwc_dev->hcd->cp_irq, dwc_dev);
+
+ if (dwc_dev->hcd)
+ dwc_otg_hcd_remove(dev);
+
+ if (dwc_dev->pcd)
+ dwc_otg_pcd_remove(dev);
+
+ if (dwc_dev->core_if)
+ dwc_otg_cil_remove(dwc_dev->core_if);
+
+ /* Return the memory. */
+ if (dwc_dev->base)
+ iounmap(dwc_dev->base);
+
+ if (dwc_dev->phys_addr)
+ release_mem_region(dwc_dev->phys_addr, dwc_dev->base_len);
+
+ otg_put_transceiver(dwc_dev->core_if->xceiv);
+ dwc_dev->core_if->xceiv = NULL;
+
+ kfree(dwc_dev);
+
+ /* Clear the drvdata pointer. */
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static int dwc_otg_driver_probe(struct platform_device *ofdev)
+{
+ int retval,reg_val;
+ struct dwc_otg_device *dwc_dev;
+ struct device *dev = &ofdev->dev;
+ struct resource res;
+ ulong gusbcfg_addr;
+ u32 usbcfg = 0;
+
+ dwc_dev = kzalloc(sizeof(*dwc_dev), GFP_KERNEL);
+ if (!dwc_dev) {
+ dev_err(dev, "kmalloc of dwc_otg_device failed\n");
+ retval = -ENOMEM;
+ goto fail_dwc_dev;
+ }
+
+ /* Retrieve the memory and IRQ resources. */
+ dwc_dev->irq = ofdev->resource[1].start;
+ if (dwc_dev->irq == NO_IRQ) {
+ dev_err(dev, "no device irq\n");
+ retval = -ENODEV;
+ goto fail_of_irq;
+ }
+
+ res = ofdev->resource[0];
+/* if (of_address_to_resource(ofdev->dev.of_node, 0, &res)) {
+ dev_err(dev, "%s: Can't get USB-OTG register address\n",
+ __func__);
+ retval = -ENOMEM;
+ goto fail_of_irq;
+ }*/
+
+ dwc_dev->phys_addr = res.start;
+ dwc_dev->base_len = res.end - res.start + 1;
+ if (!request_mem_region(dwc_dev->phys_addr,
+ dwc_dev->base_len, dwc_driver_name)) {
+ dev_err(dev, "request_mem_region failed\n");
+ retval = -EBUSY;
+ goto fail_of_irq;
+ }
+
+ /* Map the DWC_otg Core memory into virtual address space. */
+ dwc_dev->base = ioremap(dwc_dev->phys_addr, dwc_dev->base_len);
+ if (!dwc_dev->base) {
+ dev_err(dev, "ioremap() failed\n");
+ retval = -ENOMEM;
+ goto fail_ioremap;
+ }
+ dev_dbg(dev, "mapped base=0x%08x\n", (__force u32)dwc_dev->base);
+
+ /**
+ * Attempt to ensure this device is really a Synopsys USB-OTG Controller.
+ * Read and verify the SNPSID register contents. The value should be
+ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX".
+ */
+ reg_val = readl(dwc_dev->base+0x40);
+ if ((reg_val & 0xFFFFF000) != 0x4F542000) {
+ dev_err(dev,"Bad value for SNPSID: 0x%x\n", reg_val);
+ retval = -EINVAL;
+ goto fail_invalid_device;
+ }
+
+ /*
+ * Initialize driver data to point to the global DWC_otg
+ * Device structure.
+ */
+ dev_set_drvdata(dev, dwc_dev);
+
+ dwc_dev->core_if =
+ dwc_otg_cil_init(dwc_dev->base, &dwc_otg_module_params);
+ if (!dwc_dev->core_if) {
+ dev_err(dev, "CIL initialization failed!\n");
+ retval = -ENOMEM;
+ goto fail_cil_init;
+ }
+
+ /*
+ * Validate parameter values after dwc_otg_cil_init.
+ */
+ if (check_parameters(dwc_dev->core_if)) {
+ retval = -EINVAL;
+ goto fail_check_param;
+ }
+
+ usb_nop_xceiv_register();
+ dwc_dev->core_if->xceiv = otg_get_transceiver();
+ if (!dwc_dev->core_if->xceiv) {
+ retval = -ENODEV;
+ goto fail_xceiv;
+ }
+ dwc_set_feature(dwc_dev->core_if);
+
+ /* Initialize the DWC_otg core. */
+ dwc_otg_core_init(dwc_dev->core_if);
+
+ /*
+ * Disable the global interrupt until all the interrupt
+ * handlers are installed.
+ */
+ spin_lock(&dwc_dev->hcd->lock);
+ dwc_otg_disable_global_interrupts(dwc_dev->core_if);
+ spin_unlock(&dwc_dev->hcd->lock);
+
+ /*
+ * Install the interrupt handler for the common interrupts before
+ * enabling common interrupts in core_init below.
+ */
+ retval = request_irq(dwc_dev->irq, dwc_otg_common_irq,
+ IRQF_SHARED, "dwc_otg", dwc_dev);
+ if (retval) {
+ dev_err(dev, "request of irq%d failed retval: %d\n",
+ dwc_dev->irq, retval);
+ retval = -EBUSY;
+ goto fail_req_irq;
+ } else {
+ dwc_dev->common_irq_installed = 1;
+ }
+
+ if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
+ /* Initialize the PCD */
+ retval = dwc_otg_pcd_init(dev);
+ if (retval) {
+ dev_err(dev, "dwc_otg_pcd_init failed\n");
+ dwc_dev->pcd = NULL;
+ goto fail_req_irq;
+ }
+ }
+
+ gusbcfg_addr = (ulong) (dwc_dev->core_if->core_global_regs)
+ + DWC_GUSBCFG;
+ if (!dwc_has_feature(dwc_dev->core_if, DWC_DEVICE_ONLY)) {
+ /* Initialize the HCD and force_host_mode */
+ usbcfg = dwc_reg_read(gusbcfg_addr, 0);
+ usbcfg |= DWC_USBCFG_FRC_HST_MODE;
+ dwc_reg_write(gusbcfg_addr, 0, usbcfg);
+
+ retval = dwc_otg_hcd_init(dev, dwc_dev);
+ if (retval) {
+ dev_err(dev, "dwc_otg_hcd_init failed\n");
+ dwc_dev->hcd = NULL;
+ goto fail_hcd;
+ }
+ /* configure chargepump interrupt */
+ dwc_dev->hcd->cp_irq = 0;//irq_of_parse_and_map(ofdev->dev.of_node, 3);
+ if (dwc_dev->hcd->cp_irq) {
+ retval = request_irq(dwc_dev->hcd->cp_irq,
+ dwc_otg_externalchgpump_irq,
+ IRQF_SHARED,
+ "dwc_otg_ext_chg_pump", dwc_dev);
+ if (retval) {
+ dev_err(dev,
+ "request of irq failed retval: %d\n",
+ retval);
+ retval = -EBUSY;
+ goto fail_hcd;
+ } else {
+ dev_dbg(dev, "%s: ExtChgPump Detection "
+ "IRQ registered\n", dwc_driver_name);
+ }
+ }
+ }
+ /*
+ * Enable the global interrupt after all the interrupt
+ * handlers are installed.
+ */
+ dwc_otg_enable_global_interrupts(dwc_dev->core_if);
+
+ usbcfg = dwc_reg_read(gusbcfg_addr, 0);
+ usbcfg &= ~DWC_USBCFG_FRC_HST_MODE;
+ dwc_reg_write(gusbcfg_addr, 0, usbcfg);
+
+ printk("Done\n");msleep(100);
+
+ return 0;
+fail_hcd:
+ free_irq(dwc_dev->irq, dwc_dev);
+ if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
+ if (dwc_dev->pcd)
+ dwc_otg_pcd_remove(dev);
+ }
+fail_req_irq:
+ otg_put_transceiver(dwc_dev->core_if->xceiv);
+fail_xceiv:
+ usb_nop_xceiv_unregister();
+fail_check_param:
+ dwc_otg_cil_remove(dwc_dev->core_if);
+fail_cil_init:
+ dev_set_drvdata(dev, NULL);
+fail_invalid_device:
+ iounmap(dwc_dev->base);
+fail_ioremap:
+ release_mem_region(dwc_dev->phys_addr, dwc_dev->base_len);
+fail_of_irq:
+ kfree(dwc_dev);
+fail_dwc_dev:
+ return retval;
+}
+
+/* static const struct of_device_id dwc_otg_match[] = {
+ {.compatible = "amcc,dwc-otg",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, dwc_otg_match); */
+
+struct platform_driver dwc_otg_driver = {
+ .probe = dwc_otg_driver_probe,
+ .remove = __devexit_p(dwc_otg_driver_remove),
+ .driver = {
+ .name = "dwc_otg",
+ .owner = THIS_MODULE,
+// .of_match_table = dwc_otg_match,
+ },
+};
+
+/*static int dwc_otg_driver_init(void)
+{
+
+ return platform_driver_register(&dwc_otg_driver);
+}
+
+module_init(dwc_otg_driver_init);
+
+static void __exit dwc_otg_driver_cleanup(void)
+{
+ platform_driver_unregister(&dwc_otg_driver);
+}
+
+module_exit(dwc_otg_driver_cleanup);*/
+
+MODULE_DESCRIPTION(DWC_DRIVER_DESC);
+MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@apm.com");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc/cil.c b/drivers/usb/dwc/cil.c
new file mode 100644
index 0000000..8d7a9e8
--- /dev/null
+++ b/drivers/usb/dwc/cil.c
@@ -0,0 +1,893 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/delay.h>
+
+#include "cil.h"
+
+void dwc_otg_enable_global_interrupts(struct core_if *core_if)
+{
+ u32 ahbcfg = 0;
+
+ ahbcfg |= DWC_AHBCFG_GLBL_INT_MASK;
+ dwc_reg_modify(core_if->core_global_regs, DWC_GAHBCFG, 0,
+ ahbcfg);
+}
+
+void dwc_otg_disable_global_interrupts(struct core_if *core_if)
+{
+ u32 ahbcfg = 0;
+
+ ahbcfg |= DWC_AHBCFG_GLBL_INT_MASK;
+ dwc_reg_modify(core_if->core_global_regs, DWC_GAHBCFG,
+ ahbcfg, 0);
+}
+
+/**
+ * Tests if the current hardware is using a full speed phy.
+ */
+static inline int full_speed_phy(struct core_if *core_if)
+{
+ if ((DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+ DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 &&
+ core_if->core_params->ulpi_fs_ls) ||
+ core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)
+ return 1;
+ return 0;
+}
+
+/**
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
+ * type.
+ */
+void init_fslspclksel(struct core_if *core_if)
+{
+ u32 val;
+ u32 hcfg = 0;
+
+ if (full_speed_phy(core_if))
+ val = DWC_HCFG_48_MHZ;
+ else
+ /* High speed PHY running at full speed or high speed */
+ val = DWC_HCFG_30_60_MHZ;
+
+ hcfg = dwc_reg_read(core_if->host_if->host_global_regs, DWC_HCFG);
+ hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg, val);
+ dwc_reg_write(core_if->host_if->host_global_regs, DWC_HCFG, hcfg);
+}
+
+/**
+ * Initializes the DevSpd field of the DCFG register depending on the PHY type
+ * and the enumeration speed of the device.
+ */
+static void init_devspd(struct core_if *core_if)
+{
+ u32 val;
+ u32 dcfg;
+
+ if (full_speed_phy(core_if))
+ val = 0x3;
+ else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL)
+ /* High speed PHY running at full speed */
+ val = 0x1;
+ else
+ /* High speed PHY running at high speed */
+ val = 0x0;
+
+ dcfg = dwc_reg_read(core_if->dev_if->dev_global_regs, DWC_DCFG);
+ dcfg = DWC_DCFG_DEV_SPEED_WR(dcfg, val);
+ dwc_reg_write(core_if->dev_if->dev_global_regs, DWC_DCFG, dcfg);
+}
+
+/**
+ * This function calculates the number of IN EPS using GHWCFG1 and GHWCFG2
+ * registers values
+ */
+static u32 calc_num_in_eps(struct core_if *core_if)
+{
+ u32 num_in_eps = 0;
+ u32 num_eps = DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+ u32 hwcfg1 = core_if->hwcfg1 >> 2;
+ u32 num_tx_fifos = DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4);
+ u32 i;
+
+ for (i = 0; i < num_eps; ++i) {
+ if (!(hwcfg1 & 0x1))
+ num_in_eps++;
+ hwcfg1 >>= 2;
+ }
+
+ if (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4))
+ num_in_eps = num_in_eps > num_tx_fifos ?
+ num_tx_fifos : num_in_eps;
+
+ return num_in_eps;
+}
+
+/**
+ * This function calculates the number of OUT EPS using GHWCFG1 and GHWCFG2
+ * registers values
+ */
+static u32 calc_num_out_eps(struct core_if *core_if)
+{
+ u32 num_out_eps = 0;
+ u32 num_eps = DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+ u32 hwcfg1 = core_if->hwcfg1 >> 2;
+ u32 i;
+
+ for (i = 0; i < num_eps; ++i) {
+ if (!(hwcfg1 & 0x2))
+ num_out_eps++;
+ hwcfg1 >>= 2;
+ }
+ return num_out_eps;
+}
+
+/**
+ * Do core a soft reset of the core. Be careful with this because it
+ * resets all the internal state machines of the core.
+ */
+extern void otg_host_phy_init(void);
+static void dwc_otg_core_reset(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 greset = 0;
+ int count = 0;
+
+ otg_host_phy_init();
+
+ /* Wait for AHB master IDLE state. */
+ do {
+ udelay(10);
+ greset = dwc_reg_read(global_regs, DWC_GRSTCTL);
+ if (++count > 100000) {
+ pr_warning("%s() HANG! AHB Idle GRSTCTL=%0x\n",
+ __func__, greset);
+ return;
+ }
+ } while (!(greset & DWC_RSTCTL_AHB_IDLE));
+
+ /* Core Soft Reset */
+ count = 0;
+ greset |= DWC_RSTCTL_SFT_RST;
+ dwc_reg_write(global_regs, DWC_GRSTCTL, greset);
+
+ do {
+ greset = dwc_reg_read(global_regs, DWC_GRSTCTL);
+ if (++count > 10000) {
+ pr_warning("%s() HANG! Soft Reset "
+ "GRSTCTL=%0x\n", __func__, greset);
+ break;
+ }
+ udelay(1);
+ } while (greset & DWC_RSTCTL_SFT_RST);
+
+ /* Wait for 3 PHY Clocks */
+ msleep(100);
+}
+
+/**
+ * This function initializes the commmon interrupts, used in both
+ * device and host modes.
+ */
+void dwc_otg_enable_common_interrupts(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 intr_mask = 0;
+
+ /* Clear any pending OTG Interrupts */
+ dwc_reg_write(global_regs, DWC_GOTGINT, 0xFFFFFFFF);
+
+ /* Clear any pending interrupts */
+ dwc_reg_write(global_regs, DWC_GINTSTS, 0xFFFFFFFF);
+
+ /* Enable the interrupts in the GINTMSK. */
+ intr_mask |= DWC_INTMSK_MODE_MISMTC;
+ intr_mask |= DWC_INTMSK_OTG;
+ intr_mask |= DWC_INTMSK_CON_ID_STS_CHG;
+ intr_mask |= DWC_INTMSK_WKP;
+ intr_mask |= DWC_INTMSK_SES_DISCON_DET;
+ intr_mask |= DWC_INTMSK_USB_SUSP;
+ intr_mask |= DWC_INTMSK_NEW_SES_DET;
+ if (!core_if->dma_enable)
+ intr_mask |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+ dwc_reg_write(global_regs, DWC_GINTMSK, intr_mask);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers and prepares the
+ * core for device mode or host mode operation.
+ */
+void dwc_otg_core_init(struct core_if *core_if)
+{
+ u32 i;
+ ulong global_reg = core_if->core_global_regs;
+ struct device_if *dev_if = core_if->dev_if;
+ u32 ahbcfg = 0;
+ u32 i2cctl = 0;
+ u32 gusbcfg;
+
+ /* Common Initialization */
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+
+ /* Program the ULPI External VBUS bit if needed */
+ gusbcfg |= DWC_USBCFG_ULPI_EXT_VBUS_DRV;
+
+ /* Set external TS Dline pulsing */
+ if (core_if->core_params->ts_dline == 1)
+ gusbcfg |= DWC_USBCFG_TERM_SEL_DL_PULSE;
+ else
+ gusbcfg = gusbcfg & (~((u32) DWC_USBCFG_TERM_SEL_DL_PULSE));
+
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+
+ /* Reset the Controller */
+ dwc_otg_core_reset(core_if);
+
+ /* Initialize parameters from Hardware configuration registers. */
+ dev_if->num_in_eps = calc_num_in_eps(core_if);
+ dev_if->num_out_eps = calc_num_out_eps(core_if);
+
+ for (i = 0; i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+ i++) {
+ dev_if->perio_tx_fifo_size[i] =
+ dwc_reg_read(global_reg, DWC_DPTX_FSIZ_DIPTXF(i)) >> 16;
+ }
+ for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); i++) {
+ dev_if->tx_fifo_size[i] =
+ dwc_reg_read(global_reg, DWC_DPTX_FSIZ_DIPTXF(i)) >> 16;
+ }
+
+ core_if->total_fifo_size = DWC_HWCFG3_DFIFO_DEPTH_RD(core_if->hwcfg3);
+ core_if->rx_fifo_size = dwc_reg_read(global_reg, DWC_GRXFSIZ);
+ core_if->nperio_tx_fifo_size =
+ dwc_reg_read(global_reg, DWC_GRXFSIZ) >> 16;
+ /*
+ * This programming sequence needs to happen in FS mode before any
+ * other programming occurs
+ */
+ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL &&
+ core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
+ /*
+ * core_init() is now called on every switch so only call the
+ * following for the first time through.
+ */
+ if (!core_if->phy_init_done) {
+ core_if->phy_init_done = 1;
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+
+ /* Reset after a PHY select */
+ dwc_otg_core_reset(core_if);
+ }
+
+ /*
+ * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS.
+ * Also do this on HNP Dev/Host mode switches (done in dev_init
+ * and host_init).
+ */
+ if (dwc_otg_is_host_mode(core_if))
+ init_fslspclksel(core_if);
+ else
+ init_devspd(core_if);
+
+ if (core_if->core_params->i2c_enable) {
+ /* Program GUSBCFG.OtgUtmifsSel to I2C */
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ gusbcfg |= DWC_USBCFG_OTGUTMIFSSEL;
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+
+ /* Program GI2CCTL.I2CEn */
+ i2cctl = dwc_reg_read(global_reg, DWC_GI2CCTL);
+ i2cctl |= DWC_I2CCTL_I2CDEVADDR(1);
+ i2cctl &= ~DWC_I2CCTL_I2CEN;
+ dwc_reg_write(global_reg, DWC_GI2CCTL, i2cctl);
+ i2cctl |= DWC_I2CCTL_I2CEN;
+ dwc_reg_write(global_reg, DWC_GI2CCTL, i2cctl);
+ }
+ } else if (!core_if->phy_init_done) {
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ core_if->phy_init_done = 1;
+ if (core_if->core_params->phy_type)
+ gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_ULPI_UTMI_SEL);
+
+ if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
+ /* ULPI interface */
+ gusbcfg |= DWC_USBCFG_PHYIF;
+ if (core_if->core_params->phy_ulpi_ddr)
+ gusbcfg |= DWC_USBCFG_DDRSEL;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_DDRSEL);
+ } else {
+ /* UTMI+ interface */
+ if (core_if->core_params->phy_utmi_width == 16)
+ gusbcfg |= DWC_USBCFG_PHYIF;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_PHYIF);
+ }
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+
+ /* Reset after setting the PHY parameters */
+ dwc_otg_core_reset(core_if);
+ }
+
+ if (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+ DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 &&
+ core_if->core_params->ulpi_fs_ls) {
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ gusbcfg |= DWC_USBCFG_ULPI_FSLS;
+ gusbcfg |= DWC_USBCFG_ULPI_CLK_SUS_M;
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+ } else {
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ gusbcfg &= ~((u32) DWC_USBCFG_ULPI_FSLS);
+ gusbcfg &= ~((u32) DWC_USBCFG_ULPI_CLK_SUS_M);
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+ }
+
+ /* Program the GAHBCFG Register. */
+ switch (DWC_HWCFG2_ARCH_RD(core_if->hwcfg2)) {
+ case DWC_SLAVE_ONLY_ARCH:
+ ahbcfg &= ~DWC_AHBCFG_NPFIFO_EMPTY; /* HALF empty */
+ ahbcfg &= ~DWC_AHBCFG_FIFO_EMPTY; /* HALF empty */
+ core_if->dma_enable = 0;
+ break;
+ case DWC_EXT_DMA_ARCH:
+ ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
+ DWC_AHBCFG_BURST_LEN(core_if->core_params->dma_burst_size);
+ core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+ break;
+ case DWC_INT_DMA_ARCH:
+ ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
+ DWC_AHBCFG_BURST_LEN(DWC_GAHBCFG_INT_DMA_BURST_INCR);
+ core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+ break;
+ }
+
+ if (core_if->dma_enable)
+ ahbcfg |= DWC_AHBCFG_DMA_ENA;
+ else
+ ahbcfg &= ~DWC_AHBCFG_DMA_ENA;
+ dwc_reg_write(global_reg, DWC_GAHBCFG, ahbcfg);
+ core_if->en_multiple_tx_fifo =
+ DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4);
+
+ /* Program the GUSBCFG register. */
+ gusbcfg = dwc_reg_read(global_reg, DWC_GUSBCFG);
+ switch (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2)) {
+ case DWC_MODE_HNP_SRP_CAPABLE:
+ if (core_if->core_params->otg_cap ==
+ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
+ gusbcfg |= DWC_USBCFG_HNP_CAP;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ if (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+ gusbcfg |= DWC_USBCFG_SRP_CAP;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_SRP_ONLY_CAPABLE:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ if (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+ gusbcfg |= DWC_USBCFG_SRP_CAP;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_NO_HNP_SRP_CAPABLE:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_SRP_CAPABLE_DEVICE:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ if (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+ gusbcfg |= DWC_USBCFG_SRP_CAP;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_SRP_CAPABLE_HOST:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ if (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+ gusbcfg |= DWC_USBCFG_SRP_CAP;
+ else
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ case DWC_MODE_NO_SRP_CAPABLE_HOST:
+ gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+ gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+ break;
+ }
+ dwc_reg_write(global_reg, DWC_GUSBCFG, gusbcfg);
+
+ /* Enable common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /*
+ * Do device or host intialization based on mode during PCD
+ * and HCD initialization
+ */
+ if (dwc_otg_is_host_mode(core_if)) {
+ core_if->xceiv->state = OTG_STATE_A_HOST;
+ } else {
+ core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ if (dwc_has_feature(core_if, DWC_DEVICE_ONLY))
+ dwc_otg_core_dev_init(core_if);
+ }
+}
+
+/**
+ * This function enables the Device mode interrupts.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void dwc_otg_enable_device_interrupts(struct core_if *core_if)
+{
+ u32 intr_mask = 0;
+ u32 msk = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ /* Disable all interrupts. */
+ dwc_reg_write(global_regs, DWC_GINTMSK, 0);
+
+ /* Clear any pending interrupts */
+ dwc_reg_write(global_regs, DWC_GINTSTS, 0xFFFFFFFF);
+
+ /* Enable the common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /* Enable interrupts */
+ intr_mask |= DWC_INTMSK_USB_RST;
+ intr_mask |= DWC_INTMSK_ENUM_DONE;
+ intr_mask |= DWC_INTMSK_IN_ENDP;
+ intr_mask |= DWC_INTMSK_OUT_ENDP;
+ intr_mask |= DWC_INTMSK_EARLY_SUSP;
+ if (!core_if->en_multiple_tx_fifo)
+ intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
+
+ /* Periodic EP */
+ intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
+ intr_mask |= DWC_INTMSK_END_OF_PFRM;
+ intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
+ intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
+
+ dwc_reg_modify(global_regs, DWC_GINTMSK, intr_mask, intr_mask);
+
+ msk = DWC_DIEPMSK_TXFIFO_UNDERN_RW(msk, 1);
+ dwc_reg_modify(core_if->dev_if->dev_global_regs, DWC_DIEPMSK,
+ msk, msk);
+}
+
+/**
+ * Configures the device data fifo sizes when dynamic sizing is enabled.
+ */
+static void config_dev_dynamic_fifos(struct core_if *core_if)
+{
+ u32 i;
+ ulong regs = core_if->core_global_regs;
+ struct core_params *params = core_if->core_params;
+ u32 txsize = 0;
+ u32 nptxsize = 0;
+ u32 ptxsize = 0;
+
+ /* Rx FIFO */
+ dwc_reg_write(regs, DWC_GRXFSIZ, params->dev_rx_fifo_size);
+
+ /* Set Periodic and Non-periodic Tx FIFO Mask bits to all 0 */
+ core_if->p_tx_msk = 0;
+ core_if->tx_msk = 0;
+
+ if (core_if->en_multiple_tx_fifo == 0) {
+ /* Non-periodic Tx FIFO */
+ nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
+ params->
+ dev_nperio_tx_fifo_size);
+ nptxsize =
+ DWC_RX_FIFO_START_ADDR_WR(nptxsize,
+ params->dev_rx_fifo_size);
+ dwc_reg_write(regs, DWC_GNPTXFSIZ, nptxsize);
+
+ ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
+ (DWC_RX_FIFO_START_ADDR_RD
+ (nptxsize) +
+ DWC_RX_FIFO_DEPTH_RD
+ (nptxsize)));
+ for (i = 0;
+ i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+ i++) {
+ ptxsize =
+ DWC_RX_FIFO_DEPTH_WR(ptxsize,
+ params->
+ dev_perio_tx_fifo_size[i]);
+ dwc_reg_write(regs, DWC_DPTX_FSIZ_DIPTXF(i), ptxsize);
+ ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
+ (DWC_RX_FIFO_START_ADDR_RD
+ (ptxsize) +
+ DWC_RX_FIFO_DEPTH_RD
+ (ptxsize)));
+ }
+ } else {
+ nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
+ params->
+ dev_nperio_tx_fifo_size);
+ nptxsize =
+ DWC_RX_FIFO_START_ADDR_WR(nptxsize,
+ params->dev_rx_fifo_size);
+ dwc_reg_write(regs, DWC_GNPTXFSIZ, nptxsize);
+
+ txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
+ (DWC_RX_FIFO_START_ADDR_RD
+ (nptxsize) +
+ DWC_RX_FIFO_DEPTH_RD
+ (nptxsize)));
+ for (i = 1;
+ i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+ i++) {
+ txsize =
+ DWC_RX_FIFO_DEPTH_WR(txsize,
+ params->dev_tx_fifo_size[i]);
+ dwc_reg_write(regs, DWC_DPTX_FSIZ_DIPTXF(i - 1),
+ txsize);
+ txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
+ (DWC_RX_FIFO_START_ADDR_RD
+ (txsize) +
+ DWC_RX_FIFO_DEPTH_RD
+ (txsize)));
+ }
+ }
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * device mode.
+ */
+void dwc_otg_core_dev_init(struct core_if *c_if)
+{
+ u32 i;
+ struct device_if *d_if = c_if->dev_if;
+ struct core_params *params = c_if->core_params;
+ u32 dcfg = 0;
+ u32 resetctl = 0;
+ u32 dthrctl = 0;
+
+ /* Restart the Phy Clock */
+ dwc_reg_write(c_if->pcgcctl, 0, 0);
+
+ /* Device configuration register */
+ init_devspd(c_if);
+ dcfg = dwc_reg_read(d_if->dev_global_regs, DWC_DCFG);
+ dcfg = DWC_DCFG_P_FRM_INTRVL_WR(dcfg, DWC_DCFG_FRAME_INTERVAL_80);
+ dwc_reg_write(d_if->dev_global_regs, DWC_DCFG, dcfg);
+
+ /* If needed configure data FIFO sizes */
+ if (DWC_HWCFG2_DYN_FIFO_RD(c_if->hwcfg2) && params->enable_dynamic_fifo)
+ config_dev_dynamic_fifos(c_if);
+
+ /* Flush the FIFOs */
+ dwc_otg_flush_tx_fifo(c_if, DWC_GRSTCTL_TXFNUM_ALL);
+ dwc_otg_flush_rx_fifo(c_if);
+
+ /* Flush the Learning Queue. */
+ resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
+ dwc_reg_write(c_if->core_global_regs, DWC_GRSTCTL, resetctl);
+
+ /* Clear all pending Device Interrupts */
+ dwc_reg_write(d_if->dev_global_regs, DWC_DIEPMSK, 0);
+ dwc_reg_write(d_if->dev_global_regs, DWC_DOEPMSK, 0);
+ dwc_reg_write(d_if->dev_global_regs, DWC_DAINT, 0xFFFFFFFF);
+ dwc_reg_write(d_if->dev_global_regs, DWC_DAINTMSK, 0);
+
+ for (i = 0; i <= d_if->num_in_eps; i++) {
+ u32 depctl = 0;
+
+ depctl = dwc_reg_read(d_if->in_ep_regs[i], DWC_DIEPCTL);
+ if (DWC_DEPCTL_EPENA_RD(depctl)) {
+ depctl = 0;
+ depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+ depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
+ } else {
+ depctl = 0;
+ }
+
+ dwc_reg_write(d_if->in_ep_regs[i], DWC_DIEPCTL, depctl);
+ dwc_reg_write(d_if->in_ep_regs[i], DWC_DIEPTSIZ, 0);
+ dwc_reg_write(d_if->in_ep_regs[i], DWC_DIEPDMA, 0);
+ dwc_reg_write(d_if->in_ep_regs[i], DWC_DIEPINT, 0xFF);
+ }
+
+ for (i = 0; i <= d_if->num_out_eps; i++) {
+ u32 depctl = 0;
+ depctl = dwc_reg_read(d_if->out_ep_regs[i], DWC_DOEPCTL);
+ if (DWC_DEPCTL_EPENA_RD(depctl)) {
+ depctl = 0;
+ depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+ depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
+ } else {
+ depctl = 0;
+ }
+ dwc_reg_write(d_if->out_ep_regs[i], DWC_DOEPCTL, depctl);
+ dwc_reg_write(d_if->out_ep_regs[i], DWC_DOEPTSIZ, 0);
+ dwc_reg_write(d_if->out_ep_regs[i], DWC_DOEPDMA, 0);
+ dwc_reg_write(d_if->out_ep_regs[i], DWC_DOEPINT, 0xFF);
+ }
+
+ if (c_if->en_multiple_tx_fifo && c_if->dma_enable) {
+ d_if->non_iso_tx_thr_en = c_if->core_params->thr_ctl & 0x1;
+ d_if->iso_tx_thr_en = (c_if->core_params->thr_ctl >> 1) & 0x1;
+ d_if->rx_thr_en = (c_if->core_params->thr_ctl >> 2) & 0x1;
+ d_if->rx_thr_length = c_if->core_params->rx_thr_length;
+ d_if->tx_thr_length = c_if->core_params->tx_thr_length;
+
+ dthrctl = 0;
+ dthrctl = DWC_DTHCTRL_NON_ISO_THR_ENA_RW
+ (dthrctl, d_if->non_iso_tx_thr_en);
+ dthrctl = DWC_DTHCTRL_ISO_THR_EN_RW
+ (dthrctl, d_if->iso_tx_thr_en);
+ dthrctl = DWC_DTHCTRL_TX_THR_LEN_RW
+ (dthrctl, d_if->tx_thr_length);
+ dthrctl = DWC_DTHCTRL_RX_THR_EN_RW(dthrctl, d_if->rx_thr_en);
+ dthrctl = DWC_DTHCTRL_RX_THR_LEN_RW
+ (dthrctl, d_if->rx_thr_length);
+ dwc_reg_write(d_if->dev_global_regs,
+ DWC_DTKNQR3_DTHRCTL, dthrctl);
+
+ }
+
+ dwc_otg_enable_device_interrupts(c_if);
+}
+
+/**
+ * This function reads a packet from the Rx FIFO into the destination buffer.
+ * To read SETUP data use dwc_otg_read_setup_packet.
+ */
+void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 _bytes)
+{
+ u32 i;
+ int word_count = (_bytes + 3) / 4;
+ u32 fifo = core_if->data_fifo[0];
+ u32 *data_buff = (u32 *) dest;
+
+ /*
+ * This requires reading data from the FIFO into a u32 temp buffer,
+ * then moving it into the data buffer.
+ */
+ for (i = 0; i < word_count; i++, data_buff++)
+ *data_buff = dwc_read_fifo32(fifo);
+}
+
+/**
+ * Flush a Tx FIFO.
+ */
+void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int num)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 greset = 0;
+ int count = 0;
+
+ greset |= DWC_RSTCTL_TX_FIFO_FLUSH;
+ greset = DWC_RSTCTL_TX_FIFO_NUM(greset, num);
+ dwc_reg_write(global_regs, DWC_GRSTCTL, greset);
+
+ do {
+ greset = dwc_reg_read(global_regs, DWC_GRSTCTL);
+ if (++count > 10000) {
+ pr_warning("%s() HANG! GRSTCTL=%0x "
+ "GNPTXSTS=0x%08x\n", __func__, greset,
+ dwc_reg_read(global_regs, DWC_GNPTXSTS));
+ break;
+ }
+ udelay(1);
+ } while (greset & DWC_RSTCTL_TX_FIFO_FLUSH);
+
+ /* Wait for 3 PHY Clocks */
+ udelay(1);
+}
+
+/**
+ * Flush Rx FIFO.
+ */
+void dwc_otg_flush_rx_fifo(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 greset = 0;
+ int count = 0;
+
+ greset |= DWC_RSTCTL_RX_FIFO_FLUSH;
+ dwc_reg_write(global_regs, DWC_GRSTCTL, greset);
+
+ do {
+ greset = dwc_reg_read(global_regs, DWC_GRSTCTL);
+ if (++count > 10000) {
+ pr_warning("%s() HANG! GRSTCTL=%0x\n",
+ __func__, greset);
+ break;
+ }
+ udelay(1);
+ } while (greset & DWC_RSTCTL_RX_FIFO_FLUSH);
+
+ /* Wait for 3 PHY Clocks */
+ udelay(1);
+}
+
+/**
+ * Register HCD callbacks.
+ * The callbacks are used to start and stop the HCD for interrupt processing.
+ */
+void dwc_otg_cil_register_hcd_callbacks(struct core_if *c_if,
+ struct cil_callbacks *cb,
+ void *p)
+{
+ c_if->hcd_cb = cb;
+ cb->p = p;
+}
+
+/**
+ * Register PCD callbacks.
+ * The callbacks are used to start and stop the PCD for interrupt processing.
+ */
+void dwc_otg_cil_register_pcd_callbacks(struct core_if *c_if,
+ struct cil_callbacks *cb,
+ void *p)
+{
+ c_if->pcd_cb = cb;
+ cb->p = p;
+}
+
+/**
+ * This function is called to initialize the DWC_otg CSR data structures.
+ *
+ * The register addresses in the device and host structures are initialized from
+ * the base address supplied by the caller. The calling function must make the
+ * OS calls to get the base address of the DWC_otg controller registers.
+ *
+ * The params argument holds the parameters that specify how the core should be
+ * configured.
+ */
+struct core_if *dwc_otg_cil_init(const __iomem u32 * base,
+ struct core_params *params)
+{
+ struct core_if *core_if;
+ struct device_if *dev_if;
+ struct dwc_host_if *host_if;
+ u8 *reg_base = (__force u8 *)base;
+ u32 offset;
+ u32 i;
+
+ core_if = kzalloc(sizeof(*core_if), GFP_KERNEL);
+ if (!core_if)
+ return NULL;
+
+ core_if->core_params = params;
+ core_if->core_global_regs = (ulong)reg_base;
+
+ /* Allocate the Device Mode structures. */
+ dev_if = kmalloc(sizeof(*dev_if), GFP_KERNEL);
+ if (!dev_if) {
+ kfree(core_if);
+ return NULL;
+ }
+
+ dev_if->dev_global_regs = (ulong)(reg_base + DWC_DEV_GLOBAL_REG_OFFSET);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ offset = i * DWC_EP_REG_OFFSET;
+
+ dev_if->in_ep_regs[i] = (ulong)(reg_base +
+ DWC_DEV_IN_EP_REG_OFFSET +
+ offset);
+
+ dev_if->out_ep_regs[i] = (ulong)(reg_base +
+ DWC_DEV_OUT_EP_REG_OFFSET +
+ offset);
+ }
+
+ dev_if->speed = 0; /* unknown */
+ core_if->dev_if = dev_if;
+
+ /* Allocate the Host Mode structures. */
+ host_if = kmalloc(sizeof(*host_if), GFP_KERNEL);
+ if (!host_if) {
+ kfree(dev_if);
+ kfree(core_if);
+ return NULL;
+ }
+
+ host_if->host_global_regs = (ulong)(reg_base +
+ DWC_OTG_HOST_GLOBAL_REG_OFFSET);
+
+ host_if->hprt0 = (ulong)(reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ offset = i * DWC_OTG_CHAN_REGS_OFFSET;
+
+ host_if->hc_regs[i] = (ulong)(reg_base +
+ DWC_OTG_HOST_CHAN_REGS_OFFSET +
+ offset);
+ }
+
+ host_if->num_host_channels = MAX_EPS_CHANNELS;
+ core_if->host_if = host_if;
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ core_if->data_fifo[i] =
+ (ulong)(reg_base + DWC_OTG_DATA_FIFO_OFFSET +
+ (i * DWC_OTG_DATA_FIFO_SIZE));
+ }
+ core_if->pcgcctl = (ulong)(reg_base + DWC_OTG_PCGCCTL_OFFSET);
+
+ /*
+ * Store the contents of the hardware configuration registers here for
+ * easy access later.
+ */
+ core_if->hwcfg1 =
+ dwc_reg_read(core_if->core_global_regs, DWC_GHWCFG1);
+ core_if->hwcfg2 =
+ dwc_reg_read(core_if->core_global_regs, DWC_GHWCFG2);
+ core_if->hwcfg3 =
+ dwc_reg_read(core_if->core_global_regs, DWC_GHWCFG3);
+ core_if->hwcfg4 =
+ dwc_reg_read(core_if->core_global_regs, DWC_GHWCFG4);
+
+ /* Set the SRP sucess bit for FS-I2c */
+ core_if->srp_success = 0;
+ core_if->srp_timer_started = 0;
+ return core_if;
+}
+
+/**
+ * This function frees the structures allocated by dwc_otg_cil_init().
+ */
+void dwc_otg_cil_remove(struct core_if *core_if)
+{
+ /* Disable all interrupts */
+ dwc_reg_modify(core_if->core_global_regs, DWC_GAHBCFG, 1, 0);
+ dwc_reg_write(core_if->core_global_regs, DWC_GINTMSK, 0);
+
+ if (core_if) {
+ kfree(core_if->dev_if);
+ kfree(core_if->host_if);
+ }
+ kfree(core_if);
+}
diff --git a/drivers/usb/dwc/cil.h b/drivers/usb/dwc/cil.h
new file mode 100644
index 0000000..80c4361
--- /dev/null
+++ b/drivers/usb/dwc/cil.h
@@ -0,0 +1,1179 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_CIL_H__)
+#define __DWC_CIL_H__
+#include <linux/io.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/interrupt.h>
+#include <linux/dmapool.h>
+#include <linux/spinlock.h>
+#include <linux/usb/otg.h>
+
+#include "regs.h"
+
+#ifdef CONFIG_DWC_DEBUG
+#define DEBUG
+#endif
+
+#ifdef CONFIG_ARM
+#define in_le32(x) readl(x)
+#define out_le32(x,y) writel(y,x)
+#endif
+
+/**
+ * Reads the content of a register.
+ */
+static inline u32 dwc_reg_read(ulong reg , u32 offset)
+{
+
+#ifdef CONFIG_DWC_OTG_REG_LE
+ return in_le32((unsigned __iomem *)(reg + offset));
+#else
+ return in_be32((unsigned __iomem *)(reg + offset));
+#endif
+}
+
+static inline void dwc_reg_write(ulong reg, u32 offset, const u32 value)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+ out_le32((unsigned __iomem *)(reg + offset), value);
+#else
+ out_be32((unsigned __iomem *)(reg + offset), value);
+#endif
+};
+/**
+ * This function modifies bit values in a register. Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+static inline void dwc_reg_modify(ulong reg, u32 offset, const u32 _clear_mask,
+ const u32 _set_mask)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+ out_le32((unsigned __iomem *)(reg + offset),
+ (in_le32((unsigned __iomem *)(reg + offset))
+ & ~_clear_mask) | _set_mask);
+#else
+ out_be32((unsigned __iomem *)(reg + offset),
+ (in_be32(((unsigned __iomem *))(reg + offset))
+ & ~_clear_mask) | _set_mask);
+#endif
+};
+
+static inline void dwc_write_fifo32(ulong reg, const u32 _value)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+ out_le32((unsigned __iomem *)reg, _value);
+#else
+ out_be32((unsigned __iomem *)reg, _value);
+#endif
+};
+
+static inline u32 dwc_read_fifo32(ulong _reg)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+ return in_le32((unsigned __iomem *) _reg);
+#else
+ return in_be32((unsigned __iomem *) _reg);
+#endif
+};
+
+/*
+ * Debugging support vanishes in non-debug builds.
+ */
+/* Display CIL Debug messages */
+#define dwc_dbg_cil (0x2)
+
+/* Display CIL Verbose debug messages */
+#define dwc_dbg_cilv (0x20)
+
+/* Display PCD (Device) debug messages */
+#define dwc_dbg_pcd (0x4)
+
+/* Display PCD (Device) Verbose debug messages */
+#define dwc_dbg_pcdv (0x40)
+
+/* Display Host debug messages */
+#define dwc_dbg_hcd (0x8)
+
+/* Display Verbose Host debug messages */
+#define dwc_dbg_hcdv (0x80)
+
+/* Display enqueued URBs in host mode. */
+#define dwc_dbg_hcd_urb (0x800)
+
+/* Display "special purpose" debug messages */
+#define dwc_dbg_sp (0x400)
+
+/* Display all debug messages */
+#define dwc_dbg_any (0xFF)
+
+/* All debug messages off */
+#define dwc_dbg_off 0
+
+/* Prefix string for DWC_DEBUG print macros. */
+#define usb_dwc "dwc_otg: "
+
+/*
+ * This file contains the interface to the Core Interface Layer.
+ */
+
+/*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger transfers
+ * into multiple transfers of 1024 bytes sized messages.
+ * I happens often, that transfers of 4096 bytes are
+ * required (zero-gadget, file_storage-gadget).
+ *
+ * MAX_XFER_LEN is set to 1024 right now, but could be 2047,
+ * since the xfer-size field in the 405EZ USB device controller
+ * implementation has 11 bits. Using 1024 seems to work for now.
+ */
+#define MAX_XFER_LEN 1024
+
+/*
+ * The dwc_ep structure represents the state of a single endpoint when acting in
+ * device mode. It contains the data items needed for an endpoint to be
+ * activated and transfer packets.
+ */
+struct dwc_ep {
+ /* EP number used for register address lookup */
+ u8 num;
+ /* EP direction 0 = OUT */
+ unsigned is_in:1;
+ /* EP active. */
+ unsigned active:1;
+
+ /*
+ * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use
+ * non-periodic Tx FIFO If dedicated Tx FIFOs are enabled for all
+ * IN Eps - Tx FIFO # FOR IN EPs
+ */
+ unsigned tx_fifo_num:4;
+ /* EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */
+ unsigned type:2;
+#define DWC_OTG_EP_TYPE_CONTROL 0
+#define DWC_OTG_EP_TYPE_ISOC 1
+#define DWC_OTG_EP_TYPE_BULK 2
+#define DWC_OTG_EP_TYPE_INTR 3
+
+ /* DATA start PID for INTR and BULK EP */
+ unsigned data_pid_start:1;
+ /* Frame (even/odd) for ISOC EP */
+ unsigned even_odd_frame:1;
+ /* Max Packet bytes */
+ unsigned maxpacket:11;
+
+ ulong dma_addr;
+
+ /*
+ * Pointer to the beginning of the transfer buffer -- do not modify
+ * during transfer.
+ */
+ u8 *start_xfer_buff;
+ /* pointer to the transfer buffer */
+ u8 *xfer_buff;
+ /* Number of bytes to transfer */
+ unsigned xfer_len:19;
+ /* Number of bytes transferred. */
+ unsigned xfer_count:19;
+ /* Sent ZLP */
+ unsigned sent_zlp:1;
+ /* Total len for control transfer */
+ unsigned total_len:19;
+
+ /* stall clear flag */
+ unsigned stall_clear_flag:1;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger transfers
+ * into multiple transfers of 1024 bytes sized messages.
+ * I happens often, that transfers of 4096 bytes are
+ * required (zero-gadget, file_storage-gadget).
+ *
+ * "bytes_pending" will hold the amount of bytes that are
+ * still pending to be send in further messages to complete
+ * the bigger transfer.
+ */
+ u32 bytes_pending;
+};
+
+/*
+ * States of EP0.
+ */
+enum ep0_state {
+ EP0_DISCONNECT = 0, /* no host */
+ EP0_IDLE = 1,
+ EP0_IN_DATA_PHASE = 2,
+ EP0_OUT_DATA_PHASE = 3,
+ EP0_STATUS = 4,
+ EP0_STALL = 5,
+};
+
+/* Fordward declaration.*/
+struct dwc_pcd;
+
+/*
+ * This structure describes an EP, there is an array of EPs in the PCD
+ * structure.
+ */
+struct pcd_ep {
+ /* USB EP data */
+ struct usb_ep ep;
+ /* USB EP Descriptor */
+ const struct usb_endpoint_descriptor *desc;
+
+ /* queue of dwc_otg_pcd_requests. */
+ struct list_head queue;
+ unsigned stopped:1;
+ unsigned disabling:1;
+ unsigned dma:1;
+ unsigned queue_sof:1;
+ unsigned wedged:1;
+
+ /* DWC_otg ep data. */
+ struct dwc_ep dwc_ep;
+
+ /* Pointer to PCD */
+ struct dwc_pcd *pcd;
+};
+
+/*
+ * DWC_otg PCD Structure.
+ * This structure encapsulates the data for the dwc_otg PCD.
+ */
+struct dwc_pcd {
+ /* USB gadget */
+ struct usb_gadget gadget;
+ /* USB gadget driver pointer */
+ struct usb_gadget_driver *driver;
+ /* The DWC otg device pointer. */
+ struct dwc_otg_device *otg_dev;
+
+ /* State of EP0 */
+ enum ep0_state ep0state;
+ /* EP0 Request is pending */
+ unsigned ep0_pending:1;
+ /* Indicates when SET CONFIGURATION Request is in process */
+ unsigned request_config:1;
+ /* The state of the Remote Wakeup Enable. */
+ unsigned remote_wakeup_enable:1;
+ /* The state of the B-Device HNP Enable. */
+ unsigned b_hnp_enable:1;
+ /* The state of A-Device HNP Support. */
+ unsigned a_hnp_support:1;
+ /* The state of the A-Device Alt HNP support. */
+ unsigned a_alt_hnp_support:1;
+ /* Count of pending Requests */
+ unsigned request_pending;
+
+ /*
+ * SETUP packet for EP0. This structure is allocated as a DMA buffer on
+ * PCD initialization with enough space for up to 3 setup packets.
+ */
+ union {
+ struct usb_ctrlrequest req;
+ u32 d32[2];
+ } *setup_pkt;
+
+ struct dma_pool *dwc_pool;
+ dma_addr_t setup_pkt_dma_handle;
+
+ /* 2-byte dma buffer used to return status from GET_STATUS */
+ u16 *status_buf;
+ dma_addr_t status_buf_dma_handle;
+
+ /* Array of EPs. */
+ struct pcd_ep ep0;
+ /* Array of IN EPs. */
+ struct pcd_ep in_ep[MAX_EPS_CHANNELS - 1];
+ /* Array of OUT EPs. */
+ struct pcd_ep out_ep[MAX_EPS_CHANNELS - 1];
+ spinlock_t lock;
+ /*
+ * Timer for SRP. If it expires before SRP is successful clear the
+ * SRP.
+ */
+ struct timer_list srp_timer;
+
+ /*
+ * Tasklet to defer starting of TEST mode transmissions until Status
+ * Phase has been completed.
+ */
+ struct tasklet_struct test_mode_tasklet;
+
+ /* Tasklet to delay starting of xfer in DMA mode */
+ struct tasklet_struct *start_xfer_tasklet;
+
+ /* The test mode to enter when the tasklet is executed. */
+ unsigned test_mode;
+};
+
+/*
+ * This structure holds the state of the HCD, including the non-periodic and
+ * periodic schedules.
+ */
+struct dwc_hcd {
+ spinlock_t lock;
+
+ /* DWC OTG Core Interface Layer */
+ struct core_if *core_if;
+
+ /* Internal DWC HCD Flags */
+ union dwc_otg_hcd_internal_flags {
+ u32 d32;
+ struct {
+ unsigned port_connect_status_change:1;
+ unsigned port_connect_status:1;
+ unsigned port_reset_change:1;
+ unsigned port_enable_change:1;
+ unsigned port_suspend_change:1;
+ unsigned port_over_current_change:1;
+ unsigned reserved:27;
+ } b;
+ } flags;
+
+ /*
+ * Inactive items in the non-periodic schedule. This is a list of
+ * Queue Heads. Transfers associated with these Queue Heads are not
+ * currently assigned to a host channel.
+ */
+ struct list_head non_periodic_sched_inactive;
+
+ /*
+ * Deferred items in the non-periodic schedule. This is a list of
+ * Queue Heads. Transfers associated with these Queue Heads are not
+ * currently assigned to a host channel.
+ * When we get an NAK, the QH goes here.
+ */
+ struct list_head non_periodic_sched_deferred;
+
+ /*
+ * Active items in the non-periodic schedule. This is a list of
+ * Queue Heads. Transfers associated with these Queue Heads are
+ * currently assigned to a host channel.
+ */
+ struct list_head non_periodic_sched_active;
+
+ /*
+ * Pointer to the next Queue Head to process in the active
+ * non-periodic schedule.
+ */
+ struct list_head *non_periodic_qh_ptr;
+
+ /*
+ * Inactive items in the periodic schedule. This is a list of QHs for
+ * periodic transfers that are _not_ scheduled for the next frame.
+ * Each QH in the list has an interval counter that determines when it
+ * needs to be scheduled for execution. This scheduling mechanism
+ * allows only a simple calculation for periodic bandwidth used (i.e.
+ * must assume that all periodic transfers may need to execute in the
+ * same frame). However, it greatly simplifies scheduling and should
+ * be sufficient for the vast majority of OTG hosts, which need to
+ * connect to a small number of peripherals at one time.
+ *
+ * Items move from this list to periodic_sched_ready when the QH
+ * interval counter is 0 at SOF.
+ */
+ struct list_head periodic_sched_inactive;
+
+ /*
+ * List of periodic QHs that are ready for execution in the next
+ * frame, but have not yet been assigned to host channels.
+ *
+ * Items move from this list to periodic_sched_assigned as host
+ * channels become available during the current frame.
+ */
+ struct list_head periodic_sched_ready;
+
+ /*
+ * List of periodic QHs to be executed in the next frame that are
+ * assigned to host channels.
+ *
+ * Items move from this list to periodic_sched_queued as the
+ * transactions for the QH are queued to the DWC_otg controller.
+ */
+ struct list_head periodic_sched_assigned;
+
+ /*
+ * List of periodic QHs that have been queued for execution.
+ *
+ * Items move from this list to either periodic_sched_inactive or
+ * periodic_sched_ready when the channel associated with the transfer
+ * is released. If the interval for the QH is 1, the item moves to
+ * periodic_sched_ready because it must be rescheduled for the next
+ * frame. Otherwise, the item moves to periodic_sched_inactive.
+ */
+ struct list_head periodic_sched_queued;
+
+ /*
+ * Total bandwidth claimed so far for periodic transfers. This value
+ * is in microseconds per (micro)frame. The assumption is that all
+ * periodic transfers may occur in the same (micro)frame.
+ */
+ u16 periodic_usecs;
+
+ /*
+ * Total bandwidth claimed so far for all periodic transfers
+ * in a frame.
+ * This will include a mixture of HS and FS transfers.
+ * Units are microseconds per (micro)frame.
+ * We have a budget per frame and have to schedule
+ * transactions accordingly.
+ * Watch out for the fact that things are actually scheduled for the
+ * "next frame".
+ */
+ u16 frame_usecs[8];
+
+ /*
+ * Frame number read from the core at SOF. The value ranges from 0 to
+ * DWC_HFNUM_MAX_FRNUM.
+ */
+ u16 frame_number;
+
+ /*
+ * Free host channels in the controller. This is a list of
+ * struct dwc_hc items.
+ */
+ struct list_head free_hc_list;
+
+ /*
+ * Number of available host channels.
+ */
+ u32 available_host_channels;
+
+ /*
+ * Array of pointers to the host channel descriptors. Allows accessing
+ * a host channel descriptor given the host channel number. This is
+ * useful in interrupt handlers.
+ */
+ struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
+
+ /*
+ * Buffer to use for any data received during the status phase of a
+ * control transfer. Normally no data is transferred during the status
+ * phase. This buffer is used as a bit bucket.
+ */
+ u8 *status_buf;
+
+ /*
+ * DMA address for status_buf.
+ */
+ dma_addr_t status_buf_dma;
+#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
+
+ /*
+ * Structure to allow starting the HCD in a non-interrupt context
+ * during an OTG role change.
+ */
+ struct work_struct start_work;
+ struct usb_hcd *_p;
+
+ /*
+ * Connection timer. An OTG host must display a message if the device
+ * does not connect. Started when the VBus power is turned on via
+ * sysfs attribute "buspower".
+ */
+ struct timer_list conn_timer;
+
+ /* workqueue for port wakeup */
+ struct work_struct usb_port_reset;
+
+ /* Addition HCD interrupt */
+ int cp_irq; /* charge pump interrupt */
+ int cp_irq_installed;
+};
+
+/*
+ * Reasons for halting a host channel.
+ */
+enum dwc_halt_status {
+ DWC_OTG_HC_XFER_NO_HALT_STATUS,
+ DWC_OTG_HC_XFER_COMPLETE,
+ DWC_OTG_HC_XFER_URB_COMPLETE,
+ DWC_OTG_HC_XFER_ACK,
+ DWC_OTG_HC_XFER_NAK,
+ DWC_OTG_HC_XFER_NYET,
+ DWC_OTG_HC_XFER_STALL,
+ DWC_OTG_HC_XFER_XACT_ERR,
+ DWC_OTG_HC_XFER_FRAME_OVERRUN,
+ DWC_OTG_HC_XFER_BABBLE_ERR,
+ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
+ DWC_OTG_HC_XFER_AHB_ERR,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+ DWC_OTG_HC_XFER_URB_DEQUEUE
+};
+
+/*
+ * Host channel descriptor. This structure represents the state of a single
+ * host channel when acting in host mode. It contains the data items needed to
+ * transfer packets to an endpoint via a host channel.
+ */
+struct dwc_hc {
+ /* Host channel number used for register address lookup */
+ u8 hc_num;
+
+ /* Device to access */
+ unsigned dev_addr:7;
+
+ /* EP to access */
+ unsigned ep_num:4;
+
+ /* EP direction. 0: OUT, 1: IN */
+ unsigned ep_is_in:1;
+
+ /*
+ * EP speed.
+ * One of the following values:
+ * - DWC_OTG_EP_SPEED_LOW
+ * - DWC_OTG_EP_SPEED_FULL
+ * - DWC_OTG_EP_SPEED_HIGH
+ */
+ unsigned speed:2;
+#define DWC_OTG_EP_SPEED_LOW 0
+#define DWC_OTG_EP_SPEED_FULL 1
+#define DWC_OTG_EP_SPEED_HIGH 2
+
+ /*
+ * Endpoint type.
+ * One of the following values:
+ * - DWC_OTG_EP_TYPE_CONTROL: 0
+ * - DWC_OTG_EP_TYPE_ISOC: 1
+ * - DWC_OTG_EP_TYPE_BULK: 2
+ * - DWC_OTG_EP_TYPE_INTR: 3
+ */
+ unsigned ep_type:2;
+
+ /* Max packet size in bytes */
+ unsigned max_packet:11;
+
+ /*
+ * PID for initial transaction.
+ * 0: DATA0,
+ * 1: DATA2,
+ * 2: DATA1,
+ * 3: MDATA (non-Control EP),
+ * SETUP (Control EP)
+ */
+ unsigned data_pid_start:2;
+#define DWC_OTG_HC_PID_DATA0 0
+#define DWC_OTG_HC_PID_DATA2 1
+#define DWC_OTG_HC_PID_DATA1 2
+#define DWC_OTG_HC_PID_MDATA 3
+#define DWC_OTG_HC_PID_SETUP 3
+
+ /* Number of periodic transactions per (micro)frame */
+ unsigned multi_count:2;
+
+ /* Pointer to the current transfer buffer position. */
+ u8 *xfer_buff;
+ /* Total number of bytes to transfer. */
+ u32 xfer_len;
+ /* Number of bytes transferred so far. */
+ u32 xfer_count;
+ /* Packet count at start of transfer. */
+ u16 start_pkt_count;
+
+ /*
+ * Flag to indicate whether the transfer has been started. Set to 1 if
+ * it has been started, 0 otherwise.
+ */
+ u8 xfer_started;
+
+ /*
+ * Set to 1 to indicate that a PING request should be issued on this
+ * channel. If 0, process normally.
+ */
+ u8 do_ping;
+
+ /*
+ * Set to 1 to indicate that the error count for this transaction is
+ * non-zero. Set to 0 if the error count is 0.
+ */
+ u8 error_state;
+
+ /*
+ * Set to 1 to indicate that this channel should be halted the next
+ * time a request is queued for the channel. This is necessary in
+ * slave mode if no request queue space is available when an attempt
+ * is made to halt the channel.
+ */
+ u8 halt_on_queue;
+
+ /*
+ * Set to 1 if the host channel has been halted, but the core is not
+ * finished flushing queued requests. Otherwise 0.
+ */
+ u8 halt_pending;
+
+ /* Reason for halting the host channel. */
+ enum dwc_halt_status halt_status;
+
+ /* Split settings for the host channel */
+ u8 do_split; /* Enable split for the channel */
+ u8 complete_split; /* Enable complete split */
+ u8 hub_addr; /* Address of high speed hub */
+ u8 port_addr; /* Port of the low/full speed device */
+
+ /*
+ * Split transaction position. One of the following values:
+ * - DWC_HCSPLIT_XACTPOS_MID
+ * - DWC_HCSPLIT_XACTPOS_BEGIN
+ * - DWC_HCSPLIT_XACTPOS_END
+ * - DWC_HCSPLIT_XACTPOS_ALL */
+ u8 xact_pos;
+
+ /* Set when the host channel does a short read. */
+ u8 short_read;
+
+ /*
+ * Number of requests issued for this channel since it was assigned to
+ * the current transfer (not counting PINGs).
+ */
+ u8 requests;
+
+ /* Queue Head for the transfer being processed by this channel. */
+ struct dwc_qh *qh;
+
+ /* Entry in list of host channels. */
+ struct list_head hc_list_entry;
+};
+
+/*
+ * The following parameters may be specified when starting the module. These
+ * parameters define how the DWC_otg controller should be configured. Parameter
+ * values are passed to the CIL initialization function dwc_otg_cil_init.
+ */
+struct core_params {
+ /*
+ * Specifies the OTG capabilities. The driver will automatically
+ * detect the value for this parameter if none is specified.
+ * 0 - HNP and SRP capable (default)
+ * 1 - SRP Only capable
+ * 2 - No HNP/SRP capable
+ */
+ int otg_cap;
+#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0
+#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1
+#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
+
+#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
+
+ /*
+ * Specifies whether to use slave or DMA mode for accessing the data
+ * FIFOs. The driver will automatically detect the value for this
+ * parameter if none is specified.
+ * 0 - Slave
+ * 1 - DMA (default, if available)
+ */
+ int dma_enable;
+#ifdef CONFIG_DWC_SLAVE
+#define dwc_param_dma_enable_default 0
+#else
+#define dwc_param_dma_enable_default 1
+#endif
+
+ /*
+ * The DMA Burst size (applicable only for External DMA Mode).
+ * 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ */
+ int dma_burst_size; /* Translate this to GAHBCFG values */
+#define dwc_param_dma_burst_size_default 32
+
+ /*
+ * Specifies the maximum speed of operation in host and device mode.
+ * The actual speed depends on the speed of the attached device and
+ * the value of phy_type. The actual speed depends on the speed of the
+ * attached device.
+ * 0 - High Speed (default)
+ * 1 - Full Speed
+ */
+ int speed;
+#define dwc_param_speed_default 0
+#define DWC_SPEED_PARAM_HIGH 0
+#define DWC_SPEED_PARAM_FULL 1
+
+ /*
+ * Specifies whether low power mode is supported when attached to a Full
+ * Speed or Low Speed device in host mode.
+ * 0 - Don't support low power mode (default)
+ * 1 - Support low power mode
+ */
+ int host_support_fs_ls_low_power;
+#define dwc_param_host_support_fs_ls_low_power_default 0
+
+ /*
+ * Specifies the PHY clock rate in low power mode when connected to a
+ * Low Speed device in host mode. This parameter is applicable only if
+ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+ * then defaults to 6 MHZ otherwise 48 MHZ.
+ *
+ * 0 - 48 MHz
+ * 1 - 6 MHz
+ */
+ int host_ls_low_power_phy_clk;
+#define dwc_param_host_ls_low_power_phy_clk_default 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
+
+ /*
+ * 0 - Use cC FIFO size parameters
+ * 1 - Allow dynamic FIFO sizing (default)
+ */
+ int enable_dynamic_fifo;
+#define dwc_param_enable_dynamic_fifo_default 1
+
+ /*
+ * Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ * FIFO sizing is enabled. 16 to 32768 (default 1064)
+ */
+ int dev_rx_fifo_size;
+#define dwc_param_dev_rx_fifo_size_default 1064
+
+ /*
+ * Number of 4-byte words in the non-periodic Tx FIFO in device mode
+ * when dynamic FIFO sizing is enabled. 16 to 32768 (default 1024)
+ */
+ int dev_nperio_tx_fifo_size;
+#define dwc_param_dev_nperio_tx_fifo_size_default 1024
+
+ /*
+ * Number of 4-byte words in each of the periodic Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled. 4 to 768 (default 256)
+ */
+ u32 dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+#define dwc_param_dev_perio_tx_fifo_size_default 256
+
+ /*
+ * Number of 4-byte words in the Rx FIFO in host mode when dynamic
+ * FIFO sizing is enabled. 16 to 32768 (default 1024)
+ */
+ int host_rx_fifo_size;
+#define dwc_param_host_rx_fifo_size_default 1024
+
+ /*
+ * Number of 4-byte words in the non-periodic Tx FIFO in host mode
+ * when Dynamic FIFO sizing is enabled in the core. 16 to 32768
+ * (default 1024)
+ */
+ int host_nperio_tx_fifo_size;
+#define dwc_param_host_nperio_tx_fifo_size_default 1024
+
+ /*
+ Number of 4-byte words in the host periodic Tx FIFO when dynamic
+ * FIFO sizing is enabled. 16 to 32768 (default 1024)
+ */
+ int host_perio_tx_fifo_size;
+#define dwc_param_host_perio_tx_fifo_size_default 1024
+
+ /*
+ * The maximum transfer size supported in bytes. 2047 to 65,535
+ * (default 65,535)
+ */
+ int max_transfer_size;
+#define dwc_param_max_transfer_size_default 65535
+
+ /*
+ * The maximum number of packets in a transfer. 15 to 511 (default 511)
+ */
+ int max_packet_count;
+#define dwc_param_max_packet_count_default 511
+
+ /*
+ * The number of host channel registers to use.
+ * 1 to 16 (default 12)
+ * Note: The FPGA configuration supports a maximum of 12 host channels.
+ */
+ int host_channels;
+#define dwc_param_host_channels_default 12
+
+ /*
+ * The number of endpoints in addition to EP0 available for device
+ * mode operations.
+ * 1 to 15 (default 6 IN and OUT)
+ * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+ * endpoints in addition to EP0.
+ */
+ int dev_endpoints;
+#define dwc_param_dev_endpoints_default 6
+
+ /*
+ * Specifies the type of PHY interface to use. By default, the driver
+ * will automatically detect the phy_type.
+ *
+ * 0 - Full Speed PHY
+ * 1 - UTMI+ (default)
+ * 2 - ULPI
+ */
+ int phy_type;
+#define DWC_PHY_TYPE_PARAM_FS 0
+#define DWC_PHY_TYPE_PARAM_UTMI 1
+#define DWC_PHY_TYPE_PARAM_ULPI 2
+#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI
+
+ /*
+ * Specifies the UTMI+ Data Width. This parameter is applicable for a
+ * PHY_TYPE of UTMI+ or ULPI. (For a ULPI PHY_TYPE, this parameter
+ * indicates the data width between the MAC and the ULPI Wrapper.) Also,
+ * this parameter is applicable only if the OTG_HSPHY_WIDTH cC parameter
+ * was set to "8 and 16 bits", meaning that the core has been configured
+ * to work at either data path width.
+ *
+ * 8 or 16 bits (default 16)
+ */
+ int phy_utmi_width;
+#define dwc_param_phy_utmi_width_default 16
+
+ /*
+ * Specifies whether the ULPI operates at double or single
+ * data rate. This parameter is only applicable if PHY_TYPE is
+ * ULPI.
+ *
+ * 0 - single data rate ULPI interface with 8 bit wide data
+ * bus (default)
+ * 1 - double data rate ULPI interface with 4 bit wide data
+ * bus
+ */
+ int phy_ulpi_ddr;
+#define dwc_param_phy_ulpi_ddr_default 0
+
+ /*
+ * Specifies whether to use the internal or external supply to
+ * drive the vbus with a ULPI phy.
+ */
+ int phy_ulpi_ext_vbus;
+#define DWC_PHY_ULPI_INTERNAL_VBUS 0
+#define DWC_PHY_ULPI_EXTERNAL_VBUS 1
+#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS
+
+ /*
+ * Specifies whether to use the I2Cinterface for full speed PHY. This
+ * parameter is only applicable if PHY_TYPE is FS.
+ * 0 - No (default)
+ * 1 - Yes
+ */
+ int i2c_enable;
+#define dwc_param_i2c_enable_default 0
+
+ int ulpi_fs_ls;
+#define dwc_param_ulpi_fs_ls_default 0
+
+ int ts_dline;
+#define dwc_param_ts_dline_default 0
+
+ /*
+ * Specifies whether dedicated transmit FIFOs are enabled for non
+ * periodic IN endpoints in device mode
+ * 0 - No
+ * 1 - Yes
+ */
+ int en_multiple_tx_fifo;
+#define dwc_param_en_multiple_tx_fifo_default 1
+
+ /*
+ * Number of 4-byte words in each of the Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled. 4 to 768 (default 256)
+ */
+ u32 dev_tx_fifo_size[MAX_TX_FIFOS];
+#define dwc_param_dev_tx_fifo_size_default 256
+
+ /*
+ * Thresholding enable flag
+ * bit 0 - enable non-ISO Tx thresholding
+ * bit 1 - enable ISO Tx thresholding
+ * bit 2 - enable Rx thresholding
+ */
+ u32 thr_ctl;
+#define dwc_param_thr_ctl_default 0
+
+ /* Thresholding length for Tx FIFOs in 32 bit DWORDs */
+ u32 tx_thr_length;
+#define dwc_param_tx_thr_length_default 64
+
+ /* Thresholding length for Rx FIFOs in 32 bit DWORDs */
+ u32 rx_thr_length;
+#define dwc_param_rx_thr_length_default 64
+
+};
+
+/*
+ * The core_if structure contains information needed to manage the
+ * DWC_otg controller acting in either host or device mode. It represents the
+ * programming view of the controller as a whole.
+ */
+struct core_if {
+ /* Parameters that define how the core should be configured. */
+ struct core_params *core_params;
+
+ /* Core Global registers starting at offset 000h. */
+ ulong core_global_regs;
+
+ /* Device-specific information */
+ struct device_if *dev_if;
+ /* Host-specific information */
+ struct dwc_host_if *host_if;
+
+ /*
+ * Set to 1 if the core PHY interface bits in USBCFG have been
+ * initialized.
+ */
+ u8 phy_init_done;
+
+ /*
+ * SRP Success flag, set by srp success interrupt in FS I2C mode
+ */
+ u8 srp_success;
+ u8 srp_timer_started;
+
+ /* Common configuration information */
+ /* Power and Clock Gating Control Register */
+ ulong pcgcctl;
+#define DWC_OTG_PCGCCTL_OFFSET 0xE00
+
+ /* Push/pop addresses for endpoints or host channels. */
+ ulong data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
+#define DWC_OTG_DATA_FIFO_SIZE 0x1000
+
+ /* Total RAM for FIFOs (Bytes) */
+ u16 total_fifo_size;
+ /* Size of Rx FIFO (Bytes) */
+ u16 rx_fifo_size;
+ /* Size of Non-periodic Tx FIFO (Bytes) */
+ u16 nperio_tx_fifo_size;
+
+ /* 1 if DMA is enabled, 0 otherwise. */
+ u8 dma_enable;
+
+ /* 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
+ u8 en_multiple_tx_fifo;
+
+ /*
+ * Set to 1 if multiple packets of a high-bandwidth transfer is in
+ * process of being queued
+ */
+ u8 queuing_high_bandwidth;
+
+ /* Hardware Configuration -- stored here for convenience. */
+ ulong hwcfg1;
+ ulong hwcfg2;
+ ulong hwcfg3;
+ ulong hwcfg4;
+
+ /* HCD callbacks */
+ /* include/linux/usb/otg.h */
+
+ /* HCD callbacks */
+ struct cil_callbacks *hcd_cb;
+ /* PCD callbacks */
+ struct cil_callbacks *pcd_cb;
+
+ /* Device mode Periodic Tx FIFO Mask */
+ u32 p_tx_msk;
+ /* Device mode Periodic Tx FIFO Mask */
+ u32 tx_msk;
+
+ /* Features of various DWC implementation */
+ u32 features;
+
+ /* Added to support PLB DMA : phys-virt mapping */
+ resource_size_t phys_addr;
+
+ struct delayed_work usb_port_wakeup;
+ struct work_struct usb_port_otg;
+ struct otg_transceiver *xceiv;
+};
+
+/*
+ * The following functions support initialization of the CIL driver component
+ * and the DWC_otg controller.
+ */
+extern void dwc_otg_core_init(struct core_if *core_if);
+extern void init_fslspclksel(struct core_if *core_if);
+extern void dwc_otg_core_dev_init(struct core_if *core_if);
+extern void dwc_otg_enable_global_interrupts(struct core_if *core_if);
+extern void dwc_otg_disable_global_interrupts(struct core_if *core_if);
+extern void dwc_otg_enable_common_interrupts(struct core_if *core_if);
+
+/**
+ * This function Reads HPRT0 in preparation to modify. It keeps the WC bits 0
+ * so that if they are read as 1, they won't clear when you write it back
+ */
+static inline u32 dwc_otg_read_hprt0(struct core_if *core_if)
+{
+ u32 hprt0 = 0;
+ hprt0 = dwc_reg_read(core_if->host_if->hprt0, 0);
+ hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 0);
+ hprt0 = DWC_HPRT0_PRT_CONN_DET_RW(hprt0, 0);
+ hprt0 = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0, 0);
+ hprt0 = DWC_HPRT0_PRT_OVRCURR_ACT_RW(hprt0, 0);
+ return hprt0;
+}
+
+/*
+ * The following functions support managing the DWC_otg controller in either
+ * device or host mode.
+ */
+extern void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 bytes);
+extern void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int _num);
+extern void dwc_otg_flush_rx_fifo(struct core_if *core_if);
+
+#define NP_TXFIFO_EMPTY -1
+#define MAX_NP_TXREQUEST_Q_SLOTS 8
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline u32 dwc_otg_read_core_intr(struct core_if *core_if)
+{
+ u32 global_regs = (u32) core_if->core_global_regs;
+ return dwc_reg_read(global_regs, DWC_GINTSTS) &
+ dwc_reg_read(global_regs, DWC_GINTMSK);
+}
+
+/**
+ * This function returns the mode of the operation, host or device.
+ */
+static inline u32 dwc_otg_mode(struct core_if *core_if)
+{
+ u32 global_regs = (u32) core_if->core_global_regs;
+ return dwc_reg_read(global_regs, DWC_GINTSTS) & 0x1;
+}
+
+static inline u8 dwc_otg_is_device_mode(struct core_if *core_if)
+{
+ return dwc_otg_mode(core_if) != DWC_HOST_MODE;
+}
+static inline u8 dwc_otg_is_host_mode(struct core_if *core_if)
+{
+ return dwc_otg_mode(core_if) == DWC_HOST_MODE;
+}
+
+extern int dwc_otg_handle_common_intr(struct core_if *core_if);
+
+/*
+ * DWC_otg CIL callback structure. This structure allows the HCD and PCD to
+ * register functions used for starting and stopping the PCD and HCD for role
+ * change on for a DRD.
+ */
+struct cil_callbacks {
+ /* Start function for role change */
+ int (*start) (void *_p);
+ /* Stop Function for role change */
+ int (*stop) (void *_p);
+ /* Disconnect Function for role change */
+ int (*disconnect) (void *_p);
+ /* Resume/Remote wakeup Function */
+ int (*resume_wakeup) (void *_p);
+ /* Suspend function */
+ int (*suspend) (void *_p);
+ /* Session Start (SRP) */
+ int (*session_start) (void *_p);
+ /* Pointer passed to start() and stop() */
+ void *p;
+};
+
+extern void dwc_otg_cil_register_pcd_callbacks(struct core_if *core_if,
+ struct cil_callbacks *cb,
+ void *p);
+extern void dwc_otg_cil_register_hcd_callbacks(struct core_if *core_if,
+ struct cil_callbacks *cb,
+ void *p);
+
+#define DWC_LIMITED_XFER 0x00000000
+#define DWC_DEVICE_ONLY 0x00000000
+#define DWC_HOST_ONLY 0x00000000
+
+#ifdef DWC_LIMITED_XFER_SIZE
+#undef DWC_LIMITED_XFER
+#define DWC_LIMITED_XFER 0x00000001
+#endif
+
+#ifdef CONFIG_DWC_DEVICE_ONLY
+#undef DWC_DEVICE_ONLY
+#define DWC_DEVICE_ONLY 0x00000002
+static inline void dwc_otg_hcd_remove(struct device *dev)
+{
+}
+static inline int dwc_otg_hcd_init(struct device *_dev,
+ struct dwc_otg_device *dwc_dev)
+{
+ return 0;
+}
+#else
+extern int dwc_otg_hcd_init(struct device *_dev,
+ struct dwc_otg_device *dwc_dev);
+extern void dwc_otg_hcd_remove(struct device *_dev);
+#endif
+
+#ifdef CONFIG_DWC_HOST_ONLY
+#undef DWC_HOST_ONLY
+#define DWC_HOST_ONLY 0x00000004
+static inline void dwc_otg_pcd_remove(struct device *dev)
+{
+}
+static inline int dwc_otg_pcd_init(struct device *dev)
+{
+ return 0;
+}
+#else
+extern void dwc_otg_pcd_remove(struct device *dev);
+extern int dwc_otg_pcd_init(struct device *dev);
+#endif
+
+extern void dwc_otg_cil_remove(struct core_if *core_if);
+extern struct core_if *dwc_otg_cil_init(const __iomem u32 * base,
+ struct core_params *params);
+
+static inline void dwc_set_feature(struct core_if *core_if)
+{
+ core_if->features = DWC_LIMITED_XFER | DWC_DEVICE_ONLY | DWC_HOST_ONLY;
+}
+
+static inline int dwc_has_feature(struct core_if *core_if,
+ unsigned long feature)
+{
+ return core_if->features & feature;
+}
+extern struct core_params dwc_otg_module_params;
+extern int check_parameters(struct core_if *core_if);
+#endif
diff --git a/drivers/usb/dwc/cil_intr.c b/drivers/usb/dwc/cil_intr.c
new file mode 100644
index 0000000..a42b0e6
--- /dev/null
+++ b/drivers/usb/dwc/cil_intr.c
@@ -0,0 +1,616 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * This file contains the Common Interrupt handlers.
+ */
+#include <linux/delay.h>
+
+#include "cil.h"
+
+/**
+ * This function will log a debug message
+ */
+static int dwc_otg_handle_mode_mismatch_intr(struct core_if *core_if)
+{
+ u32 gintsts = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ pr_warning("Mode Mismatch Interrupt: currently in %s mode\n",
+ dwc_otg_mode(core_if) ? "Host" : "Device");
+
+ /* Clear interrupt */
+ gintsts |= DWC_INTSTS_MODE_MISMTC;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+
+ return 1;
+}
+
+/**
+ * Start the HCD. Helper function for using the HCD callbacks.
+ */
+static inline void hcd_start(struct core_if *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->start)
+ core_if->hcd_cb->start(core_if->hcd_cb->p);
+}
+
+/**
+ * Stop the HCD. Helper function for using the HCD callbacks.
+ */
+static inline void hcd_stop(struct core_if *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->stop)
+ core_if->hcd_cb->stop(core_if->hcd_cb->p);
+}
+
+/**
+ * Disconnect the HCD. Helper function for using the HCD callbacks.
+ */
+static inline void hcd_disconnect(struct core_if *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->disconnect)
+ core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
+}
+
+/**
+ * Inform the HCD the a New Session has begun. Helper function for using the
+ * HCD callbacks.
+ */
+static inline void hcd_session_start(struct core_if *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->session_start)
+ core_if->hcd_cb->session_start(core_if->hcd_cb->p);
+}
+
+/**
+ * Start the PCD. Helper function for using the PCD callbacks.
+ */
+static inline void pcd_start(struct core_if *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->start) {
+ struct dwc_pcd *pcd;
+
+ pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+ spin_lock(&pcd->lock);
+ core_if->pcd_cb->start(core_if->pcd_cb->p);
+ spin_unlock(&pcd->lock);
+ }
+}
+
+/**
+ * Stop the PCD. Helper function for using the PCD callbacks.
+ */
+static inline void pcd_stop(struct core_if *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->stop) {
+ struct dwc_pcd *pcd;
+
+ pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+ spin_lock(&pcd->lock);
+ core_if->pcd_cb->stop(core_if->pcd_cb->p);
+ spin_unlock(&pcd->lock);
+ }
+}
+
+/**
+ * Suspend the PCD. Helper function for using the PCD callbacks.
+ */
+static inline void pcd_suspend(struct core_if *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
+ struct dwc_pcd *pcd;
+
+ pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+ spin_lock(&pcd->lock);
+ core_if->pcd_cb->suspend(core_if->pcd_cb->p);
+ spin_unlock(&pcd->lock);
+ }
+}
+
+/**
+ * Resume the PCD. Helper function for using the PCD callbacks.
+ */
+static inline void pcd_resume(struct core_if *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ struct dwc_pcd *pcd;
+
+ pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+ spin_lock(&pcd->lock);
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ spin_unlock(&pcd->lock);
+ }
+}
+
+/**
+ * This function handles the OTG Interrupts. It reads the OTG
+ * Interrupt Register (GOTGINT) to determine what interrupt has
+ * occurred.
+ */
+static int dwc_otg_handle_otg_intr(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 gotgint;
+ u32 gotgctl;
+ u32 gintmsk;
+
+ gotgint = dwc_reg_read(global_regs, DWC_GOTGINT);
+ if (gotgint & DWC_GINT_SES_ENDDET) {
+ gotgctl = dwc_reg_read(global_regs, DWC_GOTGCTL);
+ if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+ pcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ } else {
+ /*
+ * If not B_HOST and Device HNP still set. HNP did not
+ * succeed
+ */
+ if (gotgctl & DWC_GCTL_DEV_HNP_ENA)
+ pr_err("Device Not Connected / "
+ "Responding\n");
+ /*
+ * If Session End Detected the B-Cable has been
+ * disconnected. Reset PCD and Gadget driver to a
+ * clean state.
+ */
+ pcd_stop(core_if);
+ }
+ gotgctl = 0;
+ gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+ dwc_reg_modify(global_regs, DWC_GOTGCTL, gotgctl, 0);
+ }
+ if (gotgint & DWC_GINT_SES_REQSUC) {
+ gotgctl = dwc_reg_read(global_regs, DWC_GOTGCTL);
+ if (gotgctl & DWC_GCTL_SES_REQ_SUCCESS) {
+ if (core_if->core_params->phy_type ==
+ DWC_PHY_TYPE_PARAM_FS &&
+ core_if->core_params->i2c_enable) {
+ core_if->srp_success = 1;
+ } else {
+ pcd_resume(core_if);
+
+ /* Clear Session Request */
+ gotgctl = 0;
+ gotgctl |= DWC_GCTL_SES_REQ;
+ dwc_reg_modify(global_regs, DWC_GOTGCTL,
+ gotgctl, 0);
+ }
+ }
+ }
+ if (gotgint & DWC_GINT_HST_NEGSUC) {
+ /*
+ * Print statements during the HNP interrupt handling can cause
+ * it to fail.
+ */
+ gotgctl = dwc_reg_read(global_regs, DWC_GOTGCTL);
+ if (gotgctl & DWC_GCTL_HOST_NEG_SUCCES) {
+ if (dwc_otg_is_host_mode(core_if)) {
+ core_if->xceiv->state = OTG_STATE_B_HOST;
+ /*
+ * Need to disable SOF interrupt immediately.
+ * When switching from device to host, the PCD
+ * interrupt handler won't handle the
+ * interrupt if host mode is already set. The
+ * HCD interrupt handler won't get called if
+ * the HCD state is HALT. This means that the
+ * interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk = 0;
+ gintmsk |= DWC_INTMSK_STRT_OF_FRM;
+ dwc_reg_modify(global_regs, DWC_GINTMSK,
+ gintmsk, 0);
+ pcd_stop(core_if);
+ /* Initialize the Core for Host mode. */
+ hcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_B_HOST;
+ }
+ } else {
+ gotgctl = 0;
+ gotgctl |= DWC_GCTL_HNP_REQ;
+ gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+ dwc_reg_modify(global_regs, DWC_GOTGCTL, gotgctl, 0);
+
+ pr_err("Device Not Connected / Responding\n");
+ }
+ }
+ if (gotgint & DWC_GINT_HST_NEGDET) {
+ /*
+ * The disconnect interrupt is set at the same time as
+ * Host Negotiation Detected. During the mode
+ * switch all interrupts are cleared so the disconnect
+ * interrupt handler will not get executed.
+ */
+ if (dwc_otg_is_device_mode(core_if)) {
+ hcd_disconnect(core_if);
+ pcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_A_PERIPHERAL;
+ } else {
+ /*
+ * Need to disable SOF interrupt immediately. When
+ * switching from device to host, the PCD interrupt
+ * handler won't handle the interrupt if host mode is
+ * already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that
+ * the interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk = 0;
+ gintmsk |= DWC_INTMSK_STRT_OF_FRM;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, gintmsk, 0);
+ pcd_stop(core_if);
+ hcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_A_HOST;
+ }
+ }
+ if (gotgint & DWC_GINT_DEVTOUT)
+ pr_info(" ++OTG Interrupt: A-Device Timeout " "Change++\n");
+ if (gotgint & DWC_GINT_DEBDONE)
+ pr_info(" ++OTG Interrupt: Debounce Done++\n");
+
+ /* Clear GOTGINT */
+ dwc_reg_write(global_regs, DWC_GOTGINT, gotgint);
+ return 1;
+}
+
+/*
+ * Wakeup Workqueue implementation
+ */
+static void port_otg_wqfunc(struct work_struct *work)
+{
+ struct core_if *core_if = container_of(work, struct core_if,
+ usb_port_otg);
+ ulong global_regs = core_if->core_global_regs;
+ u32 count = 0;
+ u32 gotgctl;
+
+ pr_info("%s\n", __func__);
+
+ gotgctl = dwc_reg_read(global_regs, DWC_GOTGCTL);
+ if (gotgctl & DWC_GCTL_CONN_ID_STATUS) {
+ /*
+ * B-Device connector (device mode) wait for switch to device
+ * mode.
+ */
+ while (!dwc_otg_is_device_mode(core_if) && ++count <= 10000) {
+ pr_info("Waiting for Peripheral Mode, "
+ "Mode=%s\n", dwc_otg_is_host_mode(core_if) ?
+ "Host" : "Peripheral");
+ msleep(100);
+ }
+ BUG_ON(count > 10000);
+ core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ pcd_start(core_if);
+ } else {
+ /*
+ * A-Device connector (host mode) wait for switch to host
+ * mode.
+ */
+ while (!dwc_otg_is_host_mode(core_if) && ++count <= 10000) {
+ pr_info("Waiting for Host Mode, Mode=%s\n",
+ dwc_otg_is_host_mode(core_if) ?
+ "Host" : "Peripheral");
+ msleep(100);
+ }
+ BUG_ON(count > 10000);
+ core_if->xceiv->state = OTG_STATE_A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ hcd_start(core_if);
+ }
+}
+
+/**
+ * This function handles the Connector ID Status Change Interrupt. It
+ * reads the OTG Interrupt Register (GOTCTL) to determine whether this
+ * is a Device to Host Mode transition or a Host Mode to Device
+ * Transition.
+ *
+ * This only occurs when the cable is connected/removed from the PHY
+ * connector.
+ */
+static int dwc_otg_handle_conn_id_status_change_intr(struct core_if *core_if)
+{
+ u32 gintsts = 0;
+ u32 gintmsk = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ /*
+ * Need to disable SOF interrupt immediately. If switching from device
+ * to host, the PCD interrupt handler won't handle the interrupt if
+ * host mode is already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that the interrupt does
+ * not get handled and Linux complains loudly.
+ */
+ gintmsk |= DWC_INTSTS_STRT_OF_FRM;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, gintmsk, 0);
+
+ INIT_WORK(&core_if->usb_port_otg, port_otg_wqfunc);
+ schedule_work(&core_if->usb_port_otg);
+
+ /* Set flag and clear interrupt */
+ gintsts |= DWC_INTSTS_CON_ID_STS_CHG;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device is initiating the Session
+ * Request Protocol to request the host to turn on bus power so a new
+ * session can begin. The handler responds by turning on bus power. If
+ * the DWC_otg controller is in low power mode, the handler brings the
+ * controller out of low power mode before turning on bus power.
+ */
+static int dwc_otg_handle_session_req_intr(struct core_if *core_if)
+{
+ u32 gintsts = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
+ u32 hprt0;
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ pr_info("SRP: Device mode\n");
+ } else {
+ pr_info("SRP: Host mode\n");
+
+ /* Turn on the port power bit. */
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+
+ /*
+ * Start the Connection timer.
+ * A message can be displayed,
+ * if connect does not occur within 10 seconds.
+ */
+ hcd_session_start(core_if);
+ }
+ }
+ /* Clear interrupt */
+ gintsts |= DWC_INTSTS_NEW_SES_DET;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the DWC_otg controller has detected a
+ * resume or remote wakeup sequence. If the DWC_otg controller is in
+ * low power mode, the handler must brings the controller out of low
+ * power mode. The controller automatically begins resume
+ * signaling. The handler schedules a time to stop resume signaling.
+ */
+static int dwc_otg_handle_wakeup_detected_intr(struct core_if *core_if)
+{
+ u32 gintsts = 0;
+ struct device_if *dev_if = core_if->dev_if;
+ ulong global_regs = core_if->core_global_regs;
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ u32 dctl = 0;
+
+ /* Clear the Remote Wakeup Signalling */
+ dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+ dwc_reg_modify(dev_if->dev_global_regs, DWC_DCTL, dctl, 0);
+
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ } else {
+ u32 pcgcctl = 0;
+
+ /* Restart the Phy Clock */
+ pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
+ dwc_reg_modify(core_if->pcgcctl, 0, pcgcctl, 0);
+ schedule_delayed_work(&core_if->usb_port_wakeup, 10);
+ }
+
+ /* Clear interrupt */
+ gintsts |= DWC_INTSTS_WKP;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device has been disconnected from
+ * the root port.
+ */
+static int dwc_otg_handle_disconnect_intr(struct core_if *core_if)
+{
+ u32 gintsts = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
+ if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+ hcd_disconnect(core_if);
+ pcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ } else if (dwc_otg_is_device_mode(core_if)) {
+ u32 gotgctl;
+
+ gotgctl = dwc_reg_read(global_regs, DWC_GOTGCTL);
+
+ /*
+ * If HNP is in process, do nothing.
+ * The OTG "Host Negotiation Detected"
+ * interrupt will do the mode switch.
+ * Otherwise, since we are in device mode,
+ * disconnect and stop the HCD,
+ * then start the PCD.
+ */
+ if ((gotgctl) & DWC_GCTL_DEV_HNP_ENA) {
+ hcd_disconnect(core_if);
+ pcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ }
+ } else if (core_if->xceiv->state == OTG_STATE_A_HOST) {
+ /* A-Cable still connected but device disconnected. */
+ hcd_disconnect(core_if);
+ }
+ }
+ gintsts |= DWC_INTSTS_SES_DISCON_DET;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that SUSPEND state has been detected on
+ * the USB.
+ *
+ * For HNP the USB Suspend interrupt signals the change from
+ * "a_peripheral" to "a_host".
+ *
+ * When power management is enabled the core will be put in low power
+ * mode.
+ */
+static int dwc_otg_handle_usb_suspend_intr(struct core_if *core_if)
+{
+ u32 dsts = 0;
+ u32 gintsts = 0;
+ ulong global_regs = core_if->core_global_regs;
+ struct device_if *dev_if = core_if->dev_if;
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ /*
+ * Check the Device status register to determine if the Suspend
+ * state is active.
+ */
+ dsts = dwc_reg_read(dev_if->dev_global_regs, DWC_DSTS);
+ /* PCD callback for suspend. */
+ pcd_suspend(core_if);
+ } else {
+ if (core_if->xceiv->state == OTG_STATE_A_PERIPHERAL) {
+ /* Clear the a_peripheral flag, back to a_host. */
+ pcd_stop(core_if);
+ hcd_start(core_if);
+ core_if->xceiv->state = OTG_STATE_A_HOST;
+ }
+ }
+
+ /* Clear interrupt */
+ gintsts |= DWC_INTMSK_USB_SUSP;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This function returns the Core Interrupt register.
+ *
+ * Although the Host Port interrupt (portintr) is documented as host mode
+ * only, it appears to occur in device mode when Port Enable / Disable Changed
+ * bit in HPRT0 is set. The code in dwc_otg_handle_common_intr checks if in
+ * device mode and just clears the interrupt.
+ */
+static inline u32 dwc_otg_read_common_intr(struct core_if *core_if)
+{
+ u32 gintsts;
+ u32 gintmsk;
+ u32 gintmsk_common = 0;
+ ulong global_regs = core_if->core_global_regs;
+
+ gintmsk_common |= DWC_INTMSK_WKP;
+ gintmsk_common |= DWC_INTMSK_NEW_SES_DET;
+ gintmsk_common |= DWC_INTMSK_CON_ID_STS_CHG;
+ gintmsk_common |= DWC_INTMSK_OTG;
+ gintmsk_common |= DWC_INTMSK_MODE_MISMTC;
+ gintmsk_common |= DWC_INTMSK_SES_DISCON_DET;
+ gintmsk_common |= DWC_INTMSK_USB_SUSP;
+ gintmsk_common |= DWC_INTMSK_HST_PORT;
+
+ gintsts = dwc_reg_read(global_regs, DWC_GINTSTS);
+ gintmsk = dwc_reg_read(global_regs, DWC_GINTMSK);
+
+ return (gintsts & gintmsk) & gintmsk_common;
+}
+
+/**
+ * Common interrupt handler.
+ *
+ * The common interrupts are those that occur in both Host and Device mode.
+ * This handler handles the following interrupts:
+ * - Mode Mismatch Interrupt
+ * - Disconnect Interrupt
+ * - OTG Interrupt
+ * - Connector ID Status Change Interrupt
+ * - Session Request Interrupt.
+ * - Resume / Remote Wakeup Detected Interrupt.
+ *
+ * - Host Port Interrupt. Although this interrupt is documented as only
+ * occurring in Host mode, it also occurs in Device mode when Port Enable /
+ * Disable Changed bit in HPRT0 is set. If it is seen here, while in Device
+ * mode, the interrupt is just cleared.
+ *
+ */
+int dwc_otg_handle_common_intr(struct core_if *core_if)
+{
+ int retval = 0;
+ u32 gintsts;
+ ulong global_regs = core_if->core_global_regs;
+
+ gintsts = dwc_otg_read_common_intr(core_if);
+
+ if (gintsts & DWC_INTSTS_MODE_MISMTC)
+ retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+ if (gintsts & DWC_INTSTS_OTG)
+ retval |= dwc_otg_handle_otg_intr(core_if);
+ if (gintsts & DWC_INTSTS_CON_ID_STS_CHG)
+ retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
+ if (gintsts & DWC_INTSTS_SES_DISCON_DET)
+ retval |= dwc_otg_handle_disconnect_intr(core_if);
+ if (gintsts & DWC_INTSTS_NEW_SES_DET)
+ retval |= dwc_otg_handle_session_req_intr(core_if);
+ if (gintsts & DWC_INTSTS_WKP)
+ retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
+ if (gintsts & DWC_INTMSK_USB_SUSP)
+ retval |= dwc_otg_handle_usb_suspend_intr(core_if);
+
+ if ((gintsts & DWC_INTSTS_HST_PORT) &&
+ dwc_otg_is_device_mode(core_if)) {
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_HST_PORT;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+ retval |= 1;
+ pr_info("RECEIVED PORTINT while in Device mode\n");
+ }
+
+ return retval;
+}
diff --git a/drivers/usb/dwc/driver.h b/drivers/usb/dwc/driver.h
new file mode 100644
index 0000000..a86532b
--- /dev/null
+++ b/drivers/usb/dwc/driver.h
@@ -0,0 +1,76 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_OTG_DRIVER_H__)
+#define __DWC_OTG_DRIVER_H__
+
+/*
+ * This file contains the interface to the Linux driver.
+ */
+#include "cil.h"
+
+/*
+ * This structure is a wrapper that encapsulates the driver components used to
+ * manage a single DWC_otg controller.
+ */
+struct dwc_otg_device {
+ /* Base address returned from ioremap() */
+ __iomem void *base;
+
+ /* Pointer to the core interface structure. */
+ struct core_if *core_if;
+
+ /* Pointer to the PCD structure. */
+ struct dwc_pcd *pcd;
+
+ /* Pointer to the HCD structure. */
+ struct dwc_hcd *hcd;
+
+ /* Flag to indicate whether the common IRQ handler is installed. */
+ u8 common_irq_installed;
+
+ /* Interrupt request number. */
+ unsigned int irq;
+
+ /*
+ * Physical address of Control and Status registers, used by
+ * release_mem_region().
+ */
+ resource_size_t phys_addr;
+
+ /* Length of memory region, used by release_mem_region(). */
+ unsigned long base_len;
+};
+#endif
diff --git a/drivers/usb/dwc/hcd.c b/drivers/usb/dwc/hcd.c
new file mode 100644
index 0000000..2718b95
--- /dev/null
+++ b/drivers/usb/dwc/hcd.c
@@ -0,0 +1,2473 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file contains the implementation of the HCD. In Linux, the HCD
+ * implements the hc_driver API.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/dma-mapping.h>
+
+#include "hcd.h"
+
+static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
+
+/**
+ * Clears the transfer state for a host channel. This function is normally
+ * called after a transfer is done and the host channel is being released. It
+ * clears the channel interrupt enables and any unhandled channel interrupt
+ * conditions.
+ */
+void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc)
+{
+ ulong regs;
+
+ hc->xfer_started = 0;
+ regs = core_if->host_if->hc_regs[hc->hc_num];
+ dwc_reg_write(regs, DWC_HCINTMSK, 0);
+ dwc_reg_write(regs, DWC_HCINT, 0xFFFFFFFF);
+}
+
+/**
+ * This function enables the Host mode interrupts.
+ */
+static void dwc_otg_enable_host_interrupts(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ u32 intr_mask = 0;
+
+ /* Disable all interrupts. */
+ dwc_reg_write(global_regs, DWC_GINTMSK, 0);
+
+ /* Clear any pending interrupts. */
+ dwc_reg_write(global_regs, DWC_GINTSTS, 0xFFFFFFFF);
+
+ /* Enable the common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /*
+ * Enable host mode interrupts without disturbing common
+ * interrupts.
+ */
+ intr_mask |= DWC_INTMSK_STRT_OF_FRM;
+ intr_mask |= DWC_INTMSK_HST_PORT;
+ intr_mask |= DWC_INTMSK_HST_CHAN;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, intr_mask, intr_mask);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * host mode.
+ *
+ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
+ * request queues. Host channels are reset to ensure that they are ready for
+ * performing transfers.
+ */
+static void dwc_otg_core_host_init(struct core_if *core_if)
+{
+ ulong global_regs = core_if->core_global_regs;
+ struct dwc_host_if *host_if = core_if->host_if;
+ struct core_params *params = core_if->core_params;
+ u32 hprt0 = 0;
+ u32 nptxfifosize = 0;
+ u32 ptxfifosize = 0;
+ u32 i;
+ u32 hcchar;
+ ulong hcfg;
+ ulong hc_regs;
+ int num_channels;
+ u32 gotgctl = 0;
+
+ /* Restart the Phy Clock */
+ dwc_reg_write(core_if->pcgcctl, 0, 0);
+
+ /* Initialize Host Configuration Register */
+ init_fslspclksel(core_if);
+ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+ hcfg = dwc_reg_read(host_if->host_global_regs, DWC_HCFG);
+ hcfg = DWC_HCFG_FSLSUPP_RW(hcfg, 1);
+ dwc_reg_write(host_if->host_global_regs, DWC_HCFG, hcfg);
+ }
+
+ /* Configure data FIFO sizes */
+ if (DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2)
+ && params->enable_dynamic_fifo) {
+ /* Rx FIFO */
+ dwc_reg_write(global_regs, DWC_GRXFSIZ,
+ params->host_rx_fifo_size);
+
+ /* Non-periodic Tx FIFO */
+ nptxfifosize = DWC_RX_FIFO_DEPTH_WR(nptxfifosize,
+ params->
+ host_nperio_tx_fifo_size);
+ nptxfifosize =
+ DWC_RX_FIFO_START_ADDR_WR(nptxfifosize,
+ params->host_rx_fifo_size);
+ dwc_reg_write(global_regs, DWC_GNPTXFSIZ, nptxfifosize);
+
+ /* Periodic Tx FIFO */
+ ptxfifosize = DWC_RX_FIFO_DEPTH_WR(ptxfifosize,
+ params->
+ host_perio_tx_fifo_size);
+ ptxfifosize =
+ DWC_RX_FIFO_START_ADDR_WR(ptxfifosize,
+ (DWC_RX_FIFO_START_ADDR_RD
+ (nptxfifosize) +
+ DWC_RX_FIFO_DEPTH_RD
+ (nptxfifosize)));
+ dwc_reg_write(global_regs, DWC_HPTXFSIZ, ptxfifosize);
+ }
+
+ /* Clear Host Set HNP Enable in the OTG Control Register */
+ gotgctl |= DWC_GCTL_HOST_HNP_ENA;
+ dwc_reg_modify(global_regs, DWC_GOTGCTL, gotgctl, 0);
+
+ /* Make sure the FIFOs are flushed. */
+ dwc_otg_flush_tx_fifo(core_if, DWC_GRSTCTL_TXFNUM_ALL);
+ dwc_otg_flush_rx_fifo(core_if);
+
+ /* Flush out any leftover queued requests. */
+ num_channels = core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ hc_regs = core_if->host_if->hc_regs[i];
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+ }
+
+ /* Halt all channels to put them into a known state. */
+ for (i = 0; i < num_channels; i++) {
+ int count = 0;
+
+ hc_regs = core_if->host_if->hc_regs[i];
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+
+ do {
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ if (++count > 200) {
+ pr_err("%s: Unable to clear halt on "
+ "channel %d\n", __func__, i);
+ break;
+ }
+ udelay(100);
+ } while (DWC_HCCHAR_ENA_RD(hcchar));
+ }
+
+ /* Turn on the vbus power. */
+ pr_info("Init: Port Power? op_state=%s\n",
+ otg_state_string(core_if->xceiv->state));
+
+ if (core_if->xceiv->state == OTG_STATE_A_HOST) {
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ pr_info("Init: Power Port (%d)\n", DWC_HPRT0_PRT_PWR_RD(hprt0));
+ if (DWC_HPRT0_PRT_PWR_RD(hprt0) == 0) {
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+ dwc_reg_write(host_if->hprt0, 0, hprt0);
+ }
+ }
+ dwc_otg_enable_host_interrupts(core_if);
+}
+
+/**
+ * Initializes dynamic portions of the DWC_otg HCD state.
+ */
+static void hcd_reinit(struct dwc_hcd *hcd)
+{
+ struct list_head *item;
+ int num_channels;
+ u32 i;
+ struct dwc_hc *channel;
+
+ hcd->flags.d32 = 0;
+ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+ hcd->available_host_channels = hcd->core_if->core_params->host_channels;
+
+ /*
+ * Put all channels in the free channel list and clean up channel
+ * states.
+ */
+ item = hcd->free_hc_list.next;
+ while (item != &hcd->free_hc_list) {
+ list_del(item);
+ item = hcd->free_hc_list.next;
+ }
+
+ num_channels = hcd->core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ channel = hcd->hc_ptr_array[i];
+ list_add_tail(&channel->hc_list_entry, &hcd->free_hc_list);
+ dwc_otg_hc_cleanup(hcd->core_if, channel);
+ }
+
+ /* Initialize the DWC core for host mode operation. */
+ dwc_otg_core_host_init(hcd->core_if);
+}
+
+/* Gets the dwc_hcd from a struct usb_hcd */
+static inline struct dwc_hcd *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
+{
+ return (struct dwc_hcd *)hcd->hcd_priv;
+}
+
+/**
+ * Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure.
+*/
+static int dwc_otg_hcd_start(struct usb_hcd *hcd)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ struct usb_bus *bus = hcd_to_bus(hcd);
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Inform the HUB driver to resume. */
+ if (bus->root_hub)
+ usb_hcd_resume_root_hub(hcd);
+
+ hcd_reinit(dwc_hcd);
+ return 0;
+}
+
+/**
+ * Work queue function for starting the HCD when A-Cable is connected.
+ * The dwc_otg_hcd_start() must be called in a process context.
+ */
+static void hcd_start_func(struct work_struct *work)
+{
+ struct dwc_hcd *priv = container_of(work, struct dwc_hcd, start_work);
+ struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p;
+
+ if (usb_hcd)
+ dwc_otg_hcd_start(usb_hcd);
+}
+
+/**
+ * HCD Callback function for starting the HCD when A-Cable is
+ * connected.
+ */
+static int dwc_otg_hcd_start_cb(void *_p)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(_p);
+ struct core_if *core_if = dwc_hcd->core_if;
+ u32 hprt0;
+
+ if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+ /*
+ * Reset the port. During a HNP mode switch the reset
+ * needs to occur within 1ms and have a duration of at
+ * least 50ms.
+ */
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ ((struct usb_hcd *)_p)->self.is_b_host = 1;
+ } else {
+ ((struct usb_hcd *)_p)->self.is_b_host = 0;
+ }
+
+ /* Need to start the HCD in a non-interrupt context. */
+ dwc_hcd->_p = _p;
+ schedule_work(&dwc_hcd->start_work);
+ return 1;
+}
+
+/**
+ * This function disables the Host Mode interrupts.
+ */
+static void dwc_otg_disable_host_interrupts(struct core_if *core_if)
+{
+ u32 global_regs = core_if->core_global_regs;
+ u32 intr_mask = 0;
+
+ /*
+ * Disable host mode interrupts without disturbing common
+ * interrupts.
+ */
+ intr_mask |= DWC_INTMSK_STRT_OF_FRM;
+ intr_mask |= DWC_INTMSK_HST_PORT;
+ intr_mask |= DWC_INTMSK_HST_CHAN;
+ intr_mask |= DWC_INTMSK_P_TXFIFO_EMPTY;
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, intr_mask, 0);
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+static void dwc_otg_hcd_stop(struct usb_hcd *hcd)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ u32 hprt0 = 0;
+
+ /* Turn off all host-specific interrupts. */
+ spin_lock(&dwc_hcd->lock);
+ dwc_otg_disable_host_interrupts(dwc_hcd->core_if);
+ spin_unlock(&dwc_hcd->lock);
+
+ /*
+ * The root hub should be disconnected before this function is called.
+ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+ * and the QH lists (via ..._hcd_endpoint_disable).
+ */
+
+ /* Turn off the vbus power */
+ pr_info("PortPower off\n");
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+ dwc_reg_write(dwc_hcd->core_if->host_if->hprt0, 0, hprt0);
+}
+
+/**
+ * HCD Callback function for stopping the HCD.
+ */
+static int dwc_otg_hcd_stop_cb(void *_p)
+{
+ struct usb_hcd *usb_hcd = (struct usb_hcd *)_p;
+
+ dwc_otg_hcd_stop(usb_hcd);
+ return 1;
+}
+
+static void del_timers(struct dwc_hcd *hcd)
+{
+ del_timer_sync(&hcd->conn_timer);
+}
+
+/**
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ETIMEDOUT and frees the QTD.
+ */
+static void kill_urbs_in_qh_list(struct dwc_hcd *hcd, struct list_head *qh_list)
+{
+ struct list_head *qh_item, *q;
+
+ qh_item = qh_list->next;
+ list_for_each_safe(qh_item, q, qh_list) {
+ struct dwc_qh *qh;
+ struct list_head *qtd_item;
+ struct dwc_qtd *qtd;
+
+ qh = list_entry(qh_item, struct dwc_qh, qh_list_entry);
+ qtd_item = qh->qtd_list.next;
+ qtd = list_entry(qtd_item, struct dwc_qtd, qtd_list_entry);
+ if (qtd->urb != NULL) {
+ spin_lock(&hcd->lock);
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -ETIMEDOUT);
+ dwc_otg_hcd_qtd_remove_and_free(qtd);
+ spin_unlock(&hcd->lock);
+ }
+ }
+}
+
+/**
+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ */
+static void kill_all_urbs(struct dwc_hcd *hcd)
+{
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_deferred);
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ */
+static int dwc_otg_hcd_disconnect_cb(void *_p)
+{
+ u32 intr;
+ struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
+ struct core_if *core_if = hcd->core_if;
+
+ /* Set status flags for the hub driver. */
+ hcd->flags.b.port_connect_status_change = 1;
+ hcd->flags.b.port_connect_status = 0;
+
+ /*
+ * Shutdown any transfers in process by clearing the Tx FIFO Empty
+ * interrupt mask and status bits and disabling subsequent host
+ * channel interrupts.
+ */
+ intr = 0;
+ intr |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ intr |= DWC_INTMSK_P_TXFIFO_EMPTY;
+ intr |= DWC_INTMSK_HST_CHAN;
+ spin_lock(&hcd->lock);
+ dwc_reg_modify(gintmsk_reg(hcd), 0, intr, 0);
+ dwc_reg_modify(gintsts_reg(hcd), 0, intr, 0);
+ spin_unlock(&hcd->lock);
+
+ del_timers(hcd);
+
+ /*
+ * Turn off the vbus power only if the core has transitioned to device
+ * mode. If still in host mode, need to keep power on to detect a
+ * reconnection.
+ */
+ if (dwc_otg_is_device_mode(core_if)) {
+ if (core_if->xceiv->state != OTG_STATE_A_SUSPEND) {
+ u32 hprt0 = 0;
+
+ pr_info("Disconnect: PortPower off\n");
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ }
+ dwc_otg_disable_host_interrupts(core_if);
+ }
+
+ /* Respond with an error status to all URBs in the schedule. */
+ kill_all_urbs(hcd);
+ if (dwc_otg_is_host_mode(core_if)) {
+ /* Clean up any host channels that were in use. */
+ int num_channels;
+ u32 i;
+ struct dwc_hc *channel;
+ ulong regs;
+ u32 hcchar;
+
+ num_channels = core_if->core_params->host_channels;
+ if (!core_if->dma_enable) {
+ /* Flush out any channel requests in slave mode. */
+ for (i = 0; i < num_channels; i++) {
+ channel = hcd->hc_ptr_array[i];
+ if (list_empty(&channel->hc_list_entry)) {
+ regs =
+ core_if->host_if->hc_regs[i];
+ hcchar = dwc_reg_read(regs, DWC_HCCHAR);
+
+ if (DWC_HCCHAR_ENA_RD(hcchar)) {
+ hcchar =
+ DWC_HCCHAR_ENA_RW(hcchar,
+ 0);
+ hcchar =
+ DWC_HCCHAR_DIS_RW(hcchar,
+ 1);
+ hcchar =
+ DWC_HCCHAR_EPDIR_RW(hcchar,
+ 0);
+ dwc_reg_write(regs, DWC_HCCHAR,
+ hcchar);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ channel = hcd->hc_ptr_array[i];
+ if (list_empty(&channel->hc_list_entry)) {
+ regs = core_if->host_if->hc_regs[i];
+ hcchar = dwc_reg_read(regs, DWC_HCCHAR);
+
+ if (DWC_HCCHAR_ENA_RD(hcchar)) {
+ /* Halt the channel. */
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+ dwc_reg_write(regs, DWC_HCCHAR, hcchar);
+ }
+ dwc_otg_hc_cleanup(core_if, channel);
+ list_add_tail(&channel->hc_list_entry,
+ &hcd->free_hc_list);
+ }
+ }
+ }
+
+ /*
+ * A disconnect will end the session so the B-Device is no
+ * longer a B-host.
+ */
+ ((struct usb_hcd *)_p)->self.is_b_host = 0;
+ return 1;
+}
+
+/**
+ * Connection timeout function. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.
+ */
+static void dwc_otg_hcd_connect_timeout(unsigned long _ptr)
+{
+ pr_info("Connect Timeout\n");
+ pr_err("Device Not Connected/Responding\n");
+}
+
+/**
+ * Start the connection timer. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds. The
+ * timer is deleted if a port connect interrupt occurs before the
+ * timer expires.
+ */
+static void dwc_otg_hcd_start_connect_timer(struct dwc_hcd *hcd)
+{
+ init_timer(&hcd->conn_timer);
+ hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;
+ hcd->conn_timer.data = (unsigned long)0;
+ hcd->conn_timer.expires = jiffies + (HZ * 10);
+ add_timer(&hcd->conn_timer);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ */
+static int dwc_otg_hcd_session_start_cb(void *_p)
+{
+ struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
+
+ dwc_otg_hcd_start_connect_timer(hcd);
+ return 1;
+}
+
+/* HCD Callback structure for handling mode switching. */
+static struct cil_callbacks hcd_cil_callbacks = {
+ .start = dwc_otg_hcd_start_cb,
+ .stop = dwc_otg_hcd_stop_cb,
+ .disconnect = dwc_otg_hcd_disconnect_cb,
+ .session_start = dwc_otg_hcd_session_start_cb,
+ .p = NULL,
+};
+
+/*
+ * Reset Workqueue implementation
+ */
+static void port_reset_wqfunc(struct work_struct *work)
+{
+ struct dwc_hcd *hcd = container_of(work, struct dwc_hcd,
+ usb_port_reset);
+ struct core_if *core_if = hcd->core_if;
+ u32 hprt0 = 0;
+ unsigned long flags;
+
+ pr_info("%s\n", __func__);
+ spin_lock_irqsave(&hcd->lock, flags);
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ spin_unlock_irqrestore(&hcd->lock, flags);
+ msleep(60);
+ spin_lock_irqsave(&hcd->lock, flags);
+ hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ hcd->flags.b.port_reset_change = 1;
+ spin_unlock_irqrestore(&hcd->lock, flags);
+}
+
+/*
+ * Wakeup Workqueue implementation
+ */
+static void port_wakeup_wqfunc(struct work_struct *work)
+{
+ struct core_if *core_if = container_of(to_delayed_work(work),
+ struct core_if, usb_port_wakeup);
+ u32 hprt0;
+
+ pr_info("%s\n", __func__);
+ /* Now wait for 70 ms. */
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ msleep(70);
+ hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+}
+
+/**
+ * Starts processing a USB transfer request specified by a USB Request Block
+ * (URB). mem_flags indicates the type of memory allocation to use while
+ * processing this URB.
+ */
+static int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t _mem_flags)
+{
+ int retval;
+ unsigned long flags;
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ struct dwc_qtd *qtd;
+
+ if (!dwc_hcd->flags.b.port_connect_status) {
+ /* No longer connected. */
+ retval = -ENODEV;
+ goto err_enq;
+ }
+
+ qtd = dwc_otg_hcd_qtd_create(urb, _mem_flags);
+ if (!qtd) {
+ pr_err("DWC OTG HCD URB Enqueue failed creating " "QTD\n");
+ retval = -ENOMEM;
+ goto err_enq;
+ }
+
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (unlikely(retval))
+ goto fail;
+
+ retval = dwc_otg_hcd_qtd_add(qtd, dwc_hcd);
+ if (retval < 0) {
+ pr_err("DWC OTG HCD URB Enqueue failed adding QTD. "
+ "Error status %d\n", retval);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ goto fail;
+ }
+
+fail:
+ if (retval)
+ dwc_otg_hcd_qtd_free(qtd);
+
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+err_enq:
+
+ return retval;
+}
+
+/**
+ * Attempts to halt a host channel. This function should only be called in
+ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under
+ * normal circumstances in DMA mode, the controller halts the channel when the
+ * transfer is complete or a condition occurs that requires application
+ * intervention.
+ *
+ * In slave mode, checks for a free request queue entry, then sets the Channel
+ * Enable and Channel Disable bits of the Host Channel Characteristics
+ * register of the specified channel to intiate the halt. If there is no free
+ * request queue entry, sets only the Channel Disable bit of the HCCHARn
+ * register to flush requests for this channel. In the latter case, sets a
+ * flag to indicate that the host channel needs to be halted when a request
+ * queue slot is open.
+ *
+ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
+ * HCCHARn register. The controller ensures there is space in the request
+ * queue before submitting the halt request.
+ *
+ * Some time may elapse before the core flushes any posted requests for this
+ * host channel and halts. The Channel Halted interrupt handler completes the
+ * deactivation of the host channel.
+ */
+void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
+ enum dwc_halt_status hlt_sts)
+{
+ u32 nptxsts;
+ u32 hptxsts = 0;
+ u32 hcchar;
+ ulong hc_regs;
+ ulong global_regs = core_if->core_global_regs;
+ ulong host_global_regs;
+
+ hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ host_global_regs = core_if->host_if->host_global_regs;
+
+ WARN_ON(hlt_sts == DWC_OTG_HC_XFER_NO_HALT_STATUS);
+
+ if (hlt_sts == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+ hlt_sts == DWC_OTG_HC_XFER_AHB_ERR) {
+ /*
+ * Disable all channel interrupts except Ch Halted. The QTD
+ * and QH state associated with this transfer has been cleared
+ * (in the case of URB_DEQUEUE), so the channel needs to be
+ * shut down carefully to prevent crashes.
+ */
+ u32 hcintmsk;
+ hcintmsk = 0;
+ hcintmsk = DWC_HCINTMSK_CHAN_HALTED_RW(hcintmsk, 1);
+ dwc_reg_write(hc_regs, DWC_HCINTMSK, hcintmsk);
+
+ /*
+ * Make sure no other interrupts besides halt are currently
+ * pending. Handling another interrupt could cause a crash due
+ * to the QTD and QH state.
+ */
+ dwc_reg_write(hc_regs, DWC_HCINT, ~hcintmsk);
+
+ /*
+ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
+ * even if the channel was already halted for some other reason.
+ */
+ hc->halt_status = hlt_sts;
+
+ /*
+ * If the channel is not enabled, the channel is either already
+ * halted or it hasn't started yet. In DMA mode, the transfer
+ * may halt if it finishes normally or a condition occurs that
+ * requires driver intervention. Don't want to halt the channel
+ * again. In either Slave or DMA mode, it's possible that the
+ * transfer has been assigned to a channel, but not started yet
+ * when an URB is dequeued. Don't want to halt a channel that
+ * hasn't started yet.
+ */
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ if (!DWC_HCCHAR_ENA_RD(hcchar))
+ return;
+ }
+
+ if (hc->halt_pending)
+ /*
+ * A halt has already been issued for this channel. This might
+ * happen when a transfer is aborted by a higher level in
+ * the stack.
+ */
+ return;
+
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+ if (!core_if->dma_enable) {
+ /* Check for space in the request queue to issue the halt. */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+ hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+ nptxsts = dwc_reg_read(global_regs, DWC_GNPTXSTS);
+
+ if (!DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(nptxsts))
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+ } else {
+ hptxsts =
+ dwc_reg_read(host_global_regs, DWC_HPTXSTS);
+
+ if (!DWC_HPTXSTS_PTXSPC_AVAIL_RD(hptxsts) ||
+ core_if->queuing_high_bandwidth)
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+ }
+ }
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+
+ hc->halt_status = hlt_sts;
+ if (DWC_HCCHAR_ENA_RD(hcchar)) {
+ hc->halt_pending = 1;
+ hc->halt_on_queue = 0;
+ } else {
+ hc->halt_on_queue = 1;
+ }
+}
+
+/**
+ * Aborts/cancels a USB transfer request. Always returns 0 to indicate
+ * success.
+ */
+static int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ unsigned long flags;
+ struct dwc_hcd *dwc_hcd;
+ struct dwc_qtd *urb_qtd;
+ struct dwc_qh *qh;
+ int retval;
+
+ urb_qtd = (struct dwc_qtd *)urb->hcpriv;
+ if (!urb_qtd)
+ return -EINVAL;
+ qh = (struct dwc_qh *)urb_qtd->qtd_qh_ptr;
+ if (!qh)
+ return -EINVAL;
+
+ dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval) {
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ return retval;
+ }
+
+ if (urb_qtd == qh->qtd_in_process) {
+ /* The QTD is in process (it has been assigned to a channel). */
+ if (dwc_hcd->flags.b.port_connect_status) {
+ /*
+ * If still connected (i.e. in host mode), halt the
+ * channel so it can be used for other transfers. If
+ * no longer connected, the host registers can't be
+ * written to halt the channel since the core is in
+ * device mode.
+ */
+ dwc_otg_hc_halt(dwc_hcd->core_if, qh->channel,
+ DWC_OTG_HC_XFER_URB_DEQUEUE);
+ }
+ }
+
+ /*
+ * Free the QTD and clean up the associated QH. Leave the QH in the
+ * schedule if it has any remaining QTDs.
+ */
+ dwc_otg_hcd_qtd_remove_and_free(urb_qtd);
+ if (qh && urb_qtd == qh->qtd_in_process) {
+ dwc_otg_hcd_qh_deactivate(dwc_hcd, qh, 0);
+ qh->channel = NULL;
+ qh->qtd_in_process = NULL;
+ } else if (qh && list_empty(&qh->qtd_list)) {
+ dwc_otg_hcd_qh_remove(dwc_hcd, qh);
+ }
+
+ urb->hcpriv = NULL;
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+
+ /* Higher layer software sets URB status. */
+ usb_hcd_giveback_urb(hcd, urb, status);
+
+ return 0;
+}
+
+/* Remove and free a QH */
+static inline void dwc_otg_hcd_qh_remove_and_free(struct dwc_hcd *hcd,
+ struct dwc_qh *qh)
+{
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ dwc_otg_hcd_qh_free(qh);
+}
+
+static void qh_list_free(struct dwc_hcd *hcd, struct list_head *_qh_list)
+{
+ struct list_head *item, *tmp;
+ struct dwc_qh *qh;
+
+ /* If the list hasn't been initialized yet, return. */
+ if (_qh_list->next == NULL)
+ return;
+
+ /* Ensure there are no QTDs or URBs left. */
+ kill_urbs_in_qh_list(hcd, _qh_list);
+
+ list_for_each_safe(item, tmp, _qh_list) {
+ qh = list_entry(item, struct dwc_qh, qh_list_entry);
+ dwc_otg_hcd_qh_remove_and_free(hcd, qh);
+ }
+}
+
+/**
+ * Frees resources in the DWC_otg controller related to a given endpoint. Also
+ * clears state in the HCD related to the endpoint. Any URBs for the endpoint
+ * must already be dequeued.
+ */
+static void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct dwc_qh *qh;
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ qh = (struct dwc_qh *)ep->hcpriv;
+ if (qh) {
+ dwc_otg_hcd_qh_remove_and_free(dwc_hcd, qh);
+ ep->hcpriv = NULL;
+ }
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+}
+
+/**
+ * Creates Status Change bitmap for the root hub and root port. The bitmap is
+ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
+ * is the status change indicator for the single root port. Returns 1 if either
+ * change indicator is 1, otherwise returns 0.
+ */
+static int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *buf)
+{
+ struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_hcd);
+
+ buf[0] = 0;
+ buf[0] |= (hcd->flags.b.port_connect_status_change
+ || hcd->flags.b.port_reset_change
+ || hcd->flags.b.port_enable_change
+ || hcd->flags.b.port_suspend_change
+ || hcd->flags.b.port_over_current_change) << 1;
+
+ return (buf[0] != 0);
+}
+
+/* Handles the hub class-specific ClearPortFeature request.*/
+static int do_clear_port_feature(struct dwc_hcd *hcd, u16 val)
+{
+ struct core_if *core_if = hcd->core_if;
+ u32 hprt0 = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hcd->lock, flags);
+ switch (val) {
+ case USB_PORT_FEAT_ENABLE:
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+
+ /* Clear Resume bit */
+ spin_unlock_irqrestore(&hcd->lock, flags);
+ msleep(100);
+ spin_lock_irqsave(&hcd->lock, flags);
+ hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ break;
+ case USB_PORT_FEAT_POWER:
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ break;
+ case USB_PORT_FEAT_INDICATOR:
+ /* Port inidicator not supported */
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ /* Clears drivers internal connect status change flag */
+ hcd->flags.b.port_connect_status_change = 0;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* Clears driver's internal Port Reset Change flag */
+ hcd->flags.b.port_reset_change = 0;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ /* Clears driver's internal Port Enable/Disable Change flag */
+ hcd->flags.b.port_enable_change = 0;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ /*
+ * Clears the driver's internal Port Suspend
+ * Change flag, which is set when resume signaling on
+ * the host port is complete
+ */
+ hcd->flags.b.port_suspend_change = 0;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ hcd->flags.b.port_over_current_change = 0;
+ break;
+ default:
+ pr_err("DWC OTG HCD - ClearPortFeature request %xh "
+ "unknown or unsupported\n", val);
+ spin_unlock_irqrestore(&hcd->lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&hcd->lock, flags);
+ return 0;
+}
+
+/* Handles the hub class-specific SetPortFeature request.*/
+static int do_set_port_feature(struct usb_hcd *hcd, u16 val, u16 index)
+{
+ struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
+ u32 hprt0 = 0;
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ unsigned long flags;
+ u32 pcgcctl = 0;
+
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+
+ switch (val) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (hcd->self.otg_port == index && hcd->self.b_hnp_enable) {
+ u32 gotgctl = 0;
+ gotgctl |= DWC_GCTL_HOST_HNP_ENA;
+ dwc_reg_modify(core_if->core_global_regs,
+ DWC_GOTGCTL, 0, gotgctl);
+ core_if->xceiv->state = OTG_STATE_A_SUSPEND;
+ }
+
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_SUS_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+
+ /* Suspend the Phy Clock */
+ pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
+ dwc_reg_write(core_if->pcgcctl, 0, pcgcctl);
+
+ /* For HNP the bus must be suspended for at least 200ms. */
+ if (hcd->self.b_hnp_enable) {
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ msleep(200);
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ }
+ break;
+ case USB_PORT_FEAT_POWER:
+ hprt0 = dwc_otg_read_hprt0(core_if);
+ hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ break;
+ case USB_PORT_FEAT_RESET:
+ hprt0 = dwc_otg_read_hprt0(core_if);
+
+ /*
+ * When B-Host the Port reset bit is set in the Start HCD
+ * Callback function, so that the reset is started within 1ms
+ * of the HNP success interrupt.
+ */
+ if (!hcd->self.is_b_host) {
+ hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ }
+
+ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ msleep(60);
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
+ dwc_reg_write(core_if->host_if->hprt0, 0, hprt0);
+ break;
+ case USB_PORT_FEAT_INDICATOR:
+ /* Not supported */
+ break;
+ default:
+ pr_err("DWC OTG HCD - "
+ "SetPortFeature request %xh "
+ "unknown or unsupported\n", val);
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ return 0;
+}
+
+/* Handles hub class-specific requests.*/
+static int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, u16 req_type, u16 val,
+ u16 index, char *buf, u16 len)
+{
+ int retval = 0;
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
+ struct usb_hub_descriptor *desc;
+ u32 hprt0 = 0;
+ u32 port_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ switch (req_type) {
+ case ClearHubFeature:
+ switch (val) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* Nothing required here */
+ break;
+ default:
+ retval = -EINVAL;
+ pr_err("DWC OTG HCD - ClearHubFeature request"
+ " %xh unknown\n", val);
+ }
+ break;
+ case ClearPortFeature:
+ if (!index || index > 1)
+ goto error;
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ retval = do_clear_port_feature(dwc_hcd, val);
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ break;
+ case GetHubDescriptor:
+ desc = (struct usb_hub_descriptor *)buf;
+ desc->bDescLength = 9;
+ desc->bDescriptorType = 0x29;
+ desc->bNbrPorts = 1;
+ desc->wHubCharacteristics = 0x08;
+ desc->bPwrOn2PwrGood = 1;
+ desc->bHubContrCurrent = 0;
+ break;
+ case GetHubStatus:
+ memset(buf, 0, 4);
+ break;
+ case GetPortStatus:
+ if (!index || index > 1)
+ goto error;
+
+ port_status = 0;
+ if (dwc_hcd->flags.b.port_connect_status_change)
+ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+ if (dwc_hcd->flags.b.port_enable_change)
+ port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
+ if (dwc_hcd->flags.b.port_suspend_change)
+ port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+ if (dwc_hcd->flags.b.port_reset_change)
+ port_status |= (1 << USB_PORT_FEAT_C_RESET);
+ if (dwc_hcd->flags.b.port_over_current_change) {
+ pr_err("Device Not Supported\n");
+ port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT);
+ }
+ if (!dwc_hcd->flags.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return 0's for the remainder of the port status
+ * since the port register can't be read if the core
+ * is in device mode.
+ */
+ *((__le32 *) buf) = cpu_to_le32(port_status);
+ break;
+ }
+
+ hprt0 = dwc_reg_read(core_if->host_if->hprt0, 0);
+
+ if (DWC_HPRT0_PRT_STS_RD(hprt0))
+ port_status |= USB_PORT_STAT_CONNECTION;
+ if (DWC_HPRT0_PRT_ENA_RD(hprt0))
+ port_status |= USB_PORT_STAT_ENABLE;
+ if (DWC_HPRT0_PRT_SUS_RD(hprt0))
+ port_status |= USB_PORT_STAT_SUSPEND;
+ if (DWC_HPRT0_PRT_OVRCURR_ACT_RD(hprt0))
+ port_status |= USB_PORT_STAT_OVERCURRENT;
+ if (DWC_HPRT0_PRT_RST_RD(hprt0))
+ port_status |= USB_PORT_STAT_RESET;
+ if (DWC_HPRT0_PRT_PWR_RD(hprt0))
+ port_status |= USB_PORT_STAT_POWER;
+
+ if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_HIGH_SPEED)
+ port_status |= USB_PORT_STAT_HIGH_SPEED;
+ else if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
+ DWC_HPRT0_PRTSPD_LOW_SPEED)
+ port_status |= USB_PORT_STAT_LOW_SPEED;
+
+ if (DWC_HPRT0_PRT_TST_CTL_RD(hprt0))
+ port_status |= (1 << USB_PORT_FEAT_TEST);
+
+ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+ *((__le32 *) buf) = cpu_to_le32(port_status);
+ break;
+ case SetHubFeature:
+ /* No HUB features supported */
+ break;
+ case SetPortFeature:
+ if (val != USB_PORT_FEAT_TEST && (!index || index > 1))
+ goto error;
+
+ if (!dwc_hcd->flags.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return without doing anything since the port
+ * register can't be written if the core is in device
+ * mode.
+ */
+ break;
+ }
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ retval = do_set_port_feature(hcd, val, index);
+ spin_lock_irqsave(&dwc_hcd->lock, flags);
+ break;
+ default:
+error:
+ retval = -EINVAL;
+ pr_warning("DWC OTG HCD - Unknown hub control request"
+ " type or invalid req_type: %xh index: %xh "
+ "val: %xh\n", req_type, index, val);
+ break;
+ }
+ spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+ return retval;
+}
+
+/**
+ * Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
+ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
+ * interrupt.
+ *
+ * This function is called by the USB core when an interrupt occurs
+ */
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+ return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_hcd));
+}
+
+static const struct hc_driver dwc_otg_hc_driver = {
+ .description = dwc_otg_hcd_name,
+ .product_desc = "DWC OTG Controller",
+ .hcd_priv_size = sizeof(struct dwc_hcd),
+ .irq = dwc_otg_hcd_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+ .start = dwc_otg_hcd_start,
+ .stop = dwc_otg_hcd_stop,
+ .urb_enqueue = dwc_otg_hcd_urb_enqueue,
+ .urb_dequeue = dwc_otg_hcd_urb_dequeue,
+ .endpoint_disable = dwc_otg_hcd_endpoint_disable,
+ .get_frame_number = dwc_otg_hcd_get_frame_number,
+ .hub_status_data = dwc_otg_hcd_hub_status_data,
+ .hub_control = dwc_otg_hcd_hub_control,
+};
+
+/**
+ * Frees secondary storage associated with the dwc_hcd structure contained
+ * in the struct usb_hcd field.
+ */
+static void dwc_otg_hcd_free(struct usb_hcd *hcd)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ u32 i;
+
+ del_timers(dwc_hcd);
+
+ /* Free memory for QH/QTD lists */
+ qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_inactive);
+ qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_deferred);
+ qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_active);
+ qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_inactive);
+ qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_ready);
+ qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_assigned);
+ qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_queued);
+
+ /* Free memory for the host channels. */
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ struct dwc_hc *hc = dwc_hcd->hc_ptr_array[i];
+
+ kfree(hc);
+ }
+ if (dwc_hcd->core_if->dma_enable) {
+ if (dwc_hcd->status_buf_dma)
+ dma_free_coherent(hcd->self.controller,
+ DWC_OTG_HCD_STATUS_BUF_SIZE,
+ dwc_hcd->status_buf,
+ dwc_hcd->status_buf_dma);
+ } else {
+ kfree(dwc_hcd->status_buf);
+ }
+
+}
+
+/**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_hcd structures. It also registers the
+ * USB bus with the core and calls the hc_driver->start() function. It returns
+ * a negative error on failure.
+ */
+int dwc_otg_hcd_init(struct device *_dev,
+ struct dwc_otg_device *dwc_otg_device)
+{
+ struct usb_hcd *hcd;
+ struct dwc_hcd *dwc_hcd;
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
+ int num_channels;
+ u32 i;
+ struct dwc_hc *channel;
+ int retval = 0;
+
+ /*
+ * Allocate memory for the base HCD plus the DWC OTG HCD.
+ * Initialize the base HCD.
+ */
+ hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dwc_otg_hcd_name);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto error1;
+ }
+ dev_set_drvdata(_dev, dwc_otg_device);
+ hcd->regs = otg_dev->base;
+ hcd->rsrc_start = otg_dev->phys_addr;
+ hcd->rsrc_len = otg_dev->base_len;
+ hcd->self.otg_port = 1;
+
+ /* Initialize the DWC OTG HCD. */
+ dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ dwc_hcd->core_if = otg_dev->core_if;
+ spin_lock_init(&dwc_hcd->lock);
+ otg_dev->hcd = dwc_hcd;
+
+ /* Register the HCD CIL Callbacks */
+ dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, &hcd_cil_callbacks,
+ hcd);
+
+ /* Initialize the non-periodic schedule. */
+ INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_inactive);
+ INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_active);
+ INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_deferred);
+
+ /* Initialize the periodic schedule. */
+ INIT_LIST_HEAD(&dwc_hcd->periodic_sched_inactive);
+ INIT_LIST_HEAD(&dwc_hcd->periodic_sched_ready);
+ INIT_LIST_HEAD(&dwc_hcd->periodic_sched_assigned);
+ INIT_LIST_HEAD(&dwc_hcd->periodic_sched_queued);
+
+ /*
+ * Create a host channel descriptor for each host channel implemented
+ * in the controller. Initialize the channel descriptor array.
+ */
+ INIT_LIST_HEAD(&dwc_hcd->free_hc_list);
+ num_channels = dwc_hcd->core_if->core_params->host_channels;
+
+ for (i = 0; i < num_channels; i++) {
+ channel = kzalloc(sizeof(struct dwc_hc), GFP_KERNEL);
+ if (!channel) {
+ retval = -ENOMEM;
+ pr_err("%s: host channel allocation failed\n",
+ __func__);
+ goto error2;
+ }
+
+ channel->hc_num = i;
+ dwc_hcd->hc_ptr_array[i] = channel;
+ }
+
+ /* Initialize the Connection timeout timer. */
+ init_timer(&dwc_hcd->conn_timer);
+
+ /* Initialize workqueue */
+ INIT_WORK(&dwc_hcd->usb_port_reset, port_reset_wqfunc);
+ INIT_WORK(&dwc_hcd->start_work, hcd_start_func);
+ INIT_WORK(&dwc_hcd->core_if->usb_port_otg, NULL);
+ INIT_DELAYED_WORK(&dwc_hcd->core_if->usb_port_wakeup,
+ port_wakeup_wqfunc);
+
+ /* Set device flags indicating whether the HCD supports DMA. */
+ if (otg_dev->core_if->dma_enable) {
+ static u64 dummy_mask = DMA_BIT_MASK(32);
+
+ pr_info("Using DMA mode\n");
+ _dev->dma_mask = (void *)&dummy_mask;
+ _dev->coherent_dma_mask = ~0;
+ } else {
+ pr_info("Using Slave mode\n");
+ _dev->dma_mask = (void *)0;
+ _dev->coherent_dma_mask = 0;
+ }
+
+ init_hcd_usecs(dwc_hcd);
+ /*
+ * Finish generic HCD initialization and start the HCD. This function
+ * allocates the DMA buffer pool, registers the USB bus, requests the
+ * IRQ line, and calls dwc_otg_hcd_start method.
+ */
+ printk("before hcd\n");msleep(100);
+ retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED);
+ printk("after hcd\n");msleep(100);
+ if (retval < 0)
+ goto error2;
+ hcd->rsrc_start = otg_dev->phys_addr;
+ hcd->rsrc_len = otg_dev->base_len;
+
+ /*
+ * Allocate space for storing data on status transactions. Normally no
+ * data is sent, but this space acts as a bit bucket. This must be
+ * done after usb_add_hcd since that function allocates the DMA buffer
+ * pool.
+ */
+ if (otg_dev->core_if->dma_enable) {
+ dwc_hcd->status_buf =
+ dma_alloc_coherent(_dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
+ &dwc_hcd->status_buf_dma,
+ GFP_KERNEL | GFP_DMA);
+ } else {
+ dwc_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE,
+ GFP_KERNEL);
+ }
+ if (!dwc_hcd->status_buf) {
+ retval = -ENOMEM;
+ pr_err("%s: status_buf allocation failed\n", __func__);
+ goto error3;
+ }
+ return 0;
+
+error3:
+ usb_remove_hcd(hcd);
+error2:
+ dwc_otg_hcd_free(hcd);
+ usb_put_hcd(hcd);
+error1:
+ return retval;
+}
+
+/**
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void __devexit dwc_otg_hcd_remove(struct device *_dev)
+{
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
+ struct dwc_hcd *dwc_hcd = otg_dev->hcd;
+ struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_hcd);
+
+ /* Turn off all interrupts */
+ dwc_reg_write(gintmsk_reg(dwc_hcd), 0, 0);
+ spin_lock(&dwc_hcd->lock);
+ dwc_reg_modify(gahbcfg_reg(dwc_hcd), 0, 1, 0);
+ spin_unlock(&dwc_hcd->lock);
+
+ cancel_work_sync(&dwc_hcd->start_work);
+ cancel_work_sync(&dwc_hcd->usb_port_reset);
+ cancel_work_sync(&dwc_hcd->core_if->usb_port_otg);
+
+ usb_remove_hcd(hcd);
+ dwc_otg_hcd_free(hcd);
+ usb_put_hcd(hcd);
+}
+
+/** Returns the current frame number. */
+int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+ struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+ u32 hfnum = 0;
+
+ hfnum = dwc_reg_read(dwc_hcd->core_if->host_if->
+ host_global_regs, DWC_HFNUM);
+
+ return DWC_HFNUM_FRNUM_RD(hfnum);
+}
+
+/**
+ * Prepares a host channel for transferring packets to/from a specific
+ * endpoint. The HCCHARn register is set up with the characteristics specified
+ * in _hc. Host channel interrupts that may need to be serviced while this
+ * transfer is in progress are enabled.
+ */
+static void dwc_otg_hc_init(struct core_if *core_if, struct dwc_hc *hc)
+{
+ u32 intr_enable;
+ ulong global_regs = core_if->core_global_regs;
+ u32 hc_intr_mask = 0;
+ u32 gintmsk = 0;
+ u32 hcchar;
+ u32 hcsplt;
+ u8 hc_num = hc->hc_num;
+ struct dwc_host_if *host_if = core_if->host_if;
+ ulong hc_regs = host_if->hc_regs[hc_num];
+
+ /* Clear old interrupt conditions for this host channel. */
+ hc_intr_mask = 0x3FF;
+ dwc_reg_write(hc_regs, DWC_HCINT, hc_intr_mask);
+
+ /* Enable channel interrupts required for this transfer. */
+ hc_intr_mask = 0;
+ hc_intr_mask = DWC_HCINTMSK_CHAN_HALTED_RW(hc_intr_mask, 1);
+ if (core_if->dma_enable) {
+ hc_intr_mask = DWC_HCINTMSK_AHB_ERR_RW(hc_intr_mask, 1);
+
+ if (hc->error_state && !hc->do_split &&
+ hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
+ if (hc->ep_is_in) {
+ hc_intr_mask =
+ DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask,
+ 1);
+ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR)
+ hc_intr_mask =
+ DWC_HCINTMSK_NAK_RESP_REC_RW
+ (hc_intr_mask, 1);
+ }
+ }
+ } else {
+ switch (hc->ep_type) {
+ case DWC_OTG_EP_TYPE_CONTROL:
+ case DWC_OTG_EP_TYPE_BULK:
+ hc_intr_mask =
+ DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
+
+ if (hc->ep_is_in) {
+ hc_intr_mask =
+ DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+ } else {
+ hc_intr_mask =
+ DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
+ 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_NYET_RESP_REC_RW(hc_intr_mask,
+ 1);
+ if (hc->do_ping)
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW
+ (hc_intr_mask, 1);
+ }
+
+ if (hc->do_split) {
+ hc_intr_mask =
+ DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
+ 1);
+ if (hc->complete_split)
+ hc_intr_mask =
+ DWC_HCINTMSK_NYET_RESP_REC_RW
+ (hc_intr_mask, 1);
+ else
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW
+ (hc_intr_mask, 1);
+ }
+
+ if (hc->error_state)
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
+ 1);
+ break;
+ case DWC_OTG_EP_TYPE_INTR:
+ hc_intr_mask =
+ DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask, 1);
+
+ if (hc->ep_is_in)
+ hc_intr_mask =
+ DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+ if (hc->error_state)
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
+ 1);
+
+ if (hc->do_split) {
+ if (hc->complete_split)
+ hc_intr_mask =
+ DWC_HCINTMSK_NYET_RESP_REC_RW
+ (hc_intr_mask, 1);
+ else
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW
+ (hc_intr_mask, 1);
+ }
+ break;
+ case DWC_OTG_EP_TYPE_ISOC:
+ hc_intr_mask =
+ DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
+
+ if (hc->ep_is_in) {
+ hc_intr_mask =
+ DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+ hc_intr_mask =
+ DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+ }
+ break;
+ }
+ }
+ dwc_reg_write(hc_regs, DWC_HCINTMSK, hc_intr_mask);
+
+ /* Enable the top level host channel interrupt. */
+ intr_enable = (1 << hc_num);
+ dwc_reg_modify(host_if->host_global_regs, DWC_HAINTMSK, 0,
+ intr_enable);
+
+ /* Make sure host channel interrupts are enabled. */
+ gintmsk |= DWC_INTMSK_HST_CHAN;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, 0, gintmsk);
+
+ /*
+ * Program the HCCHARn register with the endpoint characteristics for
+ * the current transfer.
+ */
+ hcchar = 0;
+ hcchar = DWC_HCCHAR_DEV_ADDR_RW(hcchar, hc->dev_addr);
+ hcchar = DWC_HCCHAR_EP_NUM_RW(hcchar, hc->ep_num);
+ hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, hc->ep_is_in);
+ hcchar = DWC_HCCHAR_LSP_DEV_RW(hcchar, (hc->speed ==
+ DWC_OTG_EP_SPEED_LOW));
+ hcchar = DWC_HCCHAR_EPTYPE_RW(hcchar, hc->ep_type);
+ hcchar = DWC_HCCHAR_MPS_RW(hcchar, hc->max_packet);
+ dwc_reg_write(host_if->hc_regs[hc_num], DWC_HCCHAR, hcchar);
+
+ /* Program the HCSPLIT register for SPLITs */
+ hcsplt = 0;
+ if (hc->do_split) {
+ hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, hc->complete_split);
+ hcsplt = DWC_HCSPLT_TRANS_POS_RW(hcsplt, hc->xact_pos);
+ hcsplt = DWC_HCSPLT_HUB_ADDR_RW(hcsplt, hc->hub_addr);
+ hcsplt = DWC_HCSPLT_PRT_ADDR_RW(hcsplt, hc->port_addr);
+ }
+ dwc_reg_write(host_if->hc_regs[hc_num], DWC_HCSPLT, hcsplt);
+}
+
+/**
+ * Assigns transactions from a QTD to a free host channel and initializes the
+ * host channel to perform the transactions. The host channel is removed from
+ * the free list.
+ */
+static void assign_and_init_hc(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ struct dwc_hc *hc;
+ struct dwc_qtd *qtd;
+ struct urb *urb;
+ struct usb_iso_packet_descriptor *frame_desc;
+
+ hc = list_entry(hcd->free_hc_list.next, struct dwc_hc, hc_list_entry);
+
+ /* Remove the host channel from the free list. */
+ list_del_init(&hc->hc_list_entry);
+ qtd = list_entry(qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+ urb = qtd->urb;
+ qh->channel = hc;
+ qh->qtd_in_process = qtd;
+
+ /*
+ * Use usb_pipedevice to determine device address. This address is
+ * 0 before the SET_ADDRESS command and the correct address afterward.
+ */
+ hc->dev_addr = usb_pipedevice(urb->pipe);
+ hc->ep_num = usb_pipeendpoint(urb->pipe);
+
+ if (urb->dev->speed == USB_SPEED_LOW)
+ hc->speed = DWC_OTG_EP_SPEED_LOW;
+ else if (urb->dev->speed == USB_SPEED_FULL)
+ hc->speed = DWC_OTG_EP_SPEED_FULL;
+ else
+ hc->speed = DWC_OTG_EP_SPEED_HIGH;
+
+ hc->max_packet = dwc_max_packet(qh->maxp);
+ hc->xfer_started = 0;
+ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
+ hc->error_state = (qtd->error_count > 0);
+ hc->halt_on_queue = 0;
+ hc->halt_pending = 0;
+ hc->requests = 0;
+
+ /*
+ * The following values may be modified in the transfer type section
+ * below. The xfer_len value may be reduced when the transfer is
+ * started to accommodate the max widths of the XferSize and PktCnt
+ * fields in the HCTSIZn register.
+ */
+ hc->do_ping = qh->ping_state;
+ hc->ep_is_in = (usb_pipein(urb->pipe) != 0);
+ hc->data_pid_start = qh->data_toggle;
+ hc->multi_count = 1;
+
+ if (hcd->core_if->dma_enable)
+ hc->xfer_buff = urb->transfer_dma + (u8 *) urb->actual_length;
+ else
+ hc->xfer_buff = (u8 *) urb->transfer_buffer +
+ urb->actual_length;
+
+ hc->xfer_len = urb->transfer_buffer_length - urb->actual_length;
+ hc->xfer_count = 0;
+
+ /*
+ * Set the split attributes
+ */
+ hc->do_split = 0;
+ if (qh->do_split) {
+ hc->do_split = 1;
+ hc->xact_pos = qtd->isoc_split_pos;
+ hc->complete_split = qtd->complete_split;
+ hc->hub_addr = urb->dev->tt->hub->devnum;
+ hc->port_addr = urb->dev->ttport;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
+
+ switch (qtd->control_phase) {
+ case DWC_OTG_CONTROL_SETUP:
+ hc->do_ping = 0;
+ hc->ep_is_in = 0;
+ hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
+
+ if (hcd->core_if->dma_enable)
+ hc->xfer_buff = (u8 *) (u32) urb->setup_dma;
+ else
+ hc->xfer_buff = (u8 *) urb->setup_packet;
+
+ hc->xfer_len = 8;
+ break;
+ case DWC_OTG_CONTROL_DATA:
+ hc->data_pid_start = qtd->data_toggle;
+ break;
+ case DWC_OTG_CONTROL_STATUS:
+ /*
+ * Direction is opposite of data direction or IN if no
+ * data.
+ */
+ if (urb->transfer_buffer_length == 0)
+ hc->ep_is_in = 1;
+ else
+ hc->ep_is_in = (usb_pipein(urb->pipe) !=
+ USB_DIR_IN);
+
+ if (hc->ep_is_in)
+ hc->do_ping = 0;
+
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+ hc->xfer_len = 0;
+ if (hcd->core_if->dma_enable)
+ hc->xfer_buff =
+ (u8 *) (u32) hcd->status_buf_dma;
+ else
+ hc->xfer_buff = (u8 *) hcd->status_buf;
+ break;
+ }
+ break;
+ case PIPE_BULK:
+ hc->ep_type = DWC_OTG_EP_TYPE_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ hc->ep_type = DWC_OTG_EP_TYPE_INTR;
+ break;
+ case PIPE_ISOCHRONOUS:
+ frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
+ hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
+
+ if (hcd->core_if->dma_enable)
+ hc->xfer_buff = (u8 *) (u32) urb->transfer_dma;
+ else
+ hc->xfer_buff = (u8 *) urb->transfer_buffer;
+
+ hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset;
+ hc->xfer_len = frame_desc->length - qtd->isoc_split_offset;
+
+ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+ if (hc->xfer_len <= 188)
+ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ else
+ hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN;
+ }
+ break;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ /*
+ * This value may be modified when the transfer is started to
+ * reflect the actual transfer length.
+ */
+ hc->multi_count = dwc_hb_mult(qh->maxp);
+
+ dwc_otg_hc_init(hcd->core_if, hc);
+ hc->qh = qh;
+}
+
+/**
+ * This function selects transactions from the HCD transfer schedule and
+ * assigns them to available host channels. It is called from HCD interrupt
+ * handler functions.
+ */
+enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct dwc_hcd *hcd)
+{
+ struct list_head *qh_ptr;
+ struct dwc_qh *qh;
+ int num_channels;
+ enum dwc_transaction_type ret_val = DWC_OTG_TRANSACTION_NONE;
+
+ /* Process entries in the periodic ready list. */
+ num_channels = hcd->core_if->core_params->host_channels;
+ qh_ptr = hcd->periodic_sched_ready.next;
+ while (qh_ptr != &hcd->periodic_sched_ready &&
+ !list_empty(&hcd->free_hc_list)) {
+ /* Leave one channel for non periodic transactions. */
+ if (hcd->available_host_channels <= 1)
+ break;
+ hcd->available_host_channels--;
+ qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+ assign_and_init_hc(hcd, qh);
+ /*
+ * Move the QH from the periodic ready schedule to the
+ * periodic assigned schedule.
+ */
+ qh_ptr = qh_ptr->next;
+ list_move(&qh->qh_list_entry, &hcd->periodic_sched_assigned);
+ ret_val = DWC_OTG_TRANSACTION_PERIODIC;
+ }
+
+ /*
+ * Process entries in the deferred portion of the non-periodic list.
+ * A NAK put them here and, at the right time, they need to be
+ * placed on the sched_inactive list.
+ */
+ qh_ptr = hcd->non_periodic_sched_deferred.next;
+ while (qh_ptr != &hcd->non_periodic_sched_deferred) {
+ u16 frame_number =
+ dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
+ qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+ qh_ptr = qh_ptr->next;
+
+ if (dwc_frame_num_le(qh->sched_frame, frame_number))
+ /*
+ * Move the QH from the non periodic deferred schedule
+ * to the non periodic inactive schedule.
+ */
+ list_move(&qh->qh_list_entry,
+ &hcd->non_periodic_sched_inactive);
+ }
+
+ /*
+ * Process entries in the inactive portion of the non-periodic
+ * schedule. Some free host channels may not be used if they are
+ * reserved for periodic transfers.
+ */
+ qh_ptr = hcd->non_periodic_sched_inactive.next;
+ num_channels = hcd->core_if->core_params->host_channels;
+
+ while (qh_ptr != &hcd->non_periodic_sched_inactive
+ && !list_empty(&hcd->free_hc_list)) {
+ if (hcd->available_host_channels < 1)
+ break;
+ hcd->available_host_channels--;
+ qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+ assign_and_init_hc(hcd, qh);
+ /*
+ * Move the QH from the non-periodic inactive schedule to the
+ * non-periodic active schedule.
+ */
+ qh_ptr = qh_ptr->next;
+ list_move(&qh->qh_list_entry, &hcd->non_periodic_sched_active);
+ if (ret_val == DWC_OTG_TRANSACTION_NONE)
+ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
+ else
+ ret_val = DWC_OTG_TRANSACTION_ALL;
+
+ }
+ return ret_val;
+}
+
+/**
+ * Sets the channel property that indicates in which frame a periodic transfer
+ * should occur. This is always set to the _next_ frame. This function has no
+ * effect on non-periodic transfers.
+ */
+static inline void hc_set_even_odd_frame(struct core_if *core_if,
+ struct dwc_hc *hc, u32 * hcchar)
+{
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ u32 hfnum = 0;
+
+ hfnum = dwc_reg_read(core_if->host_if->host_global_regs,
+ DWC_HFNUM);
+
+ /* 1 if _next_ frame is odd, 0 if it's even */
+ *hcchar = DWC_HCCHAR_ODD_FRAME_RW(*hcchar,
+ ((DWC_HFNUM_FRNUM_RD(hfnum) &
+ 0x1) ? 0 : 1));
+ }
+}
+
+static void set_initial_xfer_pid(struct dwc_hc *hc)
+{
+ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+ if (hc->ep_is_in) {
+ if (hc->multi_count == 1)
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ else if (hc->multi_count == 2)
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+ else
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
+ } else {
+ if (hc->multi_count == 1)
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ else
+ hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
+ }
+ } else {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ }
+}
+
+/**
+ * Starts a PING transfer. This function should only be called in Slave mode.
+ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
+ */
+static void dwc_otg_hc_do_ping(struct core_if *core_if, struct dwc_hc *hc)
+{
+ u32 hcchar;
+ u32 hctsiz = 0;
+
+ ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ hctsiz = 0;
+ hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
+ hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, 1);
+ dwc_reg_write(hc_regs, DWC_HCTSIZ, hctsiz);
+
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+static void dwc_otg_hc_write_packet(struct core_if *core_if, struct dwc_hc *hc)
+{
+ u32 i;
+ u32 remaining_count;
+ u32 byte_count;
+ u32 dword_count;
+ u32 *data_buff = (u32 *) (hc->xfer_buff);
+ u32 data_fifo = core_if->data_fifo[hc->hc_num];
+
+ remaining_count = hc->xfer_len - hc->xfer_count;
+ if (remaining_count > hc->max_packet)
+ byte_count = hc->max_packet;
+ else
+ byte_count = remaining_count;
+
+ dword_count = (byte_count + 3) / 4;
+
+ if (((unsigned long)data_buff) & 0x3)
+ /* xfer_buff is not DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++)
+ dwc_write_fifo32(data_fifo,
+ get_unaligned(data_buff));
+ else
+ /* xfer_buff is DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++)
+ dwc_write_fifo32(data_fifo, *data_buff);
+
+ hc->xfer_count += byte_count;
+ hc->xfer_buff += byte_count;
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel and
+ * starts the transfer. May be called in either Slave mode or DMA mode. In
+ * Slave mode, the caller must ensure that there is sufficient space in the
+ * request queue and Tx Data FIFO.
+ *
+ * For an OUT transfer in Slave mode, it loads a data packet into the
+ * appropriate FIFO. If necessary, additional data packets will be loaded in
+ * the Host ISR.
+ *
+ * For an IN transfer in Slave mode, a data packet is requested. The data
+ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
+ * additional data packets are requested in the Host ISR.
+ *
+ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
+ * register along with a packet count of 1 and the channel is enabled. This
+ * causes a single PING transaction to occur. Other fields in HCTSIZ are
+ * simply set to 0 since no data transfer occurs in this case.
+ *
+ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
+ * all the information required to perform the subsequent data transfer. In
+ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
+ * controller performs the entire PING protocol, then starts the data
+ * transfer.
+ */
+static void dwc_otg_hc_start_transfer(struct core_if *core_if,
+ struct dwc_hc *hc)
+{
+ u32 hcchar;
+ u32 hctsiz = 0;
+ u16 num_packets;
+ u32 max_hc_xfer_size = core_if->core_params->max_transfer_size;
+ u16 max_hc_pkt_count = core_if->core_params->max_packet_count;
+ ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ hctsiz = 0;
+
+ if (hc->do_ping) {
+ if (!core_if->dma_enable) {
+ dwc_otg_hc_do_ping(core_if, hc);
+ hc->xfer_started = 1;
+ return;
+ } else {
+ hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
+ }
+ }
+
+ if (hc->do_split) {
+ num_packets = 1;
+
+ if (hc->complete_split && !hc->ep_is_in)
+ /*
+ * For CSPLIT OUT Transfer, set the size to 0 so the
+ * core doesn't expect any data written to the FIFO
+ */
+ hc->xfer_len = 0;
+ else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet))
+ hc->xfer_len = hc->max_packet;
+ else if (!hc->ep_is_in && (hc->xfer_len > 188))
+ hc->xfer_len = 188;
+
+ hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
+ } else {
+ /*
+ * Ensure that the transfer length and packet count will fit
+ * in the widths allocated for them in the HCTSIZn register.
+ */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ u32 max_len = hc->multi_count * hc->max_packet;
+
+ /*
+ * Make sure the transfer size is no larger than one
+ * (micro)frame's worth of data. (A check was done
+ * when the periodic transfer was accepted to ensure
+ * that a (micro)frame's worth of data can be
+ * programmed into a channel.)
+ */
+ if (hc->xfer_len > max_len)
+ hc->xfer_len = max_len;
+ } else if (hc->xfer_len > max_hc_xfer_size) {
+ /*
+ * Make sure that xfer_len is a multiple of max packet
+ * size.
+ */
+ hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1;
+ }
+ if (hc->xfer_len > 0) {
+ num_packets = (hc->xfer_len + hc->max_packet - 1) /
+ hc->max_packet;
+ if (num_packets > max_hc_pkt_count) {
+ num_packets = max_hc_pkt_count;
+ hc->xfer_len = num_packets * hc->max_packet;
+ }
+ } else {
+ /* Need 1 packet for transfer length of 0. */
+ num_packets = 1;
+ }
+
+ if (hc->ep_is_in)
+ /*
+ * Always program an integral # of max packets for IN
+ * transfers.
+ */
+ hc->xfer_len = num_packets * hc->max_packet;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ /*
+ * Make sure that the multi_count field matches the
+ * actual transfer length.
+ */
+ hc->multi_count = num_packets;
+
+ /* Set up the initial PID for the transfer. */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ set_initial_xfer_pid(hc);
+
+ hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
+ }
+
+ hc->start_pkt_count = num_packets;
+ hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, num_packets);
+ hctsiz = DWC_HCTSIZ_PKT_PID_RW(hctsiz, hc->data_pid_start);
+ dwc_reg_write(hc_regs, DWC_HCTSIZ, hctsiz);
+
+ if (core_if->dma_enable)
+ dwc_reg_write(hc_regs, DWC_HCDMA, (u32) hc->xfer_buff);
+
+ /* Start the split */
+ if (hc->do_split) {
+ u32 hcsplt;
+
+ hcsplt = dwc_reg_read(hc_regs, DWC_HCSPLT);
+ hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, 1);
+ dwc_reg_write(hc_regs, DWC_HCSPLT, hcsplt);
+ }
+
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hcchar = DWC_HCCHAR_MULTI_CNT_RW(hcchar, hc->multi_count);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+
+ /* Set host channel enable after all other setup is complete. */
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+
+ hc->xfer_started = 1;
+ hc->requests++;
+ if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0)
+ /* Load OUT packet into the appropriate Tx FIFO. */
+ dwc_otg_hc_write_packet(core_if, hc);
+}
+
+/**
+ * This function continues a data transfer that was started by previous call
+ * to dwc_otg_hc_start_transfer</code>. The caller must ensure there is
+ * sufficient space in the request queue and Tx Data FIFO. This function
+ * should only be called in Slave mode. In DMA mode, the controller acts
+ * autonomously to complete transfers programmed to a host channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ */
+static int dwc_otg_hc_continue_transfer(struct core_if *core_if,
+ struct dwc_hc *hc)
+{
+ if (hc->do_split) {
+ /* SPLITs always queue just once per channel */
+ return 0;
+ } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+ /* SETUPs are queued only once since they can't be NAKed. */
+ return 0;
+ } else if (hc->ep_is_in) {
+ /*
+ * Always queue another request for other IN transfers. If
+ * back-to-back INs are issued and NAKs are received for both,
+ * the driver may still be processing the first NAK when the
+ * second NAK is received. When the interrupt handler clears
+ * the NAK interrupt for the first NAK, the second NAK will
+ * not be seen. So we can't depend on the NAK interrupt
+ * handler to requeue a NAKed request. Instead, IN requests
+ * are issued each time this function is called. When the
+ * transfer completes, the extra requests for the channel will
+ * be flushed.
+ */
+ u32 hcchar;
+ ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+
+ hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+ hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+ dwc_reg_write(hc_regs, DWC_HCCHAR, hcchar);
+
+ hc->requests++;
+ return 1;
+ } else {
+ /* OUT transfers. */
+ if (hc->xfer_count < hc->xfer_len) {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ u32 hcchar;
+ u32 hc_regs;
+
+ hc_regs =
+ core_if->host_if->hc_regs[hc->hc_num];
+ hcchar = dwc_reg_read(hc_regs, DWC_HCCHAR);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+ }
+
+ /* Load OUT packet into the appropriate Tx FIFO. */
+ dwc_otg_hc_write_packet(core_if, hc);
+ hc->requests++;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+
+/**
+ * Attempts to queue a single transaction request for a host channel
+ * associated with either a periodic or non-periodic transfer. This function
+ * assumes that there is space available in the appropriate request queue. For
+ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space
+ * is available in the appropriate Tx FIFO.
+ */
+static int queue_transaction(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u16 _fifo_dwords_avail)
+{
+ int retval;
+
+ if (hcd->core_if->dma_enable) {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ hc->qh->ping_state = 0;
+ }
+ retval = 0;
+ } else if (hc->halt_pending) {
+ /* Don't queue a request if the channel has been halted. */
+ retval = 0;
+ } else if (hc->halt_on_queue) {
+ dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
+ retval = 0;
+ } else if (hc->do_ping) {
+ if (!hc->xfer_started)
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ retval = 0;
+ } else if (!hc->ep_is_in || hc->data_pid_start ==
+ DWC_OTG_HC_PID_SETUP) {
+ if ((_fifo_dwords_avail * 4) >= hc->max_packet) {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ retval = 1;
+ } else {
+ retval =
+ dwc_otg_hc_continue_transfer(hcd->core_if,
+ hc);
+ }
+ } else {
+ retval = -1;
+ }
+ } else {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ retval = 1;
+ } else {
+ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc);
+ }
+ }
+ return retval;
+}
+
+/**
+ * Processes active non-periodic channels and queues transactions for these
+ * channels to the DWC_otg controller. After queueing transactions, the NP Tx
+ * FIFO Empty interrupt is enabled if there are more transactions to queue as
+ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
+ * FIFO Empty interrupt is disabled.
+ */
+static void process_non_periodic_channels(struct dwc_hcd *hcd)
+{
+ u32 tx_status = 0;
+ struct list_head *orig_qh_ptr;
+ struct dwc_qh *qh;
+ int status;
+ int no_queue_space = 0;
+ int no_fifo_space = 0;
+ int more_to_do = 0;
+ ulong regs = hcd->core_if->core_global_regs;
+
+ /*
+ * Keep track of the starting point. Skip over the start-of-list
+ * entry.
+ */
+ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
+ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+ orig_qh_ptr = hcd->non_periodic_qh_ptr;
+
+ /*
+ * Process once through the active list or until no more space is
+ * available in the request queue or the Tx FIFO.
+ */
+ do {
+ tx_status = dwc_reg_read(regs, DWC_GNPTXSTS);
+ if (!hcd->core_if->dma_enable &&
+ DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(tx_status) == 0) {
+ no_queue_space = 1;
+ break;
+ }
+
+ qh = list_entry(hcd->non_periodic_qh_ptr, struct dwc_qh,
+ qh_list_entry);
+ status = queue_transaction(hcd, qh->channel,
+ DWC_GNPTXSTS_NPTXFSPCAVAIL_RD
+ (tx_status));
+
+ if (status > 0) {
+ more_to_do = 1;
+ } else if (status < 0) {
+ no_fifo_space = 1;
+ break;
+ }
+
+ /* Advance to next QH, skipping start-of-list entry. */
+ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
+ hcd->non_periodic_qh_ptr =
+ hcd->non_periodic_qh_ptr->next;
+ } while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
+
+ if (!hcd->core_if->dma_enable) {
+ u32 intr_mask = 0;
+
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ if (more_to_do || no_queue_space || no_fifo_space) {
+ /*
+ * May need to queue more transactions as the request
+ * queue or Tx FIFO empties. Enable the non-periodic
+ * Tx FIFO empty interrupt. (Always use the half-empty
+ * level to ensure that new requests are loaded as
+ * soon as possible.)
+ */
+ dwc_reg_modify(gintmsk_reg(hcd), 0, 0, intr_mask);
+ } else {
+ /*
+ * Disable the Tx FIFO empty interrupt since there are
+ * no more transactions that need to be queued right
+ * now. This function is called from interrupt
+ * handlers to queue more transactions as transfer
+ * states change.
+ */
+ dwc_reg_modify(gintmsk_reg(hcd), 0, intr_mask, 0);
+ }
+ }
+}
+
+/**
+ * Processes periodic channels for the next frame and queues transactions for
+ * these channels to the DWC_otg controller. After queueing transactions, the
+ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
+ * to queue as Periodic Tx FIFO or request queue space becomes available.
+ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+ */
+static void process_periodic_channels(struct dwc_hcd *hcd)
+{
+ u32 tx_status = 0;
+ struct list_head *qh_ptr;
+ struct dwc_qh *qh;
+ int status;
+ int no_queue_space = 0;
+ int no_fifo_space = 0;
+ ulong host_regs;
+
+ host_regs = hcd->core_if->host_if->host_global_regs;
+
+ qh_ptr = hcd->periodic_sched_assigned.next;
+ while (qh_ptr != &hcd->periodic_sched_assigned) {
+ tx_status = dwc_reg_read(host_regs, DWC_HPTXSTS);
+ if (DWC_HPTXSTS_PTXSPC_AVAIL_RD(tx_status) == 0) {
+ no_queue_space = 1;
+ break;
+ }
+
+ qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+
+ /*
+ * Set a flag if we're queuing high-bandwidth in slave mode.
+ * The flag prevents any halts to get into the request queue in
+ * the middle of multiple high-bandwidth packets getting queued.
+ */
+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1)
+ hcd->core_if->queuing_high_bandwidth = 1;
+
+ status = queue_transaction(hcd, qh->channel,
+ DWC_HPTXSTS_PTXFSPC_AVAIL_RD
+ (tx_status));
+ if (status < 0) {
+ no_fifo_space = 1;
+ break;
+ }
+
+ /*
+ * In Slave mode, stay on the current transfer until there is
+ * nothing more to do or the high-bandwidth request count is
+ * reached. In DMA mode, only need to queue one request. The
+ * controller automatically handles multiple packets for
+ * high-bandwidth transfers.
+ */
+ if (hcd->core_if->dma_enable || (status == 0 ||
+ qh->channel->requests ==
+ qh->channel->multi_count)) {
+ qh_ptr = qh_ptr->next;
+
+ /*
+ * Move the QH from the periodic assigned schedule to
+ * the periodic queued schedule.
+ */
+ list_move(&qh->qh_list_entry,
+ &hcd->periodic_sched_queued);
+
+ /* done queuing high bandwidth */
+ hcd->core_if->queuing_high_bandwidth = 0;
+ }
+ }
+
+ if (!hcd->core_if->dma_enable) {
+ u32 intr_mask = 0;
+
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+
+ if (!list_empty(&hcd->periodic_sched_assigned) ||
+ no_queue_space || no_fifo_space)
+ /*
+ * May need to queue more transactions as the request
+ * queue or Tx FIFO empties. Enable the periodic Tx
+ * FIFO empty interrupt. (Always use the half-empty
+ * level to ensure that new requests are loaded as
+ * soon as possible.)
+ */
+ dwc_reg_modify(gintmsk_reg(hcd), 0, 0, intr_mask);
+ else
+ /*
+ * Disable the Tx FIFO empty interrupt since there are
+ * no more transactions that need to be queued right
+ * now. This function is called from interrupt
+ * handlers to queue more transactions as transfer
+ * states change.
+ */
+ dwc_reg_modify(gintmsk_reg(hcd), 0, intr_mask, 0);
+ }
+}
+
+/**
+ * This function processes the currently active host channels and queues
+ * transactions for these channels to the DWC_otg controller. It is called
+ * from HCD interrupt handler functions.
+ */
+void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
+ enum dwc_transaction_type tr_type)
+{
+ /* Process host channels associated with periodic transfers. */
+ if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
+ tr_type == DWC_OTG_TRANSACTION_ALL) &&
+ !list_empty(&hcd->periodic_sched_assigned))
+ process_periodic_channels(hcd);
+
+ /* Process host channels associated with non-periodic transfers. */
+ if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
+ tr_type == DWC_OTG_TRANSACTION_ALL) {
+ if (!list_empty(&hcd->non_periodic_sched_active)) {
+ process_non_periodic_channels(hcd);
+ } else {
+ /*
+ * Ensure NP Tx FIFO empty interrupt is disabled when
+ * there are no non-periodic transfers to process.
+ */
+ u32 gintmsk = 0;
+ gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify(gintmsk_reg(hcd), 0, gintmsk, 0);
+ }
+ }
+}
+
+/**
+ * Sets the final status of an URB and returns it to the device driver. Any
+ * required cleanup of the URB is performed.
+ */
+void dwc_otg_hcd_complete_urb(struct dwc_hcd *hcd, struct urb *urb, int status)
+__releases(hcd->lock) __acquires(hcd->lock)
+{
+ urb->hcpriv = NULL;
+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+
+ spin_unlock(&hcd->lock);
+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
+ spin_lock(&hcd->lock);
+}
diff --git a/drivers/usb/dwc/hcd.h b/drivers/usb/dwc/hcd.h
new file mode 100644
index 0000000..2d25abc
--- /dev/null
+++ b/drivers/usb/dwc/hcd.h
@@ -0,0 +1,416 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_HCD_H__)
+#define __DWC_HCD_H__
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "driver.h"
+
+/*
+ * This file contains the structures, constants, and interfaces for
+ * the Host Contoller Driver (HCD).
+ *
+ * The Host Controller Driver (HCD) is responsible for translating requests
+ * from the USB Driver into the appropriate actions on the DWC_otg controller.
+ * It isolates the USBD from the specifics of the controller by providing an
+ * API to the USBD.
+ */
+
+/* Phases for control transfers. */
+enum dwc_control_phase {
+ DWC_OTG_CONTROL_SETUP,
+ DWC_OTG_CONTROL_DATA,
+ DWC_OTG_CONTROL_STATUS
+};
+
+/* Transaction types. */
+enum dwc_transaction_type {
+ DWC_OTG_TRANSACTION_NONE,
+ DWC_OTG_TRANSACTION_PERIODIC,
+ DWC_OTG_TRANSACTION_NON_PERIODIC,
+ DWC_OTG_TRANSACTION_ALL
+};
+
+/*
+ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
+ * interrupt, or isochronous transfer. A single QTD is created for each URB
+ * (of one of these types) submitted to the HCD. The transfer associated with
+ * a QTD may require one or multiple transactions.
+ *
+ * A QTD is linked to a Queue Head, which is entered in either the
+ * non-periodic or periodic schedule for execution. When a QTD is chosen for
+ * execution, some or all of its transactions may be executed. After
+ * execution, the state of the QTD is updated. The QTD may be retired if all
+ * its transactions are complete or if an error occurred. Otherwise, it
+ * remains in the schedule so more transactions can be executed later.
+ */
+struct dwc_qtd {
+ /*
+ * Determines the PID of the next data packet for the data phase of
+ * control transfers. Ignored for other transfer types.
+ * One of the following values:
+ * - DWC_OTG_HC_PID_DATA0
+ * - DWC_OTG_HC_PID_DATA1
+ */
+ u8 data_toggle;
+
+ /* Current phase for control transfers (Setup, Data, or Status). */
+ enum dwc_control_phase control_phase;
+
+ /*
+ * Keep track of the current split type
+ * for FS/LS endpoints on a HS Hub
+ */
+ u8 complete_split;
+
+ /* How many bytes transferred during SSPLIT OUT */
+ u32 ssplit_out_xfer_count;
+
+ /*
+ * Holds the number of bus errors that have occurred for a transaction
+ * within this transfer.
+ */
+ u8 error_count;
+
+ /*
+ * Index of the next frame descriptor for an isochronous transfer. A
+ * frame descriptor describes the buffer position and length of the
+ * data to be transferred in the next scheduled (micro)frame of an
+ * isochronous transfer. It also holds status for that transaction.
+ * The frame index starts at 0.
+ */
+ int isoc_frame_index;
+
+ /* Position of the ISOC split on full/low speed */
+ u8 isoc_split_pos;
+
+ /* Position of the ISOC split in the buffer for the current frame */
+ u16 isoc_split_offset;
+
+ /* URB for this transfer */
+ struct urb *urb;
+
+ /* This list of QTDs */
+ struct list_head qtd_list_entry;
+
+ /* Field to track the qh pointer */
+ struct dwc_qh *qtd_qh_ptr;
+};
+
+/*
+ * A Queue Head (QH) holds the static characteristics of an endpoint and
+ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
+ * be entered in either the non-periodic or periodic schedule.
+ */
+struct dwc_qh {
+ /*
+ * Endpoint type.
+ * One of the following values:
+ * - USB_ENDPOINT_XFER_CONTROL
+ * - USB_ENDPOINT_XFER_ISOC
+ * - USB_ENDPOINT_XFER_BULK
+ * - USB_ENDPOINT_XFER_INT
+ */
+ u8 ep_type;
+ u8 ep_is_in;
+
+ /* wMaxPacketSize Field of Endpoint Descriptor. */
+ u16 maxp;
+
+ /*
+ * Determines the PID of the next data packet for non-control
+ * transfers. Ignored for control transfers.
+ * One of the following values:
+ * - DWC_OTG_HC_PID_DATA0
+ * - DWC_OTG_HC_PID_DATA1
+ */
+ u8 data_toggle;
+
+ /* Ping state if 1. */
+ u8 ping_state;
+
+ /* List of QTDs for this QH. */
+ struct list_head qtd_list;
+
+ /* Host channel currently processing transfers for this QH. */
+ struct dwc_hc *channel;
+
+ /* QTD currently assigned to a host channel for this QH. */
+ struct dwc_qtd *qtd_in_process;
+
+ /* Full/low speed endpoint on high-speed hub requires split. */
+ u8 do_split;
+
+ /* Periodic schedule information */
+
+ /* Bandwidth in microseconds per (micro)frame. */
+ u8 usecs;
+
+ /* Interval between transfers in (micro)frames. */
+ u16 interval;
+
+ /*
+ * (micro)frame to initialize a periodic transfer. The transfer
+ * executes in the following (micro)frame.
+ */
+ u16 sched_frame;
+
+ /* (micro)frame at which last start split was initialized. */
+ u16 start_split_frame;
+
+ u16 speed;
+ u16 frame_usecs[8];
+
+ /* Entry for QH in either the periodic or non-periodic schedule. */
+ struct list_head qh_list_entry;
+};
+
+/* Gets the struct usb_hcd that contains a struct dwc_hcd. */
+static inline struct usb_hcd *dwc_otg_hcd_to_hcd(struct dwc_hcd *dwc_hcd)
+{
+ return container_of((void *)dwc_hcd, struct usb_hcd, hcd_priv);
+}
+
+/* HCD Create/Destroy Functions */
+extern int dwc_otg_hcd_init(struct device *_dev,
+ struct dwc_otg_device *dwc_dev);
+extern void dwc_otg_hcd_remove(struct device *_dev);
+
+/*
+ * The following functions support managing the DWC_otg controller in host
+ * mode.
+ */
+extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
+extern void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc);
+extern void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
+ enum dwc_halt_status _halt_status);
+
+/* Transaction Execution Functions */
+extern enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct dwc_hcd
+ *hcd);
+extern void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
+ enum dwc_transaction_type tr_type);
+extern void dwc_otg_hcd_complete_urb(struct dwc_hcd *_hcd, struct urb *urb,
+ int status);
+
+/* Interrupt Handler Functions */
+extern int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd);
+
+/* Schedule Queue Functions */
+extern int init_hcd_usecs(struct dwc_hcd *hcd);
+extern void dwc_otg_hcd_qh_free(struct dwc_qh *qh);
+extern void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh);
+extern void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
+ int sched_csplit);
+extern int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh,
+ int delay);
+extern struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb,
+ gfp_t _mem_flags);
+extern int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *dwc_hcd);
+
+/*
+ * Frees the memory for a QTD structure. QTD should already be removed from
+ * list.
+ */
+static inline void dwc_otg_hcd_qtd_free(struct dwc_qtd *_qtd)
+{
+ kfree(_qtd);
+}
+
+/* Removes a QTD from list. */
+static inline void dwc_otg_hcd_qtd_remove(struct dwc_qtd *_qtd)
+{
+ list_del(&_qtd->qtd_list_entry);
+}
+
+/* Remove and free a QTD */
+static inline void dwc_otg_hcd_qtd_remove_and_free(struct dwc_qtd *_qtd)
+{
+ dwc_otg_hcd_qtd_remove(_qtd);
+ dwc_otg_hcd_qtd_free(_qtd);
+}
+
+struct dwc_qh *dwc_urb_to_qh(struct urb *_urb);
+
+/* Gets the usb_host_endpoint associated with an URB. */
+static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *_urb)
+{
+ struct usb_device *dev = _urb->dev;
+ int ep_num = usb_pipeendpoint(_urb->pipe);
+
+ if (usb_pipein(_urb->pipe))
+ return dev->ep_in[ep_num];
+ else
+ return dev->ep_out[ep_num];
+}
+
+/*
+ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
+ * qualified with its direction (possible 32 endpoints per device).
+ */
+#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) \
+ ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
+ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
+
+/* Gets the QH that contains the list_head */
+#define dwc_list_to_qh(_list_head_ptr_) \
+ (container_of(_list_head_ptr_, struct dwc_qh, qh_list_entry))
+
+/* Gets the QTD that contains the list_head */
+#define dwc_list_to_qtd(_list_head_ptr_) \
+ (container_of(_list_head_ptr_, struct dwc_qtd, qtd_list_entry))
+
+/* Check if QH is non-periodic */
+#define dwc_qh_is_non_per(_qh_ptr_) \
+ ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
+ (_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))
+
+/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */
+#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/* Packet size for any kind of endpoint descriptor */
+#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * Returns true if _frame1 is less than or equal to _frame2. The comparison is
+ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
+ * frame number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_le(u16 _frame1, u16 _frame2)
+{
+ return ((_frame2 - _frame1) & DWC_HFNUM_MAX_FRNUM) <=
+ (DWC_HFNUM_MAX_FRNUM >> 1);
+}
+
+/*
+ * Returns true if _frame1 is greater than _frame2. The comparison is done
+ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
+ * number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_gt(u16 _frame1, u16 _frame2)
+{
+ return (_frame1 != _frame2) &&
+ (((_frame1 - _frame2) &
+ DWC_HFNUM_MAX_FRNUM) < (DWC_HFNUM_MAX_FRNUM >> 1));
+}
+
+/*
+ * Increments _frame by the amount specified by _inc. The addition is done
+ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
+ */
+static inline u16 dwc_frame_num_inc(u16 _frame, u16 _inc)
+{
+ return (_frame + _inc) & DWC_HFNUM_MAX_FRNUM;
+}
+
+static inline u16 dwc_full_frame_num(u16 _frame)
+{
+ return ((_frame) & DWC_HFNUM_MAX_FRNUM) >> 3;
+}
+
+static inline u16 dwc_micro_frame_num(u16 _frame)
+{
+ return (_frame) & 0x7;
+}
+
+static inline ulong gintsts_reg(struct dwc_hcd *hcd)
+{
+ ulong global_regs = hcd->core_if->core_global_regs;
+ return global_regs + DWC_GINTSTS;
+}
+
+static inline ulong gintmsk_reg(struct dwc_hcd *hcd)
+{
+ ulong global_regs = hcd->core_if->core_global_regs;
+ return global_regs + DWC_GINTMSK;
+}
+
+static inline ulong gahbcfg_reg(struct dwc_hcd *hcd)
+{
+ ulong global_regs = hcd->core_if->core_global_regs;
+ return global_regs + DWC_GAHBCFG;
+}
+
+static inline const char *pipetype_str(unsigned int pipe)
+{
+ switch (usb_pipetype(pipe)) {
+ case PIPE_CONTROL:
+ return "control";
+ case PIPE_BULK:
+ return "bulk";
+ case PIPE_INTERRUPT:
+ return "interrupt";
+ case PIPE_ISOCHRONOUS:
+ return "isochronous";
+ default:
+ return "unknown";
+ }
+}
+
+static inline const char *dev_speed_str(enum usb_device_speed speed)
+{
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ return "high";
+ case USB_SPEED_FULL:
+ return "full";
+ case USB_SPEED_LOW:
+ return "low";
+ default:
+ return "unknown";
+ }
+}
+
+static inline const char *ep_type_str(u8 type)
+{
+ switch (type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ return "isochronous";
+ case USB_ENDPOINT_XFER_INT:
+ return "interrupt";
+ case USB_ENDPOINT_XFER_CONTROL:
+ return "control";
+ case USB_ENDPOINT_XFER_BULK:
+ return "bulk";
+ default:
+ return "?";
+ }
+}
+#endif
diff --git a/drivers/usb/dwc/hcd_intr.c b/drivers/usb/dwc/hcd_intr.c
new file mode 100644
index 0000000..b16934d
--- /dev/null
+++ b/drivers/usb/dwc/hcd_intr.c
@@ -0,0 +1,1477 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "hcd.h"
+
+/* This file contains the implementation of the HCD Interrupt handlers. */
+static const int erratum_usb09_patched;
+static const int deferral_on = 1;
+static const int nak_deferral_delay = 8;
+static const int nyet_deferral_delay = 1;
+
+/**
+ * Handles the start-of-frame interrupt in host mode. Non-periodic
+ * transactions may be queued to the DWC_otg controller for the current
+ * (micro)frame. Periodic transactions may be queued to the controller for the
+ * next (micro)frame.
+ */
+static int dwc_otg_hcd_handle_sof_intr(struct dwc_hcd *hcd)
+{
+ u32 hfnum = 0;
+ struct list_head *qh_entry;
+ struct dwc_qh *qh;
+ enum dwc_transaction_type tr_type;
+ u32 gintsts = 0;
+
+ hfnum =
+ dwc_reg_read(hcd->core_if->host_if->host_global_regs,
+ DWC_HFNUM);
+
+ hcd->frame_number = DWC_HFNUM_FRNUM_RD(hfnum);
+
+ /* Determine whether any periodic QHs should be executed. */
+ qh_entry = hcd->periodic_sched_inactive.next;
+ while (qh_entry != &hcd->periodic_sched_inactive) {
+ qh = list_entry(qh_entry, struct dwc_qh, qh_list_entry);
+ qh_entry = qh_entry->next;
+
+ /*
+ * If needed, move QH to the ready list to be executed next
+ * (micro)frame.
+ */
+ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number))
+ list_move(&qh->qh_list_entry,
+ &hcd->periodic_sched_ready);
+ }
+
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE)
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+
+ /* Clear interrupt */
+ gintsts |= DWC_INTMSK_STRT_OF_FRM;
+ dwc_reg_write(gintsts_reg(hcd), 0, gintsts);
+ return 1;
+}
+
+/**
+ * Handles the Rx Status Queue Level Interrupt, which indicates that there is at
+ * least one packet in the Rx FIFO. The packets are moved from the FIFO to
+ * memory if the DWC_otg controller is operating in Slave mode.
+ */
+static int dwc_otg_hcd_handle_rx_status_q_level_intr(struct dwc_hcd *hcd)
+{
+ u32 grxsts;
+ struct dwc_hc *hc;
+
+ grxsts = dwc_reg_read(hcd->core_if->core_global_regs, DWC_GRXSTSP);
+ hc = hcd->hc_ptr_array[grxsts & DWC_HM_RXSTS_CHAN_NUM_RD(grxsts)];
+
+ /* Packet Status */
+ switch (DWC_HM_RXSTS_PKT_STS_RD(grxsts)) {
+ case DWC_GRXSTS_PKTSTS_IN:
+ /* Read the data into the host buffer. */
+ if (DWC_HM_RXSTS_BYTE_CNT_RD(grxsts) > 0) {
+ dwc_otg_read_packet(hcd->core_if, hc->xfer_buff,
+ DWC_HM_RXSTS_BYTE_CNT_RD(grxsts));
+ /* Update the HC fields for the next packet received. */
+ hc->xfer_count += DWC_HM_RXSTS_BYTE_CNT_RD(grxsts);
+ hc->xfer_buff += DWC_HM_RXSTS_BYTE_CNT_RD(grxsts);
+ }
+ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+ case DWC_GRXSTS_PKTSTS_CH_HALTED:
+ /* Handled in interrupt, just ignore data */
+ break;
+ default:
+ pr_err("RX_STS_Q Interrupt: Unknown status %d\n",
+ DWC_HM_RXSTS_PKT_STS_RD(grxsts));
+ break;
+ }
+ return 1;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
+ * data packets may be written to the FIFO for OUT transfers. More requests
+ * may be written to the non-periodic request queue for IN transfers. This
+ * interrupt is enabled only in Slave mode.
+ */
+static int dwc_otg_hcd_handle_np_tx_fifo_empty_intr(struct dwc_hcd *hcd)
+{
+ dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_NON_PERIODIC);
+ return 1;
+}
+
+/**
+ * This interrupt occurs when the periodic Tx FIFO is half-empty. More data
+ * packets may be written to the FIFO for OUT transfers. More requests may be
+ * written to the periodic request queue for IN transfers. This interrupt is
+ * enabled only in Slave mode.
+ */
+static int dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(struct dwc_hcd *hcd)
+{
+ dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_PERIODIC);
+ return 1;
+}
+
+/**
+ * When the port changes to enabled it may be necessary to adjust the phy clock
+ * speed.
+ */
+static int adjusted_phy_clock_speed(struct dwc_hcd *hcd, u32 hprt0)
+{
+ int adjusted = 0;
+ u32 usbcfg;
+ ulong global_regs = hcd->core_if->core_global_regs;
+ struct core_params *params = hcd->core_if->core_params;
+ ulong h_regs = hcd->core_if->host_if->host_global_regs;
+
+ usbcfg = dwc_reg_read(global_regs, DWC_GUSBCFG);
+
+ if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_LOW_SPEED ||
+ DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_FULL_SPEED) {
+ /* Low power */
+ u32 hcfg;
+
+ if (!(usbcfg & DWC_USBCFG_PHYLPWRCLKSEL)) {
+ /* Set PHY low power clock select for FS/LS devices */
+ usbcfg |= DWC_USBCFG_PHYLPWRCLKSEL;
+ dwc_reg_write(global_regs, DWC_GUSBCFG, usbcfg);
+ adjusted = 1;
+ }
+
+ hcfg = dwc_reg_read(h_regs, DWC_HCFG);
+ if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_LOW_SPEED &&
+ params->host_ls_low_power_phy_clk ==
+ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) {
+ /* 6 MHZ, check for 6 MHZ clock select */
+ if (DWC_HCFG_FSLSP_CLK_RD(hcfg) != DWC_HCFG_6_MHZ) {
+ hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg,
+ DWC_HCFG_6_MHZ);
+ dwc_reg_write(h_regs, DWC_HCFG, hcfg);
+ adjusted = 1;
+ }
+ } else if (DWC_HCFG_FSLSP_CLK_RD(hcfg) != DWC_HCFG_48_MHZ) {
+ /* 48 MHZ and clock select is not 48 MHZ */
+ hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg, DWC_HCFG_48_MHZ);
+ dwc_reg_write(h_regs, DWC_HCFG, hcfg);
+ adjusted = 1;
+ }
+ } else if (usbcfg & DWC_USBCFG_PHYLPWRCLKSEL) {
+ usbcfg &= ~((u32) DWC_USBCFG_PHYLPWRCLKSEL);
+ dwc_reg_write(global_regs, DWC_GUSBCFG, usbcfg);
+ adjusted = 1;
+ }
+ if (adjusted)
+ schedule_work(&hcd->usb_port_reset);
+
+ return adjusted;
+}
+
+/**
+ * Helper function to handle the port enable changed interrupt when the port
+ * becomes enabled. Checks if we need to adjust the PHY clock speed for low
+ * power and adjusts it if needed.
+ */
+static void port_enabled(struct dwc_hcd *hcd, u32 hprt0)
+{
+ if (hcd->core_if->core_params->host_support_fs_ls_low_power)
+ if (!adjusted_phy_clock_speed(hcd, hprt0))
+ hcd->flags.b.port_reset_change = 1;
+}
+
+/**
+ * There are multiple conditions that can cause a port interrupt. This function
+ * determines which interrupt conditions have occurred and handles them
+ * appropriately.
+ */
+static int dwc_otg_hcd_handle_port_intr(struct dwc_hcd *hcd)
+{
+ int retval = 0;
+ u32 hprt0;
+ u32 hprt0_modify;
+
+ hprt0 = dwc_reg_read(hcd->core_if->host_if->hprt0, 0);
+ hprt0_modify = dwc_reg_read(hcd->core_if->host_if->hprt0, 0);
+
+ /*
+ * Clear appropriate bits in HPRT0 to clear the interrupt bit in
+ * GINTSTS
+ */
+ hprt0_modify = DWC_HPRT0_PRT_ENA_RW(hprt0_modify, 0);
+ hprt0_modify = DWC_HPRT0_PRT_CONN_DET_RW(hprt0_modify, 0);
+ hprt0_modify = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0_modify, 0);
+ hprt0_modify = DWC_HPRT0_PRT_OVRCURR_CHG_RW(hprt0_modify, 0);
+
+ /* Port connect detected interrupt */
+ if (DWC_HPRT0_PRT_CONN_DET_RD(hprt0)) {
+ /* Set the status flags and clear interrupt */
+ hcd->flags.b.port_connect_status_change = 1;
+ hcd->flags.b.port_connect_status = 1;
+ hprt0_modify = DWC_HPRT0_PRT_CONN_DET_RW(hprt0_modify, 1);
+
+ /* B-Device has connected, Delete the connection timer. */
+ del_timer_sync(&hcd->conn_timer);
+
+ /*
+ * The Hub driver asserts a reset when it sees port connect
+ * status change flag
+ */
+ retval |= 1;
+ }
+
+ /* Port enable changed interrupt */
+ if (DWC_HPRT0_PRT_ENA_DIS_CHG_RD(hprt0)) {
+ /* Set the internal flag if the port was disabled */
+ if (DWC_HPRT0_PRT_ENA_RD(hprt0))
+ port_enabled(hcd, hprt0);
+ else
+ hcd->flags.b.port_enable_change = 1;
+
+ /* Clear the interrupt */
+ hprt0_modify = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0_modify, 1);
+ retval |= 1;
+ }
+
+ /* Overcurrent change interrupt */
+ if (DWC_HPRT0_PRT_OVRCURR_CHG_RD(hprt0)) {
+ hcd->flags.b.port_over_current_change = 1;
+ hprt0_modify = DWC_HPRT0_PRT_OVRCURR_CHG_RW(hprt0_modify, 1);
+ retval |= 1;
+ }
+
+ /* Clear the port interrupts */
+ dwc_reg_write(hcd->core_if->host_if->hprt0, 0, hprt0_modify);
+ return retval;
+}
+
+/**
+ * Gets the actual length of a transfer after the transfer halts. halt_status
+ * holds the reason for the halt.
+ *
+ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, _short_read
+ * is set to 1 upon return if less than the requested number of bytes were
+ * transferred. Otherwise, _short_read is set to 0 upon return. _short_read may
+ * also be NULL on entry, in which case it remains unchanged.
+ */
+static u32 get_actual_xfer_length(struct dwc_hc *hc, ulong regs,
+ struct dwc_qtd *qtd,
+ enum dwc_halt_status halt_status,
+ int *_short_read)
+{
+ u32 hctsiz = 0;
+ u32 length;
+
+ if (_short_read)
+ *_short_read = 0;
+
+ hctsiz = dwc_reg_read(regs, DWC_HCTSIZ);
+ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+ if (hc->ep_is_in) {
+ length = hc->xfer_len - DWC_HCTSIZ_XFER_SIZE_RD(hctsiz);
+ if (_short_read)
+ *_short_read =
+ (DWC_HCTSIZ_XFER_SIZE_RD(hctsiz) != 0);
+ } else if (hc->qh->do_split) {
+ length = qtd->ssplit_out_xfer_count;
+ } else {
+ length = hc->xfer_len;
+ }
+ } else {
+ /*
+ * Must use the hctsiz.pktcnt field to determine how much data
+ * has been transferred. This field reflects the number of
+ * packets that have been transferred via the USB. This is
+ * always an integral number of packets if the transfer was
+ * halted before its normal completion. (Can't use the
+ * hctsiz.xfersize field because that reflects the number of
+ * bytes transferred via the AHB, not the USB).
+ */
+ length = (hc->start_pkt_count - DWC_HCTSIZ_PKT_CNT_RD(hctsiz)) *
+ hc->max_packet;
+ }
+ return length;
+}
+
+/**
+ * Updates the state of the URB after a Transfer Complete interrupt on the
+ * host channel. Updates the actual_length field of the URB based on the
+ * number of bytes transferred via the host channel. Sets the URB status
+ * if the data transfer is finished.
+ */
+static int update_urb_state_xfer_comp(struct dwc_hc *hc,
+ ulong regs, struct urb *urb,
+ struct dwc_qtd *qtd, int *status)
+{
+ int xfer_done = 0;
+ int short_read = 0;
+
+ urb->actual_length += get_actual_xfer_length(hc, regs, qtd,
+ DWC_OTG_HC_XFER_COMPLETE,
+ &short_read);
+
+ if (short_read || urb->actual_length == urb->transfer_buffer_length) {
+ xfer_done = 1;
+ if (short_read && (urb->transfer_flags & URB_SHORT_NOT_OK))
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ return xfer_done;
+}
+
+/*
+ * Save the starting data toggle for the next transfer. The data toggle is
+ * saved in the QH for non-control transfers and it's saved in the QTD for
+ * control transfers.
+ */
+static void save_data_toggle(struct dwc_hc *hc, ulong regs, struct dwc_qtd *qtd)
+{
+ u32 hctsiz = 0;
+ hctsiz = dwc_reg_read(regs, DWC_HCTSIZ);
+
+ if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {
+ struct dwc_qh *qh = hc->qh;
+
+ if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) == DWC_HCTSIZ_DATA0)
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ else
+ qh->data_toggle = DWC_OTG_HC_PID_DATA1;
+ } else {
+ if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) == DWC_HCTSIZ_DATA0)
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA0;
+ else
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+ }
+}
+
+/**
+ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
+ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are
+ * still linked to the QH, the QH is added to the end of the inactive
+ * non-periodic schedule. For periodic QHs, removes the QH from the periodic
+ * schedule if no more QTDs are linked to the QH.
+ */
+static void deactivate_qh(struct dwc_hcd *hcd, struct dwc_qh *qh, int free_qtd)
+{
+ int continue_split = 0;
+ struct dwc_qtd *qtd;
+
+ qtd = list_entry(qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+ if (qtd->complete_split)
+ continue_split = 1;
+ else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
+ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END)
+ continue_split = 1;
+
+ if (free_qtd) {
+ dwc_otg_hcd_qtd_remove(qtd);
+ continue_split = 0;
+ }
+
+ qh->channel = NULL;
+ qh->qtd_in_process = NULL;
+ dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
+}
+
+/**
+ * Updates the state of an Isochronous URB when the transfer is stopped for
+ * any reason. The fields of the current entry in the frame descriptor array
+ * are set based on the transfer state and the input status. Completes the
+ * Isochronous URB if all the URB frames have been completed.
+ */
+static enum dwc_halt_status update_isoc_urb_state(struct dwc_hcd *hcd,
+ struct dwc_hc *hc, u32 regs,
+ struct dwc_qtd *qtd,
+ enum dwc_halt_status status)
+{
+ struct urb *urb = qtd->urb;
+ enum dwc_halt_status ret_val = status;
+ struct usb_iso_packet_descriptor *frame_desc;
+ frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
+
+ switch (status) {
+ case DWC_OTG_HC_XFER_COMPLETE:
+ frame_desc->status = 0;
+ frame_desc->actual_length =
+ get_actual_xfer_length(hc, regs, qtd, status, NULL);
+ break;
+ case DWC_OTG_HC_XFER_FRAME_OVERRUN:
+ urb->error_count++;
+ if (hc->ep_is_in)
+ frame_desc->status = -ENOSR;
+ else
+ frame_desc->status = -ECOMM;
+
+ frame_desc->actual_length = 0;
+ break;
+ case DWC_OTG_HC_XFER_BABBLE_ERR:
+ /* Don't need to update actual_length in this case. */
+ urb->error_count++;
+ frame_desc->status = -EOVERFLOW;
+ break;
+ case DWC_OTG_HC_XFER_XACT_ERR:
+ urb->error_count++;
+ frame_desc->status = -EPROTO;
+ frame_desc->actual_length =
+ get_actual_xfer_length(hc, regs, qtd, status, NULL);
+ default:
+ pr_err("%s: Unhandled halt_status (%d)\n", __func__, status);
+ BUG();
+ break;
+ }
+
+ if (++qtd->isoc_frame_index == urb->number_of_packets) {
+ /*
+ * urb->status is not used for isoc transfers.
+ * The individual frame_desc statuses are used instead.
+ */
+ dwc_otg_hcd_complete_urb(hcd, urb, 0);
+ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
+ } else {
+ ret_val = DWC_OTG_HC_XFER_COMPLETE;
+ }
+ return ret_val;
+}
+
+/**
+ * Releases a host channel for use by other transfers. Attempts to select and
+ * queue more transactions since at least one host channel is available.
+ */
+static void release_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ struct dwc_qtd *qtd,
+ enum dwc_halt_status halt_status, int *must_free)
+{
+ enum dwc_transaction_type tr_type;
+ int free_qtd;
+ int deact = 1;
+ struct dwc_qh *qh;
+ int retry_delay = 1;
+
+ switch (halt_status) {
+ case DWC_OTG_HC_XFER_NYET:
+ case DWC_OTG_HC_XFER_NAK:
+ if (halt_status == DWC_OTG_HC_XFER_NYET)
+ retry_delay = nyet_deferral_delay;
+ else
+ retry_delay = nak_deferral_delay;
+ free_qtd = 0;
+ if (deferral_on && hc->do_split) {
+ qh = hc->qh;
+ if (qh)
+ deact = dwc_otg_hcd_qh_deferr(hcd, qh,
+ retry_delay);
+ }
+ break;
+ case DWC_OTG_HC_XFER_URB_COMPLETE:
+ free_qtd = 1;
+ break;
+ case DWC_OTG_HC_XFER_AHB_ERR:
+ case DWC_OTG_HC_XFER_STALL:
+ case DWC_OTG_HC_XFER_BABBLE_ERR:
+ free_qtd = 1;
+ break;
+ case DWC_OTG_HC_XFER_XACT_ERR:
+ if (qtd->error_count >= 3) {
+ free_qtd = 1;
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPROTO);
+ } else {
+ free_qtd = 0;
+ }
+ break;
+ case DWC_OTG_HC_XFER_URB_DEQUEUE:
+ /*
+ * The QTD has already been removed and the QH has been
+ * deactivated. Don't want to do anything except release the
+ * host channel and try to queue more transfers.
+ */
+ goto cleanup;
+ case DWC_OTG_HC_XFER_NO_HALT_STATUS:
+ pr_err("%s: No halt_status, channel %d\n", __func__,
+ hc->hc_num);
+ free_qtd = 0;
+ break;
+ default:
+ free_qtd = 0;
+ break;
+ }
+ if (free_qtd)
+ /* must_free pre-initialized to zero */
+ *must_free = 1;
+ if (deact)
+ deactivate_qh(hcd, hc->qh, free_qtd);
+
+cleanup:
+ /*
+ * Release the host channel for use by other transfers. The cleanup
+ * function clears the channel interrupt enables and conditions, so
+ * there's no need to clear the Channel Halted interrupt separately.
+ */
+ dwc_otg_hc_cleanup(hcd->core_if, hc);
+ list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list);
+ hcd->available_host_channels++;
+ /* Try to queue more transfers now that there's a free channel. */
+ if (!erratum_usb09_patched) {
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE)
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+}
+
+/**
+ * Halts a host channel. If the channel cannot be halted immediately because
+ * the request queue is full, this function ensures that the FIFO empty
+ * interrupt for the appropriate queue is enabled so that the halt request can
+ * be queued when there is space in the request queue.
+ *
+ * This function may also be called in DMA mode. In that case, the channel is
+ * simply released since the core always halts the channel automatically in
+ * DMA mode.
+ */
+static void halt_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ struct dwc_qtd *qtd, enum dwc_halt_status halt_status,
+ int *must_free)
+{
+ if (hcd->core_if->dma_enable) {
+ release_channel(hcd, hc, qtd, halt_status, must_free);
+ return;
+ }
+
+ /* Slave mode processing... */
+ dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+ if (hc->halt_on_queue) {
+ u32 gintmsk = 0;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+ hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+ /*
+ * Make sure the Non-periodic Tx FIFO empty interrupt
+ * is enabled so that the non-periodic schedule will
+ * be processed.
+ */
+ gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify(gintmsk_reg(hcd), 0, 0, gintmsk);
+ } else {
+ /*
+ * Move the QH from the periodic queued schedule to
+ * the periodic assigned schedule. This allows the
+ * halt to be queued when the periodic schedule is
+ * processed.
+ */
+ list_move(&hc->qh->qh_list_entry,
+ &hcd->periodic_sched_assigned);
+
+ /*
+ * Make sure the Periodic Tx FIFO Empty interrupt is
+ * enabled so that the periodic schedule will be
+ * processed.
+ */
+ gintmsk |= DWC_INTMSK_P_TXFIFO_EMPTY;
+ dwc_reg_modify(gintmsk_reg(hcd), 0, 0, gintmsk);
+ }
+ }
+}
+
+/**
+ * Performs common cleanup for non-periodic transfers after a Transfer
+ * Complete interrupt. This function should be called after any endpoint type
+ * specific handling is finished to release the host channel.
+ */
+static void complete_non_periodic_xfer(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ ulong regs, struct dwc_qtd *qtd,
+ enum dwc_halt_status halt_status,
+ int *must_free)
+{
+ u32 hcint;
+
+ qtd->error_count = 0;
+ hcint = dwc_reg_read(regs, DWC_HCINT);
+ if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
+ u32 hcint_clear = 0;
+
+ hcint_clear = DWC_HCINT_NYET_RESP_REC_RW(hcint_clear, 1);
+ /*
+ * Got a NYET on the last transaction of the transfer. This
+ * means that the endpoint should be in the PING state at the
+ * beginning of the next transfer.
+ */
+ hc->qh->ping_state = 1;
+ dwc_reg_write(regs, DWC_HCINT, hcint_clear);
+ }
+
+ /*
+ * Always halt and release the host channel to make it available for
+ * more transfers. There may still be more phases for a control
+ * transfer or more data packets for a bulk transfer at this point,
+ * but the host channel is still halted. A channel will be reassigned
+ * to the transfer when the non-periodic schedule is processed after
+ * the channel is released. This allows transactions to be queued
+ * properly via dwc_otg_hcd_queue_transactions, which also enables the
+ * Tx FIFO Empty interrupt if necessary.
+ *
+ * IN transfers in Slave mode require an explicit disable to
+ * halt the channel. (In DMA mode, this call simply releases
+ * the channel.)
+ *
+ * The channel is automatically disabled by the core for OUT
+ * transfers in Slave mode.
+ */
+ if (hc->ep_is_in)
+ halt_channel(hcd, hc, qtd, halt_status, must_free);
+ else
+ release_channel(hcd, hc, qtd, halt_status, must_free);
+}
+
+/**
+ * Performs common cleanup for periodic transfers after a Transfer Complete
+ * interrupt. This function should be called after any endpoint type specific
+ * handling is finished to release the host channel.
+ */
+static void complete_periodic_xfer(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ ulong regs, struct dwc_qtd *qtd,
+ enum dwc_halt_status halt_status,
+ int *must_free)
+{
+ u32 hctsiz = 0;
+
+ hctsiz = dwc_reg_read(regs, DWC_HCTSIZ);
+ qtd->error_count = 0;
+
+ /*
+ * For OUT transfers and 0 packet count, the Core halts the channel,
+ * otherwise, Flush any outstanding requests from the Tx queue.
+ */
+ if (!hc->ep_is_in || (DWC_HCTSIZ_PKT_CNT_RD(hctsiz) == 0))
+ release_channel(hcd, hc, qtd, halt_status, must_free);
+ else
+ halt_channel(hcd, hc, qtd, halt_status, must_free);
+}
+
+/**
+ * Handles a host channel Transfer Complete interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_xfercomp_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ ulong regs, struct dwc_qtd *qtd,
+ int *must_free)
+{
+ int urb_xfer_done;
+ enum dwc_halt_status halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ struct urb *urb = qtd->urb;
+ int pipe_type = usb_pipetype(urb->pipe);
+ int status = -EINPROGRESS;
+ u32 hcintmsk = 0;
+
+ /* Handle xfer complete on CSPLIT. */
+ if (hc->qh->do_split)
+ qtd->complete_split = 0;
+
+ /* Update the QTD and URB states. */
+ switch (pipe_type) {
+ case PIPE_CONTROL:
+ switch (qtd->control_phase) {
+ case DWC_OTG_CONTROL_SETUP:
+ if (urb->transfer_buffer_length > 0)
+ qtd->control_phase = DWC_OTG_CONTROL_DATA;
+ else
+ qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ break;
+ case DWC_OTG_CONTROL_DATA:
+ urb_xfer_done = update_urb_state_xfer_comp(hc, regs,
+ urb, qtd,
+ &status);
+ if (urb_xfer_done)
+ qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+ else
+ save_data_toggle(hc, regs, qtd);
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ break;
+ case DWC_OTG_CONTROL_STATUS:
+ if (status == -EINPROGRESS)
+ status = 0;
+ dwc_otg_hcd_complete_urb(hcd, urb, status);
+ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+ break;
+ }
+ complete_non_periodic_xfer(hcd, hc, regs, qtd,
+ halt_status, must_free);
+ break;
+ case PIPE_BULK:
+ urb_xfer_done = update_urb_state_xfer_comp(hc, regs, urb, qtd,
+ &status);
+ if (urb_xfer_done) {
+ dwc_otg_hcd_complete_urb(hcd, urb, status);
+ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+ } else {
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ }
+
+ save_data_toggle(hc, regs, qtd);
+ complete_non_periodic_xfer(hcd, hc, regs, qtd,
+ halt_status, must_free);
+ break;
+ case PIPE_INTERRUPT:
+ update_urb_state_xfer_comp(hc, regs, urb, qtd, &status);
+ /*
+ * Interrupt URB is done on the first transfer complete
+ * interrupt.
+ */
+ dwc_otg_hcd_complete_urb(hcd, urb, status);
+ save_data_toggle(hc, regs, qtd);
+ complete_periodic_xfer(hcd, hc, regs, qtd,
+ DWC_OTG_HC_XFER_URB_COMPLETE, must_free);
+ break;
+ case PIPE_ISOCHRONOUS:
+ if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+ halt_status = update_isoc_urb_state(hcd, hc, regs, qtd,
+ DWC_OTG_HC_XFER_COMPLETE);
+ }
+ complete_periodic_xfer(hcd, hc, regs, qtd,
+ halt_status, must_free);
+ break;
+ }
+
+ /* disable xfercompl */
+ hcintmsk = DWC_HCINTMSK_TXFER_CMPL_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel STALL interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int handle_hc_stall_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ struct urb *urb = qtd->urb;
+ int pipe_type = usb_pipetype(urb->pipe);
+ u32 hcintmsk = 0;
+
+ if (pipe_type == PIPE_CONTROL)
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPIPE);
+
+ if (pipe_type == PIPE_BULK || pipe_type == PIPE_INTERRUPT) {
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPIPE);
+ /*
+ * USB protocol requires resetting the data toggle for bulk
+ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
+ * setup command is issued to the endpoint. Anticipate the
+ * CLEAR_FEATURE command since a STALL has occurred and reset
+ * the data toggle now.
+ */
+ hc->qh->data_toggle = 0;
+ }
+
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL, must_free);
+ /* disable stall */
+ hcintmsk = DWC_HCINTMSK_STALL_RESP_REC_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Updates the state of the URB when a transfer has been stopped due to an
+ * abnormal condition before the transfer completes. Modifies the
+ * actual_length field of the URB to reflect the number of bytes that have
+ * actually been transferred via the host channel.
+ */
+static void update_urb_state_xfer_intr(struct dwc_hc *hc,
+ u32 regs, struct urb *urb,
+ struct dwc_qtd *qtd,
+ enum dwc_halt_status sts)
+{
+ u32 xfr_len = get_actual_xfer_length(hc, regs, qtd, sts, NULL);
+ urb->actual_length += xfr_len;
+}
+
+/**
+ * Handles a host channel NAK interrupt. This handler may be called in either
+ * DMA mode or Slave mode.
+ */
+static int handle_hc_nak_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ u32 hcintmsk = 0;
+
+ /*
+ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+ * interrupt. Re-start the SSPLIT transfer.
+ */
+ if (hc->do_split) {
+ if (hc->complete_split)
+ qtd->error_count = 0;
+
+ qtd->complete_split = 0;
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+ goto handle_nak_done;
+ }
+ switch (usb_pipetype(qtd->urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (hcd->core_if->dma_enable && hc->ep_is_in) {
+ /*
+ * NAK interrupts are enabled on bulk/control IN
+ * transfers in DMA mode for the sole purpose of
+ * resetting the error count after a transaction error
+ * occurs. The core will continue transferring data.
+ */
+ qtd->error_count = 0;
+ goto handle_nak_done;
+ }
+
+ /*
+ * NAK interrupts normally occur during OUT transfers in DMA
+ * or Slave mode. For IN transfers, more requests will be
+ * queued as request queue space is available.
+ */
+ qtd->error_count = 0;
+ if (!hc->qh->ping_state) {
+ update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+ DWC_OTG_HC_XFER_NAK);
+
+ save_data_toggle(hc, regs, qtd);
+ if (qtd->urb->dev->speed == USB_SPEED_HIGH)
+ hc->qh->ping_state = 1;
+ }
+
+ /*
+ * Halt the channel so the transfer can be re-started from
+ * the appropriate point or the PING protocol will
+ * start/continue.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+ break;
+ case PIPE_INTERRUPT:
+ qtd->error_count = 0;
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+ break;
+ case PIPE_ISOCHRONOUS:
+ /* Should never get called for isochronous transfers. */
+ BUG();
+ break;
+ }
+
+handle_nak_done:
+ /* disable nak */
+ hcintmsk = DWC_HCINTMSK_NAK_RESP_REC_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Helper function for handle_hc_ack_intr(). Sets the split values for an ACK
+ * on SSPLIT for ISOC OUT.
+ */
+static void set_isoc_out_vals(struct dwc_hc *hc, struct dwc_qtd *qtd)
+{
+ struct usb_iso_packet_descriptor *frame_desc;
+
+ switch (hc->xact_pos) {
+ case DWC_HCSPLIT_XACTPOS_ALL:
+ break;
+ case DWC_HCSPLIT_XACTPOS_END:
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ qtd->isoc_split_offset = 0;
+ break;
+ case DWC_HCSPLIT_XACTPOS_BEGIN:
+ case DWC_HCSPLIT_XACTPOS_MID:
+ /*
+ * For BEGIN or MID, calculate the length for the next
+ * microframe to determine the correct SSPLIT token, either MID
+ * or END.
+ */
+ frame_desc = &qtd->urb->iso_frame_desc[qtd->isoc_frame_index];
+ qtd->isoc_split_offset += 188;
+
+ if ((frame_desc->length - qtd->isoc_split_offset) <= 188)
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_END;
+ else
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_MID;
+
+ break;
+ }
+}
+
+/**
+ * Handles a host channel ACK interrupt. This interrupt is enabled when
+ * performing the PING protocol in Slave mode, when errors occur during
+ * either Slave mode or DMA mode, and during Start Split transactions.
+ */
+static int handle_hc_ack_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ u32 hcintmsk = 0;
+
+ if (hc->do_split) {
+ /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */
+ if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP)
+ qtd->ssplit_out_xfer_count = hc->xfer_len;
+
+ /* Don't need complete for isochronous out transfers. */
+ if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in))
+ qtd->complete_split = 1;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)
+ set_isoc_out_vals(hc, qtd);
+ else
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK,
+ must_free);
+ } else {
+ qtd->error_count = 0;
+ if (hc->qh->ping_state) {
+ hc->qh->ping_state = 0;
+
+ /*
+ * Halt the channel so the transfer can be re-started
+ * from the appropriate point. This only happens in
+ * Slave mode. In DMA mode, the ping_state is cleared
+ * when the transfer is started because the core
+ * automatically executes the PING, then the transfer.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK,
+ must_free);
+ }
+ }
+
+ /*
+ * If the ACK occurred when _not_ in the PING state, let the channel
+ * continue transferring data after clearing the error count.
+ */
+ /* disable ack */
+ hcintmsk = DWC_HCINTMSK_ACK_RESP_REC_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel NYET interrupt. This interrupt should only occur on
+ * Bulk and Control OUT endpoints and for complete split transactions. If a
+ * NYET occurs at the same time as a Transfer Complete interrupt, it is
+ * handled in the xfercomp interrupt handler, not here. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_nyet_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ u32 hcintmsk = 0;
+ u32 hcint_clear = 0;
+
+ /*
+ * NYET on CSPLIT
+ * re-do the CSPLIT immediately on non-periodic
+ */
+ if (hc->do_split && hc->complete_split) {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ int frnum =
+ dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd
+ (hcd));
+ if (dwc_full_frame_num(frnum) !=
+ dwc_full_frame_num(hc->qh->sched_frame)) {
+ qtd->complete_split = 0;
+ halt_channel(hcd, hc, qtd,
+ DWC_OTG_HC_XFER_XACT_ERR,
+ must_free);
+ goto handle_nyet_done;
+ }
+ }
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET, must_free);
+ goto handle_nyet_done;
+ }
+ hc->qh->ping_state = 1;
+ qtd->error_count = 0;
+ update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+ DWC_OTG_HC_XFER_NYET);
+ save_data_toggle(hc, regs, qtd);
+ /*
+ * Halt the channel and re-start the transfer so the PING
+ * protocol will start.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET, must_free);
+
+handle_nyet_done:
+ /* disable nyet */
+ hcintmsk = DWC_HCINTMSK_NYET_RESP_REC_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+ /* clear nyet */
+ hcint_clear = DWC_HCINT_NYET_RESP_REC_RW(hcint_clear, 1);
+ dwc_reg_write(regs, DWC_HCINT, hcint_clear);
+ return 1;
+}
+
+/**
+ * Handles a host channel babble interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int handle_hc_babble_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ u32 hcintmsk = 0;
+
+ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EOVERFLOW);
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR,
+ must_free);
+ } else {
+ enum dwc_halt_status halt_status;
+ halt_status = update_isoc_urb_state(hcd, hc, regs, qtd,
+ DWC_OTG_HC_XFER_BABBLE_ERR);
+ halt_channel(hcd, hc, qtd, halt_status, must_free);
+ }
+ /* disable bblerr */
+ hcintmsk = DWC_HCINTMSK_BBL_ERR_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+ return 1;
+}
+
+/**
+ * Handles a host channel AHB error interrupt. This handler is only called in
+ * DMA mode.
+ */
+static int handle_hc_ahberr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd)
+{
+ u32 hcchar;
+ u32 hcsplt;
+ u32 hctsiz = 0;
+ u32 hcdma;
+ struct urb *urb = qtd->urb;
+ u32 hcintmsk = 0;
+
+ hcchar = dwc_reg_read(regs, DWC_HCCHAR);
+ hcsplt = dwc_reg_read(regs, DWC_HCSPLT);
+ hctsiz = dwc_reg_read(regs, DWC_HCTSIZ);
+ hcdma = dwc_reg_read(regs, DWC_HCDMA);
+
+ pr_err("AHB ERROR, Channel %d\n", hc->hc_num);
+ pr_err(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt);
+ pr_err(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz, hcdma);
+
+ pr_err(" Device address: %d\n", usb_pipedevice(urb->pipe));
+ pr_err(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
+ (usb_pipein(urb->pipe) ? "IN" : "OUT"));
+
+ pr_err(" Endpoint type: %s\n", pipetype_str(urb->pipe));
+ pr_err(" Speed: %s\n", dev_speed_str(urb->dev->speed));
+ pr_err(" Max packet size: %d\n",
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+ pr_err(" Data buffer length: %d\n", urb->transfer_buffer_length);
+ pr_err(" Transfer buffer: %p, Transfer DMA: %p\n",
+ urb->transfer_buffer, (void *)(u32) urb->transfer_dma);
+ pr_err(" Setup buffer: %p, Setup DMA: %p\n",
+ urb->setup_packet, (void *)(u32) urb->setup_dma);
+ pr_err(" Interval: %d\n", urb->interval);
+
+ dwc_otg_hcd_complete_urb(hcd, urb, -EIO);
+
+ /*
+ * Force a channel halt. Don't call halt_channel because that won't
+ * write to the HCCHARn register in DMA mode to force the halt.
+ */
+ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
+ /* disable ahberr */
+ hcintmsk = DWC_HCINTMSK_AHB_ERR_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel transaction error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_xacterr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+ enum dwc_halt_status status = DWC_OTG_HC_XFER_XACT_ERR;
+ u32 hcintmsk = 0;
+
+ switch (usb_pipetype(qtd->urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ qtd->error_count++;
+ if (!hc->qh->ping_state) {
+ update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+ status);
+ save_data_toggle(hc, regs, qtd);
+
+ if (!hc->ep_is_in && qtd->urb->dev->speed ==
+ USB_SPEED_HIGH)
+ hc->qh->ping_state = 1;
+ }
+ /*
+ * Halt the channel so the transfer can be re-started from
+ * the appropriate point or the PING protocol will start.
+ */
+ halt_channel(hcd, hc, qtd, status, must_free);
+ break;
+ case PIPE_INTERRUPT:
+ qtd->error_count++;
+ if (hc->do_split && hc->complete_split)
+ qtd->complete_split = 0;
+
+ halt_channel(hcd, hc, qtd, status, must_free);
+ break;
+ case PIPE_ISOCHRONOUS:
+ status = update_isoc_urb_state(hcd, hc, regs, qtd, status);
+ halt_channel(hcd, hc, qtd, status, must_free);
+ break;
+ }
+ /* Disable xacterr */
+ hcintmsk = DWC_HCINTMSK_TRANS_ERR_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel frame overrun interrupt. This handler may be called
+ * in either DMA mode or Slave mode.
+ */
+static int handle_hc_frmovrun_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd,
+ int *must_free)
+{
+ enum dwc_halt_status status = DWC_OTG_HC_XFER_FRAME_OVERRUN;
+ u32 hcintmsk = 0;
+
+ switch (usb_pipetype(qtd->urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ break;
+ case PIPE_INTERRUPT:
+ halt_channel(hcd, hc, qtd, status, must_free);
+ break;
+ case PIPE_ISOCHRONOUS:
+ status = update_isoc_urb_state(hcd, hc, regs, qtd, status);
+ halt_channel(hcd, hc, qtd, status, must_free);
+ break;
+ }
+ /* Disable frmovrun */
+ hcintmsk = DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel data toggle error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_datatglerr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ u32 regs, struct dwc_qtd *qtd)
+{
+ u32 hcintmsk = 0;
+
+ if (hc->ep_is_in)
+ qtd->error_count = 0;
+ else
+ pr_err("Data Toggle Error on OUT transfer, channel "
+ "%d\n", hc->hc_num);
+
+ /* disable datatglerr */
+ hcintmsk = DWC_HCINTMSK_DATA_TOG_ERR_RW(hcintmsk, 1);
+ dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
+
+ return 1;
+}
+
+/**
+ * Handles a host Channel Halted interrupt in DMA mode. This handler
+ * determines the reason the channel halted and proceeds accordingly.
+ */
+static void handle_hc_chhltd_intr_dma(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ ulong regs, struct dwc_qtd *qtd,
+ int *must_free)
+{
+ u32 hcint;
+ u32 hcintmsk = 0;
+
+ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+ hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+ /*
+ * Just release the channel. A dequeue can happen on a
+ * transfer timeout. In the case of an AHB Error, the channel
+ * was forced to halt because there's no way to gracefully
+ * recover.
+ */
+ release_channel(hcd, hc, qtd, hc->halt_status, must_free);
+ return;
+ }
+
+ /* Read the HCINTn register to determine the cause for the halt. */
+ hcint = dwc_reg_read(regs, DWC_HCINT);
+ hcintmsk = dwc_reg_read(regs, DWC_HCINTMSK);
+ if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
+ /*
+ * This is here because of a possible hardware bug. Spec
+ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
+ * interrupt w/ACK bit set should occur, but I only see the
+ * XFERCOMP bit, even with it masked out. This is a workaround
+ * for that behavior. Should fix this when hardware is fixed.
+ */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)
+ handle_hc_ack_intr(hcd, hc, regs, qtd, must_free);
+
+ handle_hc_xfercomp_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_STALL_RESP_REC_RD(hcint)) {
+ handle_hc_stall_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_TRANS_ERR_RD(hcint)) {
+ /*
+ * Must handle xacterr before nak or ack. Could get a xacterr
+ * at the same time as either of these on a BULK/CONTROL OUT
+ * that started with a PING. The xacterr takes precedence.
+ */
+ handle_hc_xacterr_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
+ /*
+ * Must handle nyet before nak or ack. Could get a nyet at the
+ * same time as either of those on a BULK/CONTROL OUT that
+ * started with a PING. The nyet takes precedence.
+ */
+ handle_hc_nyet_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_BBL_ERR_RD(hcint)) {
+ handle_hc_babble_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint)) {
+ handle_hc_frmovrun_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_DATA_TOG_ERR_RD(hcint)) {
+ handle_hc_datatglerr_intr(hcd, hc, regs, qtd);
+ hc->qh->data_toggle = 0;
+ halt_channel(hcd, hc, qtd, hc->halt_status, must_free);
+ } else if (DWC_HCINT_NAK_RESP_REC_RD(hcint) &&
+ !DWC_HCINTMSK_NAK_RESP_REC_RD(hcintmsk)) {
+ /*
+ * If nak is not masked, it's because a non-split IN transfer
+ * is in an error state. In that case, the nak is handled by
+ * the nak interrupt handler, not here. Handle nak here for
+ * BULK/CONTROL OUT transfers, which halt on a NAK to allow
+ * rewinding the buffer pointer.
+ */
+ handle_hc_nak_intr(hcd, hc, regs, qtd, must_free);
+ } else if (DWC_HCINT_ACK_RESP_REC_RD(hcint) &&
+ !DWC_HCINTMSK_ACK_RESP_REC_RD(hcintmsk)) {
+ /*
+ * If ack is not masked, it's because a non-split IN transfer
+ * is in an error state. In that case, the ack is handled by
+ * the ack interrupt handler, not here. Handle ack here for
+ * split transfers. Start splits halt on ACK.
+ */
+ handle_hc_ack_intr(hcd, hc, regs, qtd, must_free);
+ } else {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * A periodic transfer halted with no other channel
+ * interrupts set. Assume it was halted by the core
+ * because it could not be completed in its scheduled
+ * (micro)frame.
+ */
+ halt_channel(hcd, hc, qtd,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+ must_free);
+ } else {
+ pr_err("%s: Channel %d, DMA Mode -- ChHltd "
+ "set, but reason for halting is unknown, "
+ "hcint 0x%08x, intsts 0x%08x\n",
+ __func__, hc->hc_num, hcint,
+ dwc_reg_read(gintsts_reg(hcd), 0));
+ }
+ }
+}
+
+/**
+ * Handles a host channel Channel Halted interrupt.
+ *
+ * In slave mode, this handler is called only when the driver specifically
+ * requests a halt. This occurs during handling other host channel interrupts
+ * (e.g. nak, xacterr, stall, nyet, etc.).
+ *
+ * In DMA mode, this is the interrupt that occurs when the core has finished
+ * processing a transfer on a channel. Other host channel interrupts (except
+ * ahberr) are disabled in DMA mode.
+ */
+static int handle_hc_chhltd_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+ ulong regs, struct dwc_qtd *qtd, int *must_free)
+{
+ if (hcd->core_if->dma_enable)
+ handle_hc_chhltd_intr_dma(hcd, hc, regs, qtd, must_free);
+ else
+ release_channel(hcd, hc, qtd, hc->halt_status, must_free);
+
+ return 1;
+}
+
+/* Handles interrupt for a specific Host Channel */
+static int dwc_otg_hcd_handle_hc_n_intr(struct dwc_hcd *hcd, u32 num)
+{
+ int must_free = 0;
+ int retval = 0;
+ u32 hcint;
+ u32 hcintmsk = 0;
+ struct dwc_hc *hc;
+ ulong hc_regs;
+ struct dwc_qtd *qtd;
+
+ hc = hcd->hc_ptr_array[num];
+ hc_regs = hcd->core_if->host_if->hc_regs[num];
+ qtd = list_entry(hc->qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+
+ hcint = dwc_reg_read(hc_regs, DWC_HCINT);
+ hcintmsk = dwc_reg_read(hc_regs, DWC_HCINTMSK);
+
+ hcint = hcint & hcintmsk;
+ if (!hcd->core_if->dma_enable && DWC_HCINT_CHAN_HALTED_RD(hcint)
+ && hcint != 0x2)
+ hcint = DWC_HCINT_CHAN_HALTED_RW(hcint, 0);
+
+ if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
+ retval |= handle_hc_xfercomp_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ /*
+ * If NYET occurred at same time as Xfer Complete, the NYET is
+ * handled by the Xfer Complete interrupt handler. Don't want
+ * to call the NYET interrupt handler in this case.
+ */
+ hcint = DWC_HCINT_NYET_RESP_REC_RW(hcint, 0);
+ }
+
+ if (DWC_HCINT_CHAN_HALTED_RD(hcint))
+ retval |= handle_hc_chhltd_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_AHB_ERR_RD(hcint))
+ retval |= handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+ if (DWC_HCINT_STALL_RESP_REC_RD(hcint))
+ retval |= handle_hc_stall_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_NAK_RESP_REC_RD(hcint))
+ retval |= handle_hc_nak_intr(hcd, hc, hc_regs, qtd, &must_free);
+ if (DWC_HCINT_ACK_RESP_REC_RD(hcint))
+ retval |= handle_hc_ack_intr(hcd, hc, hc_regs, qtd, &must_free);
+ if (DWC_HCINT_NYET_RESP_REC_RD(hcint))
+ retval |= handle_hc_nyet_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_TRANS_ERR_RD(hcint))
+ retval |= handle_hc_xacterr_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_BBL_ERR_RD(hcint))
+ retval |= handle_hc_babble_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint))
+ retval |= handle_hc_frmovrun_intr(hcd, hc, hc_regs,
+ qtd, &must_free);
+ if (DWC_HCINT_DATA_TOG_ERR_RD(hcint))
+ retval |= handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+
+ if (must_free)
+ /* Free the qtd here now that we are done using it. */
+ dwc_otg_hcd_qtd_free(qtd);
+ return retval;
+}
+
+/**
+ * This function returns the Host All Channel Interrupt register
+ */
+static inline u32 dwc_otg_read_host_all_channels_intr(struct core_if
+ *core_if)
+{
+ return dwc_reg_read(core_if->host_if->host_global_regs, DWC_HAINT);
+}
+
+/**
+ * This interrupt indicates that one or more host channels has a pending
+ * interrupt. There are multiple conditions that can cause each host channel
+ * interrupt. This function determines which conditions have occurred for each
+ * host channel interrupt and handles them appropriately.
+ */
+static int dwc_otg_hcd_handle_hc_intr(struct dwc_hcd *hcd)
+{
+ u32 i;
+ int retval = 0;
+ u32 haint;
+
+ /*
+ * Clear appropriate bits in HCINTn to clear the interrupt bit in
+ * GINTSTS
+ */
+ haint = dwc_otg_read_host_all_channels_intr(hcd->core_if);
+ for (i = 0; i < hcd->core_if->core_params->host_channels; i++)
+ if (DWC_HAINT_RD(haint) & (1 << i))
+ retval |= dwc_otg_hcd_handle_hc_n_intr(hcd, i);
+
+ return retval;
+}
+
+/* This function handles interrupts for the HCD.*/
+int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd)
+{
+ int ret = 0;
+ struct core_if *core_if = hcd->core_if;
+ u32 gintsts;
+
+ /* Check if HOST Mode */
+ if (dwc_otg_is_host_mode(core_if)) {
+ spin_lock(&hcd->lock);
+ gintsts = dwc_otg_read_core_intr(core_if);
+ if (!gintsts) {
+ spin_unlock(&hcd->lock);
+ return IRQ_NONE;
+ }
+
+ if (gintsts & DWC_INTMSK_STRT_OF_FRM)
+ ret |= dwc_otg_hcd_handle_sof_intr(hcd);
+ if (gintsts & DWC_INTMSK_RXFIFO_NOT_EMPT)
+ ret |= dwc_otg_hcd_handle_rx_status_q_level_intr(hcd);
+ if (gintsts & DWC_INTMSK_NP_TXFIFO_EMPT)
+ ret |= dwc_otg_hcd_handle_np_tx_fifo_empty_intr(hcd);
+ if (gintsts & DWC_INTMSK_HST_PORT)
+ ret |= dwc_otg_hcd_handle_port_intr(hcd);
+ if (gintsts & DWC_INTMSK_HST_CHAN)
+ ret |= dwc_otg_hcd_handle_hc_intr(hcd);
+ if (gintsts & DWC_INTMSK_P_TXFIFO_EMPTY)
+ ret |= dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(hcd);
+
+ spin_unlock(&hcd->lock);
+ }
+ return ret;
+}
diff --git a/drivers/usb/dwc/hcd_queue.c b/drivers/usb/dwc/hcd_queue.c
new file mode 100644
index 0000000..67f0409
--- /dev/null
+++ b/drivers/usb/dwc/hcd_queue.c
@@ -0,0 +1,696 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file contains the functions to manage Queue Heads and Queue
+ * Transfer Descriptors.
+ */
+
+#include "hcd.h"
+
+static inline int is_fs_ls(enum usb_device_speed speed)
+{
+ return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
+}
+
+/* Allocates memory for a QH structure. */
+static inline struct dwc_qh *dwc_otg_hcd_qh_alloc(void)
+{
+ return kmalloc(sizeof(struct dwc_qh), GFP_ATOMIC);
+}
+
+/**
+ * Initializes a QH structure to initialize the QH.
+ */
+#define SCHEDULE_SLOP 10
+static void dwc_otg_hcd_qh_init(struct dwc_hcd *hcd, struct dwc_qh *qh,
+ struct urb *urb)
+{
+ memset(qh, 0, sizeof(struct dwc_qh));
+
+ /* Initialize QH */
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ qh->ep_type = USB_ENDPOINT_XFER_CONTROL;
+ break;
+ case PIPE_BULK:
+ qh->ep_type = USB_ENDPOINT_XFER_BULK;
+ break;
+ case PIPE_ISOCHRONOUS:
+ qh->ep_type = USB_ENDPOINT_XFER_ISOC;
+ break;
+ case PIPE_INTERRUPT:
+ qh->ep_type = USB_ENDPOINT_XFER_INT;
+ break;
+ }
+
+ qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0;
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe)));
+
+ INIT_LIST_HEAD(&qh->qtd_list);
+ INIT_LIST_HEAD(&qh->qh_list_entry);
+
+ qh->channel = NULL;
+ qh->speed = urb->dev->speed;
+
+ /*
+ * FS/LS Enpoint on HS Hub NOT virtual root hub
+ */
+ qh->do_split = 0;
+ if (is_fs_ls(urb->dev->speed) && urb->dev->tt && urb->dev->tt->hub &&
+ urb->dev->tt->hub->devnum != 1)
+ qh->do_split = 1;
+
+ if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+ qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+ /* Compute scheduling parameters once and save them. */
+ u32 hprt;
+ int bytecount = dwc_hb_mult(qh->maxp) *
+ dwc_max_packet(qh->maxp);
+
+ qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed,
+ usb_pipein(urb->pipe),
+ (qh->ep_type ==
+ USB_ENDPOINT_XFER_ISOC),
+ bytecount));
+
+ /* Start in a slightly future (micro)frame. */
+ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
+ SCHEDULE_SLOP);
+ qh->interval = urb->interval;
+
+ hprt = dwc_reg_read(hcd->core_if->host_if->hprt0, 0);
+ if (DWC_HPRT0_PRT_SPD_RD(hprt) == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
+ is_fs_ls(urb->dev->speed)) {
+ qh->interval *= 8;
+ qh->sched_frame |= 0x7;
+ qh->start_split_frame = qh->sched_frame;
+ }
+ }
+}
+
+/**
+ * This function allocates and initializes a QH.
+ */
+static struct dwc_qh *dwc_otg_hcd_qh_create(struct dwc_hcd *hcd,
+ struct urb *urb)
+{
+ struct dwc_qh *qh;
+
+ /* Allocate memory */
+ qh = dwc_otg_hcd_qh_alloc();
+ if (qh == NULL)
+ return NULL;
+
+ dwc_otg_hcd_qh_init(hcd, qh, urb);
+ return qh;
+}
+
+/**
+ * Free each QTD in the QH's QTD-list then free the QH. QH should already be
+ * removed from a list. QTD list should already be empty if called from URB
+ * Dequeue.
+ */
+void dwc_otg_hcd_qh_free(struct dwc_qh *qh)
+{
+ struct dwc_qtd *qtd;
+ struct list_head *pos, *temp;
+
+ /* Free each QTD in the QTD list */
+ list_for_each_safe(pos, temp, &qh->qtd_list) {
+ list_del(pos);
+ qtd = dwc_list_to_qtd(pos);
+ dwc_otg_hcd_qtd_free(qtd);
+ }
+ kfree(qh);
+}
+
+/**
+ * Microframe scheduler
+ * track the total use in hcd->frame_usecs
+ * keep each qh use in qh->frame_usecs
+ * when surrendering the qh then donate the time back
+ */
+static const u16 max_uframe_usecs[] = { 100, 100, 100, 100, 100, 100, 30, 0 };
+
+/*
+ * called from dwc_otg_hcd.c:dwc_otg_hcd_init
+ */
+int init_hcd_usecs(struct dwc_hcd *hcd)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ hcd->frame_usecs[i] = max_uframe_usecs[i];
+
+ return 0;
+}
+
+static int find_single_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int i;
+ u16 utime;
+ int t_left;
+ int ret;
+ int done;
+
+ ret = -1;
+ utime = qh->usecs;
+ t_left = utime;
+ i = 0;
+ done = 0;
+ while (done == 0) {
+ /* At the start hcd->frame_usecs[i] = max_uframe_usecs[i]; */
+ if (utime <= hcd->frame_usecs[i]) {
+ hcd->frame_usecs[i] -= utime;
+ qh->frame_usecs[i] += utime;
+ t_left -= utime;
+ ret = i;
+ done = 1;
+ return ret;
+ } else {
+ i++;
+ if (i == 8) {
+ done = 1;
+ ret = -1;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * use this for FS apps that can span multiple uframes
+ */
+static int find_multi_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int i;
+ int j;
+ u16 utime;
+ int t_left;
+ int ret;
+ int done;
+ u16 xtime;
+
+ ret = -1;
+ utime = qh->usecs;
+ t_left = utime;
+ i = 0;
+ done = 0;
+loop:
+ while (done == 0) {
+ if (hcd->frame_usecs[i] <= 0) {
+ i++;
+ if (i == 8) {
+ done = 1;
+ ret = -1;
+ }
+ goto loop;
+ }
+
+ /*
+ * We need n consequtive slots so use j as a start slot.
+ * j plus j+1 must be enough time (for now)
+ */
+ xtime = hcd->frame_usecs[i];
+ for (j = i + 1; j < 8; j++) {
+ /*
+ * if we add this frame remaining time to xtime we may
+ * be OK, if not we need to test j for a complete frame.
+ */
+ if ((xtime + hcd->frame_usecs[j]) < utime) {
+ if (hcd->frame_usecs[j] < max_uframe_usecs[j]) {
+ j = 8;
+ ret = -1;
+ continue;
+ }
+ }
+ if (xtime >= utime) {
+ ret = i;
+ j = 8; /* stop loop with a good value ret */
+ continue;
+ }
+ /* add the frame time to x time */
+ xtime += hcd->frame_usecs[j];
+ /* we must have a fully available next frame or break */
+ if ((xtime < utime) &&
+ (hcd->frame_usecs[j] == max_uframe_usecs[j])) {
+ ret = -1;
+ j = 8; /* stop loop with a bad value ret */
+ continue;
+ }
+ }
+ if (ret >= 0) {
+ t_left = utime;
+ for (j = i; (t_left > 0) && (j < 8); j++) {
+ t_left -= hcd->frame_usecs[j];
+ if (t_left <= 0) {
+ qh->frame_usecs[j] +=
+ hcd->frame_usecs[j] + t_left;
+ hcd->frame_usecs[j] = -t_left;
+ ret = i;
+ done = 1;
+ } else {
+ qh->frame_usecs[j] +=
+ hcd->frame_usecs[j];
+ hcd->frame_usecs[j] = 0;
+ }
+ }
+ } else {
+ i++;
+ if (i == 8) {
+ done = 1;
+ ret = -1;
+ }
+ }
+ }
+ return ret;
+}
+
+static int find_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int ret = -1;
+
+ if (qh->speed == USB_SPEED_HIGH)
+ /* if this is a hs transaction we need a full frame */
+ ret = find_single_uframe(hcd, qh);
+ else
+ /* FS transaction may need a sequence of frames */
+ ret = find_multi_uframe(hcd, qh);
+
+ return ret;
+}
+
+/**
+ * Checks that the max transfer size allowed in a host channel is large enough
+ * to handle the maximum data transfer in a single (micro)frame for a periodic
+ * transfer.
+ */
+static int check_max_xfer_size(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int status = 0;
+ u32 max_xfer_size;
+ u32 max_channel_xfer_size;
+
+ max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
+ max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
+
+ if (max_xfer_size > max_channel_xfer_size) {
+ pr_notice("%s: Periodic xfer length %d > max xfer "
+ "length for channel %d\n", __func__, max_xfer_size,
+ max_channel_xfer_size);
+ status = -ENOSPC;
+ }
+
+ return status;
+}
+
+/**
+ * Schedules an interrupt or isochronous transfer in the periodic schedule.
+ */
+static int schedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int status;
+ struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+ int frame;
+
+ status = find_uframe(hcd, qh);
+ frame = -1;
+ if (status == 0) {
+ frame = 7;
+ } else {
+ if (status > 0)
+ frame = status - 1;
+ }
+ /* Set the new frame up */
+ if (frame > -1) {
+ qh->sched_frame &= ~0x7;
+ qh->sched_frame |= (frame & 7);
+ }
+ if (status != -1)
+ status = 0;
+ if (status) {
+ pr_notice("%s: Insufficient periodic bandwidth for "
+ "periodic transfer.\n", __func__);
+ return status;
+ }
+ status = check_max_xfer_size(hcd, qh);
+ if (status) {
+ pr_notice("%s: Channel max transfer size too small "
+ "for periodic transfer.\n", __func__);
+ return status;
+ }
+ /* Always start in the inactive schedule. */
+ list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
+
+ /* Update claimed usecs per (micro)frame. */
+ hcd->periodic_usecs += qh->usecs;
+
+ /*
+ * Update average periodic bandwidth claimed and # periodic reqs for
+ * usbfs.
+ */
+ bus->bandwidth_allocated += qh->usecs / qh->interval;
+
+ if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+ bus->bandwidth_int_reqs++;
+ else
+ bus->bandwidth_isoc_reqs++;
+
+ return status;
+}
+
+/**
+ * This function adds a QH to either the non periodic or periodic schedule if
+ * it is not already in the schedule. If the QH is already in the schedule, no
+ * action is taken.
+ */
+static int dwc_otg_hcd_qh_add(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ int status = 0;
+
+ /* QH may already be in a schedule. */
+ if (!list_empty(&qh->qh_list_entry))
+ goto done;
+ /*
+ * Add the new QH to the appropriate schedule. For non-periodic, always
+ * start in the inactive schedule.
+ */
+ if (dwc_qh_is_non_per(qh))
+ list_add_tail(&qh->qh_list_entry,
+ &hcd->non_periodic_sched_inactive);
+ else
+ status = schedule_periodic(hcd, qh);
+
+done:
+ return status;
+}
+
+/**
+ * This function adds a QH to the non periodic deferred schedule.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int dwc_otg_hcd_qh_add_deferred(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ if (!list_empty(&qh->qh_list_entry))
+ /* QH already in a schedule. */
+ goto done;
+
+ /* Add the new QH to the non periodic deferred schedule */
+ if (dwc_qh_is_non_per(qh))
+ list_add_tail(&qh->qh_list_entry,
+ &hcd->non_periodic_sched_deferred);
+done:
+ return 0;
+}
+
+/**
+ * Removes an interrupt or isochronous transfer from the periodic schedule.
+ */
+static void deschedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+ int i;
+
+ list_del_init(&qh->qh_list_entry);
+ /* Update claimed usecs per (micro)frame. */
+ hcd->periodic_usecs -= qh->usecs;
+ for (i = 0; i < 8; i++) {
+ hcd->frame_usecs[i] += qh->frame_usecs[i];
+ qh->frame_usecs[i] = 0;
+ }
+ /*
+ * Update average periodic bandwidth claimed and # periodic reqs for
+ * usbfs.
+ */
+ bus->bandwidth_allocated -= qh->usecs / qh->interval;
+
+ if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+ bus->bandwidth_int_reqs--;
+ else
+ bus->bandwidth_isoc_reqs--;
+}
+
+/**
+ * Removes a QH from either the non-periodic or periodic schedule. Memory is
+ * not freed.
+ */
+void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ /* Do nothing if QH is not in a schedule */
+ if (list_empty(&qh->qh_list_entry))
+ return;
+
+ if (dwc_qh_is_non_per(qh)) {
+ if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry)
+ hcd->non_periodic_qh_ptr =
+ hcd->non_periodic_qh_ptr->next;
+ list_del_init(&qh->qh_list_entry);
+ } else {
+ deschedule_periodic(hcd, qh);
+ }
+}
+
+/**
+ * Defers a QH. For non-periodic QHs, removes the QH from the active
+ * non-periodic schedule. The QH is added to the deferred non-periodic
+ * schedule if any QTDs are still attached to the QH.
+ */
+int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh, int delay)
+{
+ int deact = 1;
+
+ if (dwc_qh_is_non_per(qh)) {
+ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, delay);
+ qh->channel = NULL;
+ qh->qtd_in_process = NULL;
+ deact = 0;
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ if (!list_empty(&qh->qtd_list))
+ /* Add back to deferred non-periodic schedule. */
+ dwc_otg_hcd_qh_add_deferred(hcd, qh);
+ }
+ return deact;
+}
+
+/**
+ * Schedule the next continuing periodic split transfer
+ */
+static void sched_next_per_split_xfr(struct dwc_qh *qh, u16 fr_num,
+ int sched_split)
+{
+ if (sched_split) {
+ qh->sched_frame = fr_num;
+ if (dwc_frame_num_le(fr_num,
+ dwc_frame_num_inc(qh->start_split_frame,
+ 1))) {
+ /*
+ * Allow one frame to elapse after start split
+ * microframe before scheduling complete split, but DONT
+ * if we are doing the next start split in the
+ * same frame for an ISOC out.
+ */
+ if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+ qh->ep_is_in)
+ qh->sched_frame =
+ dwc_frame_num_inc(qh->sched_frame, 1);
+ }
+ } else {
+ qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame,
+ qh->interval);
+
+ if (dwc_frame_num_le(qh->sched_frame, fr_num))
+ qh->sched_frame = fr_num;
+ qh->sched_frame |= 0x7;
+ qh->start_split_frame = qh->sched_frame;
+ }
+}
+
+/**
+ * Deactivates a periodic QH. The QH is removed from the periodic queued
+ * schedule. If there are any QTDs still attached to the QH, the QH is added to
+ * either the periodic inactive schedule or the periodic ready schedule and its
+ * next scheduled frame is calculated. The QH is placed in the ready schedule if
+ * the scheduled frame has been reached already. Otherwise it's placed in the
+ * inactive schedule. If there are no QTDs attached to the QH, the QH is
+ * completely removed from the periodic schedule.
+ */
+static void deactivate_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh,
+ int sched_next_split)
+{
+ /* unsigned long flags; */
+ u16 fr_num = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
+
+ if (qh->do_split) {
+ sched_next_per_split_xfr(qh, fr_num, sched_next_split);
+ } else {
+ qh->sched_frame = dwc_frame_num_inc(qh->sched_frame,
+ qh->interval);
+ if (dwc_frame_num_le(qh->sched_frame, fr_num))
+ qh->sched_frame = fr_num;
+ }
+
+ if (list_empty(&qh->qtd_list)) {
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ } else {
+ /*
+ * Remove from periodic_sched_queued and move to appropriate
+ * queue.
+ */
+ if (qh->sched_frame == fr_num)
+ list_move(&qh->qh_list_entry,
+ &hcd->periodic_sched_ready);
+ else
+ list_move(&qh->qh_list_entry,
+ &hcd->periodic_sched_inactive);
+ }
+}
+
+/**
+ * Deactivates a non-periodic QH. Removes the QH from the active non-periodic
+ * schedule. The QH is added to the inactive non-periodic schedule if any QTDs
+ * are still attached to the QH.
+ */
+static void deactivate_non_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ if (!list_empty(&qh->qtd_list))
+ dwc_otg_hcd_qh_add(hcd, qh);
+}
+
+/**
+ * Deactivates a QH. Determines if the QH is periodic or non-periodic and takes
+ * the appropriate action.
+ */
+void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
+ int sched_next_periodic_split)
+{
+ if (dwc_qh_is_non_per(qh))
+ deactivate_non_periodic_qh(hcd, qh);
+ else
+ deactivate_periodic_qh(hcd, qh, sched_next_periodic_split);
+}
+
+/**
+ * Initializes a QTD structure.
+ */
+static void dwc_otg_hcd_qtd_init(struct dwc_qtd *qtd, struct urb *urb)
+{
+ memset(qtd, 0, sizeof(struct dwc_qtd));
+ qtd->urb = urb;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ /*
+ * The only time the QTD data toggle is used is on the data
+ * phase of control transfers. This phase always starts with
+ * DATA1.
+ */
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+ qtd->control_phase = DWC_OTG_CONTROL_SETUP;
+ }
+
+ /* start split */
+ qtd->complete_split = 0;
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ qtd->isoc_split_offset = 0;
+
+ /* Store the qtd ptr in the urb to reference what QTD. */
+ urb->hcpriv = qtd;
+
+ INIT_LIST_HEAD(&qtd->qtd_list_entry);
+ return;
+}
+
+/* Allocates memory for a QTD structure. */
+static inline struct dwc_qtd *dwc_otg_hcd_qtd_alloc(gfp_t _mem_flags)
+{
+ return kmalloc(sizeof(struct dwc_qtd), _mem_flags);
+}
+
+/**
+ * This function allocates and initializes a QTD.
+ */
+struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb, gfp_t _mem_flags)
+{
+ struct dwc_qtd *qtd = dwc_otg_hcd_qtd_alloc(_mem_flags);
+
+ if (!qtd)
+ return NULL;
+
+ dwc_otg_hcd_qtd_init(qtd, urb);
+ return qtd;
+}
+
+/**
+ * This function adds a QTD to the QTD-list of a QH. It will find the correct
+ * QH to place the QTD into. If it does not find a QH, then it will create a
+ * new QH. If the QH to which the QTD is added is not currently scheduled, it
+ * is placed into the proper schedule based on its EP type.
+ *
+ */
+int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *hcd)
+{
+ struct usb_host_endpoint *ep;
+ struct dwc_qh *qh;
+ int retval = 0;
+ struct urb *urb = qtd->urb;
+
+ /*
+ * Get the QH which holds the QTD-list to insert to. Create QH if it
+ * doesn't exist.
+ */
+ ep = dwc_urb_to_endpoint(urb);
+
+ qh = (struct dwc_qh *)ep->hcpriv;
+ if (!qh) {
+ qh = dwc_otg_hcd_qh_create(hcd, urb);
+ if (!qh) {
+ retval = -1;
+ goto done;
+ }
+ ep->hcpriv = qh;
+ }
+ qtd->qtd_qh_ptr = qh;
+ retval = dwc_otg_hcd_qh_add(hcd, qh);
+ if (!retval)
+ list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
+
+done:
+ return retval;
+}
diff --git a/drivers/usb/dwc/param.c b/drivers/usb/dwc/param.c
new file mode 100644
index 0000000..0dc73d2
--- /dev/null
+++ b/drivers/usb/dwc/param.c
@@ -0,0 +1,180 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file provides dwc_otg driver parameter and parameter checking.
+ */
+
+#include "cil.h"
+
+/*
+ * Encapsulate the module parameter settings
+ */
+struct core_params dwc_otg_module_params = {
+ .otg_cap = -1,
+ .dma_enable = -1,
+ .dma_burst_size = -1,
+ .speed = -1,
+ .host_support_fs_ls_low_power = -1,
+ .host_ls_low_power_phy_clk = -1,
+ .enable_dynamic_fifo = -1,
+ .dev_rx_fifo_size = -1,
+ .dev_nperio_tx_fifo_size = -1,
+ .dev_perio_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1}, /* 15 */
+ .host_rx_fifo_size = -1,
+ .host_nperio_tx_fifo_size = -1,
+ .host_perio_tx_fifo_size = -1,
+ .max_transfer_size = -1,
+ .max_packet_count = -1,
+ .host_channels = -1,
+ .dev_endpoints = -1,
+ .phy_type = -1,
+ .phy_utmi_width = -1,
+ .phy_ulpi_ddr = -1,
+ .phy_ulpi_ext_vbus = -1,
+ .i2c_enable = -1,
+ .ulpi_fs_ls = -1,
+ .ts_dline = -1,
+ .en_multiple_tx_fifo = -1,
+ .dev_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1}, /* 15 */
+ .thr_ctl = -1,
+ .tx_thr_length = -1,
+ .rx_thr_length = -1,
+};
+
+/**
+ * Checks that parameter settings for the periodic Tx FIFO sizes are correct
+ * according to the hardware configuration. Sets the size to the hardware
+ * configuration if an incorrect size is detected.
+ */
+static int set_valid_perio_tx_fifo_sizes(struct core_if *core_if)
+{
+ ulong regs = (u32) core_if->core_global_regs;
+ u32 *param_size = &dwc_otg_module_params.dev_perio_tx_fifo_size[0];
+ u32 i, size;
+
+ for (i = 0; i < MAX_PERIO_FIFOS; i++, param_size++) {
+ size = dwc_reg_read(regs, DWC_DPTX_FSIZ_DIPTXF(i));
+ *param_size = size;
+ }
+ return 0;
+}
+
+/**
+ * Checks that parameter settings for the Tx FIFO sizes are correct according to
+ * the hardware configuration. Sets the size to the hardware configuration if
+ * an incorrect size is detected.
+ */
+static int set_valid_tx_fifo_sizes(struct core_if *core_if)
+{
+ ulong regs = (u32) core_if->core_global_regs;
+ u32 *param_size = &dwc_otg_module_params.dev_tx_fifo_size[0];
+ u32 i, size;
+
+ for (i = 0; i < MAX_TX_FIFOS; i++, param_size) {
+ size = dwc_reg_read(regs, DWC_DPTX_FSIZ_DIPTXF(i));
+ *param_size = size;
+ }
+ return 0;
+}
+
+/**
+ * This function is called during module intialization to verify that
+ * the module parameters are in a valid state.
+ */
+int check_parameters(struct core_if *core_if)
+{
+ /* Default values */
+ dwc_otg_module_params.otg_cap = dwc_param_otg_cap_default;
+ dwc_otg_module_params.dma_enable = dwc_param_dma_enable_default;
+ dwc_otg_module_params.speed = dwc_param_speed_default;
+ dwc_otg_module_params.host_support_fs_ls_low_power =
+ dwc_param_host_support_fs_ls_low_power_default;
+ dwc_otg_module_params.host_ls_low_power_phy_clk =
+ dwc_param_host_ls_low_power_phy_clk_default;
+ dwc_otg_module_params.phy_type = dwc_param_phy_type_default;
+ dwc_otg_module_params.phy_ulpi_ddr = dwc_param_phy_ulpi_ddr_default;
+ dwc_otg_module_params.phy_ulpi_ext_vbus =
+ dwc_param_phy_ulpi_ext_vbus_default;
+ dwc_otg_module_params.i2c_enable = dwc_param_i2c_enable_default;
+ dwc_otg_module_params.ulpi_fs_ls = dwc_param_ulpi_fs_ls_default;
+ dwc_otg_module_params.ts_dline = dwc_param_ts_dline_default;
+
+ dwc_otg_module_params.dma_burst_size = dwc_param_dma_burst_size_default;
+ dwc_otg_module_params.phy_utmi_width = dwc_param_phy_utmi_width_default;
+ dwc_otg_module_params.thr_ctl = dwc_param_thr_ctl_default;
+ dwc_otg_module_params.tx_thr_length = dwc_param_tx_thr_length_default;
+ dwc_otg_module_params.rx_thr_length = dwc_param_rx_thr_length_default;
+
+ /*
+ * Hardware configurations of the OTG core.
+ */
+ dwc_otg_module_params.enable_dynamic_fifo =
+ DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2);
+ dwc_otg_module_params.dev_rx_fifo_size =
+ dwc_reg_read(core_if->core_global_regs, DWC_GRXFSIZ);
+ dwc_otg_module_params.dev_nperio_tx_fifo_size =
+ dwc_reg_read(core_if->core_global_regs, DWC_GNPTXFSIZ) >> 16;
+
+ dwc_otg_module_params.host_rx_fifo_size =
+ dwc_reg_read(core_if->core_global_regs, DWC_GRXFSIZ);
+ dwc_otg_module_params.host_nperio_tx_fifo_size =
+ dwc_reg_read(core_if->core_global_regs, DWC_GNPTXFSIZ) >> 16;
+ dwc_otg_module_params.host_perio_tx_fifo_size =
+ dwc_reg_read(core_if->core_global_regs, DWC_HPTXFSIZ) >> 16;
+ dwc_otg_module_params.max_transfer_size =
+ (1 << (DWC_HWCFG3_XFERSIZE_CTR_WIDTH_RD(core_if->hwcfg3) + 11))
+ - 1;
+ dwc_otg_module_params.max_packet_count =
+ (1 << (DWC_HWCFG3_PKTSIZE_CTR_WIDTH_RD(core_if->hwcfg3) + 4))
+ - 1;
+
+ dwc_otg_module_params.host_channels =
+ DWC_HWCFG2_NO_HST_CHAN_RD(core_if->hwcfg2) + 1;
+ dwc_otg_module_params.dev_endpoints =
+ DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+ dwc_otg_module_params.en_multiple_tx_fifo =
+ (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4) == 0)
+ ? 0 : 1, 0;
+ set_valid_perio_tx_fifo_sizes(core_if);
+ set_valid_tx_fifo_sizes(core_if);
+
+ return 0;
+}
+
+module_param_named(dma_enable, dwc_otg_module_params.dma_enable, bool, 0444);
+MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
diff --git a/drivers/usb/dwc/pcd.c b/drivers/usb/dwc/pcd.c
new file mode 100644
index 0000000..cfcb0ba
--- /dev/null
+++ b/drivers/usb/dwc/pcd.c
@@ -0,0 +1,1791 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file implements the Peripheral Controller Driver.
+ *
+ * The Peripheral Controller Driver (PCD) is responsible for
+ * translating requests from the Function Driver into the appropriate
+ * actions on the DWC_otg controller. It isolates the Function Driver
+ * from the specifics of the controller by providing an API to the
+ * Function Driver.
+ *
+ * The Peripheral Controller Driver for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.
+ * (Gadget Driver is the Linux terminology for a Function Driver.)
+ *
+ * The Linux Gadget API is defined in the header file linux/usb/gadget.h. The
+ * USB EP operations API is defined in the structure usb_ep_ops and the USB
+ * Controller API is defined in the structure usb_gadget_ops
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include "pcd.h"
+
+/*
+ * Static PCD pointer for use in usb_gadget_register_driver and
+ * usb_gadget_unregister_driver. Initialized in dwc_otg_pcd_init.
+ */
+static struct dwc_pcd *s_pcd;
+
+static inline int need_stop_srp_timer(struct core_if *core_if)
+{
+ if (core_if->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS ||
+ !core_if->core_params->i2c_enable)
+ return core_if->srp_timer_started ? 1 : 0;
+ return 0;
+}
+
+/**
+ * Tests if the module is set to FS or if the PHY_TYPE is FS. If so, then the
+ * gadget should not report as dual-speed capable.
+ */
+static inline int check_is_dual_speed(struct core_if *core_if)
+{
+ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL ||
+ (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+ DWC_HWCFG2_P_2_P_RD(core_if->hwcfg2) == 1 &&
+ core_if->core_params->ulpi_fs_ls))
+ return 0;
+ return 1;
+}
+
+/**
+ * Tests if driver is OTG capable.
+ */
+static inline int check_is_otg(struct core_if *core_if)
+{
+ if (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE ||
+ DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST ||
+ DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE ||
+ DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
+ return 0;
+ return 1;
+}
+
+/**
+ * This function completes a request. It calls the request call back.
+ */
+void request_done(struct pcd_ep *ep, struct pcd_request *req, int status)
+{
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (GET_CORE_IF(ep->pcd)->dma_enable) {
+ if (req->mapped) {
+ dma_unmap_single(ep->pcd->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep->dwc_ep.is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else {
+ dma_sync_single_for_cpu(ep->pcd->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep->dwc_ep.
+ is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ }
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&ep->pcd->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->pcd->lock);
+
+ if (ep->pcd->request_pending > 0)
+ --ep->pcd->request_pending;
+ ep->stopped = stopped;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Finally, when the current request is done, mark this endpoint
+ * as not active, so that new requests can be processed.
+ */
+ if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 0;
+}
+
+/**
+ * This function terminates all the requsts in the EP request queue.
+ */
+void request_nuke(struct pcd_ep *ep)
+{
+ struct pcd_request *req;
+
+ ep->stopped = 1;
+
+ /* called with irqs blocked?? */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+ request_done(ep, req, -ESHUTDOWN);
+ }
+}
+
+/*
+ * The following sections briefly describe the behavior of the Gadget
+ * API endpoint operations implemented in the DWC_otg driver
+ * software. Detailed descriptions of the generic behavior of each of
+ * these functions can be found in the Linux header file
+ * include/linux/usb_gadget.h.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
+ * function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper
+ * functions. Within each section, the corresponding DWC_otg PCD
+ * function name is specified.
+ *
+ */
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_perio_tx_fifo(struct core_if *core_if)
+{
+ u32 mask = 1;
+ u32 i;
+
+ for (i = 0; i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+ ++i) {
+ if (!(mask & core_if->p_tx_msk)) {
+ core_if->p_tx_msk |= mask;
+ return i + 1;
+ }
+ mask <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_perio_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+ core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1)))
+ ^ core_if->p_tx_msk;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_tx_fifo(struct core_if *core_if)
+{
+ u32 mask = 1;
+ u32 i;
+
+ for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); ++i) {
+ if (!(mask & core_if->tx_msk)) {
+ core_if->tx_msk |= mask;
+ return i + 1;
+ }
+ mask <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+ core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1)))
+ ^ core_if->tx_msk;
+}
+
+/**
+ * Sets an in endpoint's tx fifo based on the hardware configuration.
+ */
+static void set_in_ep_tx_fifo(struct dwc_pcd *pcd, struct pcd_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ if (pcd->otg_dev->core_if->en_multiple_tx_fifo) {
+ ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if);
+ } else {
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* If ISOC EP then assign a Periodic Tx FIFO. */
+ if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC)
+ ep->dwc_ep.tx_fifo_num =
+ assign_perio_tx_fifo(pcd->otg_dev->core_if);
+ }
+}
+
+/**
+ * This function activates an EP. The Device EP control register for
+ * the EP is configured as defined in the ep structure. Note: This function is
+ * not used for EP0.
+ */
+void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 depctl = 0;
+ ulong addr;
+ u32 daintmsk = 0;
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = dev_if->in_ep_regs[ep->num] + DWC_DIEPCTL;
+ daintmsk = DWC_DAINTMSK_IN_EP_RW(daintmsk, ep->num);
+ } else {
+ addr = dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL;
+ daintmsk = DWC_DAINTMSK_OUT_EP_RW(daintmsk, ep->num);
+ }
+
+ /* If the EP is already active don't change the EP Control register */
+ depctl = dwc_reg_read(addr, 0);
+ if (!DWC_DEPCTL_ACT_EP_RD(depctl)) {
+ depctl = DWC_DEPCTL_MPS_RW(depctl, ep->maxpacket);
+ depctl = DWC_DEPCTL_EP_TYPE_RW(depctl, ep->type);
+ depctl = DWC_DEPCTL_TX_FIFO_NUM_RW(depctl, ep->tx_fifo_num);
+ depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+ depctl = DWC_DEPCTL_ACT_EP_RW(depctl, 1);
+ dwc_reg_write(addr, 0, depctl);
+ }
+
+ /* Enable the Interrupt for this EP */
+ dwc_reg_modify(dev_if->dev_global_regs, DWC_DAINTMSK, 0, daintmsk);
+
+ ep->stall_clear_flag = 0;
+}
+
+/**
+ * This function is called by the Gadget Driver for each EP to be
+ * configured for the current configuration (SET_CONFIGURATION).
+ *
+ * This function initializes the dwc_otg_ep_t data structure, and then
+ * calls dwc_otg_ep_activate.
+ */
+static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pcd_ep *ep;
+ struct dwc_pcd *pcd;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !desc || ep->desc || desc->bDescriptorType !=
+ USB_DT_ENDPOINT) {
+ pr_warning("%s, bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ep == &ep->pcd->ep0) {
+ pr_warning("%s, bad ep(0)\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check FIFO size */
+ if (!desc->wMaxPacketSize) {
+ pr_warning("%s, bad %s maxpacket\n", __func__, _ep->name);
+ return -ERANGE;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ pr_warning("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&pcd->lock, flags);
+ ep->desc = desc;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Activate the EP */
+ ep->stopped = 0;
+ ep->wedged = 0;
+ ep->dwc_ep.is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+ ep->dwc_ep.maxpacket = ep->ep.maxpacket;
+ ep->dwc_ep.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (ep->dwc_ep.is_in)
+ set_in_ep_tx_fifo(pcd, ep, desc);
+
+ /* Set initial data PID. */
+ if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)
+ ep->dwc_ep.data_pid_start = 0;
+
+ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+/**
+ * This function deactivates an EP. This is done by clearing the USB Active EP
+ * bit in the Device EP control register. Note: This function is not used for
+ * EP0. EP0 cannot be deactivated.
+ */
+static void dwc_otg_ep_deactivate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ u32 depctl = 0;
+ ulong addr;
+ u32 daintmsk = 0;
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = core_if->dev_if->in_ep_regs[ep->num] + DWC_DIEPCTL;
+ daintmsk = DWC_DAINTMSK_IN_EP_RW(daintmsk, ep->num);
+ } else {
+ addr =
+ core_if->dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL;
+ daintmsk = DWC_DAINTMSK_OUT_EP_RW(daintmsk, ep->num);
+ }
+
+ depctl = DWC_DEPCTL_ACT_EP_RW(depctl, 0);
+ dwc_reg_write(addr, 0, depctl);
+
+ /* Disable the Interrupt for this EP */
+ dwc_reg_modify(core_if->dev_if->dev_global_regs, DWC_DAINTMSK,
+ daintmsk, 0);
+}
+
+/**
+ * This function is called when an EP is disabled due to disconnect or
+ * change in configuration. Any pending requests will terminate with a
+ * status of -ESHUTDOWN.
+ *
+ * This function modifies the dwc_otg_ep_t data structure for this EP,
+ * and then calls dwc_otg_ep_deactivate.
+ */
+static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep)
+{
+ struct pcd_ep *ep;
+ struct core_if *core_if;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !ep->desc)
+ return -EINVAL;
+
+ core_if = ep->pcd->otg_dev->core_if;
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ request_nuke(ep);
+ dwc_otg_ep_deactivate(core_if, &ep->dwc_ep);
+
+ ep->desc = NULL;
+ ep->stopped = 1;
+ if (ep->dwc_ep.is_in) {
+ release_perio_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+ release_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+ }
+
+ spin_unlock_irqrestore(&ep->pcd->lock, flags);
+
+ return 0;
+}
+
+/**
+ * This function allocates a request object to use with the specified
+ * endpoint.
+ */
+static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct pcd_request *req;
+
+ if (!_ep) {
+ pr_warning("%s() Invalid EP\n", __func__);
+ return NULL;
+ }
+
+ req = kzalloc(sizeof(struct pcd_request), gfp_flags);
+ if (!req) {
+ pr_warning("%s() request allocation failed\n", __func__);
+ return NULL;
+ }
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+/**
+ * This function frees a request object.
+ */
+static void dwc_otg_pcd_free_request(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct pcd_request *req;
+
+ if (!_ep || !_req) {
+ pr_warning("%s() nvalid ep or req argument\n", __func__);
+ return;
+ }
+
+ req = container_of(_req, struct pcd_request, req);
+ kfree(req);
+}
+
+/*
+ * In dedicated Tx FIFO mode, enable the Non-Periodic Tx FIFO empty interrupt.
+ * Otherwise, enable the Tx FIFO epmty interrupt. The data will be written into
+ * the fifo by the ISR.
+ */
+static void enable_tx_fifo_empty_intr(struct core_if *c_if, struct dwc_ep *ep)
+{
+ u32 intr_mask = 0;
+ struct device_if *d_if = c_if->dev_if;
+ ulong global_regs = c_if->core_global_regs;
+
+ if (!c_if->en_multiple_tx_fifo) {
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify(global_regs, DWC_GINTSTS, intr_mask, 0);
+ dwc_reg_modify(global_regs, DWC_GINTMSK, intr_mask, intr_mask);
+ } else if (ep->xfer_len) {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ u32 fifoemptymsk = 1 << ep->num;
+ dwc_reg_modify(d_if->dev_global_regs,
+ DWC_DTKNQR4FIFOEMPTYMSK, 0, fifoemptymsk);
+ }
+}
+
+static void set_next_ep(struct device_if *dev_if, u8 num)
+{
+ u32 depctl = 0;
+
+ depctl = dwc_reg_read(dev_if->in_ep_regs[0], 0) + DWC_DIEPCTL;
+ depctl = DWC_DEPCTL_NXT_EP_RW(depctl, num);
+
+ dwc_reg_write((dev_if->in_ep_regs[0]), DWC_DIEPCTL, depctl);
+}
+
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be loaded into the
+ * appropriate Tx FIFO in the ISR. For OUT transfers, the packets are unloaded
+ * from the Rx FIFO in the ISR.
+ *
+ */
+void dwc_otg_ep_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+ u32 depctl = 0;
+ u32 deptsiz = 0;
+ struct device_if *d_if = c_if->dev_if;
+ ulong glbl_regs = c_if->core_global_regs;
+
+ if (ep->is_in) {
+ ulong in_regs = d_if->in_ep_regs[ep->num];
+ u32 gtxstatus;
+
+ gtxstatus = dwc_reg_read(glbl_regs, DWC_GNPTXSTS);
+ if (!c_if->en_multiple_tx_fifo
+ && !DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(gtxstatus))
+ return;
+
+ depctl = dwc_reg_read(in_regs, DWC_DIEPCTL);
+ deptsiz = dwc_reg_read(in_regs, DWC_DIEPTSIZ);
+
+ /* Zero Length Packet? */
+ if (!ep->xfer_len) {
+ deptsiz = DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, 0);
+ deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz, 1);
+ } else {
+ /*
+ * Program the transfer size and packet count as
+ * follows:
+ *
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger
+ * transfers into multiple transfers of 1024 bytes sized
+ * messages. I happens often, that transfers of 4096
+ * bytes are required (zero-gadget,
+ * file_storage-gadget).
+ */
+ if (dwc_has_feature(c_if, DWC_LIMITED_XFER)) {
+ if (ep->xfer_len > MAX_XFER_LEN) {
+ ep->bytes_pending = ep->xfer_len
+ - MAX_XFER_LEN;
+ ep->xfer_len = MAX_XFER_LEN;
+ }
+ }
+
+ deptsiz =
+ DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, ep->xfer_len);
+ deptsiz =
+ DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+ ((ep->xfer_len - 1 +
+ ep->maxpacket) /
+ ep->maxpacket));
+ }
+ dwc_reg_write(in_regs, DWC_DIEPTSIZ, deptsiz);
+
+ if (c_if->dma_enable)
+ dwc_reg_write(in_regs, DWC_DIEPDMA, ep->dma_addr);
+ else if (ep->type != DWC_OTG_EP_TYPE_ISOC)
+ enable_tx_fifo_empty_intr(c_if, ep);
+
+ /* EP enable, IN data in FIFO */
+ depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+ depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+ dwc_reg_write(in_regs, DWC_DIEPCTL, depctl);
+
+ if (c_if->dma_enable)
+ set_next_ep(d_if, ep->num);
+ } else {
+ u32 out_regs = d_if->out_ep_regs[ep->num];
+
+ depctl = dwc_reg_read(out_regs, DWC_DOEPCTL);
+ deptsiz = dwc_reg_read(out_regs, DWC_DOEPTSIZ);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ *
+ * pktcnt = N
+ * xfersize = N * maxpacket
+ */
+ if (!ep->xfer_len) {
+ deptsiz =
+ DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, ep->maxpacket);
+ deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz, 1);
+ } else {
+ deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+ ((ep->xfer_len +
+ ep->maxpacket -
+ 1) / ep->maxpacket));
+ deptsiz =
+ DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz,
+ DWC_DEPTSIZ_PKT_CNT_RD
+ (deptsiz) * ep->maxpacket);
+ }
+ dwc_reg_write(out_regs, DWC_DOEPTSIZ, deptsiz);
+
+ if (c_if->dma_enable)
+ dwc_reg_write(out_regs, DWC_DOEPDMA, ep->dma_addr);
+
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (ep->even_odd_frame)
+ depctl = DWC_DEPCTL_SET_DATA1_PID_RW(depctl, 1);
+ else
+ depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+ }
+
+ /* EP enable */
+ depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+ depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+ dwc_reg_write(out_regs, DWC_DOEPCTL, depctl);
+ }
+}
+
+/**
+ * This function does the setup for a data transfer for EP0 and starts
+ * the transfer. For an IN transfer, the packets will be loaded into
+ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
+ * unloaded from the Rx FIFO in the ISR.
+ */
+void dwc_otg_ep0_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+ u32 depctl = 0;
+ u32 deptsiz = 0;
+ struct device_if *d_if = c_if->dev_if;
+ ulong glbl_regs = c_if->core_global_regs;
+
+ ep->total_len = ep->xfer_len;
+
+ if (ep->is_in) {
+ ulong in_regs = d_if->in_ep_regs[0];
+ u32 gtxstatus;
+
+ gtxstatus = dwc_reg_read(glbl_regs, DWC_GNPTXSTS);
+
+ if (!c_if->en_multiple_tx_fifo
+ && !DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(gtxstatus))
+ return;
+
+ depctl = dwc_reg_read(in_regs, DWC_DIEPCTL);
+ deptsiz = dwc_reg_read(in_regs, DWC_DIEPTSIZ);
+
+ /* Zero Length Packet? */
+ if (!ep->xfer_len) {
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz, 0);
+ deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+ } else {
+ /*
+ * Program the transfer size and packet count as
+ * follows:
+ *
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+ if (ep->xfer_len > ep->maxpacket) {
+ ep->xfer_len = ep->maxpacket;
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ ep->
+ maxpacket);
+ } else {
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ ep->
+ xfer_len);
+ }
+ deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+ }
+ dwc_reg_write(in_regs, DWC_DIEPTSIZ, deptsiz);
+
+ if (c_if->dma_enable)
+ dwc_reg_write(in_regs, DWC_DIEPDMA, ep->dma_addr);
+
+ /* EP enable, IN data in FIFO */
+ depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+ depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+ dwc_reg_write(in_regs, DWC_DIEPCTL, depctl);
+
+ if (!c_if->dma_enable)
+ enable_tx_fifo_empty_intr(c_if, ep);
+ } else {
+ ulong out_regs = d_if->out_ep_regs[ep->num];
+
+ depctl = dwc_reg_read(out_regs, DWC_DOEPCTL);
+ deptsiz = dwc_reg_read(out_regs, DWC_DOEPTSIZ);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ *
+ * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
+ * pktcnt = N
+ */
+ if (!ep->xfer_len) {
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ ep->maxpacket);
+ deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+ } else {
+ deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz,
+ (ep->xfer_len +
+ ep->maxpacket -
+ 1) / ep->maxpacket);
+ deptsiz =
+ DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ DWC_DEPTSIZ_PKT_CNT_RD
+ (deptsiz) * ep->maxpacket);
+ }
+ dwc_reg_write(out_regs, DWC_DOEPTSIZ, deptsiz);
+
+ if (c_if->dma_enable)
+ dwc_reg_write(out_regs, DWC_DOEPDMA, ep->dma_addr);
+
+ /* EP enable */
+ depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+ depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+ dwc_reg_write(out_regs, DWC_DOEPCTL, depctl);
+ }
+}
+
+/**
+ * This function is used to submit an I/O Request to an EP.
+ *
+ * - When the request completes the request's completion callback
+ * is called to return the request to the driver.
+ * - An EP, except control EPs, may have multiple requests
+ * pending.
+ * - Once submitted the request cannot be examined or modified.
+ * - Each request is turned into one or more packets.
+ * - A BULK EP can queue any amount of data; the transfer is
+ * packetized.
+ * - Zero length Packets are specified with the request 'zero'
+ * flag.
+ */
+static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ int prevented = 0;
+ struct pcd_request *req;
+ struct pcd_ep *ep;
+ struct dwc_pcd *pcd;
+ struct core_if *core_if;
+ unsigned long flags = 0;
+
+ req = container_of(_req, struct pcd_request, req);
+ if (!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue)) {
+ pr_warning("%s, bad params\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+ pr_warning("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ pr_warning("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+ core_if = pcd->otg_dev->core_if;
+
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ if (_req->dma == DMA_ADDR_INVALID) {
+ _req->dma = dma_map_single(pcd->gadget.dev.parent,
+ _req->buf, _req->length,
+ ep->dwc_ep.
+ is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(pcd->gadget.dev.parent,
+ _req->dma, _req->length,
+ ep->dwc_ep.
+ is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+ }
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* Start the transfer */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ /* EP0 Transfer? */
+ if (ep->dwc_ep.num == 0) {
+ switch (pcd->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ break;
+ case EP0_OUT_DATA_PHASE:
+ if (pcd->request_config) {
+ /* Complete STATUS PHASE */
+ ep->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_STATUS;
+ }
+ break;
+ default:
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return -EL2HLT;
+ }
+
+ ep->dwc_ep.dma_addr = _req->dma;
+ ep->dwc_ep.start_xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_len = _req->length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ } else {
+ /* Setup and start the Transfer */
+ ep->dwc_ep.dma_addr = _req->dma;
+ ep->dwc_ep.start_xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_len = _req->length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+ }
+
+ if (req || prevented) {
+ ++pcd->request_pending;
+ list_add_tail(&req->queue, &ep->queue);
+
+ if (ep->dwc_ep.is_in && ep->stopped && !core_if->dma_enable) {
+ /*
+ * Device IN endpoint interrupt mask register is laid
+ * out exactly the same as the device IN endpoint
+ * interrupt register.
+ */
+ u32 diepmsk = 0;
+ diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+
+ dwc_reg_modify(core_if->dev_if->dev_global_regs,
+ DWC_DIEPMSK, 0, diepmsk);
+ }
+ }
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+/**
+ * This function cancels an I/O request from an EP.
+ */
+static int dwc_otg_pcd_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pcd_request *req;
+ struct pcd_ep *ep;
+ struct dwc_pcd *pcd;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !_req || (!ep->desc && ep->dwc_ep.num != 0)) {
+ pr_warning("%s, bad argument\n", __func__);
+ return -EINVAL;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ pr_warning("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&pcd->lock, flags);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue)
+ if (&req->req == _req)
+ break;
+
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return -EINVAL;
+ }
+
+ if (!list_empty(&req->queue))
+ request_done(ep, req, -ECONNRESET);
+ else
+ req = NULL;
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+
+ return req ? 0 : -EOPNOTSUPP;
+}
+
+/**
+ * Set the EP STALL.
+ */
+void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+ u32 depctl = 0;
+ ulong depctl_addr;
+
+ if (ep->is_in) {
+ depctl_addr =
+ (core_if->dev_if->in_ep_regs[ep->num]) + DWC_DIEPCTL;
+ depctl = dwc_reg_read(depctl_addr, 0);
+
+ /* set the disable and stall bits */
+ if (DWC_DEPCTL_EPENA_RD(depctl))
+ depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+ depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
+ dwc_reg_write(depctl_addr, 0, depctl);
+ } else {
+ depctl_addr =
+ (core_if->dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL);
+ depctl = dwc_reg_read(depctl_addr, 0);
+
+ /* set the stall bit */
+ depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
+ dwc_reg_write(depctl_addr, 0, depctl);
+ }
+}
+
+/**
+ * Clear the EP STALL.
+ */
+void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+ u32 depctl = 0;
+ ulong depctl_addr;
+
+ if (ep->is_in == 1)
+ depctl_addr =
+ (core_if->dev_if->in_ep_regs[ep->num]) + DWC_DIEPCTL;
+ else
+ depctl_addr =
+ (core_if->dev_if->out_ep_regs[ep->num]) + DWC_DOEPCTL;
+
+ depctl = dwc_reg_read(depctl_addr, 0);
+
+ /* clear the stall bits */
+ depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 0);
+
+ /*
+ * USB Spec 9.4.5: For endpoints using data toggle, regardless
+ * of whether an endpoint has the Halt feature set, a
+ * ClearFeature(ENDPOINT_HALT) request always results in the
+ * data toggle being reinitialized to DATA0.
+ */
+ if (ep->type == DWC_OTG_EP_TYPE_INTR ||
+ ep->type == DWC_OTG_EP_TYPE_BULK)
+ depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+
+ dwc_reg_write(depctl_addr, 0, depctl);
+}
+
+/**
+ * usb_ep_set_halt stalls an endpoint.
+ *
+ * usb_ep_clear_halt clears an endpoint halt and resets its data
+ * toggle.
+ *
+ * Both of these functions are implemented with the same underlying
+ * function. The behavior depends on the val argument:
+ * - 0 means clear_halt.
+ * - 1 means set_halt,
+ * - 2 means clear stall lock flag.
+ * - 3 means set stall lock flag.
+ */
+static int dwc_otg_pcd_ep_set_halt_wedge(struct usb_ep *_ep,
+ int val, int wedged)
+{
+ int retval = 0;
+ unsigned long flags;
+ struct pcd_ep *ep;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || (!ep->desc && ep != &ep->pcd->ep0) ||
+ ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ pr_warning("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ if (ep->dwc_ep.is_in && !list_empty(&ep->queue)) {
+ pr_warning("%s() %s XFer In process\n", __func__, _ep->name);
+ retval = -EAGAIN;
+ } else if (val == 0) {
+ ep->wedged = 0;
+ dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+ } else if (val == 1) {
+ if (ep->dwc_ep.num == 0)
+ ep->pcd->ep0state = EP0_STALL;
+ if (wedged)
+ ep->wedged = 1;
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+ } else if (val == 2) {
+ ep->dwc_ep.stall_clear_flag = 0;
+ } else if (val == 3) {
+ ep->dwc_ep.stall_clear_flag = 1;
+ }
+
+ spin_unlock_irqrestore(&ep->pcd->lock, flags);
+ return retval;
+}
+static int dwc_otg_pcd_ep_set_halt(struct usb_ep *_ep, int val)
+{
+ return dwc_otg_pcd_ep_set_halt_wedge(_ep, val, 0);
+}
+static int dwc_otg_pcd_ep_set_wedge(struct usb_ep *_ep)
+{
+ return dwc_otg_pcd_ep_set_halt_wedge(_ep, 1, 1);
+}
+
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+ .enable = dwc_otg_pcd_ep_enable,
+ .disable = dwc_otg_pcd_ep_disable,
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+ .queue = dwc_otg_pcd_ep_queue,
+ .dequeue = dwc_otg_pcd_ep_dequeue,
+ .set_halt = dwc_otg_pcd_ep_set_halt,
+ .set_wedge = dwc_otg_pcd_ep_set_wedge,
+ .fifo_status = NULL,
+ .fifo_flush = NULL,
+};
+
+/**
+ * Gets the current USB frame number from the DTS register. This is the frame
+ * number from the last SOF packet.
+ */
+static u32 dwc_otg_get_frame_number(struct core_if *core_if)
+{
+ u32 dsts;
+
+ dsts = dwc_reg_read(core_if->dev_if->dev_global_regs, DWC_DSTS);
+ return DWC_DSTS_SOFFN_RD(dsts);
+}
+
+/**
+ * The following gadget operations will be implemented in the DWC_otg
+ * PCD. Functions in the API that are not described below are not
+ * implemented.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_gadget_ops. The Gadget Driver calls the
+ * wrapper function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper functions
+ * (except for ioctl, which doesn't have a wrapper function). Within
+ * each section, the corresponding DWC_otg PCD function name is
+ * specified.
+ *
+ */
+
+/**
+ *Gets the USB Frame number of the last SOF.
+ */
+static int dwc_otg_pcd_get_frame(struct usb_gadget *_gadget)
+{
+ if (!_gadget) {
+ return -ENODEV;
+ } else {
+ struct dwc_pcd *pcd;
+
+ pcd = container_of(_gadget, struct dwc_pcd, gadget);
+ dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+ }
+
+ return 0;
+}
+
+/**
+ * This function is called when the SRP timer expires. The SRP should complete
+ * within 6 seconds.
+ */
+static void srp_timeout(unsigned long data)
+{
+ u32 gotgctl;
+ struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+ struct core_if *core_if = pcd->otg_dev->core_if;
+ ulong addr = otg_ctl_reg(pcd);
+
+ gotgctl = dwc_reg_read(addr, 0);
+ core_if->srp_timer_started = 0;
+
+ if (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS &&
+ core_if->core_params->i2c_enable) {
+ pr_info("SRP Timeout\n");
+
+ if (core_if->srp_success && (gotgctl &
+ DWC_GCTL_BSESSION_VALID)) {
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->
+ p);
+
+ /* Clear Session Request */
+ gotgctl = 0;
+ gotgctl |= DWC_GCTL_SES_REQ;
+ dwc_reg_modify(addr, 0, gotgctl, 0);
+
+ core_if->srp_success = 0;
+ } else {
+ pr_err("Device not connected/responding\n");
+ gotgctl &= ~DWC_GCTL_SES_REQ;
+ dwc_reg_write(addr, 0, gotgctl);
+ }
+ } else if (gotgctl & DWC_GCTL_SES_REQ) {
+ pr_info("SRP Timeout\n");
+ pr_err("Device not connected/responding\n");
+
+ gotgctl &= ~DWC_GCTL_SES_REQ;
+ dwc_reg_write(addr, 0, gotgctl);
+ } else {
+ pr_info(" SRP GOTGCTL=%0x\n", gotgctl);
+ }
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ */
+static void dwc_otg_pcd_start_srp_timer(struct dwc_pcd *pcd)
+{
+ struct timer_list *srp_timer = &pcd->srp_timer;
+
+ GET_CORE_IF(pcd)->srp_timer_started = 1;
+ init_timer(srp_timer);
+ srp_timer->function = srp_timeout;
+ srp_timer->data = (unsigned long)pcd;
+ srp_timer->expires = jiffies + (HZ * 6);
+
+ add_timer(srp_timer);
+}
+
+static void dwc_otg_pcd_initiate_srp(struct dwc_pcd *pcd)
+{
+ u32 mem;
+ u32 val;
+ ulong addr = otg_ctl_reg(pcd);
+
+ val = dwc_reg_read(addr, 0);
+ if (val & DWC_GCTL_SES_REQ) {
+ pr_err("Session Request Already active!\n");
+ return;
+ }
+
+ pr_notice("Session Request Initated\n");
+ mem = dwc_reg_read(addr, 0);
+ mem |= DWC_GCTL_SES_REQ;
+ dwc_reg_write(addr, 0, mem);
+
+ /* Start the SRP timer */
+ dwc_otg_pcd_start_srp_timer(pcd);
+ return;
+}
+
+static void dwc_otg_pcd_remote_wakeup(struct dwc_pcd *pcd, int set)
+{
+ u32 dctl = 0;
+ ulong addr = dev_ctl_reg(pcd);
+
+ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
+ if (pcd->remote_wakeup_enable) {
+ if (set) {
+ dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+ dwc_reg_modify(addr, 0, 0, dctl);
+ msleep(20);
+ dwc_reg_modify(addr, 0, dctl, 0);
+ }
+ }
+ }
+}
+
+/**
+ * Initiates Session Request Protocol (SRP) to wakeup the host if no
+ * session is in progress. If a session is already in progress, but
+ * the device is suspended, remote wakeup signaling is started.
+ *
+ */
+static int dwc_otg_pcd_wakeup(struct usb_gadget *_gadget)
+{
+ unsigned long flags;
+ struct dwc_pcd *pcd;
+ u32 dsts;
+ u32 gotgctl;
+
+ if (!_gadget)
+ return -ENODEV;
+ else
+ pcd = container_of(_gadget, struct dwc_pcd, gadget);
+
+ spin_lock_irqsave(&pcd->lock, flags);
+
+ /*
+ * This function starts the Protocol if no session is in progress. If
+ * a session is already in progress, but the device is suspended,
+ * remote wakeup signaling is started.
+ */
+
+ /* Check if valid session */
+ gotgctl = dwc_reg_read(otg_ctl_reg(pcd), 0);
+ if (gotgctl & DWC_GCTL_BSESSION_VALID) {
+ /* Check if suspend state */
+ dsts = dwc_reg_read(dev_sts_reg(pcd), 0);
+ if (DWC_DSTS_SUSP_STS_RD(dsts))
+ dwc_otg_pcd_remote_wakeup(pcd, 1);
+ } else {
+ dwc_otg_pcd_initiate_srp(pcd);
+ }
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+static int dwc_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct dwc_pcd *pcd = container_of(gadget, struct dwc_pcd, gadget);
+
+ if (pcd->otg_dev->core_if->xceiv)
+ return -EOPNOTSUPP;
+
+ return otg_set_power(pcd->otg_dev->core_if->xceiv, mA);
+
+}
+static int dwc_gadget_start(struct usb_gadget_driver *driver,
+ int (*bind) (struct usb_gadget *));
+static int dwc_gadget_stop(struct usb_gadget_driver *driver);
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+ .get_frame = dwc_otg_pcd_get_frame,
+ .wakeup = dwc_otg_pcd_wakeup,
+ .start = dwc_gadget_start,
+ .stop = dwc_gadget_stop,
+ .vbus_draw = dwc_gadget_vbus_draw,
+};
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset)
+{
+ if (!pcd->gadget.is_otg)
+ return;
+
+ if (reset) {
+ pcd->b_hnp_enable = 0;
+ pcd->a_hnp_support = 0;
+ pcd->a_alt_hnp_support = 0;
+ }
+
+ pcd->gadget.b_hnp_enable = pcd->b_hnp_enable;
+ pcd->gadget.a_hnp_support = pcd->a_hnp_support;
+ pcd->gadget.a_alt_hnp_support = pcd->a_alt_hnp_support;
+}
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int _irq, void *dev)
+{
+ struct dwc_pcd *pcd = dev;
+ int retval;
+
+ retval = dwc_otg_pcd_handle_intr(pcd);
+ return IRQ_RETVAL(retval);
+}
+
+/**
+ * PCD Callback function for initializing the PCD when switching to
+ * device mode.
+ */
+static int dwc_otg_pcd_start_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+ /* Initialize the Core for Device mode. */
+ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd)))
+ dwc_otg_core_dev_init(GET_CORE_IF(pcd));
+
+ return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ */
+static int dwc_otg_pcd_stop_cb(void *_p)
+{
+ dwc_otg_pcd_stop((struct dwc_pcd *)_p);
+ return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param _p void pointer to the <code>struct dwc_pcd</code>
+ */
+static int dwc_otg_pcd_suspend_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+ if (pcd->driver && pcd->driver->suspend) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->suspend(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ */
+static int dwc_otg_pcd_resume_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+ struct core_if *core_if = pcd->otg_dev->core_if;
+
+ if (pcd->driver && pcd->driver->resume) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->resume(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+
+ /* Maybe stop the SRP timeout timer. */
+ if (need_stop_srp_timer(core_if)) {
+ core_if->srp_timer_started = 0;
+ del_timer_sync(&pcd->srp_timer);
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static struct cil_callbacks pcd_callbacks = {
+ .start = dwc_otg_pcd_start_cb,
+ .stop = dwc_otg_pcd_stop_cb,
+ .suspend = dwc_otg_pcd_suspend_cb,
+ .resume_wakeup = dwc_otg_pcd_resume_cb,
+ .p = NULL, /* Set at registration */
+};
+
+/**
+ * Tasklet
+ *
+ */
+static void start_xfer_tasklet_func(unsigned long data)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+ u32 diepctl = 0;
+ int num = pcd->otg_dev->core_if->dev_if->num_in_eps;
+ u32 i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcd->lock, flags);
+ diepctl = dwc_reg_read(in_ep_ctl_reg(pcd, 0), 0);
+
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ dwc_start_next_request(&pcd->ep0);
+ }
+
+ for (i = 0; i < num; i++) {
+ u32 diepctl = 0;
+
+ diepctl = dwc_reg_read(in_ep_ctl_reg(pcd, i), 0);
+ if (pcd->in_ep[i].queue_sof) {
+ pcd->in_ep[i].queue_sof = 0;
+ dwc_start_next_request(&pcd->in_ep[i]);
+ }
+ }
+ spin_unlock_irqrestore(&pcd->lock, flags);
+}
+
+static struct tasklet_struct start_xfer_tasklet = {
+ .next = NULL,
+ .state = 0,
+ .count = ATOMIC_INIT(0),
+ .func = start_xfer_tasklet_func,
+ .data = 0,
+};
+
+/**
+ * This function initialized the pcd Dp structures to there default
+ * state.
+ */
+static void dwc_otg_pcd_reinit(struct dwc_pcd *pcd)
+{
+ static const char *names[] = {
+ "ep0", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in",
+ "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in",
+ "ep12in", "ep13in", "ep14in", "ep15in", "ep1out", "ep2out",
+ "ep3out", "ep4out", "ep5out", "ep6out", "ep7out", "ep8out",
+ "ep9out", "ep10out", "ep11out", "ep12out", "ep13out",
+ "ep14out", "ep15out"
+ };
+ u32 i;
+ int in_ep_cntr, out_ep_cntr;
+ u32 hwcfg1;
+ u32 num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
+ u32 num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
+ struct pcd_ep *ep;
+
+ INIT_LIST_HEAD(&pcd->gadget.ep_list);
+ pcd->gadget.ep0 = &pcd->ep0.ep;
+ pcd->gadget.speed = USB_SPEED_UNKNOWN;
+ INIT_LIST_HEAD(&pcd->gadget.ep0->ep_list);
+
+ /* Initialize the EP0 structure. */
+ ep = &pcd->ep0;
+
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.num = 0;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = NULL;
+ ep->dwc_ep.xfer_buff = NULL;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ /* Init the usb_ep structure. */
+ ep->ep.name = names[0];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* Initialize the EP structures. */
+ in_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1 >> 3;
+
+ for (i = 1; in_ep_cntr < num_in_eps; i++) {
+ if (!(hwcfg1 & 0x1)) {
+ struct pcd_ep *ep = &pcd->in_ep[in_ep_cntr];
+
+ in_ep_cntr++;
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.is_in = 1;
+ ep->dwc_ep.num = i;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = NULL;
+ ep->dwc_ep.xfer_buff = NULL;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ ep->ep.name = names[i];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ out_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1 >> 2;
+ for (i = 1; out_ep_cntr < num_out_eps; i++) {
+ if (!(hwcfg1 & 0x1)) {
+ struct pcd_ep *ep = &pcd->out_ep[out_ep_cntr];
+
+ out_ep_cntr++;
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.is_in = 0;
+ ep->dwc_ep.num = i;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = NULL;
+ ep->dwc_ep.xfer_buff = NULL;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ ep->ep.name = names[15 + i];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ /* remove ep0 from the list. There is a ep0 pointer. */
+ list_del_init(&pcd->ep0.ep.ep_list);
+
+ pcd->ep0state = EP0_DISCONNECT;
+ pcd->ep0.ep.maxpacket = MAX_EP0_SIZE;
+ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
+ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+}
+
+/**
+ * This function releases the Gadget device.
+ * required by device_unregister().
+ */
+static void dwc_otg_pcd_gadget_release(struct device *dev)
+{
+ pr_info("%s(%p)\n", __func__, dev);
+}
+
+/**
+ * Allocates the buffers for the setup packets when the PCD portion of the
+ * driver is first initialized.
+ */
+static int init_pkt_buffs(struct device *dev, struct dwc_pcd *pcd)
+{
+ if (pcd->otg_dev->core_if->dma_enable) {
+ pcd->dwc_pool = dma_pool_create("dwc_otg_pcd", dev,
+ sizeof(*pcd->setup_pkt) * 5, 32,
+ 0);
+ if (!pcd->dwc_pool)
+ return -ENOMEM;
+ pcd->setup_pkt = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+ &pcd->setup_pkt_dma_handle);
+ if (!pcd->setup_pkt)
+ goto error;
+ pcd->status_buf = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+ &pcd->status_buf_dma_handle);
+ if (!pcd->status_buf)
+ goto error1;
+ } else {
+ pcd->setup_pkt = kmalloc(sizeof(*pcd->setup_pkt) * 5,
+ GFP_KERNEL);
+ if (!pcd->setup_pkt)
+ return -ENOMEM;
+ pcd->status_buf = kmalloc(sizeof(u16), GFP_KERNEL);
+ if (!pcd->status_buf) {
+ kfree(pcd->setup_pkt);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+
+error1:
+ dma_pool_free(pcd->dwc_pool, pcd->setup_pkt, pcd->setup_pkt_dma_handle);
+error:
+ dma_pool_destroy(pcd->dwc_pool);
+ return -ENOMEM;
+}
+
+/**
+ * This function initializes the PCD portion of the driver.
+ */
+int dwc_otg_pcd_init(struct device *dev)
+{
+ static const char pcd_name[] = "dwc_otg_pcd";
+ struct dwc_pcd *pcd;
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+ struct core_if *core_if = otg_dev->core_if;
+ int retval;
+
+ /* Allocate PCD structure */
+ pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+ if (!pcd) {
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&pcd->lock);
+
+ otg_dev->pcd = pcd;
+ s_pcd = pcd;
+ pcd->gadget.name = pcd_name;
+
+ dev_set_name(&pcd->gadget.dev, "gadget");
+ pcd->otg_dev = otg_dev;
+ pcd->gadget.dev.parent = dev;
+ pcd->gadget.dev.release = dwc_otg_pcd_gadget_release;
+ pcd->gadget.ops = &dwc_otg_pcd_ops;
+
+ if (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4))
+ pr_info("Dedicated Tx FIFOs mode\n");
+ else
+ pr_info("Shared Tx FIFO mode\n");
+
+ pcd->gadget.is_dualspeed = check_is_dual_speed(core_if);
+ pcd->gadget.is_otg = check_is_otg(core_if);
+
+ /* Register the gadget device */
+ retval = device_register(&pcd->gadget.dev);
+ if (retval)
+ goto unreg_device;
+
+
+ /* hook up the gadget*/
+ retval = usb_add_gadget_udc(dev, &pcd->gadget);
+ if (retval)
+ goto unreg_device;
+
+ /* Initialized the Core for Device mode. */
+ if (dwc_otg_is_device_mode(core_if))
+ dwc_otg_core_dev_init(core_if);
+
+ /* Initialize EP structures */
+ dwc_otg_pcd_reinit(pcd);
+
+ /* Register the PCD Callbacks. */
+ dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
+
+ /* Setup interupt handler */
+ retval = request_irq(otg_dev->irq, dwc_otg_pcd_irq, IRQF_SHARED,
+ pcd->gadget.name, pcd);
+ if (retval) {
+ pr_err("request of irq%d failed\n", otg_dev->irq);
+ retval = -EBUSY;
+ goto err_cleanup;
+ }
+
+ /* Initialize the DMA buffer for SETUP packets */
+ retval = init_pkt_buffs(dev, pcd);
+ if (retval)
+ goto err_cleanup;
+
+ /* Initialize tasklet */
+ start_xfer_tasklet.data = (unsigned long)pcd;
+ pcd->start_xfer_tasklet = &start_xfer_tasklet;
+ return 0;
+
+err_cleanup:
+ kfree(pcd);
+ otg_dev->pcd = NULL;
+ s_pcd = NULL;
+
+unreg_device:
+ device_unregister(&pcd->gadget.dev);
+
+err:
+ return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void __devexit dwc_otg_pcd_remove(struct device *dev)
+{
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+ struct dwc_pcd *pcd = otg_dev->pcd;
+
+ /* Free the IRQ */
+ free_irq(otg_dev->irq, pcd);
+
+ /* start with the driver above us */
+ if (pcd->driver) {
+ /* should have been done already by driver model core */
+ pr_warning("driver '%s' is still registered\n",
+ pcd->driver->driver.name);
+ usb_gadget_unregister_driver(pcd->driver);
+ }
+ if (pcd->start_xfer_tasklet)
+ tasklet_kill(pcd->start_xfer_tasklet);
+ tasklet_kill(&pcd->test_mode_tasklet);
+
+ device_unregister(&pcd->gadget.dev);
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ dma_pool_free(pcd->dwc_pool, pcd->setup_pkt,
+ pcd->setup_pkt_dma_handle);
+ dma_pool_free(pcd->dwc_pool, pcd->status_buf,
+ pcd->status_buf_dma_handle);
+ dma_pool_destroy(pcd->dwc_pool);
+ } else {
+ kfree(pcd->setup_pkt);
+ kfree(pcd->status_buf);
+ }
+ kfree(pcd);
+ otg_dev->pcd = NULL;
+}
+
+/**
+ * This function registers a gadget driver with the PCD.
+ *
+ * When a driver is successfully registered, it will receive control
+ * requests including set_configuration(), which enables non-control
+ * requests. then usb traffic follows until a disconnect is reported.
+ * then a host may connect again, or the driver might get unbound.
+ */
+static int dwc_gadget_start(struct usb_gadget_driver *driver,
+ int (*bind) (struct usb_gadget *))
+{
+ int retval;
+
+ if (!driver || driver->speed == USB_SPEED_UNKNOWN || !bind ||
+ !driver->unbind || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+
+ if (s_pcd == NULL)
+ return -ENODEV;
+
+ if (s_pcd->driver != NULL)
+ return -EBUSY;
+
+ /* hook up the driver */
+ s_pcd->driver = driver;
+ s_pcd->gadget.dev.driver = &driver->driver;
+
+ retval = bind(&s_pcd->gadget);
+ if (retval) {
+ struct core_if *core_if;
+
+ pr_err("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ core_if = s_pcd->otg_dev->core_if;
+ otg_set_peripheral(core_if->xceiv, &s_pcd->gadget);
+ s_pcd->driver = NULL;
+ s_pcd->gadget.dev.driver = NULL;
+ return retval;
+ }
+ return 0;
+}
+
+/**
+ * This function unregisters a gadget driver
+ */
+static int dwc_gadget_stop(struct usb_gadget_driver *driver)
+{
+ struct core_if *core_if;
+
+ if (!s_pcd)
+ return -ENODEV;
+ if (!driver || driver != s_pcd->driver)
+ return -EINVAL;
+
+ core_if = s_pcd->otg_dev->core_if;
+ core_if->xceiv->state = OTG_STATE_UNDEFINED;
+ otg_set_peripheral(core_if->xceiv, NULL);
+
+ driver->unbind(&s_pcd->gadget);
+ s_pcd->driver = NULL;
+
+ return 0;
+}
diff --git a/drivers/usb/dwc/pcd.h b/drivers/usb/dwc/pcd.h
new file mode 100644
index 0000000..0000446
--- /dev/null
+++ b/drivers/usb/dwc/pcd.h
@@ -0,0 +1,139 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_PCD_H__)
+#define __DWC_PCD_H__
+
+#include "driver.h"
+
+/*
+ * This file contains the structures, constants, and interfaces for
+ * the Perpherial Contoller Driver (PCD).
+ *
+ * The Peripheral Controller Driver (PCD) for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used. For
+ * the Mass Storage Function driver the File-backed USB Storage Gadget
+ * (FBS) driver will be used. The FBS driver supports the
+ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
+ * transports.
+ *
+ */
+
+/* Invalid DMA Address */
+#define DMA_ADDR_INVALID (~(dma_addr_t) 0)
+/* Maxpacket size for EP0 */
+#define MAX_EP0_SIZE 64
+/* Maxpacket size for any EP */
+#define MAX_PACKET_SIZE 1024
+
+/*
+ * Get the pointer to the core_if from the pcd pointer.
+ */
+#define GET_CORE_IF(_pcd) (_pcd->otg_dev->core_if)
+
+/*
+ * DWC_otg request structure.
+ * This structure is a list of requests.
+ */
+struct pcd_request {
+ struct usb_request req; /* USB Request. */
+ struct list_head queue; /* queue of these requests. */
+ unsigned mapped:1;
+};
+
+static inline ulong in_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+ return GET_CORE_IF(pd)->dev_if->in_ep_regs[i] + DWC_DIEPINT;
+}
+static inline ulong out_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+ return GET_CORE_IF(pd)->dev_if->out_ep_regs[i] + DWC_DOEPINT;
+}
+static inline ulong in_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+ return GET_CORE_IF(pd)->dev_if->in_ep_regs[i] + DWC_DIEPCTL;
+}
+
+static inline ulong out_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+ return GET_CORE_IF(pd)->dev_if->out_ep_regs[i] + DWC_DOEPCTL;
+}
+
+static inline ulong dev_ctl_reg(struct dwc_pcd *pd)
+{
+ return GET_CORE_IF(pd)->dev_if->dev_global_regs +
+ DWC_DCTL;
+}
+
+static inline ulong dev_diepmsk_reg(struct dwc_pcd *pd)
+{
+ return GET_CORE_IF(pd)->dev_if->dev_global_regs +
+ DWC_DIEPMSK;
+}
+
+static inline ulong dev_sts_reg(struct dwc_pcd *pd)
+{
+ return GET_CORE_IF(pd)->dev_if->dev_global_regs +
+ DWC_DSTS;
+}
+
+static inline ulong otg_ctl_reg(struct dwc_pcd *pd)
+{
+ return GET_CORE_IF(pd)->core_global_regs + DWC_GOTGCTL;
+}
+
+extern int dwc_otg_pcd_init(struct device *dev);
+
+/*
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+extern void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_pcd_remove(struct device *dev);
+extern int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd);
+extern void dwc_otg_pcd_stop(struct dwc_pcd *pcd);
+extern void request_nuke(struct pcd_ep *ep);
+extern void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset);
+extern void dwc_otg_ep0_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+
+extern void request_done(struct pcd_ep *ep, struct pcd_request *req,
+ int _status);
+
+extern void dwc_start_next_request(struct pcd_ep *ep);
+#endif
diff --git a/drivers/usb/dwc/pcd_intr.c b/drivers/usb/dwc/pcd_intr.c
new file mode 100644
index 0000000..ea0ce78
--- /dev/null
+++ b/drivers/usb/dwc/pcd_intr.c
@@ -0,0 +1,2312 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "driver.h"
+#include "pcd.h"
+
+/**
+ * This function returns pointer to in ep struct with number num
+ */
+static struct pcd_ep *get_in_ep(struct dwc_pcd *pcd, u32 num)
+{
+ if (num == 0) {
+ return &pcd->ep0;
+ } else {
+ u32 i;
+ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+
+ for (i = 0; i < num_in_eps; ++i) {
+ if (pcd->in_ep[i].dwc_ep.num == num)
+ return &pcd->in_ep[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * This function returns pointer to out ep struct with number num
+ */
+static struct pcd_ep *get_out_ep(struct dwc_pcd *pcd, u32 num)
+{
+ if (num == 0) {
+ return &pcd->ep0;
+ } else {
+ u32 i;
+ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+ for (i = 0; i < num_out_eps; ++i) {
+ if (pcd->out_ep[i].dwc_ep.num == num)
+ return &pcd->out_ep[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+static struct pcd_ep *get_ep_by_addr(struct dwc_pcd *pcd, u16 index)
+{
+ struct pcd_ep *ep;
+
+ if (!(index & USB_ENDPOINT_NUMBER_MASK))
+ return &pcd->ep0;
+
+ list_for_each_entry(ep, &pcd->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((index ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+
+ if ((index & 0x0f) == (bEndpointAddress & 0x0f))
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void dwc_start_next_request(struct pcd_ep *ep)
+{
+ if (!list_empty(&ep->queue)) {
+ struct pcd_request *req;
+
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ /* Setup and start the Transfer */
+ ep->dwc_ep.start_xfer_buff = req->req.buf;
+ ep->dwc_ep.xfer_buff = req->req.buf;
+ ep->dwc_ep.xfer_len = req->req.length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.dma_addr = req->req.dma;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * When a new transfer will be started, mark this
+ * endpoint as active. This way it will be blocked
+ * for further transfers, until the current transfer
+ * is finished.
+ */
+ if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 1;
+
+ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+ }
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+static int dwc_otg_pcd_handle_sof_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ u32 gintsts;
+
+ /* Clear interrupt */
+ gintsts = 0;
+ gintsts |= DWC_INTMSK_STRT_OF_FRM;
+ dwc_reg_write((core_if->core_global_regs), DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This function reads the 8 bytes of the setup packet from the Rx FIFO into the
+ * destination buffer. It is called from the Rx Status Queue Level (RxStsQLvl)
+ * interrupt routine when a SETUP packet has been received in Slave mode.
+ */
+static void dwc_otg_read_setup_packet(struct core_if *core_if, u32 * dest)
+{
+ dest[0] = dwc_read_fifo32(core_if->data_fifo[0]);
+ dest[1] = dwc_read_fifo32(core_if->data_fifo[0]);
+}
+
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO. The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ * info
+ * -# If Receive FIFO is empty then skip to step Clear the interrupt
+ * and exit
+ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ * SETUP data to the buffer
+ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ * to the destination buffer
+ */
+static int dwc_otg_pcd_handle_rx_status_q_level_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ ulong global_regs = core_if->core_global_regs;
+ u32 gintmask = 0;
+ u32 grxsts;
+ struct pcd_ep *ep;
+ u32 gintsts;
+
+ /* Disable the Rx Status Queue Level interrupt */
+ gintmask |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, gintmask, 0);
+
+ /* Get the Status from the top of the FIFO */
+ grxsts = dwc_reg_read(global_regs, DWC_GRXSTSP);
+
+ /* Get pointer to EP structure */
+ ep = get_out_ep(pcd, DWC_DM_RXSTS_CHAN_NUM_RD(grxsts));
+
+ switch (DWC_DM_RXSTS_PKT_STS_RD(grxsts)) {
+ case DWC_DSTS_GOUT_NAK:
+ break;
+ case DWC_STS_DATA_UPDT:
+ if ((grxsts & DWC_DM_RXSTS_BYTE_CNT) && ep->dwc_ep.xfer_buff) {
+ dwc_otg_read_packet(core_if, ep->dwc_ep.xfer_buff,
+ DWC_DM_RXSTS_BYTE_CNT_RD(grxsts));
+ ep->dwc_ep.xfer_count +=
+ DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+ ep->dwc_ep.xfer_buff +=
+ DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+ }
+ break;
+ case DWC_STS_XFER_COMP:
+ break;
+ case DWC_DSTS_SETUP_COMP:
+ break;
+ case DWC_DSTS_SETUP_UPDT:
+ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+ ep->dwc_ep.xfer_count += DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+ break;
+ default:
+ pr_err("RX_STS_Q Interrupt: Unknown status %d\n",
+ DWC_HM_RXSTS_PKT_STS_RD(grxsts));
+ break;
+ }
+
+ /* Enable the Rx Status Queue Level interrupt */
+ dwc_reg_modify(global_regs, DWC_GINTMSK, 0, gintmask);
+
+ /* Clear interrupt */
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_RXFIFO_NOT_EMPT;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+
+ return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received. This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ */
+static int get_ep_of_last_in_token(struct core_if *core_if)
+{
+ ulong regs = core_if->dev_if->dev_global_regs;
+ const u32 TOKEN_Q_DEPTH =
+ DWC_HWCFG2_DEV_TKN_Q_DEPTH_RD(core_if->hwcfg2);
+ /* Number of Token Queue Registers */
+ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+ u32 dtknqr1 = 0;
+ u32 in_tkn_epnums[4];
+ int ndx;
+ u32 i;
+ u32 addr = regs + DWC_DTKNQR1;
+ int epnum = 0;
+
+ /* Read the DTKNQ Registers */
+ for (i = 0; i <= DTKNQ_REG_CNT; i++) {
+ in_tkn_epnums[i] = dwc_reg_read(addr, 0);
+
+ if (addr == (regs + DWC_DVBUSDIS))
+ addr = regs + DWC_DTKNQR3_DTHRCTL;
+ else
+ ++addr;
+ }
+
+ /* Copy the DTKNQR1 data to the bit field. */
+ dtknqr1 = in_tkn_epnums[0];
+
+ /* Get the EP numbers */
+ in_tkn_epnums[0] = DWC_DTKNQR1_EP_TKN_NO_RD(dtknqr1);
+ ndx = DWC_DTKNQR1_INT_TKN_Q_WR_PTR_RD(dtknqr1) - 1;
+
+ if (ndx == -1) {
+ /*
+ * Calculate the max queue position.
+ */
+ int cnt = TOKEN_Q_DEPTH;
+
+ if (TOKEN_Q_DEPTH <= 6)
+ cnt = TOKEN_Q_DEPTH - 1;
+ else if (TOKEN_Q_DEPTH <= 14)
+ cnt = TOKEN_Q_DEPTH - 7;
+ else if (TOKEN_Q_DEPTH <= 22)
+ cnt = TOKEN_Q_DEPTH - 15;
+ else
+ cnt = TOKEN_Q_DEPTH - 23;
+
+ epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+ } else {
+ if (ndx <= 5) {
+ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 13) {
+ ndx -= 6;
+ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 21) {
+ ndx -= 14;
+ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 29) {
+ ndx -= 22;
+ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+ }
+ }
+
+ return epnum;
+}
+
+static inline int count_dwords(struct pcd_ep *ep, u32 len)
+{
+ if (len > ep->dwc_ep.maxpacket)
+ len = ep->dwc_ep.maxpacket;
+ return (len + 3) / 4;
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the EP. For
+ * non-periodic EPs the non-periodic Tx FIFO is written. For periodic EPs the
+ * periodic Tx FIFO associated with the EP is written with all packets for the
+ * next micro-frame.
+ *
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned. The last packet, if
+ * short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * ep->xfer_count is a multiple of ep->maxpacket until the last packet
+ *
+ * FIFO access is DWORD
+ */
+static void dwc_otg_ep_write_packet(struct core_if *core_if, struct dwc_ep *ep,
+ int dma)
+{
+ u32 i;
+ u32 byte_count;
+ u32 dword_count;
+ u32 fifo;
+ u32 *data_buff = (u32 *) ep->xfer_buff;
+
+ if (ep->xfer_count >= ep->xfer_len)
+ return;
+
+ /* Find the byte length of the packet either short packet or MPS */
+ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket)
+ byte_count = ep->xfer_len - ep->xfer_count;
+ else
+ byte_count = ep->maxpacket;
+
+ /*
+ * Find the DWORD length, padded by extra bytes as neccessary if MPS
+ * is not a multiple of DWORD
+ */
+ dword_count = (byte_count + 3) / 4;
+
+ fifo = core_if->data_fifo[ep->num];
+
+ if (!dma)
+ for (i = 0; i < dword_count; i++, data_buff++)
+ dwc_write_fifo32(fifo, *data_buff);
+
+ ep->xfer_count += byte_count;
+ ep->xfer_buff += byte_count;
+ ep->dma_addr += byte_count;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_np_tx_fifo_empty_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ ulong global_regs = core_if->core_global_regs;
+ u32 txstatus = 0;
+ u32 gintsts = 0;
+ int epnum;
+ struct pcd_ep *ep;
+ u32 len;
+ int dwords;
+
+ /* Get the epnum from the IN Token Learning Queue. */
+ epnum = get_ep_of_last_in_token(core_if);
+ ep = get_in_ep(pcd, epnum);
+
+ txstatus = dwc_reg_read(global_regs, DWC_GNPTXSTS);
+
+ /*
+ * While there is space in the queue, space in the FIFO, and data to
+ * tranfer, write packets to the Tx FIFO
+ */
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ while ((DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(txstatus) > 0) &&
+ (DWC_GNPTXSTS_NPTXFSPCAVAIL_RD(txstatus) > dwords) &&
+ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * When a new transfer will be started, mark this
+ * endpoint as active. This way it will be blocked
+ * for further transfers, until the current transfer
+ * is finished.
+ */
+ if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 1;
+
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ txstatus = dwc_reg_read(global_regs, DWC_GNPTXSTS);
+ }
+
+ /* Clear nptxfempty interrupt */
+ gintsts |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+ dwc_reg_write(global_regs, DWC_GINTSTS, gintsts);
+
+ /* Re-enable tx-fifo empty interrupt, if packets are stil pending */
+ if (len)
+ dwc_reg_modify(global_regs, DWC_GINTSTS, 0, gintsts);
+ return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int write_empty_tx_fifo(struct dwc_pcd *pcd, u32 epnum)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ ulong regs;
+ u32 txstatus = 0;
+ struct pcd_ep *ep;
+ u32 len;
+ int dwords;
+ u32 diepint = 0;
+
+ ep = get_in_ep(pcd, epnum);
+ regs = core_if->dev_if->in_ep_regs[epnum];
+ txstatus = dwc_reg_read(regs, DWC_DTXFSTS);
+
+ /*
+ * While there is space in the queue, space in the FIFO and data to
+ * tranfer, write packets to the Tx FIFO
+ */
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ while (DWC_DTXFSTS_TXFSSPC_AVAI_RD(txstatus) > dwords
+ && ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len
+ && ep->dwc_ep.xfer_len != 0) {
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ txstatus = dwc_reg_read(regs, DWC_DTXFSTS);
+ }
+ /* Clear emptyintr */
+ diepint = DWC_DIEPINT_TXFIFO_EMPTY_RW(diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, epnum), 0, diepint);
+ return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected. It stops any active
+ * requests and informs the Gadget driver of the disconnect.
+ */
+void dwc_otg_pcd_stop(struct dwc_pcd *pcd)
+{
+ int i, num_in_eps, num_out_eps;
+ struct pcd_ep *ep;
+ u32 intr_mask = 0;
+ ulong global_regs = GET_CORE_IF(pcd)->core_global_regs;
+
+ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+ /* Don't disconnect drivers more than once */
+ if (pcd->ep0state == EP0_DISCONNECT)
+ return;
+ pcd->ep0state = EP0_DISCONNECT;
+
+ /* Reset the OTG state. */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Disable the NP Tx Fifo Empty Interrupt. */
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify(global_regs, DWC_GINTMSK, intr_mask, 0);
+
+ /* Flush the FIFOs */
+ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0);
+ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ ep = &pcd->ep0;
+ request_nuke(ep);
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_in_eps; i++)
+ request_nuke((struct pcd_ep *)&pcd->in_ep[i]);
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_out_eps; i++)
+ request_nuke((struct pcd_ep *)&pcd->out_ep[i]);
+
+ /* Report disconnect; the driver is already quiesced */
+ if (pcd->driver && pcd->driver->disconnect) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->disconnect(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_i2c_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts;
+
+ pr_info("Interrupt handler not implemented for i2cintr\n");
+
+ /* Turn off and clean the interrupt */
+ intr_mask |= DWC_INTMSK_I2C_INTR;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_I2C_INTR;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_early_suspend_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts;
+
+ pr_info("Early Suspend Detected\n");
+
+ /* Turn off and clean the interrupt */
+ intr_mask |= DWC_INTMSK_EARLY_SUSP;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_EARLY_SUSP;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+
+ return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet:
+ *
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ * packets)
+ *
+ * In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ */
+static void ep0_out_start(struct core_if *core_if, struct dwc_pcd *pcd)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 doeptsize0 = 0;
+
+ doeptsize0 = DWC_DEPTSIZ0_SUPCNT_RW(doeptsize0, 3);
+ doeptsize0 = DWC_DEPTSIZ0_PKT_CNT_RW(doeptsize0, 1);
+ doeptsize0 = DWC_DEPTSIZ0_XFER_SIZ_RW(doeptsize0, 8 * 3);
+ dwc_reg_write(dev_if->out_ep_regs[0], DWC_DOEPTSIZ, doeptsize0);
+
+ if (core_if->dma_enable) {
+ u32 doepctl = 0;
+
+ dwc_reg_write(dev_if->out_ep_regs[0], DWC_DOEPDMA,
+ pcd->setup_pkt_dma_handle);
+ doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+ doepctl = DWC_DEPCTL_ACT_EP_RW(doepctl, 1);
+ dwc_reg_write(out_ep_ctl_reg(pcd, 0), 0, doepctl);
+ }
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected. When the USB Reset
+ * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set
+ * to IDLE.
+ *
+ * Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ *
+ * Unmask the following interrupt bits:
+ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ * - DOEPMSK.SETUP = 1
+ * - DOEPMSK.XferCompl = 1
+ * - DIEPMSK.XferCompl = 1
+ * - DIEPMSK.TimeOut = 1
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ * packets)
+ *
+ * - In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ *
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ *
+ * Note that the bits in the Device IN endpoint mask register (diepmsk) are laid
+ * out exactly the same as the Device IN endpoint interrupt register (diepint.)
+ * Likewise for Device OUT endpoint mask / interrupt registers (doepmsk /
+ * doepint.)
+ */
+static int dwc_otg_pcd_handle_usb_reset_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ u32 doepctl = 0;
+ u32 daintmsk = 0;
+ u32 doepmsk = 0;
+ u32 diepmsk = 0;
+ u32 dcfg = 0;
+ u32 resetctl = 0;
+ u32 dctl = 0;
+ u32 i;
+ u32 gintsts = 0;
+
+ pr_info("USB RESET\n");
+
+ /* reset the HNP settings */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Clear the Remote Wakeup Signalling */
+ dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+ dwc_reg_modify(dev_ctl_reg(pcd), 0, dctl, 0);
+
+ /* Set NAK for all OUT EPs */
+ doepctl = DWC_DEPCTL_SET_NAK_RW(doepctl, 1);
+ for (i = 0; i <= dev_if->num_out_eps; i++)
+ dwc_reg_write(out_ep_ctl_reg(pcd, i), 0, doepctl);
+
+ /* Flush the NP Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, 0);
+
+ /* Flush the Learning Queue */
+ resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
+ dwc_reg_write(core_if->core_global_regs, DWC_GRSTCTL, resetctl);
+
+ daintmsk |= DWC_DAINT_INEP00;
+ daintmsk |= DWC_DAINT_OUTEP00;
+ dwc_reg_write(dev_if->dev_global_regs, DWC_DAINTMSK, daintmsk);
+
+ doepmsk = DWC_DOEPMSK_SETUP_DONE_RW(doepmsk, 1);
+ doepmsk = DWC_DOEPMSK_AHB_ERROR_RW(doepmsk, 1);
+ doepmsk = DWC_DOEPMSK_EP_DISA_RW(doepmsk, 1);
+ doepmsk = DWC_DOEPMSK_TX_COMPL_RW(doepmsk, 1);
+ dwc_reg_write(dev_if->dev_global_regs, DWC_DOEPMSK, doepmsk);
+
+ diepmsk = DWC_DIEPMSK_TX_CMPL_RW(diepmsk, 1);
+ diepmsk = DWC_DIEPMSK_TOUT_COND_RW(diepmsk, 1);
+ diepmsk = DWC_DIEPMSK_EP_DISA_RW(diepmsk, 1);
+ diepmsk = DWC_DIEPMSK_AHB_ERROR_RW(diepmsk, 1);
+ diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+ dwc_reg_write(dev_if->dev_global_regs, DWC_DIEPMSK, diepmsk);
+
+ /* Reset Device Address */
+ dcfg = dwc_reg_read(dev_if->dev_global_regs, DWC_DCFG);
+ dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg, 0);
+ dwc_reg_write(dev_if->dev_global_regs, DWC_DCFG, dcfg);
+
+ /* setup EP0 to receive SETUP packets */
+ ep0_out_start(core_if, pcd);
+
+ /* Clear interrupt */
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_USB_RST;
+ dwc_reg_write((core_if->core_global_regs), DWC_GINTSTS, gintsts);
+
+ return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ */
+static int get_device_speed(struct dwc_pcd *pcd)
+{
+ u32 dsts = 0;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ dsts = dwc_reg_read(dev_sts_reg(pcd), 0);
+
+ switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ speed = USB_SPEED_HIGH;
+ break;
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ speed = USB_SPEED_FULL;
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ speed = USB_SPEED_LOW;
+ break;
+ }
+ return speed;
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets. It is normally called when the "Enumeration
+ * Done" interrupt occurs.
+ */
+static void dwc_otg_ep0_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 dsts;
+ u32 diepctl = 0;
+ u32 doepctl = 0;
+ u32 dctl = 0;
+
+ /* Read the Device Status and Endpoint 0 Control registers */
+ dsts = dwc_reg_read(dev_if->dev_global_regs, DWC_DSTS);
+ diepctl = dwc_reg_read(dev_if->in_ep_regs[0], DWC_DIEPCTL);
+ doepctl = dwc_reg_read(dev_if->out_ep_regs[0], DWC_DOEPCTL);
+
+ /* Set the MPS of the IN EP based on the enumeration speed */
+ switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ diepctl = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_64);
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ diepctl = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_8);
+ break;
+ }
+ dwc_reg_write(dev_if->in_ep_regs[0], DWC_DIEPCTL, diepctl);
+
+ /* Enable OUT EP for receive */
+ doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+ dwc_reg_write(dev_if->out_ep_regs[0], DWC_DOEPCTL, doepctl);
+
+ dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+ dwc_reg_modify(dev_if->dev_global_regs, DWC_DCTL, dctl, dctl);
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+static int dwc_otg_pcd_handle_enum_done_intr(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+ u32 gintsts;
+ u32 gusbcfg;
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ ulong global_regs = core_if->core_global_regs;
+ u32 gsnpsid = global_regs + DWC_GSNPSID;
+ u8 utmi16b, utmi8b;
+
+ if (gsnpsid >= (u32) 0x4f54260a) {
+ utmi16b = 5;
+ utmi8b = 9;
+ } else {
+ utmi16b = 4;
+ utmi8b = 8;
+ }
+ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 0;
+ pcd->gadget.speed = get_device_speed(pcd);
+
+ gusbcfg = dwc_reg_read(global_regs, DWC_GUSBCFG);
+
+ /* Set USB turnaround time based on device speed and PHY interface. */
+ if (pcd->gadget.speed == USB_SPEED_HIGH) {
+ switch (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2)) {
+ case DWC_HWCFG2_HS_PHY_TYPE_ULPI:
+ gusbcfg =
+ (gusbcfg & (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(9);
+ break;
+ case DWC_HWCFG2_HS_PHY_TYPE_UTMI:
+ if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(core_if->hwcfg4)
+ == 0)
+ gusbcfg =
+ (gusbcfg &
+ (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(utmi8b);
+ else if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD
+ (core_if->hwcfg4) == 1)
+ gusbcfg =
+ (gusbcfg &
+ (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(utmi16b);
+ else if (core_if->core_params->phy_utmi_width == 8)
+ gusbcfg =
+ (gusbcfg &
+ (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(utmi8b);
+ else
+ gusbcfg =
+ (gusbcfg &
+ (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(utmi16b);
+ break;
+ case DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI:
+ if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
+ gusbcfg =
+ (gusbcfg &
+ (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(9);
+ } else {
+ if (core_if->core_params->phy_utmi_width == 16)
+ gusbcfg =
+ (gusbcfg &
+ (~
+ ((u32) DWC_USBCFG_TRN_TIME(0xf))))
+ | DWC_USBCFG_TRN_TIME(utmi16b);
+ else
+ gusbcfg =
+ (gusbcfg &
+ (~
+ ((u32) DWC_USBCFG_TRN_TIME(0xf))))
+ | DWC_USBCFG_TRN_TIME(utmi8b);
+ }
+ break;
+ }
+ } else {
+ /* Full or low speed */
+ gusbcfg = (gusbcfg & (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+ DWC_USBCFG_TRN_TIME(9);
+ }
+ dwc_reg_write(global_regs, DWC_GUSBCFG, gusbcfg);
+
+ /* Clear interrupt */
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_ENUM_DONE;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+static int dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts;
+
+ pr_info("Interrupt Handler not implemented for ISOC Out " "Dropped\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_ISYNC_OUTPKT_DRP;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions. If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_end_periodic_frame_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts;
+
+ pr_info("Interrupt handler not implemented for End of "
+ "Periodic Portion of Micro-Frame Interrupt");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_END_OF_PFRM;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_END_OF_PFRM;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received. The non-periodic Tx FIFO is flushed,
+ * so it can be reloaded in the order seen in the IN Token Queue.
+ */
+static int dwc_otg_pcd_handle_ep_mismatch_intr(struct core_if *core_if)
+{
+ u32 intr_mask = 0;
+ u32 gintsts;
+
+ pr_info("Interrupt handler not implemented for End Point "
+ "Mismatch\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
+ dwc_reg_modify((core_if->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts = 0;
+ gintsts |= DWC_INTSTS_ENDP_MIS_MTCH;
+ dwc_reg_write((core_if->core_global_regs), DWC_GINTSTS, gintsts);
+ return 1;
+}
+
+/**
+ * This funcion stalls EP0.
+ */
+static void ep0_do_stall(struct dwc_pcd *pcd, const int val)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+ struct usb_ctrlrequest *ctrl = &pcd->setup_pkt->req;
+
+ pr_warning("req %02x.%02x protocol STALL; err %d\n",
+ ctrl->bRequestType, ctrl->bRequest, val);
+
+ ep0->dwc_ep.is_in = 1;
+ dwc_otg_ep_set_stall(pcd->otg_dev->core_if, &ep0->dwc_ep);
+
+ pcd->ep0.stopped = 1;
+ pcd->ep0state = EP0_IDLE;
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static void do_gadget_setup(struct dwc_pcd *pcd, struct usb_ctrlrequest *ctrl)
+{
+ if (pcd->driver && pcd->driver->setup) {
+ int ret;
+
+ spin_unlock(&pcd->lock);
+ ret = pcd->driver->setup(&pcd->gadget, ctrl);
+ spin_lock(&pcd->lock);
+
+ if (ret < 0)
+ ep0_do_stall(pcd, ret);
+
+ /** This is a g_file_storage gadget driver specific
+ * workaround: a DELAYED_STATUS result from the fsg_setup
+ * routine will result in the gadget queueing a EP0 IN status
+ * phase for a two-stage control transfer.
+ *
+ * Exactly the same as a SET_CONFIGURATION/SET_INTERFACE except
+ * that this is a class specific request. Need a generic way to
+ * know when the gadget driver will queue the status phase.
+ *
+ * Can we assume when we call the gadget driver setup() function
+ * that it will always queue and require the following flag?
+ * Need to look into this.
+ */
+ if (ret == 256 + 999)
+ pcd->request_config = 1;
+ }
+}
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_in_status_phase(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ if (pcd->ep0state == EP0_STALL)
+ return;
+
+ pcd->ep0state = EP0_STATUS;
+
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 1;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_out_status_phase(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ if (pcd->ep0state == EP0_STALL)
+ return;
+ pcd->ep0state = EP0_STATUS;
+
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 0;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static void pcd_clear_halt(struct dwc_pcd *pcd, struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+
+ if (!ep->dwc_ep.stall_clear_flag)
+ dwc_otg_ep_clear_stall(core_if, &ep->dwc_ep);
+
+ /* Reactive the EP */
+ dwc_otg_ep_activate(core_if, &ep->dwc_ep);
+
+ if (ep->stopped) {
+ ep->stopped = 0;
+ /* If there is a request in the EP queue start it */
+
+ /*
+ * dwc_start_next_request(), outside of interpt contxt at some
+ * time after the current time, after a clear-halt setup packet.
+ * Still need to implement ep mismatch in the future if a gadget
+ * ever uses more than one endpoint at once
+ */
+ if (core_if->dma_enable) {
+ ep->queue_sof = 1;
+ tasklet_schedule(pcd->start_xfer_tasklet);
+ } else {
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * To re-enable this endpoint it's important to
+ * set this next_ep number. Otherwise the endpoint
+ * will not get active again after stalling.
+ */
+ if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+ dwc_start_next_request(ep);
+ }
+ }
+
+ /* Start Control Status Phase */
+ do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet is sent
+ * from the host. The Device Control register is written with the Test Mode
+ * bits set to the specified Test Mode. This is done as a tasklet so that the
+ * "Status" phase of the control transfer completes before transmitting the TEST
+ * packets.
+ *
+ */
+static void do_test_mode(unsigned long data)
+{
+ u32 dctl = 0;
+ struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+ int test_mode = pcd->test_mode;
+
+ dctl = dwc_reg_read(dev_ctl_reg(pcd), 0);
+ switch (test_mode) {
+ case 1: /* TEST_J */
+ dctl = DWC_DCTL_TST_CTL(dctl, 1);
+ break;
+ case 2: /* TEST_K */
+ dctl = DWC_DCTL_TST_CTL(dctl, 2);
+ break;
+ case 3: /* TEST_SE0_NAK */
+ dctl = DWC_DCTL_TST_CTL(dctl, 3);
+ break;
+ case 4: /* TEST_PACKET */
+ dctl = DWC_DCTL_TST_CTL(dctl, 4);
+ break;
+ case 5: /* TEST_FORCE_ENABLE */
+ dctl = DWC_DCTL_TST_CTL(dctl, 5);
+ break;
+ }
+ dwc_reg_write(dev_ctl_reg(pcd), 0, dctl);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static void do_set_feature(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ ulong regs = core_if->core_global_regs;
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ int otg_cap = core_if->core_params->otg_cap;
+ u32 gotgctl = 0;
+
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (__le16_to_cpu(ctrl.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 1;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ /*
+ * Setup the Test Mode tasklet to do the Test
+ * Packet generation after the SETUP Status
+ * phase has completed.
+ */
+
+ pcd->test_mode_tasklet.next = NULL;
+ pcd->test_mode_tasklet.state = 0;
+ atomic_set(&pcd->test_mode_tasklet.count, 0);
+
+ pcd->test_mode_tasklet.func = do_test_mode;
+ pcd->test_mode_tasklet.data = (unsigned long)pcd;
+ pcd->test_mode = __le16_to_cpu(ctrl.wIndex) >> 8;
+ tasklet_schedule(&pcd->test_mode_tasklet);
+
+ break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ /* dev may initiate HNP */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->b_hnp_enable = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ /*
+ * gotgctl.devhnpen cleared by a
+ * USB Reset?
+ */
+ gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+ gotgctl |= DWC_GCTL_HNP_REQ;
+ dwc_reg_write(regs, DWC_GOTGCTL, gotgctl);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ case USB_DEVICE_A_HNP_SUPPORT:
+ /* RH port supports HNP */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ /* other RH port does */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_alt_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ case USB_RECIP_INTERFACE:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ case USB_RECIP_ENDPOINT:
+ if (__le16_to_cpu(ctrl.wValue) == USB_ENDPOINT_HALT) {
+ struct pcd_ep *ep;
+
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+
+ if (ep == NULL) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ }
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static void do_clear_feature(struct dwc_pcd *pcd)
+{
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ struct pcd_ep *ep;
+
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (__le16_to_cpu(ctrl.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 0;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ /* Add CLEAR_FEATURE for TEST modes. */
+ break;
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+ if (ep == NULL) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+
+ pcd_clear_halt(pcd, ep);
+ break;
+ }
+}
+
+/**
+ * This function processes SETUP commands. In Linux, the USB Command processing
+ * is done in two places - the first being the PCD and the second in the Gadget
+ * Driver (for example, the File-Backed Storage Gadget Driver).
+ *
+ * GET_STATUS: Command is processed as defined in chapter 9 of the USB 2.0
+ * Specification chapter 9
+ *
+ * CLEAR_FEATURE: The Device and Endpoint requests are the ENDPOINT_HALT feature
+ * is procesed, all others the interface requests are ignored.
+ *
+ * SET_FEATURE: The Device and Endpoint requests are processed by the PCD.
+ * Interface requests are passed to the Gadget Driver.
+ *
+ * SET_ADDRESS: PCD, Program the DCFG reg, with device address received
+ *
+ * GET_DESCRIPTOR: Gadget Driver, Return the requested descriptor
+ *
+ * SET_DESCRIPTOR: Gadget Driver, Optional - not implemented by any of the
+ * existing Gadget Drivers.
+ *
+ * SET_CONFIGURATION: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_CONFIGURATION: Gadget Driver, Return the current configuration
+ *
+ * SET_INTERFACE: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_INTERFACE: Gadget Driver, Return the current interface.
+ *
+ * SYNC_FRAME: Display debug message.
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static void pcd_setup(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ struct pcd_ep *ep;
+ struct pcd_ep *ep0 = &pcd->ep0;
+ u16 *status = pcd->status_buf;
+ u32 doeptsize0 = 0;
+
+ doeptsize0 = dwc_reg_read(dev_if->out_ep_regs[0], DWC_DOEPTSIZ);
+
+ /* handle > 1 setup packet , assert error for now */
+ if (core_if->dma_enable && (DWC_DEPTSIZ0_SUPCNT_RD(doeptsize0) < 2))
+ pr_err("\n\n CANNOT handle > 1 setup packet in "
+ "DMA mode\n\n");
+
+ /* Clean up the request queue */
+ request_nuke(ep0);
+ ep0->stopped = 0;
+
+ if (ctrl.bRequestType & USB_DIR_IN) {
+ ep0->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_IN_DATA_PHASE;
+ } else {
+ ep0->dwc_ep.is_in = 0;
+ pcd->ep0state = EP0_OUT_DATA_PHASE;
+ }
+
+ if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
+ /*
+ * Handle non-standard (class/vendor) requests in the gadget
+ * driver
+ */
+ do_gadget_setup(pcd, &ctrl);
+ return;
+ }
+
+ switch (ctrl.bRequest) {
+ case USB_REQ_GET_STATUS:
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ *status = 0x1; /* Self powered */
+ *status |= pcd->remote_wakeup_enable << 1;
+ break;
+ case USB_RECIP_INTERFACE:
+ *status = 0;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+ if (ep == NULL || __le16_to_cpu(ctrl.wLength) > 2) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+ *status = ep->stopped;
+ break;
+ }
+
+ *status = __cpu_to_le16(*status);
+
+ pcd->ep0_pending = 1;
+ ep0->dwc_ep.start_xfer_buff = (u8 *) status;
+ ep0->dwc_ep.xfer_buff = (u8 *) status;
+ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+ ep0->dwc_ep.xfer_len = 2;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ do_clear_feature(pcd);
+ break;
+ case USB_REQ_SET_FEATURE:
+ do_set_feature(pcd);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ if (ctrl.bRequestType == USB_RECIP_DEVICE) {
+ u32 dcfg = 0;
+
+ dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg,
+ __le16_to_cpu(ctrl.wValue));
+ dwc_reg_modify(dev_if->dev_global_regs, DWC_DCFG,
+ 0, dcfg);
+ do_setup_in_status_phase(pcd);
+ return;
+ }
+ break;
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SET_CONFIGURATION:
+ pcd->request_config = 1; /* Configuration changed */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ default:
+ /* Call the Gadget Driver's setup functions */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ }
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int ep0_complete_request(struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(ep->pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ ulong in_regs = dev_if->in_ep_regs[ep->dwc_ep.num];
+ u32 deptsiz = 0;
+ struct pcd_request *req;
+ int is_last = 0;
+ struct dwc_pcd *pcd = ep->pcd;
+
+ if (pcd->ep0_pending && list_empty(&ep->queue)) {
+ if (ep->dwc_ep.is_in)
+ do_setup_out_status_phase(pcd);
+ else
+ do_setup_in_status_phase(pcd);
+
+ pcd->ep0_pending = 0;
+ pcd->ep0state = EP0_STATUS;
+ return 1;
+ }
+
+ if (list_empty(&ep->queue))
+ return 0;
+
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ if (pcd->ep0state == EP0_STATUS) {
+ is_last = 1;
+ } else if (ep->dwc_ep.is_in) {
+ deptsiz = dwc_reg_read(in_regs, DWC_DIEPTSIZ);
+
+ if (DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz) == 0) {
+ req->req.actual = ep->dwc_ep.xfer_count;
+ do_setup_out_status_phase(pcd);
+ }
+ } else {
+ /* This is ep0-OUT */
+ req->req.actual = ep->dwc_ep.xfer_count;
+ do_setup_in_status_phase(pcd);
+ }
+
+ /* Complete the request */
+ if (is_last) {
+ request_done(ep, req, 0);
+ ep->dwc_ep.start_xfer_buff = NULL;
+ ep->dwc_ep.xfer_buff = NULL;
+ ep->dwc_ep.xfer_len = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * This function completes the request for the EP. If there are additional
+ * requests for the EP in the queue they will be started.
+ */
+static void complete_ep(struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(ep->pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ ulong in_ep_regs = dev_if->in_ep_regs[ep->dwc_ep.num];
+ u32 deptsiz = 0;
+ struct pcd_request *req = NULL;
+ int is_last = 0;
+
+ /* Get any pending requests */
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ if (ep->dwc_ep.is_in) {
+ deptsiz = dwc_reg_read(in_ep_regs, DWC_DIEPTSIZ);
+
+ if (core_if->dma_enable && !DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz))
+ ep->dwc_ep.xfer_count = ep->dwc_ep.xfer_len;
+
+ if (DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz) == 0 &&
+ DWC_DEPTSIZ_PKT_CNT_RD(deptsiz) == 0 &&
+ ep->dwc_ep.xfer_count == ep->dwc_ep.xfer_len)
+ is_last = 1;
+ else
+ pr_warning("Incomplete transfer (%s-%s "
+ "[siz=%d pkt=%d])\n", ep->ep.name,
+ ep->dwc_ep.is_in ? "IN" : "OUT",
+ DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz),
+ DWC_DEPTSIZ_PKT_CNT_RD(deptsiz));
+ } else {
+ ulong out_ep_regs = dev_if->out_ep_regs[ep->dwc_ep.num];
+
+ deptsiz = dwc_reg_read(out_ep_regs, DWC_DOEPTSIZ);
+ is_last = 1;
+ }
+
+ /* Complete the request */
+ if (is_last) {
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger transfers
+ * into multiple transfers of 1024 bytes sized messages.
+ * I happens often, that transfers of 4096 bytes are
+ * required (zero-gadget, file_storage-gadget).
+ */
+ if ((dwc_has_feature(core_if, DWC_LIMITED_XFER)) &&
+ ep->dwc_ep.bytes_pending) {
+ ulong in_regs =
+ core_if->dev_if->in_ep_regs[ep->dwc_ep.num];
+ u32 intr_mask = 0;
+
+ ep->dwc_ep.xfer_len = ep->dwc_ep.bytes_pending;
+ if (ep->dwc_ep.xfer_len > MAX_XFER_LEN) {
+ ep->dwc_ep.bytes_pending = ep->dwc_ep.xfer_len -
+ MAX_XFER_LEN;
+ ep->dwc_ep.xfer_len = MAX_XFER_LEN;
+ } else {
+ ep->dwc_ep.bytes_pending = 0;
+ }
+
+ /*
+ * Restart the current transfer with the next "chunk"
+ * of data.
+ */
+ ep->dwc_ep.xfer_count = 0;
+
+ deptsiz = dwc_reg_read(in_regs, DWC_DIEPTSIZ);
+ deptsiz =
+ DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz,
+ ep->dwc_ep.xfer_len);
+ deptsiz =
+ DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+ ((ep->dwc_ep.xfer_len - 1 +
+ ep->dwc_ep.maxpacket) /
+ ep->dwc_ep.maxpacket));
+ dwc_reg_write(in_regs, DWC_DIEPTSIZ, deptsiz);
+
+ intr_mask |= DWC_INTSTS_NP_TXFIFO_EMPT;
+ dwc_reg_modify((core_if->core_global_regs),
+ DWC_GINTSTS, intr_mask, 0);
+ dwc_reg_modify((core_if->core_global_regs),
+ DWC_GINTMSK, intr_mask, intr_mask);
+
+ /*
+ * Just return here if message was not completely
+ * transferred.
+ */
+ return;
+ }
+ if (core_if->dma_enable)
+ req->req.actual = ep->dwc_ep.xfer_len -
+ DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz);
+ else
+ req->req.actual = ep->dwc_ep.xfer_count;
+
+ request_done(ep, req, 0);
+ ep->dwc_ep.start_xfer_buff = NULL;
+ ep->dwc_ep.xfer_buff = NULL;
+ ep->dwc_ep.xfer_len = 0;
+
+ /* If there is a request in the queue start it. */
+ dwc_start_next_request(ep);
+ }
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ */
+static void dwc_otg_ep0_continue_transfer(struct core_if *c_if,
+ struct dwc_ep *ep)
+{
+ if (ep->is_in) {
+ u32 depctl = 0;
+ u32 deptsiz = 0;
+ struct device_if *d_if = c_if->dev_if;
+ ulong in_regs = d_if->in_ep_regs[0];
+ u32 tx_status = 0;
+ ulong glbl_regs = c_if->core_global_regs;
+
+ tx_status = dwc_reg_read(glbl_regs, DWC_GNPTXSTS);
+
+ depctl = dwc_reg_read(in_regs, DWC_DIEPCTL);
+ deptsiz = dwc_reg_read(in_regs, DWC_DIEPTSIZ);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+ if (ep->total_len - ep->xfer_count > ep->maxpacket)
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ ep->maxpacket);
+ else
+ deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+ (ep->total_len -
+ ep->xfer_count));
+
+ deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+ ep->xfer_len += DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz);
+ dwc_reg_write(in_regs, DWC_DIEPTSIZ, deptsiz);
+
+ /* Write the DMA register */
+ if (DWC_HWCFG2_ARCH_RD(c_if->hwcfg2) == DWC_INT_DMA_ARCH)
+ dwc_reg_write(in_regs, DWC_DIEPDMA, ep->dma_addr);
+
+ /* EP enable, IN data in FIFO */
+ depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+ depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+ dwc_reg_write(in_regs, DWC_DIEPCTL, depctl);
+
+ /*
+ * Enable the Non-Periodic Tx FIFO empty interrupt, the
+ * data will be written into the fifo by the ISR.
+ */
+ if (!c_if->dma_enable) {
+ u32 intr_mask = 0;
+
+ /* First clear it from GINTSTS */
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_write(glbl_regs, DWC_GINTSTS, intr_mask);
+
+ /* To avoid spurious NPTxFEmp intr */
+ dwc_reg_modify(glbl_regs, DWC_GINTMSK, intr_mask,
+ intr_mask);
+ }
+ }
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in ep0state
+ */
+static void handle_ep0(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ switch (pcd->ep0state) {
+ case EP0_DISCONNECT:
+ break;
+ case EP0_IDLE:
+ pcd->request_config = 0;
+ pcd_setup(pcd);
+ break;
+ case EP0_IN_DATA_PHASE:
+ if (core_if->dma_enable)
+ /*
+ * For EP0 we can only program 1 packet at a time so we
+ * need to do the calculations after each complete.
+ * Call write_packet to make the calculations, as in
+ * slave mode, and use those values to determine if we
+ * can complete.
+ */
+ dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 1);
+ else
+ dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 0);
+
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)
+ dwc_otg_ep0_continue_transfer(core_if, &ep0->dwc_ep);
+ else
+ ep0_complete_request(ep0);
+ break;
+ case EP0_OUT_DATA_PHASE:
+ ep0_complete_request(ep0);
+ break;
+ case EP0_STATUS:
+ ep0_complete_request(ep0);
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 1;
+ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */
+
+ /* Prepare for more SETUP Packets */
+ if (core_if->dma_enable) {
+ ep0_out_start(core_if, pcd);
+ } else {
+ int i;
+ u32 diepctl = 0;
+
+ diepctl = dwc_reg_read(in_ep_ctl_reg(pcd, 0), 0);
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ dwc_start_next_request(&pcd->ep0);
+ }
+
+ diepctl = dwc_reg_read(in_ep_ctl_reg(pcd, 0), 0);
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ dwc_start_next_request(&pcd->ep0);
+ }
+
+ for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
+ diepctl = dwc_reg_read(in_ep_ctl_reg(pcd, i),
+ 0);
+
+ if (pcd->in_ep[i].queue_sof) {
+ pcd->in_ep[i].queue_sof = 0;
+ dwc_start_next_request(&pcd->in_ep[i]);
+ }
+ }
+ }
+ break;
+ case EP0_STALL:
+ pr_err("EP0 STALLed, should not get here handle_ep0()\n");
+ break;
+ }
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(struct dwc_pcd *pcd, const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ u32 dieptsiz = 0;
+ struct pcd_ep *ep;
+
+ dieptsiz = dwc_reg_read(dev_if->in_ep_regs[ep_num], DWC_DIEPTSIZ);
+ ep = get_in_ep(pcd, ep_num);
+
+ /*
+ * If pktcnt is not 0, and xfersize is 0, and there is a buffer,
+ * resend the last packet.
+ */
+ if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) &&
+ !DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz) && ep->dwc_ep.start_xfer_buff) {
+ if (ep->dwc_ep.xfer_len <= ep->dwc_ep.maxpacket) {
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+ } else {
+ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+
+ /* convert packet size to dwords. */
+ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+ }
+ ep->stopped = 0;
+
+ if (!ep_num)
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ else
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+}
+
+/**
+ * Handle the IN EP Transfer Complete interrupt.
+ *
+ * If dedicated fifos are enabled, then the Tx FIFO empty interrupt for the EP
+ * is disabled. Otherwise the NP Tx FIFO empty interrupt is disabled.
+ */
+static void handle_in_ep_xfr_complete_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ struct core_if *c_if = GET_CORE_IF(pcd);
+ struct device_if *d_if = c_if->dev_if;
+ struct dwc_ep *dwc_ep = &ep->dwc_ep;
+ u32 diepint = 0;
+
+ if (c_if->en_multiple_tx_fifo) {
+ u32 fifoemptymsk = 0x1 << dwc_ep->num;
+ dwc_reg_modify(d_if->dev_global_regs,
+ DWC_DTKNQR4FIFOEMPTYMSK, fifoemptymsk, 0);
+ } else {
+ u32 intr_mask = 0;
+
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify((c_if->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+ }
+
+ /* Clear the interrupt, then complete the transfer */
+ diepint = DWC_DIEPINT_TX_CMPL_RW(diepint, 1);
+ dwc_reg_write(d_if->in_ep_regs[num], DWC_DIEPINT, diepint);
+
+ if (!num)
+ handle_ep0(pcd);
+ else
+ complete_ep(ep);
+}
+
+/**
+ * Handle the IN EP disable interrupt.
+ */
+static void handle_in_ep_disable_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ u32 dieptsiz = 0;
+ u32 dctl = 0;
+ struct pcd_ep *ep;
+ struct dwc_ep *dwc_ep;
+ u32 diepint = 0;
+
+ ep = get_in_ep(pcd, ep_num);
+ dwc_ep = &ep->dwc_ep;
+
+ dieptsiz = dwc_reg_read(dev_if->in_ep_regs[ep_num], DWC_DIEPTSIZ);
+
+ if (ep->stopped) {
+ /* Flush the Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+
+ /* Clear the Global IN NP NAK */
+ dctl = 0;
+ dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+ dwc_reg_modify(dev_ctl_reg(pcd), 0, dctl, 0);
+
+ if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+ DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+ restart_transfer(pcd, ep_num);
+ } else {
+ if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+ DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+ restart_transfer(pcd, ep_num);
+ }
+ /* Clear epdisabled */
+ diepint = DWC_DIEPINT_EP_DISA_RW(diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, ep_num), 0, diepint);
+
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static void handle_in_ep_timeout_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct pcd_ep *ep;
+ u32 dctl = 0;
+ u32 intr_mask = 0;
+ u32 diepint = 0;
+
+ ep = get_in_ep(pcd, ep_num);
+
+ /* Disable the NP Tx Fifo Empty Interrrupt */
+ if (!core_if->dma_enable) {
+ intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+ dwc_reg_modify((core_if->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+ }
+
+ /* Non-periodic EP */
+ /* Enable the Global IN NAK Effective Interrupt */
+ intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+ dwc_reg_modify((core_if->core_global_regs), DWC_GINTMSK, 0,
+ intr_mask);
+
+ /* Set Global IN NAK */
+ dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+ dwc_reg_modify(dev_ctl_reg(pcd), 0, dctl, dctl);
+ ep->stopped = 1;
+
+ /* Clear timeout */
+ diepint = DWC_DIEPINT_TOUT_COND_RW(diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, ep_num), 0, diepint);
+}
+
+/**
+ * Handles the IN Token received with TxF Empty interrupt.
+ *
+ * For the 405EZ, only start the next transfer, when currently no other transfer
+ * is active on this endpoint.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void handle_in_ep_tx_fifo_empty_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ u32 diepint = 0;
+
+ if (!ep->stopped && num) {
+ u32 diepmsk = 0;
+
+ diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+ dwc_reg_modify(dev_diepmsk_reg(pcd), 0, diepmsk, 0);
+
+ if (dwc_has_feature(GET_CORE_IF(pcd), DWC_LIMITED_XFER)) {
+ if (!ep->dwc_ep.active)
+ dwc_start_next_request(ep);
+ } else {
+ dwc_start_next_request(ep);
+ }
+ }
+ /* Clear intktxfemp */
+ diepint = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, num), 0, diepint);
+}
+
+static void handle_in_ep_nak_effective_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ u32 diepctl = 0;
+ u32 diepint = 0;
+
+ /* Periodic EP */
+ if (ep->disabling) {
+ diepctl = 0;
+ diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+ diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+ dwc_reg_modify(in_ep_ctl_reg(pcd, num), 0, diepctl, diepctl);
+ }
+ /* Clear inepnakeff */
+ diepint = DWC_DIEPINT_IN_EP_NAK_RW(diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, num), 0, diepint);
+
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline u32 dwc_otg_read_diep_intr(struct core_if *core_if,
+ struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 v, msk, emp;
+
+ msk = dwc_reg_read(dev_if->dev_global_regs, DWC_DIEPMSK);
+ emp =
+ dwc_reg_read(dev_if->dev_global_regs, DWC_DTKNQR4FIFOEMPTYMSK);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = dwc_reg_read(dev_if->in_ep_regs[ep->num], DWC_DIEPINT) & msk;
+ return v;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_in_ep_intr(struct core_if *_if)
+{
+ u32 v;
+
+ v = dwc_reg_read(_if->dev_if->dev_global_regs, DWC_DAINT) &
+ dwc_reg_read(_if->dev_if->dev_global_regs, DWC_DAINTMSK);
+ return v & 0xffff;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register
+ * - Repeat the following for each IN EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DIEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Time-out Handshake" log error
+ * - If "IN Token Received when TxFIFO Empty" write packet to Tx FIFO.
+ * - If "IN Token EP Mismatch" (disable, this is handled by EP Mismatch
+ * Interrupt)
+ */
+static int dwc_otg_pcd_handle_in_ep_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ u32 diepint = 0;
+ u32 ep_intr;
+ u32 epnum = 0;
+ struct pcd_ep *ep;
+ struct dwc_ep *dwc_ep;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+ /* Service the Device IN interrupts for each endpoint */
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ u32 c_diepint;
+
+ /* Get EP pointer */
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ diepint = dwc_otg_read_diep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (DWC_DIEPINT_TX_CMPL_RD(diepint))
+ handle_in_ep_xfr_complete_intr(pcd, ep, epnum);
+
+ /* Endpoint disable */
+ if (DWC_DIEPINT_EP_DISA_RD(diepint))
+ handle_in_ep_disable_intr(pcd, epnum);
+
+ /* AHB Error */
+ if (DWC_DIEPINT_AHB_ERROR_RD(diepint)) {
+ /* Clear ahberr */
+ c_diepint = 0;
+ c_diepint =
+ DWC_DIEPINT_AHB_ERROR_RW(c_diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, epnum), 0,
+ c_diepint);
+ }
+
+ /* TimeOUT Handshake (non-ISOC IN EPs) */
+ if (DWC_DIEPINT_TOUT_COND_RD(diepint))
+ handle_in_ep_timeout_intr(pcd, epnum);
+
+ /* IN Token received with TxF Empty */
+ if (DWC_DIEPINT_IN_TKN_TX_EMPTY_RD(diepint))
+ handle_in_ep_tx_fifo_empty_intr(pcd, ep, epnum);
+
+ /* IN Token Received with EP mismatch */
+ if (DWC_DIEPINT_IN_TKN_EP_MISS_RD(diepint)) {
+ /* Clear intknepmis */
+ c_diepint = 0;
+ c_diepint =
+ DWC_DIEPINT_IN_TKN_EP_MISS_RW(c_diepint, 1);
+ dwc_reg_write(in_ep_int_reg(pcd, epnum), 0,
+ c_diepint);
+ }
+
+ /* IN Endpoint NAK Effective */
+ if (DWC_DIEPINT_IN_EP_NAK_RD(diepint))
+ handle_in_ep_nak_effective_intr(pcd, ep, epnum);
+
+ /* IN EP Tx FIFO Empty Intr */
+ if (DWC_DIEPINT_TXFIFO_EMPTY_RD(diepint))
+ write_empty_tx_fifo(pcd, epnum);
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_out_ep_intr(struct core_if *_if)
+{
+ u32 v;
+
+ v = dwc_reg_read(_if->dev_if->dev_global_regs, DWC_DAINT) &
+ dwc_reg_read(_if->dev_if->dev_global_regs, DWC_DAINTMSK);
+ return (v & 0xffff0000) >> 16;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline u32 dwc_otg_read_doep_intr(struct core_if *core_if,
+ struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 v;
+
+ v = dwc_reg_read(dev_if->out_ep_regs[ep->num], DWC_DOEPINT) &
+ dwc_reg_read(dev_if->dev_global_regs, DWC_DOEPMSK);
+ return v;
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register.
+ * - Repeat the following for each OUT EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DOEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Setup Phase Done" process Setup Packet (See Standard USB Command
+ * Processing)
+ */
+static int dwc_otg_pcd_handle_out_ep_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ u32 ep_intr;
+ u32 doepint = 0;
+ u32 epnum = 0;
+ struct dwc_ep *dwc_ep;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ u32 c_doepint = 0;
+
+ dwc_ep = &((get_out_ep(pcd, epnum))->dwc_ep);
+ doepint = dwc_otg_read_doep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (DWC_DOEPINT_TX_COMPL_RD(doepint)) {
+ /* Clear xfercompl */
+ c_doepint = 0;
+ c_doepint =
+ DWC_DOEPMSK_TX_COMPL_RW(c_doepint, 1);
+ dwc_reg_write(out_ep_int_reg(pcd, epnum), 0,
+ c_doepint);
+ if (epnum == 0)
+ handle_ep0(pcd);
+ else
+ complete_ep(get_out_ep(pcd, epnum));
+ }
+
+ /* Endpoint disable */
+ if (DWC_DOEPINT_EP_DISA_RD(doepint)) {
+ /* Clear epdisabled */
+ c_doepint = 0;
+ c_doepint =
+ DWC_DOEPMSK_EP_DISA_RW(c_doepint, 1);
+ dwc_reg_write(out_ep_int_reg(pcd, epnum), 0,
+ c_doepint);
+ }
+
+ /* AHB Error */
+ if (DWC_DOEPINT_AHB_ERROR_RD(doepint)) {
+ c_doepint = 0;
+ c_doepint =
+ DWC_DOEPMSK_AHB_ERROR_RW(c_doepint, 1);
+ dwc_reg_write(out_ep_int_reg(pcd, epnum), 0,
+ c_doepint);
+ }
+
+ /* Setup Phase Done (control EPs) */
+ if (DWC_DOEPINT_SETUP_DONE_RD(doepint)) {
+ c_doepint = 0;
+ c_doepint =
+ DWC_DOEPMSK_SETUP_DONE_RW(c_doepint, 1);
+ dwc_reg_write(out_ep_int_reg(pcd, epnum), 0,
+ c_doepint);
+ handle_ep0(pcd);
+ }
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+/**
+ * Incomplete ISO IN Transfer Interrupt. This interrupt indicates one of the
+ * following conditions occurred while transmitting an ISOC transaction.
+ *
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Disable EP. When "Endpoint Disabled" interrupt is received Flush FIFO
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_in_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts = 0;
+
+ pr_info("Interrupt handler not implemented for IN ISOC "
+ "Incomplete\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts |= DWC_INTSTS_INCMP_IN_ATX;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+ return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt. This interrupt indicates that the
+ * core has dropped an ISO OUT packet. The following conditions can be the
+ * cause:
+ *
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Read any data from the FIFO
+ * - Disable EP. When "Endpoint Disabled" interrupt is received re-enable EP.
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_out_intr(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts = 0;
+
+ pr_info("Interrupt handler not implemented for OUT ISOC "
+ "Incomplete\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ gintsts |= DWC_INTSTS_INCMP_OUT_PTX;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+ return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_in_nak_effective(struct dwc_pcd *pcd)
+{
+ struct device_if *dev_if = GET_CORE_IF(pcd)->dev_if;
+ u32 diepctl = 0;
+ u32 diepctl_rd = 0;
+ u32 intr_mask = 0;
+ u32 gintsts = 0;
+ u32 i;
+
+ /* Disable all active IN EPs */
+ diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+ diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+ for (i = 0; i <= dev_if->num_in_eps; i++) {
+ diepctl_rd = dwc_reg_read(in_ep_ctl_reg(pcd, i), 0);
+ if (DWC_DEPCTL_EPENA_RD(diepctl_rd))
+ dwc_reg_write(in_ep_ctl_reg(pcd, i), 0, diepctl);
+ }
+
+ /* Disable the Global IN NAK Effective Interrupt */
+ intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ /* Clear interrupt */
+ gintsts |= DWC_INTSTS_GLBL_IN_NAK;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+ return 1;
+}
+
+/**
+ * This function handles the Global OUT NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_out_nak_effective(struct dwc_pcd *pcd)
+{
+ u32 intr_mask = 0;
+ u32 gintsts = 0;
+
+ pr_info("Interrupt handler not implemented for Global IN "
+ "NAK Effective\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask |= DWC_INTMSK_GLBL_OUT_NAK;
+ dwc_reg_modify((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTMSK,
+ intr_mask, 0);
+
+ /* Clear goutnakeff */
+ gintsts |= DWC_INTSTS_GLBL_OUT_NAK;
+ dwc_reg_write((GET_CORE_IF(pcd)->core_global_regs), DWC_GINTSTS,
+ gintsts);
+ return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts. Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ u32 gintr_status;
+ int ret = 0;
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ spin_lock(&pcd->lock);
+
+ gintr_status = dwc_otg_read_core_intr(core_if);
+ if (!gintr_status) {
+ spin_unlock(&pcd->lock);
+ return 0;
+ }
+
+ if (gintr_status & DWC_INTSTS_STRT_OF_FRM)
+ ret |= dwc_otg_pcd_handle_sof_intr(pcd);
+ if (gintr_status & DWC_INTSTS_RXFIFO_NOT_EMPT)
+ ret |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+ if (gintr_status & DWC_INTSTS_NP_TXFIFO_EMPT)
+ ret |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+ if (gintr_status & DWC_INTSTS_GLBL_IN_NAK)
+ ret |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+ if (gintr_status & DWC_INTSTS_GLBL_OUT_NAK)
+ ret |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+ if (gintr_status & DWC_INTSTS_I2C_INTR)
+ ret |= dwc_otg_pcd_handle_i2c_intr(pcd);
+ if (gintr_status & DWC_INTSTS_EARLY_SUSP)
+ ret |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+ if (gintr_status & DWC_INTSTS_USB_RST)
+ ret |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+ if (gintr_status & DWC_INTSTS_ENUM_DONE)
+ ret |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+ if (gintr_status & DWC_INTSTS_ISYNC_OUTPKT_DRP)
+ ret |=
+ dwc_otg_pcd_handle_isoc_out_packet_dropped_intr
+ (pcd);
+ if (gintr_status & DWC_INTSTS_END_OF_PFRM)
+ ret |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+ if (gintr_status & DWC_INTSTS_ENDP_MIS_MTCH)
+ ret |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+ if (gintr_status & DWC_INTSTS_IN_ENDP)
+ ret |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ if (gintr_status & DWC_INTSTS_OUT_ENDP)
+ ret |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ if (gintr_status & DWC_INTSTS_INCMP_IN_ATX)
+ ret |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+ if (gintr_status & DWC_INTSTS_INCMP_OUT_PTX)
+ ret |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+
+ spin_unlock(&pcd->lock);
+ }
+ return ret;
+}
diff --git a/drivers/usb/dwc/regs.h b/drivers/usb/dwc/regs.h
new file mode 100644
index 0000000..d1748f1
--- /dev/null
+++ b/drivers/usb/dwc/regs.h
@@ -0,0 +1,1326 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * Revamped register difinitions by Tirumala R Marri(tmarri@apm.com)
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+#include <linux/types.h>
+/*Bit fields in the Device EP Transfer Size Register is 11 bits */
+#undef DWC_LIMITED_XFER_SIZE
+/*
+ * This file contains the Macro defintions for accessing the DWC_otg core
+ * registers.
+ *
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/*
+ * DWC_otg Core registers. The core_global_regs structure defines the
+ * size and relative field offsets for the Core Global registers.
+ */
+#define DWC_GOTGCTL 0x000
+#define DWC_GOTGINT 0x004
+#define DWC_GAHBCFG 0x008
+#define DWC_GUSBCFG 0x00C
+#define DWC_GRSTCTL 0x010
+#define DWC_GINTSTS 0x014
+#define DWC_GINTMSK 0x018
+#define DWC_GRXSTSR 0x01C
+#define DWC_GRXSTSP 0x020
+#define DWC_GRXFSIZ 0x024
+#define DWC_GNPTXFSIZ 0x028
+#define DWC_GNPTXSTS 0x02C
+#define DWC_GI2CCTL 0x030
+#define DWC_VDCTL 0x034
+#define DWC_GGPIO 0x038
+#define DWC_GUID 0x03C
+#define DWC_GSNPSID 0x040
+#define DWC_GHWCFG1 0x044
+#define DWC_GHWCFG2 0x048
+#define DWC_GHWCFG3 0x04c
+#define DWC_GHWCFG4 0x050
+#define DWC_HPTXFSIZ 0x100
+#define DWC_DPTX_FSIZ_DIPTXF(x) (0x104 + x * 4) /* 15 <= x > 1 */
+
+#define DWC_GLBINTRMASK 0x0001
+#define DWC_DMAENABLE 0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY 0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000
+#define DWC_PTXEMPTYLVL_EMPTY 0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000
+
+#define DWC_SLAVE_ONLY_ARCH 0
+#define DWC_EXT_DMA_ARCH 1
+#define DWC_INT_DMA_ARCH 2
+
+#define DWC_MODE_HNP_SRP_CAPABLE 0
+#define DWC_MODE_SRP_ONLY_CAPABLE 1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE 2
+#define DWC_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_MODE_SRP_CAPABLE_HOST 5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST 6
+
+/*
+ * These Macros represents the bit fields of the Core OTG Controland Status
+ * Register (GOTGCTL). Set the bits using the bit fields then write the u32
+ * value to the register.
+ */
+#define DWC_GCTL_BSESSION_VALID (1 << 19)
+#define DWC_GCTL_CSESSION_VALID (1 << 18)
+#define DWC_GCTL_DEBOUNCE (1 << 17)
+#define DWC_GCTL_CONN_ID_STATUS (1 << 16)
+#define DWC_GCTL_DEV_HNP_ENA (1 << 11)
+#define DWC_GCTL_HOST_HNP_ENA (1 << 10)
+#define DWC_GCTL_HNP_REQ (1 << 9)
+#define DWC_GCTL_HOST_NEG_SUCCES (1 << 8)
+#define DWC_GCTL_SES_REQ (1 << 1)
+#define DWC_GCTL_SES_REQ_SUCCESS (1 << 0)
+
+#define DWC_GCTL_BSESSION_VALID_RD(reg) (((reg) & (0x001 << 19)) >> 19)
+#define DWC_GCTL_CSESSION_VALID_RD(reg) (((reg) & (0x001 << 18)) >> 18)
+#define DWC_GCTL_DEBOUNCE_RD(reg) (((reg) & (0x001 << 17)) >> 17)
+#define DWC_GCTL_CONN_ID_STATUS_RD(reg) (((reg) & (0x001 << 16)) >> 16)
+#define DWC_GCTL_DEV_HNP_ENA_RD(reg) (((reg) & (0x001 << 11)) >> 11)
+#define DWC_GCTL_HOST_HNP_ENA_RD(reg) (((reg) & (0x001 << 10)) >> 10)
+#define DWC_GCTL_HNP_REQ_RD(reg) (((reg) & (0x001 << 9)) >> 9)
+#define DWC_GCTL_HOST_NEG_SUCCES_RD(reg) (((reg) & (0x001 << 8)) >> 8)
+#define DWC_GCTL_SES_REQ_RD(reg) (((reg) & (0x001 << 1)) >> 1)
+#define DWC_GCTL_SES_REQ_SUCCESS_RD(reg) (((reg) & (0x001 << 0)) >> 0)
+
+#define DWC_GCTL_BSESSION_VALID_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 19))) | ((x) << 19))
+#define DWC_GCTL_CSESSION_VALID_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 18))) | ((x) << 18))
+#define DWC_GCTL_DEBOUNCE_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 17))) | ((x) << 17))
+#define DWC_GCTL_CONN_ID_STATUS_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 16))) | ((x) << 16))
+#define DWC_GCTL_DEV_HNP_ENA_RW (reg, x) \
+ (((reg) & (~((u32)0x01 << 11))) | ((x) << 11))
+#define DWC_GCTL_HOST_HNP_ENA_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 10))) | ((x) << 10))
+#define DWC_GCTL_HNP_REQ_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 9))) | ((x) << 9))
+#define DWC_GCTL_HOST_NEG_SUCCES_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_GCTL_SES_REQ_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_GCTL_SES_REQ_SUCCESS_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+/*
+ * These Macros represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT). Set/clear the bits using the bit fields then write the u32
+ * value to the register.
+ */
+#define DWC_GINT_DEBDONE (1 << 19)
+#define DWC_GINT_DEVTOUT (1 << 18)
+#define DWC_GINT_HST_NEGDET (1 << 17)
+#define DWC_GINT_HST_NEGSUC (1 << 9)
+#define DWC_GINT_SES_REQSUC (1 << 8)
+#define DWC_GINT_SES_ENDDET (1 << 2)
+
+/*
+ * These Macros represents the bit fields of the Core AHB Configuration Register
+ * (GAHBCFG). Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_AHBCFG_FIFO_EMPTY (1 << 8)
+#define DWC_AHBCFG_NPFIFO_EMPTY (1 << 7)
+#define DWC_AHBCFG_DMA_ENA (1 << 5)
+#define DWC_AHBCFG_BURST_LEN(x) (x << 1)
+#define DWC_AHBCFG_GLBL_INT_MASK (1 << 0)
+
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+#define DWC_GAHBCFG_DMAENABLE 1
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
+
+/*
+
+ * (GUSBCFG). Set the bits using the bit fields then write the u32 value to the
+ * register.
+ */
+#define DWC_USBCFG_CORR_PKT (1 << 31)
+#define DWC_USBCFG_FRC_DEV_MODE (1 << 30)
+#define DWC_USBCFG_FRC_HST_MODE (1 << 29)
+#define DWC_USBCFG_TERM_SEL_DL_PULSE (1 << 22)
+#define DWC_USBCFG_ULPI_INTVBUS_INDICATOR (1 << 21)
+#define DWC_USBCFG_ULPI_EXT_VBUS_DRV (1 << 20)
+#define DWC_USBCFG_ULPI_CLK_SUS_M (1 << 19)
+#define DWC_USBCFG_ULPI_AUTO_RES (1 << 18)
+#define DWC_USBCFG_ULPI_FSLS (1 << 17)
+#define DWC_USBCFG_OTGUTMIFSSEL (1 << 16)
+#define DWC_USBCFG_PHYLPWRCLKSEL (1 << 15)
+#define DWC_USBCFG_NPTXFRWNDEN (1 << 14)
+#define DWC_USBCFG_TRN_TIME(x) (x << 10)
+#define DWC_USBCFG_HNP_CAP (1 << 9)
+#define DWC_USBCFG_SRP_CAP (1 << 8)
+#define DWC_USBCFG_DDRSEL (1 << 7)
+#define DWC_USBCFG_USB_2_11 (1 << 6)
+#define DWC_USBCFG_FSINTF (1 << 5)
+#define DWC_USBCFG_ULPI_UTMI_SEL (1 << 4)
+#define DWC_USBCFG_PHYIF (1 << 3)
+#define DWC_USBCFG_TOUT_CAL(x) (x << 0)
+
+/*
+ * These Macros represents the bit fields of the Core Reset Register (GRSTCTL).
+ * Set/clear the bits using the bit fields then write the u32 value to the
+ * register.
+ */
+#define DWC_RSTCTL_AHB_IDLE (1 << 31)
+#define DWC_RSTCTL_DMA_REQ (1 << 30)
+#define DWC_RSTCTL_TX_FIFO_NUM(reg, x) \
+ (((reg) & (~((u32)0x1f << 6))) | ((x) << 6))
+#define DWC_RSTCTL_TX_FIFO_FLUSH (1 << 5)
+#define DWC_RSTCTL_RX_FIFO_FLUSH (1 << 4)
+#define DWC_RSTCTL_TKN_QUE_FLUSH (1 << 3)
+#define DWC_RSTCTL_HSTFRM_CNTR_RST (1 << 2)
+#define DWC_RSTCTL_HCLK_SFT_RST (1 << 1)
+#define DWC_RSTCTL_SFT_RST (1 << 1)
+#define DWC_GRSTCTL_TXFNUM_ALL 0x10
+
+/*
+ * These Macros represents the bit fields of the Core Interrupt Mask Register
+ * (GINTMSK). Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_INTMSK_WKP (1 << 31)
+#define DWC_INTMSK_NEW_SES_DET (1 << 30)
+#define DWC_INTMSK_SES_DISCON_DET (1 << 29)
+#define DWC_INTMSK_CON_ID_STS_CHG (1 << 28)
+#define DWC_INTMSK_P_TXFIFO_EMPTY (1 << 26)
+#define DWC_INTMSK_HST_CHAN (1 << 25)
+#define DWC_INTMSK_HST_PORT (1 << 24)
+#define DWC_INTMSK_DATA_FETCH_SUS (1 << 23)
+#define DWC_INTMSK_INCMP_PTX (1 << 22)
+#define DWC_INTMSK_INCMP_OUT_PTX (1 << 21)
+#define DWC_INTMSK_INCMP_IN_ATX (1 << 20)
+#define DWC_INTMSK_OUT_ENDP (1 << 19)
+#define DWC_INTMSK_IN_ENDP (1 << 18)
+#define DWC_INTMSK_ENDP_MIS_MTCH (1 << 17)
+#define DWC_INTMSK_END_OF_PFRM (1 << 15)
+#define DWC_INTMSK_ISYNC_OUTPKT_DRP (1 << 14)
+#define DWC_INTMSK_ENUM_DONE (1 << 13)
+#define DWC_INTMSK_USB_RST (1 << 12)
+#define DWC_INTMSK_USB_SUSP (1 << 11)
+#define DWC_INTMSK_EARLY_SUSP (1 << 10)
+#define DWC_INTMSK_I2C_INTR (1 << 9)
+#define DWC_INTMSK_GLBL_OUT_NAK (1 << 7)
+#define DWC_INTMSK_GLBL_IN_NAK (1 << 6)
+#define DWC_INTMSK_NP_TXFIFO_EMPT (1 << 5)
+#define DWC_INTMSK_RXFIFO_NOT_EMPT (1 << 4)
+#define DWC_INTMSK_STRT_OF_FRM (1 << 3)
+#define DWC_INTMSK_OTG (1 << 2)
+#define DWC_INTMSK_MODE_MISMTC (1 << 1)
+/*
+ * These Macros represents the bit fields of the Core Interrupt Register
+ * (GINTSTS). Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_INTSTS_WKP (1 << 31)
+#define DWC_INTSTS_NEW_SES_DET (1 << 30)
+#define DWC_INTSTS_SES_DISCON_DET (1 << 29)
+#define DWC_INTSTS_CON_ID_STS_CHG (1 << 28)
+#define DWC_INTSTS_P_TXFIFO_EMPTY (1 << 26)
+#define DWC_INTSTS_HST_CHAN (1 << 25)
+#define DWC_INTSTS_HST_PORT (1 << 24)
+#define DWC_INTSTS_DATA_FETCH_SUS (1 << 23)
+#define DWC_INTSTS_INCMP_PTX (1 << 22)
+#define DWC_INTSTS_INCMP_OUT_PTX (1 << 21)
+#define DWC_INTSTS_INCMP_IN_ATX (1 << 20)
+#define DWC_INTSTS_OUT_ENDP (1 << 19)
+#define DWC_INTSTS_IN_ENDP (1 << 18)
+#define DWC_INTSTS_ENDP_MIS_MTCH (1 << 17)
+#define DWC_INTSTS_END_OF_PFRM (1 << 15)
+#define DWC_INTSTS_ISYNC_OUTPKT_DRP (1 << 14)
+#define DWC_INTSTS_ENUM_DONE (1 << 13)
+#define DWC_INTSTS_USB_RST (1 << 12)
+#define DWC_INTSTS_USB_SUSP (1 << 11)
+#define DWC_INTSTS_EARLY_SUSP (1 << 10)
+#define DWC_INTSTS_I2C_INTR (1 << 9)
+#define DWC_INTSTS_GLBL_OUT_NAK (1 << 7)
+#define DWC_INTSTS_GLBL_IN_NAK (1 << 6)
+#define DWC_INTSTS_NP_TXFIFO_EMPT (1 << 5)
+#define DWC_INTSTS_RXFIFO_NOT_EMPT (1 << 4)
+#define DWC_INTSTS_STRT_OF_FRM (1 << 3)
+#define DWC_INTSTS_OTG (1 << 2)
+#define DWC_INTSTS_MODE_MISMTC (1 << 1)
+#define DWC_INTSTS_CURR_MODE (1 << 0)
+#define DWC_SOF_INTR_MASK 0x0008
+#define DWC_HOST_MODE 1
+
+/*
+ * These Macros represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the u32
+ * element then read out the bits using the bit elements.
+ */
+#define DWC_DM_RXSTS_PKT_STS (0x01f << 17)
+#define DWC_DM_RXSTS_PKT_DPID (0x003 << 15)
+#define DWC_DM_RXSTS_BYTE_CNT (0x7ff << 4)
+#define DWC_DM_RXSTS_CHAN_NUM (0x00f << 0)
+
+#define DWC_DM_RXSTS_PKT_STS_RD(reg) (((reg) & (0x00f << 17)) >> 17)
+#define DWC_DM_RXSTS_PKT_DPID_RD(reg) (((reg) & (0x003 << 15)) >> 15)
+#define DWC_DM_RXSTS_BYTE_CNT_RD(reg) (((reg) & (0x7ff << 04)) >> 04)
+#define DWC_DM_RXSTS_CHAN_NUM_RD(reg) ((reg) & 0x00f)
+
+#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */
+#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */
+#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */
+#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */
+#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */
+
+/*
+ * These Macros represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the u32
+ * element then read out the bits using the bit elements.
+ */
+#define DWC_HM_RXSTS_FRM_NUM (0x00f << 21)
+#define DWC_HM_RXSTS_PKT_STS (0x01f << 17)
+#define DWC_HM_RXSTS_PKT_DPID (0x003 << 15)
+#define DWC_HM_RXSTS_BYTE_CNT (0x7ff << 4)
+#define DWC_HM_RXSTS_CHAN_NUM (0x00f << 0)
+
+#define DWC_HM_RXSTS_PKT_STS_RD(reg) (((reg) & (0x00f << 17)) >> 17)
+#define DWC_HM_RXSTS_PKT_DPID_RD(reg) (((reg) & (0x003 << 15)) >> 15)
+#define DWC_HM_RXSTS_BYTE_CNT_RD(reg) (((reg) & (0x7ff << 04)) >> 04)
+#define DWC_HM_RXSTS_CHAN_NUM_RD(reg) ((reg) & 0x00f)
+
+#define DWC_GRXSTS_PKTSTS_IN 0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7
+
+/*
+ * These Macros represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn). Read the register into the u32 element then
+ * read out the bits using the bit elements.
+ */
+#define DWC_RX_FIFO_DEPTH_RD(reg) (((reg) & ((u32)0xffff << 16)) >> 16)
+#define DWC_RX_FIFO_DEPTH_WR(reg, x) \
+ (((reg) & (~((u32)0xffff << 16))) | ((x) << 16))
+#define DWC_RX_FIFO_START_ADDR_RD(reg) ((reg) & 0xffff)
+#define DWC_RX_FIFO_START_ADDR_WR(reg, x) \
+ (((reg) & (~((u32)0xffff))) | (x))
+
+/*
+ * These Macros represents the bit fields in the Non-Periodic Tx FIFO/Queue
+ * Status Register (GNPTXSTS). Read the register into the u32 element then read
+ * out the bits using the bit elements.
+ */
+#define DWC_GNPTXSTS_NPTXQTOP_CHNEP_RD(x) (((x) & (0x3f << 26)) >> 26)
+#define DWC_GNPTXSTS_NPTXQTOP_TKN_RD(x) (((x) & (0x03 << 24)) >> 24)
+#define DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(x) (((x) & (0xff << 16)) >> 16)
+#define DWC_GNPTXSTS_NPTXFSPCAVAIL_RD(x) (0xffff & (x))
+
+/*
+ * These Macros represents the bit fields in the Transmit FIFO Status Register
+ * (DTXFSTS). Read the register into the u32 element then read out the bits
+ * using the bit elements.
+ */
+#define DWC_DTXFSTS_TXFSSPC_AVAI_RD(x) ((x) & 0xffff)
+
+/*
+ * These Macros represents the bit fields in the I2C Control Register (I2CCTL).
+ * Read the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_I2CCTL_BSYDNE (1 << 31)
+#define DWC_I2CCTL_RW (1 << 30)
+#define DWC_I2CCTL_I2CDEVADDR(x) ((x) << 27)
+#define DWC_I2CCTL_I2CSUSCTL (1 << 25)
+#define DWC_I2CCTL_ACK (1 << 24)
+#define DWC_I2CCTL_I2CEN (1 << 23)
+#define DWC_I2CCTL_ADDR (1 << 22)
+#define DWC_I2CCTL_REGADDR(x) ((x) << 14)
+#define DWC_I2CCTL_RWDATA(x) ((x) << 6)
+
+/*
+ * These Macros represents the bit fields in the User HW Config1 Register. Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG1_EPDIR15(x) ((x) << 30)
+#define DWC_HWCFG1_EPDIR14(x) ((x) << 28)
+#define DWC_HWCFG1_EPDIR13(x) ((x) << 26)
+#define DWC_HWCFG1_EPDIR12(x) ((x) << 24)
+#define DWC_HWCFG1_EPDIR11(x) ((x) << 22)
+#define DWC_HWCFG1_EPDIR10(x) ((x) << 20)
+#define DWC_HWCFG1_EPDIR9(x) ((x) << 18)
+#define DWC_HWCFG1_EPDIR8(x) ((x) << 16)
+#define DWC_HWCFG1_EPDIR7(x) ((x) << 14)
+#define DWC_HWCFG1_EPDIR6(x) ((x) << 13)
+#define DWC_HWCFG1_EPDIR5(x) ((x) << 10)
+#define DWC_HWCFG1_EPDIR4(x) ((x) << 8)
+#define DWC_HWCFG1_EPDIR3(x) ((x) << 6)
+#define DWC_HWCFG1_EPDIR2(x) ((x) << 4)
+#define DWC_HWCFG1_EPDIR1(x) ((x) << 2)
+#define DWC_HWCFG1_EPDIR0(x) ((x) << 0)
+
+/*
+ * These Macros represents the bit fields in the User HW Config2 Register. Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG2_DEV_TKN_Q_DEPTH_RD(x) (((x) & (0x1F << 26)) >> 26)
+#define DWC_HWCFG2_HOST_PERIO_Q_DEPTH_RD(x) (((x) & (0x3 << 24)) >> 24)
+#define DWC_HWCFG2_NP_TX_Q_DEPTH_RD(x) (((x) & (0x3 << 22)) >> 22)
+#define DWC_HWCFG2_RX_STS_Q_DEPTH_RD(x) (((x) & (0x3 << 20)) >> 20)
+#define DWC_HWCFG2_DYN_FIFO_RD(x) (((x) & (0x1 << 19)) >> 19)
+#define DWC_HWCFG2_PERIO_EP_SUPP_RD(x) (((x) & (0x1 << 18)) >> 18)
+#define DWC_HWCFG2_NO_HST_CHAN_RD(x) (((x) & (0xf << 14)) >> 14)
+#define DWC_HWCFG2_NO_DEV_EP_RD(x) (((x) & (0xf << 10)) >> 10)
+#define DWC_HWCFG2_FS_PHY_TYPE_RD(x) (((x) & (0x3 << 8)) >> 8)
+#define DWC_HWCFG2_HS_PHY_TYPE_RD(x) (((x) & (0x3 << 6)) >> 6)
+#define DWC_HWCFG2_P_2_P_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HWCFG2_ARCH_RD(x) (((x) & (0x3 << 3)) >> 3)
+#define DWC_HWCFG2_OP_MODE_RD(x) ((x) & 0x7)
+
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
+
+/*
+ * These Macros represents the bit fields in the User HW Config3 Register. ead
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG3_DFIFO_DEPTH_RD(x) (((x) & (0xffff << 16)) >> 16)
+#define DWC_HWCFG3_AHB_PHY_CLK_SYNC_RD(x) (((x) & (0x1 << 12)) >> 12)
+#define DWC_HWCFG3_SYNC_RST_TYPE_RD(x) (((x) & (0x1 << 11)) >> 11)
+#define DWC_HWCFG3_OPT_FEATURES_RD(x) (((x) & (0x1 << 10)) >> 10)
+#define DWC_HWCFG3_VEND_CTRL_IF_RD(x) (((x) & (0x1 << 9)) >> 9)
+#define DWC_HWCFG3_I2C_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HWCFG3_OTG_FUNC_RD(x) (((x) & (0x1 << 07)) >> 07)
+#define DWC_HWCFG3_PKTSIZE_CTR_WIDTH_RD(x) (((x) & (0x7 << 04)) >> 04)
+#define DWC_HWCFG3_XFERSIZE_CTR_WIDTH_RD(x) ((x) & 0xf)
+
+/*
+ * These Macros represents the bit fields in the User HW Config4 Register. Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG4_NUM_IN_EPS_RD(x) (((x) & (0xF << 26)) >> 26)
+#define DWC_HWCFG4_DED_FIFO_ENA_RD(x) (((x) & (0x1 << 25)) >> 25)
+#define DWC_HWCFG4_SES_END_FILT_EN_RD(x) (((x) & (0x1 << 24)) >> 24)
+#define DWC_HWCFG4_BVALID_FILT_EN_RD(x) (((x) & (0x1 << 23)) >> 23)
+#define DWC_HWCFG4_AVALID_FILT_EN_RD(x) (((x) & (0x1 << 22)) >> 22)
+#define DWC_HWCFG4_VBUS_VALID_FILT_EN_RD(x) (((x) & (0x1 << 21)) >> 21)
+#define DWC_HWCFG4_IDDIG_FILT_EN_RD(x) (((x) & (0x1 << 20)) >> 20)
+#define DWC_HWCFG4_NUM_DEV_MODE_CTRL_EP_RD(x) (((x) & (0xF << 16)) >> 16)
+#define DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(x) (((x) & (0x3 << 14)) >> 14)
+#define DWC_HWCFG4_MIN_AHB_FREQ_RD(x) (((x) & (0x1 << 05)) >> 05)
+#define DWC_HWCFG4_POWER_OPT_RD(x) (((x) & (0x1 << 04)) >> 04)
+#define DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(x) ((x) & 0xf)
+
+/*
+ * Device Global Registers. Offsets 800h-BFFh
+ *
+ * The following structures define the size and relative field offsets for the
+ * Device Mode Registers.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DCFG 0x000
+#define DWC_DCTL 0x004
+#define DWC_DSTS 0x008
+#define DWC_DIEPMSK 0x010
+#define DWC_DOEPMSK 0x014
+#define DWC_DAINT 0x018
+#define DWC_DAINTMSK 0x01C
+#define DWC_DTKNQR1 0x020
+#define DWC_DTKNQR2 0x024
+#define DWC_DVBUSDIS 0x028
+#define DWC_DVBUSPULSE 0x02C
+#define DWC_DTKNQR3_DTHRCTL 0x030
+#define DWC_DTKNQR4FIFOEMPTYMSK 0x034
+
+/*
+ * These Macros represents the bit fields in the Device Configuration
+ * Register. Read the register into the u32 member then
+ * set/clear the bits using the bit elements. Write the
+ * u32 member to the dcfg register.
+*/
+#define DWC_DCFG_IN_EP_MISMATCH_CNT_RD(x) (((x) & (0x1f << 18)) >> 18)
+#define DWC_DCFG_P_FRM_INTRVL_RD(x) (((x) & (0x03 << 11)) >> 11)
+#define DWC_DCFG_DEV_ADDR_RD(x) (((x) & (0x3f << 04)) >> 04)
+#define DWC_DCFG_NGL_STS_OUT_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_DCFG_DEV_SPEED_RD(x) ((x) & 0x3)
+
+#define DWC_DCFG_IN_EP_MISMATCH_CNT_WR(reg, x) \
+ (((reg) & (~((u32)0x1f << 18))) | ((x) << 18))
+#define DWC_DCFG_P_FRM_INTRVL_WR(reg, x) \
+ (((reg) & (~((u32)0x03 << 11))) | ((x) << 11))
+#define DWC_DCFG_DEV_ADDR_WR(reg, x) \
+ (((reg) & (~((u32)0x3f << 04))) | ((x) << 04))
+#define DWC_DCFG_NGL_STS_OUT_WR(reg, x) \
+ (((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_DCFG_DEV_SPEED_WR(reg, x) \
+ (((reg) & (~(u32)0x3)) | (x))
+
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+
+/*
+ * These Macros represents the bit fields in the Device Control Register. Read
+ * the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DCTL_PWR_ON_PROG_DONE_RD(x) (((x) & (1 << 11)) >> 11)
+
+#define DWC_DCTL_PWR_ON_PROG_DONE_WR(reg, x) \
+ (((reg) & (~((u32)0x01 << 11))) | ((x) << 11))
+#define DWC_DCTL_CLR_GLBL_OUT_NAK_WR(reg, x) \
+ (((reg) & (~((u32)0x01 << 10))) | ((x) << 10))
+#define DWC_DCTL_SET_GLBL_OUT_NAL(reg, x) \
+ (((reg) & (~((u32)0x01 << 9))) | ((x) << 9))
+#define DWC_DCTL_CLR_CLBL_NP_IN_NAK(reg, x) \
+ (((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DCTL_SET_GLBL_NP_IN_NAK(reg, x) \
+ (((reg) & (~((u32)0x01 << 07))) | ((x) << 07))
+#define DWC_DCTL_TST_CTL(reg, x) \
+ (((reg) & (~((u32)0x07 << 04))) | ((x) << 04))
+#define DWC_DCTL_GLBL_OUT_NAK_STS(reg, x) \
+ (((reg) & (~((u32)0x01 << 03))) | ((x) << 03))
+#define DWC_DCTL_GLBL_NP_IN_NAK(reg, x) \
+ (((reg) & (~((u32)0x01 << 02))) | ((x) << 02))
+#define DWC_DCTL_SFT_DISCONNECT(reg, x) \
+ (((reg) & (~((u32)0x01 << 01))) | ((x) << 01))
+#define DEC_DCTL_REMOTE_WAKEUP_SIG(reg, x) \
+ (((reg) & (~((u32)0x01 << 00))) | ((x) << 00))
+
+/*
+ * These Macros represents the bit fields in the Dev Status Register. Read the
+ * register into the u32 member then set/clear the bits using the bit elements.
+ */
+#define DWC_DSTS_SOFFN_RD(x) (((x) & (0x3fff << 8)) >> 8)
+#define DWC_DSTS_ERRTICERR_RD(x) (((x) & (0x0001 << 3)) >> 3)
+#define DWC_DSTS_ENUM_SPEED_RD(x) (((x) & (0x0003 << 1)) >> 1)
+#define DWC_DSTS_SUSP_STS_RD(x) ((x) & 1)
+
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
+
+/*
+ * These Macros represents the bit fields in the Device IN EP Interrupt Register
+ * and the Device IN EP Common Mask Register.
+ *
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DIEPINT_TXFIFO_UNDERN_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_DIEPINT_TXFIFO_EMPTY_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_DIEPINT_IN_EP_NAK_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_DIEPINT_IN_TKN_EP_MISS_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_DIEPINT_IN_TKN_TX_EMPTY_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_DIEPINT_TOUT_COND_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_DIEPINT_AHB_ERROR_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_DIEPINT_EP_DISA_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_DIEPINT_TX_CMPL_RD(x) ((x) & 0x1)
+
+#define DWC_DIEPINT_TXFIFO_UNDERN_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DIEPINT_TXFIFO_EMPTY_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 7))) | ((x) << 7))
+#define DWC_DIEPINT_IN_EP_NAK_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DIEPINT_IN_TKN_EP_MISS_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 5))) | ((x) << 5))
+#define DWC_DIEPINT_IN_TKN_TX_EMPTY_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DIEPINT_TOUT_COND_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DIEPINT_AHB_ERROR_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DIEPINT_EP_DISA_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DIEPINT_TX_CMPL_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+#define DWC_DIEPMSK_TXFIFO_UNDERN_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DIEPMSK_TXFIFO_EMPTY_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 7))) | ((x) << 7))
+#define DWC_DIEPMSK_IN_EP_NAK_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DIEPMSK_IN_TKN_EP_MISS_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 5))) | ((x) << 5))
+#define DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DIEPMSK_TOUT_COND_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DIEPMSK_AHB_ERROR_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DIEPMSK_EP_DISA_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DIEPMSK_TX_CMPL_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Device OUT EP Itr Register
+ * and Device OUT EP Common Interrupt Mask Register.
+ *
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DOEPINT_OUTPKT_ERR_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_DOEPINT_B2B_PKTS_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_DOEPINT_OUT_TKN_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_DOEPINT_SETUP_DONE_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_DOEPINT_AHB_ERROR_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_DOEPINT_EP_DISA_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_DOEPINT_TX_COMPL_RD(x) (((x) & (0x1 << 0)) >> 0)
+
+#define DWC_DOEPMSK_OUTPKT_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DOEPMSK_B2B_PKTS_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DOEPMSK_OUT_TKN_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DOEPMSK_SETUP_DONE_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DOEPMSK_AHB_ERROR_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DOEPMSK_EP_DISA_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DOEPMSK_TX_COMPL_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Device All EP Intr and Mask
+ * Registers. Read the register into the u32 member then set/clear the bits
+ * using the bit elements.
+ */
+#define DWC_DAINT_OUT_EP_RD(reg, ep) \
+ (((reg) & (1 << (ep + 16))) >> (ep + 16))
+#define DWC_DAINTMSK_OUT_EP_RW(reg, ep) \
+ (((reg) & (~(u32)(1 << (ep + 16)))) | (1 << (ep + 16)))
+#define DWC_DAINT_IN_EP_RD(reg, ep) (((reg) & (1 << ep)) >> ep)
+#define DWC_DAINTMSK_IN_EP_RW(reg, ep) \
+ (((reg) & (~(u32)(1 << ep))) | (1 << ep))
+#define DWC_DAINT_OUTEP15 (1 << 31)
+#define DWC_DAINT_OUTEP14 (1 << 30)
+#define DWC_DAINT_OUTEP13 (1 << 29)
+#define DWC_DAINT_OUTEP12 (1 << 28)
+#define DWC_DAINT_OUTEP11 (1 << 27)
+#define DWC_DAINT_OUTEP10 (1 << 26)
+#define DWC_DAINT_OUTEP09 (1 << 25)
+#define DWC_DAINT_OUTEP08 (1 << 24)
+#define DWC_DAINT_OUTEP07 (1 << 23)
+#define DWC_DAINT_OUTEP06 (1 << 22)
+#define DWC_DAINT_OUTEP05 (1 << 21)
+#define DWC_DAINT_OUTEP04 (1 << 20)
+#define DWC_DAINT_OUTEP03 (1 << 19)
+#define DWC_DAINT_OUTEP02 (1 << 18)
+#define DWC_DAINT_OUTEP01 (1 << 17)
+#define DWC_DAINT_OUTEP00 (1 << 16)
+#define DWC_DAINT_INEP15 (1 << 15)
+#define DWC_DAINT_INEP14 (1 << 14)
+#define DWC_DAINT_INEP13 (1 << 13)
+#define DWC_DAINT_INEP12 (1 << 12)
+#define DWC_DAINT_INEP11 (1 << 11)
+#define DWC_DAINT_INEP10 (1 << 10)
+#define DWC_DAINT_INEP09 (1 << 9)
+#define DWC_DAINT_INEP08 (1 << 8)
+#define DWC_DAINT_INEP07 (1 << 07)
+#define DWC_DAINT_INEP06 (1 << 06)
+#define DWC_DAINT_INEP05 (1 << 05)
+#define DWC_DAINT_INEP04 (1 << 04)
+#define DWC_DAINT_INEP03 (1 << 03)
+#define DWC_DAINT_INEP02 (1 << 02)
+#define DWC_DAINT_INEP01 (1 << 01)
+#define DWC_DAINT_INEP00 (1 << 00)
+
+/*
+ * These Macros represents the bit fields in the Device IN Token Queue Read
+ * Registers. Read the register into the u32 member. READ-ONLY Register
+ */
+#define DWC_DTKNQR1_EP_TKN_NO_RD(x) (((x) & (0xffffff << 8)) >> 8)
+#define DWC_DTKNQR1_WRAP_BIT_RD(x) (((x) & (1 << 7)) >> 7)
+#define DWC_DTKNQR1_INT_TKN_Q_WR_PTR_RD(x) ((x) & 0x1f)
+
+/*
+ * These Macros represents Threshold control Register. Read and wr the register
+ * into the u32 member. READ-WRITABLE Register
+ */
+#define DWC_DTHCTRL_RX_ARB_PARK_EN_RD(x) (((x) & (0x001 << 27)) >> 27)
+#define DWC_DTHCTRL_RX_THR_LEN_RD(x) (((x) & (0x1ff << 17)) >> 17)
+#define DWC_DTHCTRL_RX_THR_EN_RD(x) (((x) & (0x001 << 16)) >> 16)
+#define DWC_DTHCTRL_TX_THR_LEN_RD(x) (((x) & (0x1ff << 02)) >> 02)
+#define DWC_DTHCTRL_ISO_THR_EN(x) (((x) & (0x001 << 01)) >> 01)
+#define DWC_DTHCTRL_NON_ISO_THR_ENA_RD(x) (((x) & (0x001 << 00)) >> 00)
+
+#define DWC_DTHCTRL_RX_ARB_PARK_EN_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 27))) | ((x) << 27))
+#define DWC_DTHCTRL_RX_THR_LEN_RW(reg, x) \
+ (((reg) & (~((u32)0x1ff << 17))) | ((x) << 17))
+#define DWC_DTHCTRL_RX_THR_EN_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 16))) | ((x) << 16))
+#define DWC_DTHCTRL_TX_THR_LEN_RW(reg, x) \
+ (((reg) & (~((u32)0x1ff << 02))) | ((x) << 02))
+#define DWC_DTHCTRL_ISO_THR_EN_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 01))) | ((x) << 01))
+#define DWC_DTHCTRL_NON_ISO_THR_ENA_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 00))) | ((x) << 00))
+
+/*
+ * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DIEPCTL 0x00
+#define DWC_DIEPINT 0x08
+#define DWC_DIEPTSIZ 0x10
+#define DWC_DIEPDMA 0x14
+#define DWC_DTXFSTS 0x18
+
+/*
+ * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DOEPCTL 0x00
+#define DWC_DOEPFN 0x04
+#define DWC_DOEPINT 0x08
+#define DWC_DOEPTSIZ 0x10
+#define DWC_DOEPDMA 0x14
+
+/*
+ * These Macros represents the bit fields in the Device EP Ctrl Register. Read
+ * the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DEP0CTL_MPS_64 0
+#define DWC_DEP0CTL_MPS_32 1
+#define DWC_DEP0CTL_MPS_16 2
+#define DWC_DEP0CTL_MPS_8 3
+
+#define DWC_DEPCTL_EPENA_RD(x) (((x) & (0x1 << 31)) >> 31)
+#define DWC_DEPCTL_EPDIS_RD(x) (((x) & (0x1 << 30)) >> 30)
+#define DWC_DEPCTL_SET_DATA1_PID_RD(x) (((x) & (0x1 << 29)) >> 29)
+#define DWC_DEPCTL_SET_DATA0_PID_RD(x) (((x) & (0x1 << 28)) >> 28)
+#define DWC_DEPCTL_SET_NAK_RD(x) (((x) & (0x1 << 27)) >> 27)
+#define DWC_DEPCTL_CLR_NAK_RD(x) (((x) & (0x1 << 26)) >> 26)
+#define DWC_DEPCTL_TX_FIFO_NUM_RD(x) (((x) & (0xf << 22)) >> 22)
+#define DWC_DEPCTL_STALL_HNDSHK _RD(x) (((x) & (0x1 << 21)) >> 21)
+#define DWC_DEPCTL_SNP_MODE_RD(x) (((x) & (0x1 << 20)) >> 20)
+#define DWC_DEPCTL_EP_TYPE_RD(x) (((x) & (0x3 << 18)) >> 18)
+#define DWC_DEPCTL_NKASTS_RD(x) (((x) & (0x1 << 17)) >> 17)
+#define DWC_DEPCTL_DPID _RD(x) (((x) & (0x1 << 16)) >> 16)
+#define DWC_DEPCTL_ACT_EP_RD(x) (((x) & (0x1 << 15)) >> 15)
+#define DWC_DEPCTL_NXT_EP_RD(x) (((x) & (0xf << 11)) >> 11)
+#define DWC_DEPCTL_MPS_RD(x) (((x) & (0x7ff << 00)) >> 00)
+
+#define DWC_DEPCTL_EPENA_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 31))) | ((x) << 31))
+#define DWC_DEPCTL_EPDIS_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 30))) | ((x) << 30))
+#define DWC_DEPCTL_SET_DATA1_PID_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 29))) | ((x) << 29))
+#define DWC_DEPCTL_SET_DATA0_PID_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 28))) | ((x) << 28))
+#define DWC_DEPCTL_SET_NAK_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 27))) | ((x) << 27))
+#define DWC_DEPCTL_CLR_NAK_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 26))) | ((x) << 26))
+#define DWC_DEPCTL_TX_FIFO_NUM_RW(reg, x) \
+ (((reg) & (~((u32)0x00f << 22))) | ((x) << 22))
+#define DWC_DEPCTL_STALL_HNDSHK_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 21))) | ((x) << 21))
+#define DWC_DEPCTL_SNP_MODE_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 20))) | ((x) << 20))
+#define DWC_DEPCTL_EP_TYPE_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 18))) | ((x) << 18))
+#define DWC_DEPCTL_NKASTS_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 17))) | ((x) << 17))
+#define DWC_DEPCTL_DPID_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 16))) | ((x) << 16))
+#define DWC_DEPCTL_ACT_EP_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 15))) | ((x) << 15))
+#define DWC_DEPCTL_NXT_EP_RW(reg, x) \
+ (((reg) & (~((u32)0x00f << 11))) | ((x) << 11))
+#define DWC_DEPCTL_MPS_RW(reg, x) \
+ (((reg) & (~((u32)0x7ff << 00))) | ((x) << 00))
+
+/*
+ * These Macros represents the bit fields in the Device EP Txfer Size Register.
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#if defined(DWC_LIMITED_XFER_SIZE)
+#define DWC_DEPTSIZ_MCOUNT_RD(x) (((x) & (0x003 << 29)) >> 29)
+#define DWC_DEPTSIZ_PKT_CNT_RD(x) (((x) & (0x01f << 19)) >> 19)
+#define DWC_DEPTSIZ_XFER_SIZ_RD(x) (((x) & (0x7ff << 00)) >> 00)
+#define DWC_DEPTSIZ_MCOUNT_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define DWC_DEPTSIZ_PKT_CNT_RW(reg, x) \
+ (((reg) & (~((u32)0x01f << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ_XFER_SIZ_RW(reg, x) \
+ (((reg) & (~((u32)0x7ff << 00))) | ((x) << 00))
+#else
+#define DWC_DEPTSIZ_MCOUNT_RD(x) \
+ (((x) & (0x003 << 29)) >> 29)
+#define DWC_DEPTSIZ_PKT_CNT_RD(x) \
+ (((x) & (0x3ff << 19)) >> 19)
+#define DWC_DEPTSIZ_XFER_SIZ_RD(x) \
+ (((x) & (0x7ffff << 00)) >> 00)
+#define DWC_DEPTSIZ_MCOUNT_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define DWC_DEPTSIZ_PKT_CNT_RW(reg, x) \
+ (((reg) & (~((u32)0x7ff << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ_XFER_SIZ_RW(reg, x) \
+ (((reg) & (~((u32)0x7ffff << 00))) | ((x) << 00))
+#endif
+
+/*
+ * These Macros represents the bit fields in the Device EP 0 Transfer Size
+ * Register. Read the register into the u32 member then set/clear the bits
+ * using the bit elements.
+ */
+#define DWC_DEPTSIZ0_SUPCNT_RD(x) (((x) & (0x003 << 29)) >> 29)
+#define DWC_DEPTSIZ0_PKT_CNT_RD(x) (((x) & (0x001 << 19)) >> 19)
+#define DWC_DEPTSIZ0_XFER_SIZ_RD(x) (((x) & (0x07f << 00)) >> 00)
+#define DWC_DEPTSIZ0_SUPCNT_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define DWC_DEPTSIZ0_PKT_CNT_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ0_XFER_SIZ_RW(reg, x) \
+ (((reg) & (~((u32)0x07f << 00))) | ((x) << 00))
+
+#define MAX_PERIO_FIFOS 15 /* Max periodic FIFOs */
+#define MAX_TX_FIFOS 15 /* Max non-periodic FIFOs */
+
+/* Maximum number of Endpoints/HostChannels */
+#define MAX_EPS_CHANNELS 12 /* This come from device tree or defconfig */
+
+/*
+ * The device_if structure contains information needed to manage the DWC_otg
+ * controller acting in device mode. It represents the programming view of the
+ * device-specific aspects of the controller.
+ */
+struct device_if {
+ /* Device Global Registers starting at offset 800h */
+ ulong dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+ /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */
+ ulong in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+ /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+ ulong out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+ /* Device configuration information */
+ /* Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */
+ u8 speed;
+ /* Number # of Tx EP range: 0-15 exept ep0 */
+ u8 num_in_eps;
+ /* Number # of Rx EP range: 0-15 exept ep 0 */
+ u8 num_out_eps;
+
+ /* Size of periodic FIFOs (Bytes) */
+ u16 perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+ /* Size of Tx FIFOs (Bytes) */
+ u16 tx_fifo_size[MAX_TX_FIFOS];
+
+ /* Thresholding enable flags and length varaiables */
+ u16 rx_thr_en;
+ u16 iso_tx_thr_en;
+ u16 non_iso_tx_thr_en;
+ u16 rx_thr_length;
+ u16 tx_thr_length;
+};
+
+/*
+ * These Macros represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the u32 member then set/clear the
+ * bits using the bit elements.
+ */
+#define DWC_PCGCCTL_PHY_SUS_RD(x) (((x) & (0x001 << 4)) >> 4)
+#define DWC_PCGCCTL_RSTP_DWN_RD(x) (((x) & (0x001 << 3)) >> 3)
+#define DWC_PCGCCTL_PWR_CLAMP_RD(x) (((x) & (0x001 << 2)) >> 2)
+#define DWC_PCGCCTL_GATE_HCLK_RD(x) (((x) & (0x001 << 1)) >> 1)
+#define DWC_PCGCCTL_STOP_CLK_RD(x) (((x) & (0x001 << 0)) >> 0)
+
+#define DWC_PCGCCTL_RSTP_DWN_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 3))) | ((x) << 3))
+#define DWC_PCGCCTL_PWR_CLAMP_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 2))) | ((x) << 2))
+#define DWC_PCGCCTL_GATE_HCLK_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 1))) | ((x) << 1))
+#define DWC_PCGCCTL_STOP_CLK_SET(reg) \
+ (((reg) | 1))
+#define DWC_PCGCCTL_STOP_CLK_CLR(reg) \
+ (((reg) & (~((u32)0x001 << 0))))
+
+/*
+ * Host Mode Register Structures
+ */
+
+/*
+ * The Host Global Registers structure defines the size and relative field
+ * offsets for the Host Mode Global Registers. Host Global Registers offsets
+ * 400h-7FFh.
+*/
+#define DWC_HCFG 0x00
+#define DWC_HFIR 0x04
+#define DWC_HFNUM 0x08
+#define DWC_HPTXSTS 0x10
+#define DWC_HAINT 0x14
+#define DWC_HAINTMSK 0x18
+
+/*
+ * These Macros represents the bit fields in the Host Configuration Register.
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements. Write the u32 member to the hcfg register.
+ */
+#define DWC_HCFG_FSLSUPP_RD(x) (((x) & (0x001 << 2)) >> 2)
+#define DWC_HCFG_FSLSP_CLK_RD(x) (((x) & (0x003 << 0)) >> 0)
+#define DWC_HCFG_FSLSUPP_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 2))) | ((x) << 2))
+#define DWC_HCFG_FSLSP_CLK_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 0))) | ((x) << 0))
+
+#define DWC_HCFG_30_60_MHZ 0
+#define DWC_HCFG_48_MHZ 1
+#define DWC_HCFG_6_MHZ 2
+
+/*
+ * These Macros represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+#define DWC_HFIR_FRINT_RD(x) (((x) & (0xffff << 0)) >> 0)
+#define DWC_HFIR_FRINT_RW(reg, x) \
+ (((reg) & (~((u32)0xffff << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+#define DWC_HFNUM_FRREM_RD(x) (((x) & (0xffff << 16)) >> 16)
+#define DWC_HFNUM_FRNUM_RD(x) (((x) & (0xffff << 0)) >> 0)
+#define DWC_HFNUM_FRREM_RW(reg, x) \
+ (((reg) & (~((u32)0xffff << 16))) | ((x) << 16))
+#define DWC_HFNUM_FRNUM_RW(reg, x) \
+ (((reg) & (~((u32)0xffff << 0))) | ((x) << 0))
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+
+#define DWC_HPTXSTS_PTXQTOP_ODD_RD(x) (((x) & (0x01 << 31)) >> 31)
+#define DWC_HPTXSTS_PTXQTOP_CHNUM_RD(x) (((x) & (0x0f << 27)) >> 27)
+#define DWC_HPTXSTS_PTXQTOP_TKN_RD(x) (((x) & (0x03 << 25)) >> 25)
+#define DWC_HPTXSTS_PTXQTOP_TERM_RD(x) (((x) & (0x01 << 24)) >> 24)
+#define DWC_HPTXSTS_PTXSPC_AVAIL_RD(x) (((x) & (0xff << 16)) >> 16)
+#define DWC_HPTXSTS_PTXFSPC_AVAIL_RD(x) (((x) & (0xffff << 00)) >> 00)
+
+/*
+ * These Macros represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hprt0 register.
+ */
+#define DWC_HPRT0_PRT_SPD_RD(x) (((x) & (0x3 << 17)) >> 17)
+#define DWC_HPRT0_PRT_TST_CTL_RD(x) (((x) & (0xf << 13)) >> 13)
+#define DWC_HPRT0_PRT_PWR_RD(x) (((x) & (0x1 << 12)) >> 12)
+#define DWC_HPRT0_PRT_LSTS_RD(x) (((x) & (0x3 << 10)) >> 10)
+#define DWC_HPRT0_PRT_RST_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HPRT0_PRT_SUS_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_HPRT0_PRT_RES_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_HPRT0_PRT_OVRCURR_CHG_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HPRT0_PRT_OVRCURR_ACT_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_HPRT0_PRT_ENA_DIS_CHG_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_HPRT0_PRT_ENA_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_HPRT0_PRT_CONN_DET_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_HPRT0_PRT_STS_RD(x) (((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HPRT0_PRT_SPD_RW(reg, x) \
+ (((reg) & (~((u32)0x3 << 17))) | ((x) << 17))
+#define DWC_HPRT0_PRT_TST_CTL_RW(reg, x) \
+ (((reg) & (~((u32)0xf << 13))) | ((x) << 13))
+#define DWC_HPRT0_PRT_PWR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 12))) | ((x) << 12))
+#define DWC_HPRT0_PRT_LSTS_RW(reg, x) \
+ (((reg) & (~((u32)0x3 << 10))) | ((x) << 10))
+#define DWC_HPRT0_PRT_RST_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HPRT0_PRT_SUS_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HPRT0_PRT_RES_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HPRT0_PRT_OVRCURR_CHG_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HPRT0_PRT_OVRCURR_ACT_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HPRT0_PRT_ENA_DIS_CHG_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HPRT0_PRT_ENA_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HPRT0_PRT_CONN_DET_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HPRT0_PRT_STS_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED 2
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt Register.
+ */
+#define DWC_HAINT_CH15_RD(x) (((x) & (0x1 << 15)) >> 15)
+#define DWC_HAINT_CH14_RD(x) (((x) & (0x1 << 14)) >> 14)
+#define DWC_HAINT_CH13_RD(x) (((x) & (0x1 << 13)) >> 13)
+#define DWC_HAINT_CH12_RD(x) (((x) & (0x1 << 12)) >> 12)
+#define DWC_HAINT_CH11_RD(x) (((x) & (0x1 << 11)) >> 11)
+#define DWC_HAINT_CH10_RD(x) (((x) & (0x1 << 10)) >> 10)
+#define DWC_HAINT_CH09_RD(x) (((x) & (0x1 << 9)) >> 9)
+#define DWC_HAINT_CH08_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HAINT_CH07_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_HAINT_CH06_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_HAINT_CH05_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HAINT_CH04_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_HAINT_CH03_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_HAINT_CH02_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_HAINT_CH01_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_HAINT_CH00_RD(x) (((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HAINT_RD(x) (((x) & (0xffff << 0)) >> 0)
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt Register.
+ */
+#define DWC_HAINTMSK_CH15_RD(x) (((x) & (0x1 << 15)) >> 15)
+#define DWC_HAINTMSK_CH14_RD(x) (((x) & (0x1 << 14)) >> 14)
+#define DWC_HAINTMSK_CH13_RD(x) (((x) & (0x1 << 13)) >> 13)
+#define DWC_HAINTMSK_CH12_RD(x) (((x) & (0x1 << 12)) >> 12)
+#define DWC_HAINTMSK_CH11_RD(x) (((x) & (0x1 << 11)) >> 11)
+#define DWC_HAINTMSK_CH10_RD(x) (((x) & (0x1 << 10)) >> 10)
+#define DWC_HAINTMSK_CH09_RD(x) (((x) & (0x1 << 9)) >> 9)
+#define DWC_HAINTMSK_CH08_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HAINTMSK_CH07_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_HAINTMSK_CH06_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_HAINTMSK_CH05_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HAINTMSK_CH04_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_HAINTMSK_CH03_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_HAINTMSK_CH02_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_HAINTMSK_CH01_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_HAINTMSK_CH00_RD(x) (((x) & (0x1 << 0)) >> 0)
+#define DWC_HAINTMSK_RD(x) ((x) & 0xffff)
+
+#define DWC_HAINTMSK_CH15_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 15))) | ((x) << 15))
+#define DWC_HAINTMSK_CH14_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 14))) | ((x) << 14))
+#define DWC_HAINTMSK_CH13_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 13))) | ((x) << 13))
+#define DWC_HAINTMSK_CH12_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 12))) | ((x) << 12))
+#define DWC_HAINTMSK_CH11_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 11))) | ((x) << 11))
+#define DWC_HAINTMSK_CH10_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HAINTMSK_CH09_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HAINTMSK_CH08_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HAINTMSK_CH07_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HAINTMSK_CH06_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HAINTMSK_CH05_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HAINTMSK_CH04_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HAINTMSK_CH03_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HAINTMSK_CH02_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HAINTMSK_CH01_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HAINTMSK_CH00_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+#define DWC_HAINTMSK_RW(reg, x) \
+ (((reg) & (~((u32)0xffff))) | x)
+
+/*
+ * Host Channel Specific Registers. 500h-5FCh
+ */
+#define DWC_HCCHAR 0x00
+#define DWC_HCSPLT 0x04
+#define DWC_HCINT 0x08
+#define DWC_HCINTMSK 0x0C
+#define DWC_HCTSIZ 0x10
+#define DWC_HCDMA 0x14
+
+/*
+ * These Macros represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hcchar register.
+ */
+#define DWC_HCCHAR_ENA_RD(x) (((x) & (0x001 << 31)) >> 31)
+#define DWC_HCCHAR_DIS_RD(x) (((x) & (0x001 << 30)) >> 30)
+#define DWC_HCCHAR_ODD_FRAME_RD(x) (((x) & (0x001 << 29)) >> 29)
+#define DWC_HCCHAR_DEV_ADDR_RD(x) (((x) & (0x07f << 22)) >> 22)
+#define DWC_HCCHAR_MULTI_CNT_RD(x) (((x) & (0x003 << 20)) >> 20)
+#define DWC_HCCHAR_EPTYPE_RD(x) (((x) & (0x003 << 18)) >> 18)
+#define DWC_HCCHAR_LSP_DEV_RD(x) (((x) & (0x001 << 17)) >> 17)
+#define DWC_HCCHAR_EPDIR_RD(x) (((x) & (0x001 << 15)) >> 15)
+#define DWC_HCCHAR_EP_NUM_RD(x) (((x) & (0x00f << 11)) >> 11)
+#define DWC_HCCHAR_MPS_RD(x) (((x) & (0x7ff << 0)) >> 0)
+
+#define DWC_HCCHAR_ENA_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 31))) | ((x) << 31))
+#define DWC_HCCHAR_DIS_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 30))) | ((x) << 30))
+#define DWC_HCCHAR_ODD_FRAME_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 29))) | ((x) << 29))
+#define DWC_HCCHAR_DEV_ADDR_RW(reg, x) \
+ (((reg) & (~((u32)0x07f << 22))) | ((x) << 22))
+#define DWC_HCCHAR_MULTI_CNT_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 20))) | ((x) << 20))
+#define DWC_HCCHAR_EPTYPE_RW(reg, x) \
+ (((reg) & (~((u32)0x003 << 18))) | ((x) << 18))
+#define DWC_HCCHAR_LSP_DEV_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 17))) | ((x) << 17))
+#define DWC_HCCHAR_EPDIR_RW(reg, x) \
+ (((reg) & (~((u32)0x001 << 15))) | ((x) << 15))
+#define DWC_HCCHAR_EP_NUM_RW(reg, x) \
+ (((reg) & (~((u32)0x00f << 11))) | ((x) << 11))
+#define DWC_HCCHAR_MPS_RW(reg, x) \
+ (((reg) & (~((u32)0x7ff << 0))) | ((x) << 0))
+
+#define DWC_HCSPLT_ENA_RD(x) (((x) & (0x01 << 31)) >> 31)
+#define DWC_HCSPLT_COMP_SPLT_RD(x) (((x) & (0x01 << 16)) >> 16)
+#define DWC_HCSPLT_TRANS_POS_RD(x) (((x) & (0x03 << 14)) >> 14)
+#define DWC_HCSPLT_HUB_ADDR_RD(x) (((x) & (0x7f << 7)) >> 7)
+#define DWC_HCSPLT_PRT_ADDR_RD(x) (((x) & (0x7f << 0)) >> 0)
+
+#define DWC_HCSPLT_ENA_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 31))) | ((x) << 31))
+#define DWC_HCSPLT_COMP_SPLT_RW(reg, x) \
+ (((reg) & (~((u32)0x01 << 16))) | ((x) << 16))
+#define DWC_HCSPLT_TRANS_POS_RW(reg, x) \
+ (((reg) & (~((u32)0x03 << 14))) | ((x) << 14))
+#define DWC_HCSPLT_HUB_ADDR_RW(reg, x) \
+ (((reg) & (~((u32)0x7f << 7))) | ((x) << 7))
+#define DWC_HCSPLT_PRT_ADDR_RW(reg, x) \
+ (((reg) & (~((u32)0x7f << 0))) | ((x) << 0))
+
+#define DWC_HCSPLIT_XACTPOS_MID 0
+#define DWC_HCSPLIT_XACTPOS_END 1
+#define DWC_HCSPLIT_XACTPOS_BEGIN 2
+#define DWC_HCSPLIT_XACTPOS_ALL 3
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+#define DWC_HCINT_DATA_TOG_ERR_RD(x) (((x) & (0x1 << 10)) >> 10)
+#define DWC_HCINT_FRAME_OVERN_ERR_RD(x) (((x) & (0x1 << 9)) >> 9)
+#define DWC_HCINT_BBL_ERR_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HCINT_TRANS_ERR_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_HCINT_NYET_RESP_REC_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_HCINT_ACK_RESP_REC_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HCINT_NAK_RESP_REC_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_HCINT_STALL_RESP_REC_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_HCINT_AHB_ERR_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_HCINT_CHAN_HALTED_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_HCINT_TXFER_CMPL_RD(x) (((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HCINT_DATA_TOG_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HCINT_FRAME_OVERN_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HCINT_BBL_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HCINT_TRANS_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HCINT_NYET_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HCINT_ACK_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HCINT_NAK_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HCINT_STALL_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HCINT_AHB_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HCINT_CHAN_HALTED_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HCINT_TXFER_CMPL_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the u32 member then set/clear the bits
+ * using the bit elements. Write the u32 member to the hcchar register.
+ */
+#define DWC_HCTSIZ_DO_PING_PROTO_RD(x) (((x) & (0x00001 << 31)) >> 31)
+#define DWC_HCTSIZ_PKT_PID_RD(x) (((x) & (0x00003 << 29)) >> 29)
+#define DWC_HCTSIZ_PKT_CNT_RD(x) (((x) & (0x003ff << 19)) >> 19)
+#define DWC_HCTSIZ_XFER_SIZE_RD(x) (((x) & (0x7ffff << 00)) >> 00)
+
+#define DWC_HCTSIZ_DO_PING_PROTO_RW(reg, x) \
+ (((reg) & (~((u32)0x00001 << 31))) | ((x) << 31))
+#define DWC_HCTSIZ_PKT_PID_RW(reg, x) \
+ (((reg) & (~((u32)0x00003 << 29))) | ((x) << 29))
+#define DWC_HCTSIZ_PKT_CNT_RW(reg, x) \
+ (((reg) & (~((u32)0x003ff << 19))) | ((x) << 19))
+#define DWC_HCTSIZ_XFER_SIZE_RW(reg, x) \
+ (((reg) & (~((u32)0x7ffff << 00))) | ((x) << 00))
+
+#define DWC_HCTSIZ_DATA0 0
+#define DWC_HCTSIZ_DATA1 2
+#define DWC_HCTSIZ_DATA2 1
+#define DWC_HCTSIZ_MDATA 3
+#define DWC_HCTSIZ_SETUP 3
+
+/*
+ * These Macros represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hcintmsk register.
+ */
+#define DWC_HCINTMSK_DATA_TOG_ERR_RD(x) (((x) & (0x1 << 10)) >> 10)
+#define DWC_HCINTMSK_FRAME_OVERN_ERR_RD(x) (((x) & (0x1 << 9)) >> 9)
+#define DWC_HCINTMSK_BBL_ERR_RD(x) (((x) & (0x1 << 8)) >> 8)
+#define DWC_HCINTMSK_TRANS_ERR_RD(x) (((x) & (0x1 << 7)) >> 7)
+#define DWC_HCINTMSK_NYET_RESP_REC_RD(x) (((x) & (0x1 << 6)) >> 6)
+#define DWC_HCINTMSK_ACK_RESP_REC_RD(x) (((x) & (0x1 << 5)) >> 5)
+#define DWC_HCINTMSK_NAK_RESP_REC_RD(x) (((x) & (0x1 << 4)) >> 4)
+#define DWC_HCINTMSK_STALL_RESP_REC_RD(x) (((x) & (0x1 << 3)) >> 3)
+#define DWC_HCINTMSK_AHB_ERR_RD(x) (((x) & (0x1 << 2)) >> 2)
+#define DWC_HCINTMSK_CHAN_HALTED_RD(x) (((x) & (0x1 << 1)) >> 1)
+#define DWC_HCINTMSK_TXFER_CMPL_RD(x) (((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HCINTMSK_DATA_TOG_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HCINTMSK_FRAME_OVERN_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HCINTMSK_BBL_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HCINTMSK_TRANS_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HCINTMSK_NYET_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HCINTMSK_ACK_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HCINTMSK_NAK_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HCINTMSK_STALL_RESP_REC_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HCINTMSK_AHB_ERR_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HCINTMSK_CHAN_HALTED_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HCINTMSK_TXFER_CMPL_RW(reg, x) \
+ (((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+/*
+ * OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information needed to
+ * manage the DWC_otg controller acting in host mode. It represents the
+ * programming view of the host-specific aspects of the controller.
+ */
+struct dwc_host_if { /* CONFIG_DWC_OTG_REG_LE */
+ /* Host Global Registers starting at offset 400h. */
+ ulong host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
+
+ /* Host Port 0 Control and Status Register */
+ ulong hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
+
+ /* Host Channel Specific Registers at offsets 500h-5FCh. */
+ ulong hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
+#define DWC_OTG_CHAN_REGS_OFFSET 0x20
+
+ /* Host configuration information */
+ /* Number of Host Channels (range: 1-16) */
+ u8 num_host_channels;
+ /* Periodic EPs supported (0: no, 1: yes) */
+ u8 perio_eps_supported;
+ /* Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+ u16 perio_tx_fifo_size;
+};
+#endif
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index 1fc8f12..347bb05 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -450,7 +450,7 @@ static int dbgp_ehci_startup(void)
writel(FLAG_CF, &ehci_regs->configured_flag);
/* Wait until the controller is no longer halted */
- loop = 10;
+ loop = 1000;
do {
status = readl(&ehci_regs->status);
if (!(status & STS_HALT))
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index aca160e..9f30cf5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -112,6 +112,7 @@ config USB_GADGET_SELECTED
choice
prompt "USB Peripheral Controller"
depends on USB_GADGET
+ default USB_GADGET_S3C_OTGD if (ARCH_S5PV210)
help
A USB device uses a controller to talk to its host.
Systems should have only one such upstream link.
@@ -373,6 +374,20 @@ config USB_S3C_HSUDC
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_S3C_OTGD
+ boolean "S3C HS USB OTG Device"
+ depends on (ARCH_S5PV210)
+ help
+ Samsung's S3C64XX processors include high speed USB OTG2.0
+ controller. It has 15 configurable endpoints, as well as
+ endpoint zero (for control transfers).
+
+ This driver has been tested on the S3C6410, S5P6440, S5PC100 processor.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "s3c-udc-otg" and force all
+ gadget drivers to also be dynamically linked.
+
config USB_GADGET_PXA_U2O
boolean "PXA9xx Processor USB2.0 controller"
select USB_GADGET_DUALSPEED
@@ -633,6 +648,43 @@ config USB_DUMMY_HCD
endchoice
+comment "NOTE: S3C OTG device role enables the controller driver below"
+ depends on USB_GADGET_S3C_OTGD
+
+config USB_S3C_OTGD
+ tristate "S3C high speed(2.0, dual-speed) USB OTG device"
+ depends on USB_GADGET && USB_GADGET_S3C_OTGD
+ default y
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+ select USB_GADGET_DUALSPEED
+ help
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "s3c-udc-otg-hs" and force all
+ gadget drivers to also be dynamically linked.
+
+choice
+ prompt "S3C OTGD transfer mode"
+ depends on USB_S3C_OTGD
+ default y
+ help
+ S3C USB OTG conteroller supports DMA mode and Slave mode
+ for the dat transfer. You must slect one for the core
+ operation mode.
+
+config USB_GADGET_S3C_OTGD_DMA_MODE
+ bool "enabled DMA MODE"
+ depends on USB_GADGET_S3C_OTGD
+ help
+ S3C USB OTG core operates in DMA mode.
+
+config USB_GADGET_S3C_OTGD_SLAVE_MODE
+ bool "enabled Slave MODE"
+ depends on USB_GADGET_S3C_OTGD
+ help
+ S3C USB OTG core operates in Slave mode.
+endchoice
+
# Selected by UDC drivers that support high-speed operation.
config USB_GADGET_DUALSPEED
bool
@@ -944,6 +996,12 @@ config USB_G_ANDROID
The functions can be configured via a board file and may be
enabled and disabled dynamically.
+config USB_ANDROID_RNDIS_DWORD_ALIGNED
+ boolean "Use double word aligned"
+ depends on USB_G_ANDROID
+ help
+ Provides dword aligned for DMA controller.
+
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index ab17a4c..0cb5b62 100644..100755
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_IMX) += imx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+obj-$(CONFIG_USB_S3C_OTGD) += s3c_udc_otg.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 9b7360f..431cc35 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -290,6 +290,60 @@ struct usb_ep *usb_ep_autoconfig (
if (ep && ep_matches (gadget, ep, desc))
return ep;
#endif
+ } else if (gadget_is_s3c(gadget)) {
+ if (USB_ENDPOINT_XFER_INT == type) {
+ /* single buffering is enough */
+ ep = find_ep (gadget, "ep3-int");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep6-int");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep9-int");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep12-int");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ } else if (USB_ENDPOINT_XFER_BULK == type
+ && (USB_DIR_IN & desc->bEndpointAddress)) {
+ ep = find_ep (gadget, "ep2-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep5-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep8-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep11-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep14-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ } else if (USB_ENDPOINT_XFER_BULK == type
+ && !(USB_DIR_IN & desc->bEndpointAddress)) {
+ ep = find_ep (gadget, "ep1-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep4-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep7-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep10-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ ep = find_ep (gadget, "ep13-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
+ } else if (USB_ENDPOINT_XFER_ISOC == type) {
+ ep = find_ep(gadget, "ep15-iso");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+ }
}
/* Second, look at endpoints until an unclaimed one looks usable */
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index bc2f624..7405647 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -908,11 +908,13 @@ static int do_write(struct fsg_common *common)
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
+#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
spin_lock(&curlun->filp->f_lock);
curlun->filp->f_flags |= O_SYNC;
spin_unlock(&curlun->filp->f_lock);
}
+#endif
}
if (lba >= curlun->num_sectors) {
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 5308381..44d789d 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -717,6 +717,8 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
lastreq->tail->next_td_ptr =
cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
+ /* Ensure dTD's next dtd pointer to be updated */
+ wmb();
/* Read prime bit, if 1 goto done */
if (fsl_readl(&dr_regs->endpointprime) & bitmask)
goto out;
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index bcdac7c..839c9a3 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -70,6 +70,12 @@
#define gadget_is_s3c2410(g) 0
#endif
+#if CONFIG_USB_GADGET_S3C_OTGD
+#define gadget_is_s3c(g) !strcmp("s3c-udc", (g)->name)
+#else
+#define gadget_is_s3c(g) 0
+#endif
+
#ifdef CONFIG_USB_GADGET_AT91
#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name)
#else
@@ -223,6 +229,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x29;
else if (gadget_is_s3c_hsudc(gadget))
return 0x30;
+ else if (gadget_is_s3c(gadget))
+ return 0x31;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/s3c_udc.h b/drivers/usb/gadget/s3c_udc.h
new file mode 100644
index 0000000..a776167
--- /dev/null
+++ b/drivers/usb/gadget/s3c_udc.h
@@ -0,0 +1,164 @@
+/*
+ * drivers/usb/gadget/s3c_udc.h
+ * Samsung S3C on-chip full/high speed USB device controllers
+ * Copyright (C) 2005 for Samsung Electronics
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __S3C_USB_GADGET
+#define __S3C_USB_GADGET
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+/* #include <asm/hardware.h> */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Max packet size */
+#if defined(CONFIG_USB_GADGET_S3C_FS)
+#define EP0_FIFO_SIZE 8
+#define EP_FIFO_SIZE 64
+#define S3C_MAX_ENDPOINTS 5
+#elif defined(CONFIG_USB_GADGET_S3C_HS) || defined(CONFIG_ARCH_S5PV210)
+#define EP0_FIFO_SIZE 64
+#define EP_FIFO_SIZE 512
+#define EP_FIFO_SIZE2 1024
+#define S3C_MAX_ENDPOINTS 16
+#define DED_TX_FIFO 1 /* Dedicated NPTx fifo for s5p6440 */
+#else
+#define EP0_FIFO_SIZE 64
+#define EP_FIFO_SIZE 512
+#define EP_FIFO_SIZE2 1024
+#define S3C_MAX_ENDPOINTS 16
+#endif
+
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+#define RegReadErr 5
+#define FAIL_TO_SETUP 6
+
+#define TEST_J_SEL 0x1
+#define TEST_K_SEL 0x2
+#define TEST_SE0_NAK_SEL 0x3
+#define TEST_PACKET_SEL 0x4
+#define TEST_FORCE_ENABLE_SEL 0x5
+
+
+typedef enum ep_type {
+ ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt, ep_isochronous
+} ep_type_t;
+
+struct s3c_ep {
+ struct usb_ep ep;
+ struct s3c_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+
+ u8 stopped;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+
+ ep_type_t ep_type;
+ u32 fifo;
+#ifdef CONFIG_USB_GADGET_S3C_FS
+ u32 csr1;
+ u32 csr2;
+#endif
+};
+
+struct s3c_request {
+ struct usb_request req;
+ struct list_head queue;
+ unsigned char mapped;
+};
+
+struct s3c_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ /* struct device *dev; */
+ struct platform_device *dev;
+ spinlock_t lock;
+ u16 status;
+ int ep0state;
+ struct s3c_ep ep[S3C_MAX_ENDPOINTS];
+
+ unsigned char usb_address;
+
+ unsigned req_pending:1, req_std:1, req_config:1;
+
+ struct regulator *udc_vcc_d, *udc_vcc_a;
+ int udc_enabled;
+ int soft_disconnected;
+};
+
+extern struct s3c_udc *the_controller;
+extern void otg_phy_init(void);
+extern void otg_phy_off(void);
+extern struct usb_ctrlrequest usb_ctrl;
+extern struct i2c_driver fsa9480_i2c_driver;
+
+#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN)
+#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+
+#if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG
+#define USB_OTG_DRIVER_S3CHS 1
+#define USB_OTG_DRIVER_S3CFSLS 2
+#define USB_OTG_DRIVER_S3C USB_OTG_DRIVER_S3CHS | USB_OTG_DRIVER_S3CFSLS
+#define USB_OTG_DRIVER_DWC 4
+#endif
+extern atomic_t g_OtgHostMode; // actual mode: client (0) or host (1)
+extern atomic_t g_OtgOperationMode; // operation mode: 'c'lient, 'h'ost, 'o'tg or 'a'uto-host
+extern atomic_t g_OtgLastCableState; // last cable state: detached (0), client attached (1), otg attached (2)
+extern atomic_t g_OtgDriver; // driver to use: 0: S3C High-speed, 1: S3C Low-speed/Full-speed, 2: DWC
+#ifdef CONFIG_USB_S3C_OTG_HOST
+extern struct platform_driver s5pc110_otg_driver;
+#endif
+#ifdef CONFIG_USB_DWC_OTG
+extern struct platform_driver dwc_otg_driver;
+#endif
+extern int s3c_is_otgmode(void);
+extern int s3c_get_drivermode(void);
+#endif
diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c
new file mode 100644
index 0000000..7bbcedc
--- /dev/null
+++ b/drivers/usb/gadget/s3c_udc_otg.c
@@ -0,0 +1,1547 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2008 for Samsung Electronics
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "s3c_udc.h"
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/map.h>
+#include <plat/regs-otg.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#if defined(CONFIG_USB_GADGET_S3C_OTGD_DMA_MODE) /* DMA mode */
+#define OTG_DMA_MODE 1
+
+#elif defined(CONFIG_USB_GADGET_S3C_OTGD_SLAVE_MODE) /* Slave mode */
+#define OTG_DMA_MODE 0
+#error " Slave Mode is not implemented to do later"
+#else
+#error " Unknown S3C OTG operation mode, Select a correct operation mode"
+#endif
+
+#define DEBUG_S3C_UDC_SETUP
+#undef DEBUG_S3C_UDC_EP0
+#define DEBUG_S3C_UDC_ISR
+#undef DEBUG_S3C_UDC_OUT_EP
+#undef DEBUG_S3C_UDC_IN_EP
+#undef DEBUG_S3C_UDC
+
+#define EP0_CON 0
+#define EP1_OUT 1
+#define EP2_IN 2
+#define EP3_IN 3
+#define EP_MASK 0xF
+
+#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR)\
+ || defined(DEBUG_S3C_UDC_OUT_EP)
+
+static char *state_names[] = {
+ "WAIT_FOR_SETUP",
+ "DATA_STATE_XMIT",
+ "DATA_STATE_NEED_ZLP",
+ "WAIT_FOR_OUT_STATUS",
+ "DATA_STATE_RECV",
+ };
+#endif
+
+#ifdef DEBUG_S3C_UDC_SETUP
+#define DEBUG_SETUP(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define DEBUG_SETUP(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_EP0
+#define DEBUG_EP0(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_EP0(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC
+#define DEBUG(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_ISR
+#define DEBUG_ISR(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define DEBUG_ISR(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_OUT_EP
+#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_OUT_EP(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_IN_EP
+#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_IN_EP(fmt, args...) do {} while (0)
+#endif
+
+
+#define DRIVER_DESC "S3C HS USB Device Controller Driver, (c) 2008-2009 Samsung Electronics"
+#define DRIVER_VERSION "15 March 2009"
+
+struct s3c_udc *the_controller;
+
+static struct clk *otg_clock;
+static const char driver_name[] = "s3c-udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+
+/* Max packet size*/
+static unsigned int ep0_fifo_size = 64;
+static unsigned int ep_fifo_size = 512;
+static unsigned int ep_fifo_size2 = 1024;
+static int reset_available = 1;
+
+/*
+ Local declarations.
+*/
+static int s3c_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *);
+static int s3c_ep_disable(struct usb_ep *ep);
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags);
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *);
+
+static int s3c_queue(struct usb_ep *ep,
+ struct usb_request *, gfp_t gfp_flags);
+static int s3c_dequeue(struct usb_ep *ep, struct usb_request *);
+static int s3c_fifo_status(struct usb_ep *ep);
+static void s3c_fifo_flush(struct usb_ep *ep);
+static void s3c_ep0_read(struct s3c_udc *dev);
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep);
+static void s3c_handle_ep0(struct s3c_udc *dev);
+static int s3c_ep0_write(struct s3c_udc *dev);
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req);
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
+static void stop_activity(struct s3c_udc *dev,
+ struct usb_gadget_driver *driver);
+static int udc_enable(struct s3c_udc *dev);
+static void udc_set_address(struct s3c_udc *dev, unsigned char address);
+static void reconfig_usbd(void);
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed);
+static void nuke(struct s3c_ep *ep, int status);
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
+
+static struct usb_ep_ops s3c_ep_ops = {
+ .enable = s3c_ep_enable,
+ .disable = s3c_ep_disable,
+
+ .alloc_request = s3c_alloc_request,
+ .free_request = s3c_free_request,
+
+ .queue = s3c_queue,
+ .dequeue = s3c_dequeue,
+
+ .set_halt = s3c_udc_set_halt,
+ .fifo_status = s3c_fifo_status,
+ .fifo_flush = s3c_fifo_flush,
+};
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static const char proc_node_name[] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ struct s3c_udc *dev = _dev;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int t;
+
+ if (off != 0)
+ return 0;
+
+ local_irq_save(flags);
+
+ /* basic device status */
+ t = scnprintf(next, size,
+ DRIVER_DESC "\n"
+ "%s version: %s\n"
+ "Gadget driver: %s\n"
+ "\n",
+ driver_name, DRIVER_VERSION,
+ dev->driver ? dev->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ local_irq_restore(flags);
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_files() \
+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() \
+ remove_proc_entry(proc_node_name, NULL)
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+#if OTG_DMA_MODE /* DMA Mode */
+#include "s3c_udc_otg_xfer_dma.c"
+
+#else /* Slave Mode */
+#include "s3c_udc_otg_xfer_slave.c"
+#endif
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct s3c_udc *dev)
+{
+ DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+ udc_set_address(dev, 0);
+
+ dev->ep0state = WAIT_FOR_SETUP;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ dev->usb_address = 0;
+
+ otg_phy_off();
+}
+
+/*
+ * udc_reinit - initialize software state
+ */
+static void udc_reinit(struct s3c_udc *dev)
+{
+ unsigned int i;
+
+ DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ /* basic endpoint records init */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &dev->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+ ep->desc = 0;
+ ep->stopped = 0;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->pio_irqs = 0;
+ }
+
+ /* the rest was statically initialized, and is read-only */
+}
+
+#define BYTES2MAXP(x) (x / 8)
+#define MAXP2BYTES(x) (x * 8)
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static int udc_enable(struct s3c_udc *dev)
+{
+ unsigned long flags;
+
+ DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+ otg_phy_init();
+
+ spin_lock_irqsave(&dev->lock, flags);
+ reconfig_usbd();
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
+ readl(S3C_UDC_OTG_GINTMSK));
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+/*
+ Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct s3c_udc *dev = the_controller;
+ int retval;
+
+ DEBUG_SETUP("%s: %s\n", __func__, driver->driver.name);
+
+/*
+ * adb composite fail to !driver->unbind in composite.c as below
+ * static struct usb_gadget_driver composite_driver = {
+ * .speed = USB_SPEED_HIGH,
+ * .bind = composite_bind,
+ * .unbind = __exit_p(composite_unbind),
+ */
+ if (!driver
+ || (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
+ || !bind
+ || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+ /* initialize device status as self-powered.*/
+ dev->status = 1 << USB_DEVICE_SELF_POWERED;
+ dev->gadget.dev.driver = &driver->driver;
+ retval = device_add(&dev->gadget.dev);
+
+ if (retval) {
+ printk(KERN_ERR "target device_add failed, error %d\n", retval);
+ return retval;
+ }
+
+ retval = bind(&dev->gadget);
+ if (retval) {
+ printk(KERN_ERR "%s: bind to driver %s --> error %d\n",
+ dev->gadget.name, driver->driver.name, retval);
+ device_del(&dev->gadget.dev);
+
+ dev->driver = 0;
+ dev->gadget.dev.driver = 0;
+ return retval;
+ }
+
+ enable_irq(IRQ_OTG);
+
+ retval = usb_gadget_vbus_connect(&dev->gadget);
+ if (retval < 0)
+ dev_dbg(&dev->gadget.dev, "%s vbus connect %d\n",
+ __func__, retval);
+
+ if (!(readl(S3C_UDC_OTG_GOTGCTL) & B_SESSION_VALID)) {
+ retval = usb_gadget_vbus_disconnect(&dev->gadget);
+ if (retval < 0) {
+ dev_dbg(&dev->gadget.dev, "%s vbus disconnect %d\n",
+ __func__, retval);
+ }
+ }
+
+ printk(KERN_DEBUG "Registered gadget driver '%s'\n",
+ driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+/*
+ Unregister entry point for the peripheral controller driver.
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct s3c_udc *dev = the_controller;
+ unsigned long flags;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->driver = 0;
+ stop_activity(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ driver->unbind(&dev->gadget);
+ device_del(&dev->gadget.dev);
+
+ disable_irq(IRQ_OTG);
+
+ printk(KERN_DEBUG "Unregistered gadget driver '%s'\n",
+ driver->driver.name);
+
+ udc_disable(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static int s3c_udc_power(struct s3c_udc *dev, char en)
+{
+ pr_debug("%s : %s\n", __func__, en ? "ON" : "OFF");
+
+ if (en) {
+ regulator_enable(dev->udc_vcc_d);
+ regulator_enable(dev->udc_vcc_a);
+ } else {
+ regulator_disable(dev->udc_vcc_d);
+ regulator_disable(dev->udc_vcc_a);
+ }
+
+ return 0;
+}
+
+int s3c_vbus_enable(struct usb_gadget *gadget, int enable)
+{
+ unsigned long flags;
+ struct s3c_udc *dev = the_controller;
+
+ if (dev->udc_enabled != enable) {
+ dev->udc_enabled = enable;
+ if (!enable) {
+ spin_lock_irqsave(&dev->lock, flags);
+ stop_activity(dev, dev->driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ udc_disable(dev);
+ clk_disable(otg_clock);
+ s3c_udc_power(dev, 0);
+ } else {
+ s3c_udc_power(dev, 1);
+ clk_enable(otg_clock);
+ udc_reinit(dev);
+ udc_enable(dev);
+ }
+ } else
+ dev_dbg(&dev->gadget.dev, "%s, udc : %d, en : %d\n",
+ __func__, dev->udc_enabled, enable);
+
+ return 0;
+}
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
+{
+ unsigned int stopped = ep->stopped;
+ struct device *dev = &the_controller->dev->dev;
+ DEBUG("%s: %s %p, req = %p, stopped = %d\n",
+ __func__, ep->ep.name, ep, &req->req, stopped);
+
+ list_del_init(&req->queue);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (req->mapped) {
+ dma_unmap_single(dev, req->req.dma, req->req.length,
+ (ep->bEndpointAddress & USB_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ }
+ if (status && status != -ESHUTDOWN) {
+ DEBUG("complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+
+ spin_unlock(&ep->dev->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->dev->lock);
+
+ ep->stopped = stopped;
+}
+
+/*
+ * nuke - dequeue ALL requests
+ */
+static void nuke(struct s3c_ep *ep, int status)
+{
+ struct s3c_request *req;
+
+ DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep);
+
+ /* called with irqs blocked */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ done(ep, req, status);
+ }
+}
+
+static void stop_activity(struct s3c_udc *dev,
+ struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &dev->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
+}
+
+static void reconfig_usbd(void)
+{
+ struct s3c_udc *dev = the_controller;
+ /* 2. Soft-reset OTG Core and then unreset again. */
+#ifdef DED_TX_FIFO
+ int i;
+#endif
+ unsigned int uTemp;
+ writel(CORE_SOFT_RESET, S3C_UDC_OTG_GRSTCTL);
+
+ writel(0<<15 /* PHY Low Power Clock sel*/
+ |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/
+ |0x5<<10 /* Turnaround time*/
+ |0<<9|0<<8 /* [0:HNP disable, 1:HNP enable][ 0:SRP disable, 1:SRP enable] H1= 1,1*/
+ |0<<7 /* Ulpi DDR sel*/
+ |0<<6 /* 0: high speed utmi+, 1: full speed serial*/
+ |0<<4 /* 0: utmi+, 1:ulpi*/
+ |1<<3 /* phy i/f 0:8bit, 1:16bit*/
+ |0x7<<0, /* HS/FS Timeout**/
+ S3C_UDC_OTG_GUSBCFG);
+
+ /* 3. Put the OTG device core in the disconnected state.*/
+ uTemp = readl(S3C_UDC_OTG_DCTL);
+ uTemp |= SOFT_DISCONNECT;
+ writel(uTemp, S3C_UDC_OTG_DCTL);
+
+ udelay(20);
+
+ /* 4. Make the OTG device core exit from the disconnected state.*/
+ if (!dev->soft_disconnected) {
+ uTemp = readl(S3C_UDC_OTG_DCTL);
+ uTemp = uTemp & ~SOFT_DISCONNECT;
+ writel(uTemp, S3C_UDC_OTG_DCTL);
+ }
+
+ /* 5. Configure OTG Core to initial settings of device mode.*/
+ /* [][1: full speed(30Mhz) 0:high speed]*/
+ writel(1<<18|0x0<<0, S3C_UDC_OTG_DCFG);
+
+ mdelay(1);
+
+ /* 6. Unmask the core interrupts*/
+ writel(GINTMSK_INIT, S3C_UDC_OTG_GINTMSK);
+
+ /* 7. Set NAK bit of EP0, EP1, EP2*/
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ /* 8. Unmask EPO interrupts*/
+ writel(((1<<EP0_CON)<<DAINT_OUT_BIT)|(1<<EP0_CON),
+ S3C_UDC_OTG_DAINTMSK);
+
+ /* 9. Unmask device OUT EP common interrupts*/
+ writel(DOEPMSK_INIT, S3C_UDC_OTG_DOEPMSK);
+
+ /* 10. Unmask device IN EP common interrupts*/
+ writel(DIEPMSK_INIT, S3C_UDC_OTG_DIEPMSK);
+
+ /* 11. Set Rx FIFO Size (in 32-bit words) */
+ writel(RX_FIFO_SIZE, S3C_UDC_OTG_GRXFSIZ);
+
+ /* 12. Set Non Periodic Tx FIFO Size*/
+ writel((NPTX_FIFO_SIZE) << 16 | (NPTX_FIFO_START_ADDR) << 0,
+ S3C_UDC_OTG_GNPTXFSIZ);
+
+#ifdef DED_TX_FIFO
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++)
+ writel((PTX_FIFO_SIZE) << 16 |
+ (NPTX_FIFO_START_ADDR + NPTX_FIFO_SIZE + PTX_FIFO_SIZE*(i-1)) << 0,
+ S3C_UDC_OTG_DIEPTXF(i));
+#endif
+
+ /* Flush the RX FIFO */
+ writel(0x10, S3C_UDC_OTG_GRSTCTL);
+ while (readl(S3C_UDC_OTG_GRSTCTL) & 0x10)
+ ;
+
+ /* Flush all the Tx FIFO's */
+ writel(0x10<<6, S3C_UDC_OTG_GRSTCTL);
+ writel((0x10<<6)|0x20, S3C_UDC_OTG_GRSTCTL);
+ while (readl(S3C_UDC_OTG_GRSTCTL) & 0x20)
+ ;
+
+ /* 13. Initialize OTG Link Core.*/
+ writel(GAHBCFG_INIT, S3C_UDC_OTG_GAHBCFG);
+}
+
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed)
+{
+ unsigned int ep_ctrl;
+ int i;
+ if (speed == USB_SPEED_HIGH) {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 512;
+ ep_fifo_size2 = 1024;
+ dev->gadget.speed = USB_SPEED_HIGH;
+ } else {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 64;
+ ep_fifo_size2 = 64;
+ dev->gadget.speed = USB_SPEED_FULL;
+ }
+
+ dev->ep[0].ep.maxpacket = ep0_fifo_size;
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
+ /* fullspeed limitations don't apply to isochronous endpoints */
+ if (dev->ep[i].bmAttributes != USB_ENDPOINT_XFER_ISOC)
+ dev->ep[i].ep.maxpacket = ep_fifo_size;
+ }
+
+ /* EP0 - Control IN (64 bytes)*/
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ writel(ep_ctrl|(0<<0), S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ /* EP0 - Control OUT (64 bytes)*/
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+ writel(ep_ctrl|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
+}
+
+static int s3c_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+
+ DEBUG("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->bEndpointAddress != desc->bEndpointAddress
+ || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+
+ DEBUG("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->bmAttributes !=
+ (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+
+ DEBUG("%s: %s type mismatch\n", __func__, _ep->name);
+ return -EINVAL;
+ }
+
+ /* hardware _could_ do smaller, but driver doesn't */
+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+ && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
+ || !desc->wMaxPacketSize) {
+
+ DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+
+ DEBUG("%s: bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ ep->stopped = 0;
+ ep->desc = desc;
+ ep->pio_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Reset halt state */
+ s3c_udc_set_halt(_ep, 0);
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ s3c_udc_ep_activate(ep);
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n",
+ __func__, _ep->name, ep->stopped, ep->ep.maxpacket);
+ return 0;
+}
+
+/** Disable EP
+ */
+static int s3c_ep_disable(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ unsigned long flags;
+
+ DEBUG("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || !ep->desc) {
+ DEBUG("%s: %s not enabled\n", __func__,
+ _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* Nuke all pending requests */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ DEBUG("%s: disabled %s\n", __func__, _ep->name);
+ return 0;
+}
+
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+
+ DEBUG("%s: %s %p\n", __func__, ep->name, ep);
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (req)
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct s3c_request *req = container_of(_req, struct s3c_request, req);
+ DEBUG("%s: %p\n", __func__, ep);
+ kfree(req);
+}
+
+/* dequeue JUST ONE request */
+static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ unsigned long flags;
+
+ DEBUG("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/** Return bytes in EP FIFO
+ */
+static int s3c_fifo_status(struct usb_ep *_ep)
+{
+ int count = 0;
+ struct s3c_ep *ep;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep) {
+ DEBUG("%s: bad ep\n", __func__);
+ return -ENODEV;
+ }
+
+ DEBUG("%s: %d\n", __func__, ep_index(ep));
+
+ /* LPD can't report unclaimed bytes from IN fifos */
+ if (ep_is_in(ep))
+ return -EOPNOTSUPP;
+
+ return count;
+}
+
+/** Flush EP FIFO
+ */
+static void s3c_fifo_flush(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ DEBUG("%s: bad ep\n", __func__);
+ return;
+ }
+
+ DEBUG("%s: %d\n", __func__, ep_index(ep));
+}
+
+/* ---------------------------------------------------------------------------
+ * device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int s3c_udc_get_frame(struct usb_gadget *_gadget)
+{
+ unsigned int frame;
+ DEBUG("%s: %p\n", __func__, _gadget);
+ /*fram count number [21:8]*/
+ frame = readl(S3C_UDC_OTG_DSTS);
+ frame &= SOFFN_MASK;
+ frame >>= SOFFN_SHIFT;
+ return frame;
+}
+
+static int s3c_udc_wakeup(struct usb_gadget *_gadget)
+{
+ unsigned int dev_gctl, dev_dctl, dev_status;
+ unsigned long flags;
+ int retVal = -EINVAL;
+ struct s3c_udc *dev = the_controller;
+
+ DEBUG("%s: %p\n", __func__, _gadget);
+
+ if (!_gadget)
+ return -ENODEV;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (!(dev->status & (1 << USB_DEVICE_REMOTE_WAKEUP))) {
+ DEBUG_SETUP("%s::Remote Wakeup is not set\n", __func__);
+ goto wakeup_exit;
+ }
+ /* check for session */
+ dev_gctl = readl(S3C_UDC_OTG_GOTGCTL);
+ if (dev_gctl & B_SESSION_VALID) {
+ /* check for suspend state */
+ dev_status = readl(S3C_UDC_OTG_DSTS);
+ if (dev_status & USB_SUSPEND) {
+ DEBUG_SETUP("%s:: Set Remote wakeup\n", __func__);
+ dev_dctl = readl(S3C_UDC_OTG_DCTL);
+ dev_dctl |= REMOTE_WAKEUP;
+ writel(dev_dctl, S3C_UDC_OTG_DCTL);
+ mdelay(1);
+ DEBUG_SETUP("%s:: Clear Remote Wakeup\n", __func__);
+ dev_dctl = readl(S3C_UDC_OTG_DCTL);
+ dev_dctl &= (~REMOTE_WAKEUP);
+ writel(dev_dctl, S3C_UDC_OTG_DCTL);
+ } else {
+ DEBUG_SETUP("%s:: already woke up\n", __func__);
+ }
+
+ } else if (dev_gctl & SESSION_REQ) {
+ DEBUG_SETUP("%s:: Session request already active\n", __func__);
+ goto wakeup_exit;
+ }
+
+ retVal = 0;
+wakeup_exit:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return retVal;
+}
+
+void s3c_udc_soft_connect(void)
+{
+ struct s3c_udc *dev = the_controller;
+ unsigned long flags;
+ u32 uTemp;
+
+ DEBUG("[%s]\n", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ uTemp = readl(S3C_UDC_OTG_DCTL);
+ uTemp = uTemp & ~SOFT_DISCONNECT;
+ writel(uTemp, S3C_UDC_OTG_DCTL);
+ udelay(20);
+
+ reset_available = 1;
+
+ /* Unmask the core interrupt */
+ writel(readl(S3C_UDC_OTG_GINTSTS), S3C_UDC_OTG_GINTSTS);
+ writel(GINTMSK_INIT, S3C_UDC_OTG_GINTMSK);
+
+ dev->soft_disconnected = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void s3c_udc_soft_disconnect(void)
+{
+ u32 uTemp;
+ struct s3c_udc *dev = the_controller;
+ unsigned long flags;
+
+ DEBUG("[%s]\n", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* Mask the core interrupt */
+ writel(0, S3C_UDC_OTG_GINTMSK);
+
+ uTemp = readl(S3C_UDC_OTG_DCTL);
+ uTemp |= SOFT_DISCONNECT;
+ writel(uTemp, S3C_UDC_OTG_DCTL);
+
+ stop_activity(dev, dev->driver);
+
+ dev->soft_disconnected = 1;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int s3c_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ if (is_on)
+ s3c_udc_soft_connect();
+ else
+ s3c_udc_soft_disconnect();
+ return 0;
+}
+
+
+static const struct usb_gadget_ops s3c_udc_ops = {
+ .get_frame = s3c_udc_get_frame,
+ .wakeup = s3c_udc_wakeup,
+ /* current versions must always be self-powered */
+ .pullup = s3c_udc_pullup,
+ .vbus_session = s3c_vbus_enable,
+};
+
+static void nop_release(struct device *dev)
+{
+ DEBUG("%s\n", __func__);
+}
+
+static struct s3c_udc memory = {
+ .usb_address = 0,
+
+ .gadget = {
+ .ops = &s3c_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ .dev = {
+ .release = nop_release,
+ },
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = 0,
+ .bmAttributes = 0,
+
+ .ep_type = ep_control,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP0_FIFO,
+ },
+
+ /* first group of endpoints */
+ .ep[1] = {
+ .ep = {
+ .name = "ep1-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 1,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP1_FIFO,
+ },
+
+ .ep[2] = {
+ .ep = {
+ .name = "ep2-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 2,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP2_FIFO,
+ },
+
+ .ep[3] = {
+ .ep = {
+ .name = "ep3-int",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 3,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+ .ep_type = ep_interrupt,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP3_FIFO,
+ },
+ .ep[4] = {
+ .ep = {
+ .name = "ep4-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 4,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP4_FIFO,
+ },
+ .ep[5] = {
+ .ep = {
+ .name = "ep5-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 5,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP5_FIFO,
+ },
+ .ep[6] = {
+ .ep = {
+ .name = "ep6-int",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 6,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+ .ep_type = ep_interrupt,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP6_FIFO,
+ },
+ .ep[7] = {
+ .ep = {
+ .name = "ep7-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 7,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP7_FIFO,
+ },
+ .ep[8] = {
+ .ep = {
+ .name = "ep8-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 8,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP8_FIFO,
+ },
+ .ep[9] = {
+ .ep = {
+ .name = "ep9-int",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 9,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+ .ep_type = ep_interrupt,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP9_FIFO,
+ },
+ .ep[10] = {
+ .ep = {
+ .name = "ep10-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 0xa,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP10_FIFO,
+ },
+ .ep[11] = {
+ .ep = {
+ .name = "ep11-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 0xb,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP11_FIFO,
+ },
+ .ep[12] = {
+ .ep = {
+ .name = "ep12-int",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 0xc,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+ .ep_type = ep_interrupt,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP12_FIFO,
+ },
+ .ep[13] = {
+ .ep = {
+ .name = "ep13-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 0xd,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP13_FIFO,
+ },
+ .ep[14] = {
+ .ep = {
+ .name = "ep14-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 0xe,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP14_FIFO,
+ },
+ .ep[15] = {
+ .ep = {
+ .name = "ep15-iso",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 0xf,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+
+ .ep_type = ep_isochronous,
+ .fifo = (unsigned int) S3C_UDC_OTG_EP15_FIFO,
+ },
+};
+
+#if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG
+atomic_t g_OtgHostMode; // actual mode: client (0) or host (1)
+atomic_t g_OtgOperationMode; // operation mode: 'c'lient, 'h'ost, 'o'tg or 'a'uto-host
+atomic_t g_OtgLastCableState; // last cable state: detached (0), client attached (1), otg attached (2)
+atomic_t g_OtgDriver; // driver to use: 0: S3C High-speed, 1: S3C Low-speed/Full-speed, 2: DWC
+
+int s3c_is_otgmode(void)
+{
+ printk("otgmode = %d\n", atomic_read(&g_OtgHostMode));
+ return atomic_read(&g_OtgHostMode);
+}
+EXPORT_SYMBOL(s3c_is_otgmode);
+
+int s3c_get_drivermode(void)
+{
+ printk("drivermode = %d\n", atomic_read(&g_OtgDriver));
+ return atomic_read(&g_OtgDriver);
+}
+EXPORT_SYMBOL(s3c_get_drivermode);
+
+void set_otghost_mode(int mode) {
+//sztupy:
+// this function will determine what to do with the new information according to the current mode of operation
+// mode: 0: cable detached, 1: client cable attached, 2: otg cable attached, -1: operation config changed
+// modes of operation: c: always client, h: always host, g: otg mode (host if otg cable), a: automatic mode (host if cable plugged in)
+
+ struct s3c_udc *dev = the_controller;
+ int enable = 0;
+ int rval;
+ char opmode = atomic_read(&g_OtgOperationMode);
+ if (mode==-1) mode = atomic_read(&g_OtgLastCableState);
+ atomic_set(&g_OtgLastCableState, mode);
+ switch (opmode) {
+ case 'h': enable = 1; break;
+ case 'o': enable = (mode==2); break;
+ case 'a': enable = (mode>0); break;
+ default: atomic_set(&g_OtgOperationMode,'c'); enable = 0; break;
+ }
+
+ if (enable && !atomic_read(&g_OtgHostMode)) {
+ printk("Setting OTG host mode\n");
+ free_irq(IRQ_OTG, dev);
+ s3c_vbus_enable(&dev->gadget, 1);
+
+#if defined CONFIG_USB_S3C_OTG_HOST && defined CONFIG_USB_DWC_OTG
+ if (atomic_read(&g_OtgDriver) & USB_OTG_DRIVER_DWC) {
+ rval = platform_driver_register(&dwc_otg_driver);
+ } else {
+ rval = platform_driver_register(&s5pc110_otg_driver);
+ }
+ if (rval < 0)
+#elif defined CONFIG_USB_DWC_OTG
+ if (platform_driver_register(&dwc_otg_driver) < 0)
+#elif defined CONFIG_USB_S3C_OTG_HOST
+ if (platform_driver_register(&s5pc110_otg_driver) < 0)
+#endif
+ {
+ printk("platform_driver_register failed...\n");
+ atomic_set(&g_OtgHostMode , 0);
+ } else {
+ printk("platform_driver_registered\n");
+ atomic_set(&g_OtgHostMode , atomic_read(&g_OtgDriver)+1);
+ }
+ } else if (!enable && atomic_read(&g_OtgHostMode)) {
+// sztupy: also handle the disabling here
+ printk("Disabling OTG host mode\n");
+ s3c_vbus_enable(&dev->gadget, 0);
+#if defined CONFIG_USB_S3C_OTG_HOST && defined CONFIG_USB_DWC_OTG
+ if ((atomic_read(&g_OtgHostMode)-1) & USB_OTG_DRIVER_DWC) {
+ platform_driver_unregister(&dwc_otg_driver);
+ } else {
+ platform_driver_unregister(&s5pc110_otg_driver);
+ }
+#elif defined CONFIG_USB_S3C_OTG_HOST
+ platform_driver_unregister(&s5pc110_otg_driver);
+#elif defined CONFIG_USB_DWC_OTG
+ platform_driver_unregister(&dwc_otg_driver);
+#endif
+ printk("platform_driver_unregistered\n");
+ /* irq setup after old hardware state is cleaned up */
+ if (request_irq(IRQ_OTG, s3c_udc_irq, 0, driver_name, dev)) {
+ printk("Warning: Could not request IRQ for USB gadget!\n");
+ }
+ atomic_set(&g_OtgHostMode , 0);
+ } else {
+ printk("OTG: no changes needed\n");
+ }
+}
+
+EXPORT_SYMBOL(set_otghost_mode);
+
+static ssize_t usbmode_read(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ const char *msg = "client";
+ const char *msg2 = "disconnected";
+ const char *msg3 = "gadget";
+ switch(atomic_read(&g_OtgOperationMode)) {
+ case 'o': msg = "otg"; break;
+ case 'h': msg = "host"; break;
+ case 'a': msg = "auto-host"; break;
+ }
+ switch(atomic_read(&g_OtgLastCableState)) {
+ case 1: msg2 = "usb connected"; break;
+ case 2: msg2 = "otg connected"; break;
+ }
+ switch(atomic_read(&g_OtgHostMode)) {
+ case USB_OTG_DRIVER_S3CHS: msg3 = "host (S3C/HS)"; break;
+ case USB_OTG_DRIVER_S3CFSLS: msg3 = "host (S3C/FSLS)"; break;
+ case USB_OTG_DRIVER_DWC: msg3 = "host (DWC)"; break;
+ }
+
+ return sprintf(buf,"%s (cable: %s; state: %s)\n", msg, msg2, msg3);
+}
+
+static ssize_t usbmode_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ char c;
+ printk("input data --> %s", buf);
+ c = buf[0];
+ switch(c) {
+ case 'a': atomic_set(&g_OtgOperationMode, 'a'); set_otghost_mode(-1);break;
+ case 'h': atomic_set(&g_OtgOperationMode, 'h'); set_otghost_mode(-1);break;
+ case 'c': atomic_set(&g_OtgOperationMode, 'c'); set_otghost_mode(-1);break;
+ case 'o': atomic_set(&g_OtgOperationMode, 'o'); set_otghost_mode(-1);break;
+ default: printk("Invalid input data\n");
+ }
+ return size;
+}
+
+static ssize_t usbdriver_read(struct device *dev, struct device_attribute *attr, char *buf) {
+ const char *msg = "h:S3C driver (high-speed)";
+ switch(atomic_read(&g_OtgDriver)) {
+ case USB_OTG_DRIVER_S3CFSLS: msg = "l:S3C driver (low-speed / full-speed)";break;
+ case USB_OTG_DRIVER_DWC: msg = "d:DWC driver";break;
+ }
+ return sprintf(buf,"%s\n",msg);
+}
+
+static ssize_t usbdriver_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
+ char c;
+ printk("input data --> %s", buf);
+ c = buf[0];
+ switch(c) {
+#if defined CONFIG_USB_S3C_OTG_HOST
+ case 'h': atomic_set(&g_OtgDriver, USB_OTG_DRIVER_S3CHS); break;
+ case 'l': atomic_set(&g_OtgDriver, USB_OTG_DRIVER_S3CFSLS); break;
+#endif
+#if defined CONFIG_USB_DWC_OTG
+ case 'd': atomic_set(&g_OtgDriver, USB_OTG_DRIVER_DWC); break;
+#endif
+ default: printk("Invalid input data\n");
+ }
+ return size;
+
+}
+
+static ssize_t usbversion_read(struct device *dev, struct device_attribute *attr, char *buf) {
+ return sprintf(buf,"version: build 5\ndrivers:%s%s\n",
+#if defined CONFIG_USB_S3C_OTG_HOST
+ " S3CHS S3CFSLS"
+#else
+ ""
+#endif
+ ,
+#if defined CONFIG_USB_DWC_OTG
+ " DWC"
+#else
+ ""
+#endif
+ );
+}
+
+// kevinh - Allow changing USB host/target modes on S3C android devices without a special cable
+static DEVICE_ATTR(opmode, S_IRUGO | S_IWUSR, usbmode_read, usbmode_write);
+// sztupy - add some more attributes
+static DEVICE_ATTR(hostdriver, S_IRUGO | S_IWUSR, usbdriver_read, usbdriver_write);
+static DEVICE_ATTR(version, S_IRUGO, usbversion_read, NULL);
+#endif
+
+/*
+ * probe - binds to the platform device
+ */
+static int s3c_udc_probe(struct platform_device *pdev)
+{
+ struct s3c_udc *dev = &memory;
+ int retval;
+ DEBUG("%s: %p\n", __func__, pdev);
+
+ spin_lock_init(&dev->lock);
+ dev->dev = pdev;
+
+ device_initialize(&dev->gadget.dev);
+ dev->gadget.dev.parent = &pdev->dev;
+
+ dev->gadget.is_dualspeed = 1; /* Hack only*/
+ dev->gadget.is_otg = 0;
+ dev->gadget.is_a_peripheral = 0;
+ dev->gadget.b_hnp_enable = 0;
+ dev->gadget.a_hnp_support = 0;
+ dev->gadget.a_alt_hnp_support = 0;
+
+ dev->udc_vcc_d = regulator_get(&pdev->dev, "pd_io");
+ dev->udc_vcc_a = regulator_get(&pdev->dev, "pd_core");
+ if (IS_ERR(dev->udc_vcc_d) || IS_ERR(dev->udc_vcc_a)) {
+ printk(KERN_ERR "failed to find udc vcc source\n");
+ return -ENOENT;
+ }
+
+ the_controller = dev;
+ platform_set_drvdata(pdev, dev);
+
+ dev_set_name(&dev->gadget.dev, "gadget");
+ otg_clock = clk_get(&pdev->dev, "otg");
+ if (otg_clock == NULL) {
+ printk(KERN_ERR "failed to find otg clock source\n");
+ return -ENOENT;
+ }
+
+ udc_reinit(dev);
+
+ /* irq setup after old hardware state is cleaned up */
+ retval =
+ request_irq(IRQ_OTG, s3c_udc_irq, 0, driver_name, dev);
+
+ if (retval != 0) {
+ DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
+ IRQ_OTG, retval);
+ return -EBUSY;
+ }
+
+ disable_irq(IRQ_OTG);
+ create_proc_files();
+#if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG
+ if (device_create_file(&pdev->dev, &dev_attr_opmode) < 0)
+ printk("Failed to create device file(%s)!\n", dev_attr_opmode.attr.name);
+ if (device_create_file(&pdev->dev, &dev_attr_hostdriver) < 0)
+ printk("Failed to create device file(%s)!\n", dev_attr_hostdriver.attr.name);
+ if (device_create_file(&pdev->dev, &dev_attr_version) < 0)
+ printk("Failed to create device file(%s)!\n", dev_attr_version.attr.name);
+#if !defined CONFIG_USB_S3C_OTG_HOST
+ atomic_set(&g_OtgDriver, USB_OTG_DRIVER_DWC);
+#endif
+#endif
+ return retval;
+}
+
+static int s3c_udc_remove(struct platform_device *pdev)
+{
+ struct s3c_udc *dev = platform_get_drvdata(pdev);
+
+ DEBUG("%s: %p\n", __func__, pdev);
+
+ if (otg_clock != NULL) {
+ clk_disable(otg_clock);
+ clk_put(otg_clock);
+ otg_clock = NULL;
+ }
+
+ remove_proc_files();
+ usb_gadget_unregister_driver(dev->driver);
+
+ free_irq(IRQ_OTG, dev);
+
+ platform_set_drvdata(pdev, 0);
+
+ the_controller = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct s3c_udc *dev = the_controller;
+
+ disable_irq(IRQ_OTG);
+
+ if (dev->driver && dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+
+ if (dev->udc_enabled)
+ usb_gadget_vbus_disconnect(&dev->gadget);
+
+ return 0;
+}
+
+static int s3c_udc_resume(struct platform_device *pdev)
+{
+ struct s3c_udc *dev = the_controller;
+
+ if (dev->driver && dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+
+ enable_irq(IRQ_OTG);
+
+ return 0;
+}
+#else
+#define s3c_udc_suspend NULL
+#define s3c_udc_resume NULL
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver s3c_udc_driver = {
+ .probe = s3c_udc_probe,
+ .remove = s3c_udc_remove,
+ .suspend = s3c_udc_suspend,
+ .resume = s3c_udc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s3c-usbgadget",
+ },
+};
+
+static int __init udc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&s3c_udc_driver);
+ if (!ret)
+ printk(KERN_INFO "%s : %s\n"
+ "%s : version %s %s\n",
+ driver_name, DRIVER_DESC,
+ driver_name, DRIVER_VERSION, OTG_DMA_MODE ?
+ "(DMA Mode)" : "(Slave Mode)");
+
+ return ret;
+}
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&s3c_udc_driver);
+ printk(KERN_INFO "Unloaded %s version %s\n",
+ driver_name, DRIVER_VERSION);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Samsung");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
new file mode 100644
index 0000000..70cf602
--- /dev/null
+++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
@@ -0,0 +1,1387 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2009 for Samsung Electronics
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define GINTMSK_INIT (INT_OUT_EP|INT_IN_EP|INT_RESUME|INT_ENUMDONE|INT_RESET|INT_SUSPEND)
+#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE|AHB_ERROR|TRANSFER_DONE)
+#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE)
+#define GAHBCFG_INIT (PTXFE_HALF|NPTXFE_HALF|MODE_DMA|BURST_INCR4|GBL_INT_UNMASK)
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+static u8 clear_feature_num;
+static int clear_feature_flag;
+static int set_conf_done;
+
+/* Bulk-Only Mass Storage Reset (class-specific request) */
+#define GET_MAX_LUN_REQUEST 0xFE
+#define BOT_RESET_REQUEST 0xFF
+
+/* TEST MODE in set_feature request */
+#define TEST_SELECTOR_MASK 0xFF
+#define TEST_PKT_SIZE 53
+
+static u8 test_pkt[TEST_PKT_SIZE] __attribute__((aligned(8))) = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* JKJKJKJK x 9 */
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, /* JJKKJJKK x 8 */
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, /* JJJJKKKK x 8 */
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* JJJJJJJKKKKKKK x 8 - '1' */
+ 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, /* '1' + JJJJJJJK x 8 */
+ 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E /* {JKKKKKKK x 10}, JK */
+};
+
+void s3c_udc_ep_set_stall(struct s3c_ep *ep);
+
+#if defined(CONFIG_MACH_SMDKC110) || defined(CONFIG_MACH_SMDKV210)
+extern void s3c_cable_check_status(int flag);
+
+void s3c_udc_cable_connect(struct s3c_udc *dev)
+{
+ s3c_cable_check_status(1);
+}
+
+void s3c_udc_cable_disconnect(struct s3c_udc *dev)
+{
+ s3c_cable_check_status(0);
+}
+#endif
+
+static inline void s3c_udc_ep0_zlp(void)
+{
+ u32 ep_ctrl;
+
+ writel(virt_to_phys(&usb_ctrl), S3C_UDC_OTG_DIEPDMA(EP0_CON));
+ writel((1<<19 | 0<<0), S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+}
+
+static inline void s3c_udc_pre_setup(void)
+{
+ u32 ep_ctrl;
+
+ DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__);
+
+ writel((1 << 19)|sizeof(struct usb_ctrlrequest), S3C_UDC_OTG_DOEPTSIZ(EP0_CON));
+ writel(virt_to_phys(&usb_ctrl), S3C_UDC_OTG_DOEPDMA(EP0_CON));
+
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DOEPCTL(EP0_CON));
+}
+
+static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 *buf, ctrl;
+ u32 length, pktcnt;
+ u32 ep_num = ep_index(ep);
+
+ struct device *dev = &the_controller->dev->dev;
+ buf = req->req.buf + req->req.actual;
+ prefetchw(buf);
+
+ length = req->req.length - req->req.actual;
+ req->req.dma = dma_map_single(dev, buf,
+ length, DMA_FROM_DEVICE);
+ req->mapped = 1;
+
+ if (length == 0)
+ pktcnt = 1;
+ else
+ pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+ ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+ writel(virt_to_phys(buf), S3C_UDC_OTG_DOEPDMA(ep_num));
+ writel((pktcnt<<19)|(length<<0), S3C_UDC_OTG_DOEPTSIZ(ep_num));
+ writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+
+ DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x, DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n"
+ "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+ __func__, ep_num,
+ readl(S3C_UDC_OTG_DOEPDMA(ep_num)),
+ readl(S3C_UDC_OTG_DOEPTSIZ(ep_num)),
+ readl(S3C_UDC_OTG_DOEPCTL(ep_num)),
+ buf, pktcnt, length);
+ return 0;
+
+}
+
+static int setdma_tx(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 *buf, ctrl = 0;
+ u32 length, pktcnt;
+ u32 ep_num = ep_index(ep);
+ struct device *dev = &the_controller->dev->dev;
+
+ buf = req->req.buf + req->req.actual;
+ prefetch(buf);
+ length = req->req.length - req->req.actual;
+
+ if (ep_num == EP0_CON)
+ length = min(length, (u32)ep_maxpacket(ep));
+
+ req->req.actual += length;
+ req->req.dma = dma_map_single(dev, buf,
+ length, DMA_TO_DEVICE);
+ req->mapped = 1;
+
+ if (length == 0)
+ pktcnt = 1;
+ else
+ pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+#ifdef DED_TX_FIFO
+ /* Write the FIFO number to be used for this endpoint */
+ ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+ ctrl &= ~DEPCTL_TXFNUM_MASK;;
+ ctrl |= (ep_num << DEPCTL_TXFNUM_BIT);
+ writel(ctrl , S3C_UDC_OTG_DIEPCTL(ep_num));
+#endif
+
+ writel(virt_to_phys(buf), S3C_UDC_OTG_DIEPDMA(ep_num));
+ writel((pktcnt<<19)|(length<<0), S3C_UDC_OTG_DIEPTSIZ(ep_num));
+ ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+ ctrl |= DEPCTL_SET_ODD_FRM;
+ writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+
+ DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x, DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n"
+ "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+ __func__, ep_num,
+ readl(S3C_UDC_OTG_DIEPDMA(ep_num)),
+ readl(S3C_UDC_OTG_DIEPTSIZ(ep_num)),
+ readl(S3C_UDC_OTG_DIEPCTL(ep_num)),
+ buf, pktcnt, length);
+
+ return length;
+}
+
+static void complete_rx(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req = NULL;
+ u32 ep_tsr = 0, xfer_size = 0, xfer_length, is_short = 0;
+
+ if (list_empty(&ep->queue)) {
+ DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n",
+ __func__, ep_num);
+ return;
+
+ }
+
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ ep_tsr = readl(S3C_UDC_OTG_DOEPTSIZ(ep_num));
+
+ if (ep_num == EP0_CON)
+ xfer_size = (ep_tsr & 0x7f);
+
+ else
+ xfer_size = (ep_tsr & 0x7fff);
+
+ __dma_single_cpu_to_dev(req->req.buf, req->req.length, DMA_FROM_DEVICE);
+ xfer_length = req->req.length - xfer_size;
+ req->req.actual += min(xfer_length, req->req.length - req->req.actual);
+ is_short = (xfer_length < ep->ep.maxpacket);
+
+ DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, "
+ "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n",
+ __func__, ep_num, req->req.actual, req->req.length,
+ is_short, ep_tsr, xfer_size);
+
+ if (is_short || req->req.actual == xfer_length) {
+ if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) {
+ DEBUG_OUT_EP(" => Send ZLP\n");
+ dev->ep0state = WAIT_FOR_SETUP;
+ s3c_udc_ep0_zlp();
+
+ } else {
+ done(ep, req, 0);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ DEBUG_OUT_EP("%s: Next Rx request start...\n", __func__);
+ setdma_rx(ep, req);
+ }
+ }
+ }
+}
+
+static void complete_tx(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req;
+ u32 ep_tsr = 0, xfer_size = 0, xfer_length, is_short = 0;
+ u32 last;
+
+ if (list_empty(&ep->queue)) {
+ DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n",
+ __func__, ep_num);
+ return;
+
+ }
+
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ if (dev->ep0state == DATA_STATE_XMIT) {
+ DEBUG_IN_EP("%s: ep_num = %d, ep0stat == DATA_STATE_XMIT\n",
+ __func__, ep_num);
+
+ last = write_fifo_ep0(ep, req);
+
+ if (last)
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return;
+ }
+
+ ep_tsr = readl(S3C_UDC_OTG_DIEPTSIZ(ep_num));
+
+ if (ep_num == EP0_CON)
+ xfer_size = (ep_tsr & 0x7f);
+ else
+ xfer_size = (ep_tsr & 0x7fff);
+
+ req->req.actual = req->req.length - xfer_size;
+ xfer_length = req->req.length - xfer_size;
+ req->req.actual += min(xfer_length, req->req.length - req->req.actual);
+ is_short = (xfer_length < ep->ep.maxpacket);
+
+ DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, "
+ "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n",
+ __func__, ep_num, req->req.actual, req->req.length,
+ is_short, ep_tsr, xfer_size);
+
+ if (req->req.actual == req->req.length) {
+ done(ep, req, 0);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ DEBUG_IN_EP("%s: Next Tx request start...\n", __func__);
+ setdma_tx(ep, req);
+ }
+ }
+}
+static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req;
+
+ DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n", __func__, req);
+
+ if (ep_is_in(ep))
+ setdma_tx(ep, req);
+ else
+ setdma_rx(ep, req);
+ } else {
+ DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num);
+
+ return;
+ }
+
+}
+
+static void process_ep_in_intr(struct s3c_udc *dev)
+{
+ u32 ep_intr, ep_intr_status;
+ u8 ep_num = 0;
+
+ ep_intr = readl(S3C_UDC_OTG_DAINT);
+ DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n",
+ __func__, ep_intr);
+
+ ep_intr &= DAINT_MASK;
+
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ ep_intr_status = readl(S3C_UDC_OTG_DIEPINT(ep_num));
+ DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
+
+ /* Interrupt Clear */
+ writel(ep_intr_status, S3C_UDC_OTG_DIEPINT(ep_num));
+
+ if (ep_intr_status & TRANSFER_DONE) {
+ complete_tx(dev, ep_num);
+
+ if (ep_num == 0) {
+ if (dev->ep0state == WAIT_FOR_SETUP)
+ s3c_udc_pre_setup();
+
+ /* continue transfer after set_clear_halt for DMA mode */
+ if (clear_feature_flag == 1) {
+ s3c_udc_check_tx_queue(dev, clear_feature_num);
+ clear_feature_flag = 0;
+ }
+ }
+ }
+ }
+ ep_num++;
+ ep_intr >>= 1;
+ }
+
+}
+
+static void process_ep_out_intr(struct s3c_udc *dev)
+{
+ u32 ep_intr, ep_intr_status, ep_ctrl;
+ u8 ep_num = 0;
+
+ ep_intr = readl(S3C_UDC_OTG_DAINT);
+ DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n",
+ __func__, ep_intr);
+
+ ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK;
+
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ ep_intr_status = readl(S3C_UDC_OTG_DOEPINT(ep_num));
+ DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
+
+ /* Interrupt Clear */
+ writel(ep_intr_status, S3C_UDC_OTG_DOEPINT(ep_num));
+
+ if (ep_num == 0) {
+ if (ep_intr_status & CTRL_OUT_EP_SETUP_PHASE_DONE) {
+ DEBUG_OUT_EP("\tSETUP packet(transaction) arrived\n");
+ s3c_handle_ep0(dev);
+ }
+
+ if (ep_intr_status & TRANSFER_DONE) {
+ complete_rx(dev, ep_num);
+
+ writel((3<<29)|(1<<19)|sizeof(struct usb_ctrlrequest),
+ S3C_UDC_OTG_DOEPTSIZ(EP0_CON));
+ writel(virt_to_phys(&usb_ctrl),
+ S3C_UDC_OTG_DOEPDMA(EP0_CON));
+
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_SNAK,
+ S3C_UDC_OTG_DOEPCTL(EP0_CON));
+ }
+
+ } else {
+ if (ep_intr_status & TRANSFER_DONE)
+ complete_rx(dev, ep_num);
+ }
+ }
+ ep_num++;
+ ep_intr >>= 1;
+ }
+}
+
+/*
+ * usb client interrupt handler.
+ */
+static irqreturn_t s3c_udc_irq(int irq, void *_dev)
+{
+ struct s3c_udc *dev = _dev;
+ u32 intr_status;
+ u32 usb_status, gintmsk;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ intr_status = readl(S3C_UDC_OTG_GINTSTS);
+ gintmsk = readl(S3C_UDC_OTG_GINTMSK);
+
+ DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x, DAINT : 0x%x, DAINTMSK : 0x%x\n",
+ __func__, intr_status, state_names[dev->ep0state], gintmsk,
+ readl(S3C_UDC_OTG_DAINT), readl(S3C_UDC_OTG_DAINTMSK));
+
+ if (!intr_status) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ if (intr_status & INT_ENUMDONE) {
+ DEBUG_ISR("\tSpeed Detection interrupt\n");
+
+ writel(INT_ENUMDONE, S3C_UDC_OTG_GINTSTS);
+ usb_status = (readl(S3C_UDC_OTG_DSTS) & 0x6);
+
+ if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) {
+ DEBUG_ISR("\t\tFull Speed Detection\n");
+ set_max_pktsize(dev, USB_SPEED_FULL);
+
+ } else {
+ DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n", usb_status);
+ set_max_pktsize(dev, USB_SPEED_HIGH);
+ }
+ }
+
+ if (intr_status & INT_EARLY_SUSPEND) {
+ DEBUG_ISR("\tEarly suspend interrupt\n");
+ writel(INT_EARLY_SUSPEND, S3C_UDC_OTG_GINTSTS);
+ }
+
+ if (intr_status & INT_SUSPEND) {
+ usb_status = readl(S3C_UDC_OTG_DSTS);
+ DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status);
+ writel(INT_SUSPEND, S3C_UDC_OTG_GINTSTS);
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver
+ && dev->driver->suspend) {
+ spin_unlock(&dev->lock);
+ dev->driver->suspend(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+ if (dev->status & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+ DEBUG_ISR("device is under remote wakeup\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_HANDLED;
+ }
+ if (dev->driver) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+#if defined(CONFIG_MACH_SMDKC110) || defined(CONFIG_MACH_SMDKV210)
+ s3c_udc_cable_disconnect(dev);
+#endif
+ }
+
+ if (intr_status & INT_RESUME) {
+ DEBUG_ISR("\tResume interrupt\n");
+ writel(INT_RESUME, S3C_UDC_OTG_GINTSTS);
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver
+ && dev->driver->resume) {
+
+ dev->driver->resume(&dev->gadget);
+ }
+ }
+
+ if (intr_status & INT_RESET) {
+ usb_status = readl(S3C_UDC_OTG_GOTGCTL);
+ DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status);
+ writel(INT_RESET, S3C_UDC_OTG_GINTSTS);
+
+ set_conf_done = 0;
+
+ if ((usb_status & 0xc0000) == (0x3 << 18)) {
+ if (reset_available) {
+ DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", reset_available);
+ stop_activity(dev, dev->driver);
+ reconfig_usbd();
+ dev->ep0state = WAIT_FOR_SETUP;
+ reset_available = 0;
+ s3c_udc_pre_setup();
+ }
+ } else if (!(usb_status & B_SESSION_VALID)) {
+ reset_available = 1;
+ if (dev->udc_enabled) {
+ DEBUG_ISR("Reset without B_SESSION\n");
+ if (dev->driver) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+ }
+ } else {
+ reset_available = 1;
+ DEBUG_ISR("\t\tRESET handling skipped\n");
+ }
+ }
+
+ if (intr_status & INT_IN_EP)
+ process_ep_in_intr(dev);
+
+ if (intr_status & INT_OUT_EP)
+ process_ep_out_intr(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/** Queue one request
+ * Kickstart transfer if needed
+ */
+static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+ u32 ep_num, gintsts;
+
+ req = container_of(_req, struct s3c_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) {
+
+ DEBUG("%s: bad params\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+
+ DEBUG("%s: bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ ep_num = ep_index(ep);
+ dev = ep->dev;
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+
+ DEBUG("%s: bogus device state %p\n", __func__, dev->driver);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p"
+ "Q empty = %d, stopped = %d\n",
+ __func__, _ep->name, ep_is_in(ep) ? "in" : "out",
+ _req, _req->length, _req->buf,
+ list_empty(&ep->queue), ep->stopped);
+
+ if (list_empty(&ep->queue) && !ep->stopped) {
+
+ if (ep_num == 0) {
+ /* EP0 */
+ list_add_tail(&req->queue, &ep->queue);
+ s3c_ep0_kick(dev, ep);
+ req = 0;
+
+ } else if (ep_is_in(ep)) {
+ gintsts = readl(S3C_UDC_OTG_GINTSTS);
+ DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
+
+ if (set_conf_done == 1) {
+ setdma_tx(ep, req);
+ } else {
+ done(ep, req, 0);
+ DEBUG("%s: Not yet Set_configureation, ep_num = %d, req = %p\n",
+ __func__, ep_num, req);
+ req = 0;
+ }
+
+ } else {
+ gintsts = readl(S3C_UDC_OTG_GINTSTS);
+ DEBUG_OUT_EP("%s: ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
+
+ setdma_rx(ep, req);
+ }
+ }
+
+ /* pio or dma irq handler advances the queue. */
+ if (likely(req != 0))
+ list_add_tail(&req->queue, &ep->queue);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/****************************************************************/
+/* End Point 0 related functions */
+/****************************************************************/
+
+/* return: 0 = still running, 1 = completed, negative = errno */
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 max;
+ unsigned count;
+ int is_last;
+
+ max = ep_maxpacket(ep);
+
+ DEBUG_EP0("%s: max = %d\n", __func__, max);
+
+ count = setdma_tx(ep, req);
+
+ /* last packet is usually short (or a zlp) */
+ if (likely(count != max))
+ is_last = 1;
+ else {
+ if (likely(req->req.length != req->req.actual) || req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ }
+
+ DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__,
+ ep->ep.name, count,
+ is_last ? "/L" : "", req->req.length - req->req.actual, req);
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ ep->dev->ep0state = WAIT_FOR_SETUP;
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max)
+{
+ u32 bytes;
+
+ bytes = sizeof(struct usb_ctrlrequest);
+ __dma_single_cpu_to_dev(&usb_ctrl, bytes, DMA_FROM_DEVICE);
+ DEBUG_EP0("%s: bytes=%d, ep_index=%d\n", __func__, bytes, ep_index(ep));
+
+ return bytes;
+}
+
+/**
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function
+ * after it decodes a set address setup packet.
+ */
+static void udc_set_address(struct s3c_udc *dev, unsigned char address)
+{
+ u32 ctrl = readl(S3C_UDC_OTG_DCFG);
+ writel(address << 4 | ctrl, S3C_UDC_OTG_DCFG);
+
+ s3c_udc_ep0_zlp();
+
+ DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
+ __func__, address, readl(S3C_UDC_OTG_DCFG));
+
+ dev->usb_address = address;
+}
+
+static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep)
+{
+ struct s3c_udc *dev;
+ u32 ep_ctrl = 0;
+
+ dev = ep->dev;
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ /* set the disable and stall bits */
+ if (ep_ctrl & DEPCTL_EPENA)
+ ep_ctrl |= DEPCTL_EPDIS;
+
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n",
+ __func__, ep_index(ep), readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+ /*
+ * The application can only set this bit, and the core clears it,
+ * when a SETUP token is received for this endpoint
+ */
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ s3c_udc_pre_setup();
+}
+
+static void s3c_ep0_read(struct s3c_udc *dev)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep = &dev->ep[0];
+ int ret;
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ } else {
+ DEBUG("%s: ---> BUG\n", __func__);
+ BUG();
+ return;
+ }
+
+ DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
+
+ if (req->req.length == 0) {
+ /* zlp for Set_configuration, Set_interface,
+ * or Bulk-Only mass storge reset */
+
+ dev->ep0state = WAIT_FOR_SETUP;
+ set_conf_done = 1;
+ s3c_udc_ep0_zlp();
+ done(ep, req, 0);
+ DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", __func__, usb_ctrl.bRequest);
+ return;
+ }
+
+ ret = setdma_rx(ep, req);
+}
+
+/*
+ * DATA_STATE_XMIT
+ */
+static int s3c_ep0_write(struct s3c_udc *dev)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep = &dev->ep[0];
+ int ret, need_zlp = 0;
+
+ if (list_empty(&ep->queue))
+ req = 0;
+ else
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ if (!req) {
+ DEBUG_EP0("%s: NULL REQ\n", __func__);
+ return 0;
+ }
+
+ DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
+
+ if (req->req.length - req->req.actual == ep0_fifo_size) {
+ /* Next write will end with the packet size, */
+ /* so we need Zero-length-packet */
+ need_zlp = 1;
+ }
+
+ ret = write_fifo_ep0(ep, req);
+
+ if ((ret == 1) && !need_zlp) {
+ /* Last packet */
+ dev->ep0state = WAIT_FOR_SETUP;
+ DEBUG_EP0("%s: finished, waiting for status\n", __func__);
+
+ } else {
+ dev->ep0state = DATA_STATE_XMIT;
+ DEBUG_EP0("%s: not finished\n", __func__);
+ }
+
+ if (need_zlp) {
+ dev->ep0state = DATA_STATE_NEED_ZLP;
+ DEBUG_EP0("%s: Need ZLP!\n", __func__);
+ }
+
+ return 1;
+}
+
+u16 g_status __attribute__((aligned(8)));
+
+static int s3c_udc_get_status(struct s3c_udc *dev,
+ struct usb_ctrlrequest *crq)
+{
+ u8 ep_num = crq->wIndex & 0x7F;
+ u32 ep_ctrl;
+
+ DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__);
+
+ switch (crq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ g_status = 0;
+ DEBUG_SETUP("\tGET_STATUS: USB_RECIP_INTERFACE, g_stauts = %d\n", g_status);
+ break;
+
+ case USB_RECIP_DEVICE:
+ /* update device status */
+ g_status = dev->status;
+ DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", g_status);
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ if (crq->wLength > 2) {
+ DEBUG_SETUP("\tGET_STATUS: Not support EP or wLength\n");
+ return 1;
+ }
+
+ g_status = dev->ep[ep_num].stopped;
+ DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", g_status);
+
+ break;
+
+ default:
+ return 1;
+ }
+ __dma_single_cpu_to_dev(&g_status, 2, DMA_TO_DEVICE);
+
+ writel(virt_to_phys(&g_status), S3C_UDC_OTG_DIEPDMA(EP0_CON));
+ writel((1<<19)|(2<<0), S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return 0;
+}
+
+void s3c_udc_ep_set_stall(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0;
+
+ ep_num = ep_index(ep);
+ DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+
+ /* set the disable and stall bits */
+ if (ep_ctrl & DEPCTL_EPENA)
+ ep_ctrl |= DEPCTL_EPDIS;
+
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+ DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+
+ } else {
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+ /* set the stall bit */
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+ DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+ }
+
+ return;
+}
+
+void s3c_udc_ep_clear_stall(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0;
+
+ ep_num = ep_index(ep);
+ DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+
+ /* clear stall bit */
+ ep_ctrl &= ~DEPCTL_STALL;
+
+ /*
+ * USB Spec 9.4.5: For endpoints using data toggle, regardless
+ * of whether an endpoint has the Halt feature set, a
+ * ClearFeature(ENDPOINT_HALT) request always results in the
+ * data toggle being reinitialized to DATA0.
+ */
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+ || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+ }
+
+ writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+ DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+
+ } else {
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+ /* clear stall bit */
+ ep_ctrl &= ~DEPCTL_STALL;
+
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+ || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+ }
+
+ writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+ DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+ }
+
+ return;
+}
+
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value)
+{
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+
+ if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON ||
+ ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) {
+ DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name);
+ return -EINVAL;
+ }
+
+ /* Attempt to halt IN ep will fail if any transfer requests
+ * are still queue */
+ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+ DEBUG("%s: %s queue not empty, req = %p\n",
+ __func__, ep->ep.name,
+ list_entry(ep->queue.next, struct s3c_request, queue));
+
+ return -EAGAIN;
+ }
+
+ dev = ep->dev;
+ DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (value == 0) {
+ ep->stopped = 0;
+ s3c_udc_ep_clear_stall(ep);
+ } else {
+ ep->stopped = 1;
+ s3c_udc_ep_set_stall(ep);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+void s3c_udc_ep_activate(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0, daintmsk = 0;
+
+ ep_num = ep_index(ep);
+
+ /* Read DEPCTLn register */
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+ daintmsk = 1 << ep_num;
+ } else {
+ ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+ daintmsk = (1 << ep_num) << DAINT_OUT_BIT;
+ }
+
+ DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n",
+ __func__, ep_num, ep_ctrl, ep_is_in(ep));
+
+ /* If the EP is already active don't change the EP Control
+ * register. */
+ if (!(ep_ctrl & DEPCTL_USBACTEP)) {
+ ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | (ep->bmAttributes << DEPCTL_TYPE_BIT);
+ ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | (ep->ep.maxpacket << DEPCTL_MPS_BIT);
+ ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP);
+
+ if (ep_is_in(ep)) {
+ writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+ DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+ } else {
+ writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+ DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+ }
+ }
+
+ /* Unmask EP Interrtupt */
+ writel(readl(S3C_UDC_OTG_DAINTMSK)|daintmsk, S3C_UDC_OTG_DAINTMSK);
+ DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(S3C_UDC_OTG_DAINTMSK));
+
+}
+
+static int s3c_udc_clear_feature(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ u8 ep_num;
+ struct s3c_udc *dev = the_controller;
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+
+ DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n",
+ __func__, ep_num, ep_is_in(ep), clear_feature_flag);
+
+ if (usb_ctrl.wLength != 0) {
+ DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n");
+ return 1;
+ }
+
+ switch (usb_ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (usb_ctrl.wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_REMOTE_WAKEUP\n");
+ printk(KERN_INFO "%s:: USB_DEVICE_REMOTE_WAKEUP\n", __func__);
+ dev->status &= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n");
+ /** @todo Add CLEAR_FEATURE for TEST modes. */
+ break;
+ }
+
+ s3c_udc_ep0_zlp();
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ DEBUG_SETUP("\tCLEAR_FEATURE: USB_RECIP_ENDPOINT, wValue = %d\n",
+ usb_ctrl.wValue);
+
+ if (usb_ctrl.wValue == USB_ENDPOINT_HALT) {
+ if (ep_num == 0) {
+ s3c_udc_ep0_set_stall(ep);
+ return 0;
+ }
+
+ s3c_udc_ep0_zlp();
+
+ s3c_udc_ep_clear_stall(ep);
+ s3c_udc_ep_activate(ep);
+ ep->stopped = 0;
+
+ clear_feature_num = ep_num;
+ clear_feature_flag = 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/* Set into the test mode for Test Mode set_feature request */
+static inline void set_test_mode(void)
+{
+ u32 ep_ctrl, dctl;
+ u8 test_selector = (usb_ctrl.wIndex>>8) & TEST_SELECTOR_MASK;
+
+ if (test_selector > 0 && test_selector < 6) {
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ writel(1<<19 | 0<<0, S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK|EP0_CON<<DEPCTL_NEXT_EP_BIT , S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ }
+
+ switch (test_selector) {
+ case TEST_J_SEL:
+ /* some delay is necessary like printk() or udelay() */
+ printk(KERN_INFO "Test mode selector in set_feature request is TEST J\n");
+
+ dctl = readl(S3C_UDC_OTG_DCTL);
+ writel((dctl&~(TEST_CONTROL_MASK))|TEST_J_MODE, S3C_UDC_OTG_DCTL);
+ break;
+ case TEST_K_SEL:
+ /* some delay is necessary like printk() or udelay() */
+ printk(KERN_INFO "Test mode selector in set_feature request is TEST K\n");
+
+ dctl = readl(S3C_UDC_OTG_DCTL);
+ writel((dctl&~(TEST_CONTROL_MASK))|TEST_K_MODE, S3C_UDC_OTG_DCTL);
+ break;
+ case TEST_SE0_NAK_SEL:
+ /* some delay is necessary like printk() or udelay() */
+ printk(KERN_INFO "Test mode selector in set_feature request is TEST SE0 NAK\n");
+
+ dctl = readl(S3C_UDC_OTG_DCTL);
+ writel((dctl&~(TEST_CONTROL_MASK))|TEST_SE0_NAK_MODE, S3C_UDC_OTG_DCTL);
+ break;
+ case TEST_PACKET_SEL:
+ /* some delay is necessary like printk() or udelay() */
+ printk(KERN_INFO "Test mode selector in set_feature request is TEST PACKET\n");
+ __dma_single_cpu_to_dev(test_pkt, TEST_PKT_SIZE, DMA_TO_DEVICE);
+ writel(virt_to_phys(test_pkt), S3C_UDC_OTG_DIEPDMA(EP0_CON));
+
+ ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ writel(1<<19 | TEST_PKT_SIZE<<0, S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK|EP0_CON<<DEPCTL_NEXT_EP_BIT, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+ dctl = readl(S3C_UDC_OTG_DCTL);
+ writel((dctl&~(TEST_CONTROL_MASK))|TEST_PACKET_MODE, S3C_UDC_OTG_DCTL);
+ break;
+ case TEST_FORCE_ENABLE_SEL:
+ /* some delay is necessary like printk() or udelay() */
+ printk(KERN_INFO "Test mode selector in set_feature request is TEST FORCE ENABLE\n");
+
+ dctl = readl(S3C_UDC_OTG_DCTL);
+ writel((dctl&~(TEST_CONTROL_MASK))|TEST_FORCE_ENABLE_MODE, S3C_UDC_OTG_DCTL);
+ break;
+ }
+}
+
+static int s3c_udc_set_feature(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ u8 ep_num;
+ struct s3c_udc *dev = the_controller;
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+
+ DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", __func__, ep_num);
+
+ if (usb_ctrl.wLength != 0) {
+ DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n");
+ return 1;
+ }
+
+ switch (usb_ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (usb_ctrl.wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_REMOTE_WAKEUP\n");
+ printk(KERN_INFO "%s:: USB_DEVICE_REMOTE_WAKEUP\n", __func__);
+ dev->status |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_TEST_MODE\n");
+ set_test_mode();
+ break;
+
+ case USB_DEVICE_B_HNP_ENABLE:
+ DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+ break;
+
+ case USB_DEVICE_A_HNP_SUPPORT:
+ /* RH port supports HNP */
+ DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
+ break;
+
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ /* other RH port does */
+ DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+ break;
+ }
+
+ s3c_udc_ep0_zlp();
+ return 0;
+
+ case USB_RECIP_INTERFACE:
+ DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n");
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n");
+ if (usb_ctrl.wValue == USB_ENDPOINT_HALT) {
+ if (ep_num == 0) {
+ s3c_udc_ep0_set_stall(ep);
+ return 0;
+ }
+ ep->stopped = 1;
+ s3c_udc_ep_set_stall(ep);
+ }
+
+ s3c_udc_ep0_zlp();
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * WAIT_FOR_SETUP (OUT_PKT_RDY)
+ */
+static void s3c_ep0_setup(struct s3c_udc *dev)
+{
+ struct s3c_ep *ep = &dev->ep[0];
+ int i, bytes, is_in;
+ u8 ep_num;
+
+ /* Nuke all previous transfers */
+ nuke(ep, -EPROTO);
+
+ /* read control req from fifo (8 bytes) */
+ bytes = s3c_fifo_read(ep, (u32 *)&usb_ctrl, 8);
+
+ DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x"
+ "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n",
+ __func__, usb_ctrl.bRequestType,
+ (usb_ctrl.bRequestType & USB_DIR_IN) ? "IN" : "OUT", usb_ctrl.bRequest,
+ usb_ctrl.wLength, usb_ctrl.wValue, usb_ctrl.wIndex);
+
+ if (usb_ctrl.bRequest == GET_MAX_LUN_REQUEST && usb_ctrl.wLength != 1) {
+ DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid wLength = %d, setup returned\n",
+ __func__, usb_ctrl.wLength);
+
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return;
+ } else if (usb_ctrl.bRequest == BOT_RESET_REQUEST && usb_ctrl.wLength != 0) {
+ /* Bulk-Only *mass storge reset of class-specific request */
+ DEBUG_SETUP("\t%s:BOT Rest:invalid wLength = %d, setup returned\n",
+ __func__, usb_ctrl.wLength);
+
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return;
+ }
+
+ /* Set direction of EP0 */
+ if (likely(usb_ctrl.bRequestType & USB_DIR_IN)) {
+ ep->bEndpointAddress |= USB_DIR_IN;
+ is_in = 1;
+
+ } else {
+ ep->bEndpointAddress &= ~USB_DIR_IN;
+ is_in = 0;
+ }
+ /* cope with automagic for some standard requests. */
+ dev->req_std = (usb_ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD;
+ dev->req_config = 0;
+ dev->req_pending = 1;
+
+ /* Handle some SETUP packets ourselves */
+ switch (usb_ctrl.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n",
+ __func__, usb_ctrl.wValue);
+
+ if (usb_ctrl.bRequestType
+ != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+
+ udc_set_address(dev, usb_ctrl.wValue);
+ return;
+
+ case USB_REQ_SET_CONFIGURATION:
+ DEBUG_SETUP("============================================\n");
+ DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n",
+ __func__, usb_ctrl.wValue);
+
+ if (usb_ctrl.bRequestType == USB_RECIP_DEVICE) {
+ reset_available = 1;
+ dev->req_config = 1;
+ }
+
+#if defined(CONFIG_MACH_SMDKC110) || defined(CONFIG_MACH_SMDKV210)
+ s3c_udc_cable_connect(dev);
+#endif
+ break;
+
+ case USB_REQ_GET_DESCRIPTOR:
+ DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", __func__);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n",
+ __func__, usb_ctrl.wValue);
+
+ if (usb_ctrl.bRequestType == USB_RECIP_INTERFACE) {
+ reset_available = 1;
+ dev->req_config = 1;
+ }
+ break;
+
+ case USB_REQ_GET_CONFIGURATION:
+ DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", __func__);
+ break;
+
+ case USB_REQ_GET_STATUS:
+ if (dev->req_std) {
+ if (!s3c_udc_get_status(dev, &usb_ctrl))
+ return;
+ }
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ ep_num = usb_ctrl.wIndex & 0x7f;
+
+ if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep))
+ return;
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ ep_num = usb_ctrl.wIndex & 0x7f;
+
+ if (!s3c_udc_set_feature(&dev->ep[ep_num].ep))
+ return;
+ break;
+
+ default:
+ DEBUG_SETUP("%s: *** Default of usb_ctrl.bRequest=0x%x happened.\n",
+ __func__, usb_ctrl.bRequest);
+ break;
+ }
+
+ if (likely(dev->driver)) {
+ /* device-2-host (IN) or no data setup command,
+ * process immediately */
+ DEBUG_SETUP("%s: usb_ctrlrequest will be passed to fsg_setup()\n", __func__);
+
+ spin_unlock(&dev->lock);
+ i = dev->driver->setup(&dev->gadget, &usb_ctrl);
+ spin_lock(&dev->lock);
+
+ if (i < 0) {
+ if (dev->req_config) {
+ DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n",
+ (u32)&usb_ctrl.bRequest, i);
+ return;
+ }
+
+ /* setup processing failed, force stall */
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ DEBUG_SETUP("\tdev->driver->setup failed (%d), bRequest = %d\n",
+ i, usb_ctrl.bRequest);
+
+
+ } else if (dev->req_pending) {
+ dev->req_pending = 0;
+ DEBUG_SETUP("\tdev->req_pending...\n");
+ }
+
+ DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]);
+
+ }
+}
+
+/*
+ * handle ep0 interrupt
+ */
+static void s3c_handle_ep0(struct s3c_udc *dev)
+{
+ if (dev->ep0state == WAIT_FOR_SETUP) {
+ DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__);
+ s3c_ep0_setup(dev);
+
+ } else {
+ DEBUG_OUT_EP("%s: strange state!!(state = %s)\n",
+ __func__, state_names[dev->ep0state]);
+ }
+}
+
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep)
+{
+ DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep));
+ if (ep_is_in(ep)) {
+ dev->ep0state = DATA_STATE_XMIT;
+ s3c_ep0_write(dev);
+
+ } else {
+ dev->ep0state = DATA_STATE_RECV;
+ s3c_ep0_read(dev);
+ }
+}
+
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index b5a30fe..f30c307 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -249,11 +249,13 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
goto enomem;
}
+#ifndef CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED
/* Some platforms perform better when IP packets are aligned,
* but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values.
*/
skb_reserve(skb, NET_IP_ALIGN);
+#endif
req->buf = skb->data;
req->length = size;
@@ -483,7 +485,10 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
list_add(&req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
dev_kfree_skb_any(skb);
-
+#ifdef CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED
+ if (req->buf != skb->data)
+ kfree(req->buf);
+#endif
atomic_dec(&dev->tx_qlen);
if (netif_carrier_ok(dev->net))
netif_wake_queue(dev->net);
@@ -577,7 +582,21 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
length = skb->len;
}
+
+#ifdef CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED
+ if ((int)skb->data & 3) {
+ req->buf = kmalloc(skb->len, GFP_ATOMIC);
+ if (!req->buf)
+ goto drop;
+ memcpy((void *)req->buf, (void *)skb->data, skb->len);
+ }
+ else {
+ req->buf = skb->data;
+ }
+#else
req->buf = skb->data;
+#endif
+
req->context = skb;
req->complete = tx_complete;
@@ -618,6 +637,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb);
drop:
dev->net->stats.tx_dropped++;
+#ifdef CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED
+ if (req->buf != skb->data)
+ kfree(req->buf);
+#endif
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
@@ -823,12 +846,6 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
SET_ETHTOOL_OPS(net, &ops);
- /* two kinds of host-initiated state changes:
- * - iff DATA transfer is active, carrier is "on"
- * - tx queueing enabled if open *and* carrier is "on"
- */
- netif_carrier_off(net);
-
dev->gadget = g;
SET_NETDEV_DEV(net, &g->dev);
SET_NETDEV_DEVTYPE(net, &gadget_type);
@@ -842,6 +859,12 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
the_dev = dev;
+
+ /* two kinds of host-initiated state changes:
+ * - iff DATA transfer is active, carrier is "on"
+ * - tx queueing enabled if open *and* carrier is "on"
+ */
+ netif_carrier_off(net);
}
return status;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ab085f1..67c452d 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -578,3 +578,19 @@ config USB_OCTEON_OHCI
config USB_OCTEON2_COMMON
bool
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
+
+config USB_S3C_OTG_HOST
+ tristate "S3C USB OTG Host support"
+ depends on USB && (PLAT_S3C64XX || PLAT_S5P)
+ help
+ Samsung's S3C64XX processors include high speed USB OTG2.0
+ controller. It has 15 configurable endpoints, as well as
+ endpoint zero (for control transfers).
+
+ This driver support only OTG Host role. If you want to use
+ OTG Device role, select USB Gadget support and S3C OTG Device.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "s3c_otg_hcd" and force all
+ drivers to also be dynamically linked.
+
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 624a362..de59025 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,6 +15,7 @@ xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
obj-$(CONFIG_USB_WHCI_HCD) += whci/
+obj-$(CONFIG_USB_S3C_OTG_HOST) += s3c-otg/
obj-$(CONFIG_PCI) += pci-quirks.o
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 3940d28..f768314 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -144,14 +144,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
hcd->has_tt = 1;
tdi_reset(ehci);
}
- if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) {
- /* EHCI #1 or #2 on 6 Series/C200 Series chipset */
- if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) {
- ehci_info(ehci, "broken D3 during system sleep on ASUS\n");
- hcd->broken_pci_sleep = 1;
- device_set_wakeup_capable(&pdev->dev, false);
- }
- }
break;
case PCI_VENDOR_ID_TDI:
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
@@ -365,7 +357,9 @@ static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
{
return pdev->class == PCI_CLASS_SERIAL_USB_EHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL &&
- pdev->device == 0x1E26;
+ (pdev->device == 0x1E26 ||
+ pdev->device == 0x8C2D ||
+ pdev->device == 0x8C26);
}
static void ehci_enable_xhci_companion(void)
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index e4dd26a..08fdcfa 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -130,9 +130,17 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
else {
qtd = list_entry (qh->qtd_list.next,
struct ehci_qtd, qtd_list);
- /* first qtd may already be partially processed */
- if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {
+ qh->hw->hw_qtd_next = qtd->hw_next;
qtd = NULL;
+ }
}
if (qtd)
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 7fec8bd..3f623fb 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -73,7 +73,9 @@
#define NB_PIF0_PWRDOWN_1 0x01100013
#define USB_INTEL_XUSB2PR 0xD0
+#define USB_INTEL_USB2PRM 0xD4
#define USB_INTEL_USB3_PSSEN 0xD8
+#define USB_INTEL_USB3PRM 0xDC
static struct amd_chipset_info {
struct pci_dev *nb_dev;
@@ -541,7 +543,14 @@ static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = {
/* Pegatron Lucid (Ordissimo AIRIS) */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "M11JB"),
- DMI_MATCH(DMI_BIOS_VERSION, "Lucid-GE-133"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
+ },
+ },
+ {
+ /* Pegatron Lucid (Ordissimo) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "Ordissimo"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
},
},
{ }
@@ -711,12 +720,28 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,
return -ETIMEDOUT;
}
-bool usb_is_intel_switchable_xhci(struct pci_dev *pdev)
+#define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI 0x8C31
+
+bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev)
{
return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI;
}
+
+/* The Intel Lynx Point chipset also has switchable ports. */
+bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev)
+{
+ return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
+ pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI;
+}
+
+bool usb_is_intel_switchable_xhci(struct pci_dev *pdev)
+{
+ return usb_is_intel_ppt_switchable_xhci(pdev) ||
+ usb_is_intel_lpt_switchable_xhci(pdev);
+}
EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
/*
@@ -739,12 +764,21 @@ EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
*/
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
{
+#if defined(CONFIG_USB_XHCI_HCD) || defined(CONFIG_USB_XHCI_HCD_MODULE)
u32 ports_available;
- ports_available = 0xffffffff;
+ /* Read USB3PRM, the USB 3.0 Port Routing Mask Register
+ * Indicate the ports that can be changed from OS.
+ */
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB3PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable ports to enable SuperSpeed: 0x%x\n",
+ ports_available);
+
/* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
- * Register, to turn on SuperSpeed terminations for all
- * available ports.
+ * Register, to turn on SuperSpeed terminations for the
+ * switchable ports.
*/
pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
cpu_to_le32(ports_available));
@@ -754,7 +788,16 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
"under xHCI: 0x%x\n", ports_available);
- ports_available = 0xffffffff;
+ /* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
+ * Indicate the USB 2.0 ports to be controlled by the xHCI host.
+ */
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB2PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable USB 2.0 ports to hand over to xCHI: 0x%x\n",
+ ports_available);
+
/* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to
* switch the USB 2.0 power and data lines over to the xHCI
* host.
@@ -766,9 +809,28 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
&ports_available);
dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over "
"to xHCI: 0x%x\n", ports_available);
+#else
+ /* Don't switchover the ports if the user hasn't compiled the xHCI
+ * driver. Otherwise they will see "dead" USB ports that don't power
+ * the devices.
+ */
+ dev_warn(&xhci_pdev->dev,
+ "CONFIG_USB_XHCI_HCD is turned off, "
+ "defaulting to EHCI.\n");
+ dev_warn(&xhci_pdev->dev,
+ "USB 3.0 devices will work at USB 2.0 speeds.\n");
+#endif /* CONFIG_USB_XHCI_HCD || CONFIG_USB_XHCI_HCD_MODULE */
+
}
EXPORT_SYMBOL_GPL(usb_enable_xhci_ports);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+{
+ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, 0x0);
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, 0x0);
+}
+EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+
/**
* PCI Quirks for xHCI.
*
@@ -784,12 +846,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
void __iomem *op_reg_base;
u32 val;
int timeout;
+ int len = pci_resource_len(pdev, 0);
if (!mmio_resource_enabled(pdev, 0))
return;
- base = ioremap_nocache(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ base = ioremap_nocache(pci_resource_start(pdev, 0), len);
if (base == NULL)
return;
@@ -799,9 +861,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
*/
ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
do {
+ if ((ext_cap_offset + sizeof(val)) > len) {
+ /* We're reading garbage from the controller */
+ dev_warn(&pdev->dev,
+ "xHCI controller failing to respond");
+ return;
+ }
+
if (!ext_cap_offset)
/* We've reached the end of the extended capabilities */
goto hc_init;
+
val = readl(base + ext_cap_offset);
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
break;
@@ -832,9 +902,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
/* Disable any BIOS SMIs and clear all SMI events*/
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
+hc_init:
if (usb_is_intel_switchable_xhci(pdev))
usb_enable_xhci_ports(pdev);
-hc_init:
+
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
/* Wait for the host controller to be ready before writing any
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index b1002a8..7f69a39 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -10,10 +10,12 @@ void usb_amd_quirk_pll_disable(void);
void usb_amd_quirk_pll_enable(void);
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev);
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
#else
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_amd_dev_put(void) {}
+static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
#endif /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */
diff --git a/drivers/usb/host/s3c-otg/Makefile b/drivers/usb/host/s3c-otg/Makefile
new file mode 100644
index 0000000..37674c8
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for USB OTG Host Controller Drivers
+#
+
+obj-$(CONFIG_USB_S3C_OTG_HOST) += s3c_otg_hcd.o
+
+s3c_otg_hcd-objs += s3c-otg-hcdi-driver.o s3c-otg-hcdi-hcd.o
+s3c_otg_hcd-objs += s3c-otg-transfer-common.o s3c-otg-transfer-nonperiodic.o \
+ s3c-otg-transfer-periodic.o
+s3c_otg_hcd-objs += s3c-otg-scheduler-ischeduler.o s3c-otg-scheduler-scheduler.o \
+ s3c-otg-scheduler-readyq.o
+s3c_otg_hcd-objs += s3c-otg-oci.o
+s3c_otg_hcd-objs += s3c-otg-transferchecker-common.o \
+ s3c-otg-transferchecker-control.o \
+ s3c-otg-transferchecker-bulk.o \
+ s3c-otg-transferchecker-interrupt.o
+s3c_otg_hcd-objs += s3c-otg-isr.o
+s3c_otg_hcd-objs += s3c-otg-roothub.o
diff --git a/drivers/usb/host/s3c-otg/debug-mem.c b/drivers/usb/host/s3c-otg/debug-mem.c
new file mode 100644
index 0000000..de85853
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/debug-mem.c
@@ -0,0 +1,55 @@
+#include "s3c-otg-hcdi-hcd.h"
+#include "debug-mem.h"
+
+#define MAX_ALLOCS 1024
+
+typedef void *ElemType;
+
+static ElemType alloced[MAX_ALLOCS];
+static int numalloced = 0;
+
+#define fail(args...) ( printk(args), dump_stack() )
+
+void debug_alloc(void *addr) {
+ ElemType *freeloc = NULL;
+ ElemType *c = alloced;
+ int i;
+
+ if(!addr)
+ fail("MEMD alloc of NULL");
+
+ for(i = 0; i < numalloced; i++, c++) {
+ if(*c == NULL && freeloc == NULL)
+ freeloc = c;
+ else if(*c == addr)
+ fail("MEMD multiple allocs of %p", addr);
+ }
+
+ if(freeloc)
+ *freeloc = addr;
+ else {
+ if(numalloced >= MAX_ALLOCS)
+ fail("MEMD too many allocs");
+ else {
+ alloced[numalloced++] = addr;
+ }
+ }
+}
+
+void debug_free(void *addr) {
+ ElemType *c = alloced;
+ int i;
+
+ if(!addr)
+ fail("free of NULL");
+
+ for(i = 0; i < numalloced; i++, c++) {
+ if(*c == addr) {
+ *c = NULL;
+ return;
+ }
+ }
+
+ fail("MEMD freed addr %p was never alloced\n", addr);
+}
+
diff --git a/drivers/usb/host/s3c-otg/debug-mem.h b/drivers/usb/host/s3c-otg/debug-mem.h
new file mode 100644
index 0000000..4164793
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/debug-mem.h
@@ -0,0 +1,9 @@
+#ifndef __DEBUGMEM_H
+#define __DEBUGMEM_H
+
+// kevinh quick hack to see if the s3c stuff is doing memory properly
+
+void debug_alloc(void *addr);
+void debug_free(void *addr);
+
+#endif
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-common.h b/drivers/usb/host/s3c-otg/s3c-otg-common-common.h
new file mode 100644
index 0000000..720154e
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-common-common.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-common-common.h
+ * @brief it includes common header files for all modules \n
+ * @version
+ * ex)-# Jun 11,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_COMMON_COMMON_H_
+#define _S3C_OTG_COMMON_COMMON_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-errorcode.h"
+#include <linux/errno.h>
+#include <linux/usb.h>
+
+//Define OS
+#define LINUX 1
+
+//Kernel Version
+#define KERNEL_2_6_21
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _S3C_OTG_COMMON_COMMON_H_ */
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-const.h b/drivers/usb/host/s3c-otg/s3c-otg-common-const.h
new file mode 100644
index 0000000..f0f5cb9
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-common-const.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : s3c-otg-common-const.h
+ * [Description] : The Header file defines constants to be used at sub-modules of S3C6400HCD.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created s3c-otg-common-const.h file and defines some constants.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _CONST_TYPE_DEF_H_
+#define _CONST_TYPE_DEF_H_
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+#include "s3c-otg-common-common.h"
+
+//#include "s3c-otg-common-regdef.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @def OTG_PORT_NUMBER
+ *
+ * @brief write~ description
+ *
+ * describe in detail
+ */
+#define OTG_PORT_NUMBER 0
+
+
+
+//Defines Stages of Control Transfer
+#define SETUP_STAGE 1
+#define DATA_STAGE 2
+#define STATUS_STAGE 3
+#define COMPLETE_STAGE 4
+
+
+//Defines Direction of Endpoint
+#define EP_IN 1
+#define EP_OUT 0
+
+//Define speed of USB Device
+#define LOW_SPEED_OTG 2
+#define FULL_SPEED_OTG 1
+#define HIGH_SPEED_OTG 0
+#define SUPER_SPEED_OTG 3
+
+//Define multiple count of packet in periodic transfer.
+#define MULTI_COUNT_ZERO 0
+#define MULTI_COUNT_ONE 1
+#define MULTI_COUNT_TWO 2
+
+//Define USB Transfer Types.
+#define CONTROL_TRANSFER 0
+#define ISOCH_TRANSFER 1
+#define BULK_TRANSFER 2
+#define INT_TRANSFER 3
+
+#define BULK_TIMEOUT 300
+
+//Defines PID
+#define DATA0 0
+#define DATA1 2
+#define DATA2 1
+#define MDATA 3
+#define SETUP 3
+
+//Defines USB Transfer Request Size on USB2.0
+#define USB_20_STAND_DEV_REQUEST_SIZE 8
+//Define Max Channel Number
+#define MAX_CH_NUMBER 16
+//Define Channel Number
+#define CH_0 0
+#define CH_1 1
+#define CH_2 2
+#define CH_3 3
+#define CH_4 4
+#define CH_5 5
+#define CH_6 6
+#define CH_7 7
+#define CH_8 8
+#define CH_9 9
+#define CH_10 10
+#define CH_11 11
+#define CH_12 12
+#define CH_13 13
+#define CH_14 14
+#define CH_15 15
+#define CH_NONE 20
+
+
+// define the Constant for result of processing the USB Transfer.
+#define RE_TRANSMIT 1
+#define RE_SCHEDULE 2
+#define DE_ALLOCATE 3
+#define NO_ACTION 4
+
+//define the threshold value to retransmit USB Transfer
+#define RETRANSMIT_THRESHOLD 2
+
+//define the maximum size of data to be tranferred through channel.
+#define MAX_CH_TRANSFER_SIZE 65536//65535
+
+//define Max Frame Number which Synopsys OTG suppports.
+#define MAX_FRAME_NUMBER 0x3FFF
+// Channel Interrupt Status
+#define CH_STATUS_DataTglErr (0x1<<10)
+#define CH_STATUS_FrmOvrun (0x1<<9)
+#define CH_STATUS_BblErr (0x1<<8)
+#define CH_STATUS_XactErr (0x1<<7)
+#define CH_STATUS_NYET (0x1<<6)
+#define CH_STATUS_ACK (0x1<<5)
+#define CH_STATUS_NAK (0x1<<4)
+#define CH_STATUS_STALL (0x1<<3)
+#define CH_STATUS_AHBErr (0x1<<2)
+#define CH_STATUS_ChHltd (0x1<<1)
+#define CH_STATUS_XferCompl (0x1<<0)
+#define CH_STATUS_ALL 0x7FF
+
+
+//Define USB Transfer Flag..
+//typedef URB_SHORT_NOT_OK USB_TRANS_FLAG_NOT_SHORT;
+//typedef URB_ISO_ASAP USB_TRANS_FLAG_ISO_ASYNCH;
+
+#define USB_TRANS_FLAG_NOT_SHORT URB_SHORT_NOT_OK
+#define USB_TRANS_FLAG_ISO_ASYNCH URB_ISO_ASAP
+
+
+#define HFNUM_MAX_FRNUM 0x3FFF
+#define SCHEDULE_SLOT 10
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h b/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h
new file mode 100644
index 0000000..c31e8ab
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h
@@ -0,0 +1,811 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : s3c-otg-common-datastruct.h
+ * [Description] : The Header file defines Data Structures to be used at sub-modules of S3C6400HCD.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines Data Structure to be managed by Transfer.
+ * (2) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com )
+ * - modifying ED structure
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _DATA_STRUCT_DEF_H
+#define _DATA_STRUCT_DEF_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+#include <linux/wakelock.h>
+#include <plat/s5p-otghost.h>
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-hcdi-list.h"
+
+//#include "s3c-otg-common-regdef.h"
+//#include "s3c-otg-common-errorcode.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef union _hcintmsk_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ unsigned xfercompl : 1;
+ unsigned chhltd : 1;
+ unsigned ahberr : 1;
+ unsigned stall : 1;
+ unsigned nak : 1;
+ unsigned ack : 1;
+ unsigned nyet : 1;
+ unsigned xacterr : 1;
+ unsigned bblerr : 1;
+ unsigned frmovrun : 1;
+ unsigned datatglerr : 1;
+ unsigned reserved : 21;
+ } b;
+} hcintmsk_t;
+
+typedef union _hcintn_t
+{
+ u32 d32;
+ struct
+ {
+ u32 xfercompl :1;
+ u32 chhltd :1;
+ u32 abherr :1;
+ u32 stall :1;
+ u32 nak :1;
+ u32 ack :1;
+ u32 nyet :1;
+ u32 xacterr :1;
+ u32 bblerr :1;
+ u32 frmovrun :1;
+ u32 datatglerr :1;
+ u32 reserved :21;
+ }b;
+}hcintn_t;
+
+
+typedef union _pcgcctl_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned stoppclk :1;
+ unsigned gatehclk :1;
+ unsigned pwrclmp :1;
+ unsigned rstpdwnmodule :1;
+ unsigned physuspended :1;
+ unsigned Reserved5_31 :27;
+ }b;
+}pcgcctl_t;
+
+
+typedef struct isoch_packet_desc
+{
+ u32 isoch_packiet_start_addr;// start address of buffer is buffer address + uiOffsert.
+ u32 buf_size;
+ u32 transferred_szie;
+ u32 isoch_status;
+}isoch_packet_desc_t;//, *isoch_packet_desc_t *,**isoch_packet_desc_t **;
+
+
+typedef struct standard_dev_req_info
+{
+ bool is_data_stage;
+ u8 conrol_transfer_stage;
+ u32 vir_standard_dev_req_addr;
+ u32 phy_standard_dev_req_addr;
+}standard_dev_req_info_t;
+
+
+typedef struct control_data_tgl_t
+{
+ u8 setup_tgl;
+ u8 data_tgl;
+ u8 status_tgl;
+}control_data_tgl_t;
+
+
+
+typedef struct ed_status
+{
+ u8 data_tgl;
+ control_data_tgl_t control_data_tgl;
+ bool is_ping_enable;
+ bool is_in_transfer_ready_q;
+ bool is_in_transferring;
+ u32 in_transferring_td;
+ bool is_alloc_resource_for_ed;
+ bool is_complete_split;
+// sztupy: split transaction support
+#define ED_STATUS_SPLIT_POS_ALL 3
+#define ED_STATUS_SPLIT_POS_BEGIN 2
+#define ED_STATUS_SPLIT_POS_MID 1
+#define ED_STATUS_SPLIT_POS_END 0
+ u8 split_pos;
+ u32 split_offset;
+}ed_status_t;//, *ed_status_t *,**ed_status_t **;
+
+
+typedef struct ed_desc
+{
+ u8 device_addr;
+ u8 endpoint_num;
+ bool is_ep_in;
+ u8 dev_speed;
+ u8 endpoint_type;
+ u16 max_packet_size;
+ u8 mc;
+ u8 interval;
+ u32 sched_frame;
+ u32 used_bus_time;
+ u8 hub_addr;
+ u8 hub_port;
+ bool is_do_split;
+}ed_dest_t;//, *ed_dest_t *,**ed_dest_t **;
+
+
+//Defines the Data Structures of Transfer.
+typedef struct hc_reg
+{
+
+ hcintmsk_t hc_int_msk;
+ hcintn_t hc_int;
+ u32 dma_addr;
+
+}hc_reg_t;//, *hc_reg_t *, **hc_reg_t **;
+
+
+typedef struct stransfer
+{
+ u32 stransfer_id;
+ u32 parent_td;
+ ed_dest_t *ed_desc_p;
+ ed_status_t *ed_status_p;
+ u32 start_vir_buf_addr;
+ u32 start_phy_buf_addr;
+ u32 buf_size;
+ u32 packet_cnt;
+ u8 alloc_chnum;
+ hc_reg_t hc_reg;
+}stransfer_t;//, *stransfer_t *,**stransfer_t **;
+
+
+typedef struct ed
+{
+ u32 ed_id;
+ bool is_halted;
+ bool is_need_to_insert_scheduler;
+ ed_dest_t ed_desc;
+ ed_status_t ed_status;
+ otg_list_head ed_list_entry;
+ otg_list_head td_list_entry;
+ otg_list_head trans_ready_q_list_entry;
+ u32 num_td;
+ void *ed_private;
+}ed_t;//, *ed_t *, **ed_t **;
+
+
+
+typedef struct td
+{
+ u32 td_id;
+ ed_t *parent_ed_p;
+ void *call_back_func_p;
+ void *call_back_func_param_p;
+ bool is_transferring;
+ bool is_transfer_done;
+ u32 transferred_szie;
+ bool is_standard_dev_req;
+ standard_dev_req_info_t standard_dev_req_info;
+ u32 vir_buf_addr;
+ u32 phy_buf_addr;
+ u32 buf_size;
+ u32 transfer_flag;
+ stransfer_t cur_stransfer;
+ USB_ERROR_CODE error_code;
+ u32 err_cnt;
+ otg_list_head td_list_entry;
+
+ //Isochronous Transfer Specific
+ u32 isoch_packet_num;
+ isoch_packet_desc_t *isoch_packet_desc_p;
+ u32 isoch_packet_index;
+ u32 isoch_packet_position;
+ u32 sched_frame;
+ u32 interval;
+ u32 used_total_bus_time;
+
+ // the private data can be used by S3C6400Interface.
+ void *td_private;
+}td_t;//, *td_t *,**td_t **;
+
+
+//Define Data Structures of Scheduler.
+typedef struct trans_ready_q
+{
+ bool is_periodic_transfer;
+ otg_list_head trans_ready_q_list_head;
+ u32 trans_ready_entry_num;
+
+ //In case of Periodic Transfer
+ u32 total_perio_bus_bandwidth;
+ u8 total_alloc_chnum;
+}trans_ready_q_t;//, *trans_ready_q_t *,**trans_ready_q_t **;
+
+
+//Define USB OTG Reg Data Structure by Kyuhyeok.
+
+#define MAX_COUNT 10000
+#define INT_ALL 0xffffffff
+
+typedef union _haint_t
+{
+ u32 d32;
+ struct
+ {
+ u32 channel_intr_0 :1;
+ u32 channel_intr_1 :1;
+ u32 channel_intr_2 :1;
+ u32 channel_intr_3 :1;
+ u32 channel_intr_4 :1;
+ u32 channel_intr_5 :1;
+ u32 channel_intr_6 :1;
+ u32 channel_intr_7 :1;
+ u32 channel_intr_8 :1;
+ u32 channel_intr_9 :1;
+ u32 channel_intr_10 :1;
+ u32 channel_intr_11 :1;
+ u32 channel_intr_12 :1;
+ u32 channel_intr_13 :1;
+ u32 channel_intr_14 :1;
+ u32 channel_intr_15 :1;
+ u32 reserved1 :16;
+ }b;
+}haint_t;
+
+typedef union _gresetctl_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned csftrst : 1;
+ unsigned hsftrst : 1;
+ unsigned hstfrm : 1;
+ unsigned intknqflsh : 1;
+ unsigned rxfflsh : 1;
+ unsigned txfflsh : 1;
+ unsigned txfnum : 5;
+ unsigned reserved11_29 : 19;
+ unsigned dmareq : 1;
+ unsigned ahbidle : 1;
+ } b;
+} gresetctl_t;
+
+
+typedef union _gahbcfg_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned glblintrmsk : 1;
+#define GAHBCFG_GLBINT_ENABLE 1
+ unsigned hburstlen : 4;
+#define INT_DMA_MODE_SINGLE 00
+#define INT_DMA_MODE_INCR 01
+#define INT_DMA_MODE_INCR4 03
+#define INT_DMA_MODE_INCR8 05
+#define INT_DMA_MODE_INCR16 07
+ unsigned dmaenable : 1;
+#define GAHBCFG_DMAENABLE 1
+ unsigned reserved : 1;
+ unsigned nptxfemplvl : 1;
+ unsigned ptxfemplvl : 1;
+#define GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+ unsigned reserved9_31 : 22;
+ } b;
+} gahbcfg_t;
+
+typedef union _gusbcfg_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned toutcal : 3;
+ unsigned phyif : 1;
+ unsigned ulpi_utmi_sel : 1;
+ unsigned fsintf : 1;
+ unsigned physel : 1;
+ unsigned ddrsel : 1;
+ unsigned srpcap : 1;
+ unsigned hnpcap : 1;
+ unsigned usbtrdtim : 4;
+ unsigned nptxfrwnden : 1;
+ unsigned phylpwrclksel : 1;
+ unsigned reserved : 13;
+ unsigned forcehstmode : 1;
+ unsigned reserved2 : 2;
+ } b;
+} gusbcfg_t;
+
+
+typedef union _ghwcfg2_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct {
+ /* GHWCFG2 */
+ unsigned op_mode : 3;
+#define MODE_HNP_SRP_CAPABLE 0
+#define MODE_SRP_ONLY_CAPABLE 1
+#define MODE_NO_HNP_SRP_CAPABLE 2
+#define MODE_SRP_CAPABLE_DEVICE 3
+#define MODE_NO_SRP_CAPABLE_DEVICE 4
+#define MODE_SRP_CAPABLE_HOST 5
+#define MODE_NO_SRP_CAPABLE_HOST 6
+
+ unsigned architecture : 2;
+#define HWCFG2_ARCH_SLAVE_ONLY 0x00
+#define HWCFG2_ARCH_EXT_DMA 0x01
+#define HWCFG2_ARCH_INT_DMA 0x02
+
+ unsigned point2point : 1;
+ unsigned hs_phy_type : 2;
+ unsigned fs_phy_type : 2;
+ unsigned num_dev_ep : 4;
+ unsigned num_host_chan : 4;
+ unsigned perio_ep_supported : 1;
+ unsigned dynamic_fifo : 1;
+ unsigned rx_status_q_depth : 2;
+ unsigned nonperio_tx_q_depth : 2;
+ unsigned host_perio_tx_q_depth : 2;
+ unsigned dev_token_q_depth : 5;
+ unsigned reserved31 : 1;
+ } b;
+} ghwcfg2_t;
+
+typedef union _gintsts_t
+{
+ /** raw register data */
+ u32 d32;
+#define SOF_INTR_MASK 0x0008
+ /** register bits */
+ struct
+ {
+#define HOST_MODE 1
+#define DEVICE_MODE 0
+ unsigned curmode : 1;
+#define OTG_HOST_MODE 1
+#define OTG_DEVICE_MODE 0
+
+ unsigned modemismatch : 1;
+ unsigned otgintr : 1;
+ unsigned sofintr : 1;
+ unsigned rxstsqlvl : 1;
+ unsigned nptxfempty : 1;
+ unsigned ginnakeff : 1;
+ unsigned goutnakeff : 1;
+ unsigned reserved8 : 1;
+ unsigned i2cintr : 1;
+ unsigned erlysuspend : 1;
+ unsigned usbsuspend : 1;
+ unsigned usbreset : 1;
+ unsigned enumdone : 1;
+ unsigned isooutdrop : 1;
+ unsigned eopframe : 1;
+ unsigned intokenrx : 1;
+ unsigned epmismatch : 1;
+ unsigned inepint : 1;
+ unsigned outepintr : 1;
+ unsigned incompisoin : 1;
+ unsigned incompisoout : 1;
+ unsigned reserved22_23 : 2;
+ unsigned portintr : 1;
+ unsigned hcintr : 1;
+ unsigned ptxfempty : 1;
+ unsigned reserved27 : 1;
+ unsigned conidstschng : 1;
+ unsigned disconnect : 1;
+ unsigned sessreqintr : 1;
+ unsigned wkupintr : 1;
+ } b;
+} gintsts_t;
+
+
+typedef union _hcfg_t
+{
+ /** raw register data */
+ u32 d32;
+
+ /** register bits */
+ struct
+ {
+ /** FS/LS Phy Clock Select */
+ unsigned fslspclksel : 2;
+#define HCFG_30_60_MHZ 0
+#define HCFG_48_MHZ 1
+#define HCFG_6_MHZ 2
+
+ /** FS/LS Only Support */
+ unsigned fslssupp : 1;
+ unsigned reserved3_31 : 29;
+ } b;
+} hcfg_t;
+
+typedef union _hprt_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned prtconnsts : 1;
+ unsigned prtconndet : 1;
+ unsigned prtena : 1;
+ unsigned prtenchng : 1;
+ unsigned prtovrcurract : 1;
+ unsigned prtovrcurrchng : 1;
+ unsigned prtres : 1;
+ unsigned prtsusp : 1;
+ unsigned prtrst : 1;
+ unsigned reserved9 : 1;
+ unsigned prtlnsts : 2;
+ unsigned prtpwr : 1;
+ unsigned prttstctl : 4;
+ unsigned prtspd : 2;
+#define HPRT0_PRTSPD_HIGH_SPEED 0
+#define HPRT0_PRTSPD_FULL_SPEED 1
+#define HPRT0_PRTSPD_LOW_SPEED 2
+ unsigned reserved19_31 : 13;
+ } b;
+} hprt_t;
+
+
+typedef union _gintmsk_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned reserved0 : 1;
+ unsigned modemismatch : 1;
+ unsigned otgintr : 1;
+ unsigned sofintr : 1;
+ unsigned rxstsqlvl : 1;
+ unsigned nptxfempty : 1;
+ unsigned ginnakeff : 1;
+ unsigned goutnakeff : 1;
+ unsigned reserved8 : 1;
+ unsigned i2cintr : 1;
+ unsigned erlysuspend : 1;
+ unsigned usbsuspend : 1;
+ unsigned usbreset : 1;
+ unsigned enumdone : 1;
+ unsigned isooutdrop : 1;
+ unsigned eopframe : 1;
+ unsigned reserved16 : 1;
+ unsigned epmismatch : 1;
+ unsigned inepintr : 1;
+ unsigned outepintr : 1;
+ unsigned incompisoin : 1;
+ unsigned incompisoout : 1;
+ unsigned reserved22_23 : 2;
+ unsigned portintr : 1;
+ unsigned hcintr : 1;
+ unsigned ptxfempty : 1;
+ unsigned reserved27 : 1;
+ unsigned conidstschng : 1;
+ unsigned disconnect : 1;
+ unsigned sessreqintr : 1;
+ unsigned wkupintr : 1;
+ } b;
+} gintmsk_t;
+
+
+typedef struct _hc_t
+{
+
+ u8 hc_num; // Host channel number used for register address lookup
+
+ unsigned dev_addr : 7; // Device to access
+ unsigned ep_is_in : 1; // EP direction; 0: OUT, 1: IN
+
+ unsigned ep_num : 4; // EP to access
+ unsigned low_speed : 1; // 1: Low speed, 0: Not low speed
+ unsigned ep_type : 2; // Endpoint type.
+ // One of the following values:
+ // - OTG_EP_TYPE_CONTROL: 0
+ // - OTG_EP_TYPE_ISOC: 1
+ // - OTG_EP_TYPE_BULK: 2
+ // - OTG_EP_TYPE_INTR: 3
+
+ unsigned rsvdb1 : 1; // 8 bit padding
+
+ u8 rsvd2; // 4 byte boundary
+
+ unsigned max_packet : 12; // Max packet size in bytes
+
+ unsigned data_pid_start : 2;
+#define OTG_HC_PID_DATA0 0
+#define OTG_HC_PID_DATA2 1
+#define OTG_HC_PID_DATA1 2
+#define OTG_HC_PID_MDATA 3
+#define OTG_HC_PID_SETUP 3
+
+ unsigned multi_count : 2; // Number of periodic transactions per (micro)frame
+
+
+ // Flag to indicate whether the transfer has been started. Set to 1 if
+ // it has been started, 0 otherwise.
+ u8 xfer_started;
+
+
+ // Set to 1 to indicate that a PING request should be issued on this
+ // channel. If 0, process normally.
+ u8 do_ping;
+
+ // Set to 1 to indicate that the error count for this transaction is
+ // non-zero. Set to 0 if the error count is 0.
+ u8 error_state;
+ u32 *xfer_buff; // Pointer to the current transfer buffer position.
+ u16 start_pkt_count; // Packet count at start of transfer.
+
+ u32 xfer_len; // Total number of bytes to transfer.
+ u32 xfer_count; // Number of bytes transferred so far.
+
+
+ // Set to 1 if the host channel has been halted, but the core is not
+ // finished flushing queued requests. Otherwise 0.
+ u8 halt_pending;
+ u8 halt_status; // Reason for halting the host channel
+ u8 short_read; // Set when the host channel does a short read.
+ u8 rsvd3; // 4 byte boundary
+
+} hc_t;
+
+
+// Port status for the HC
+#define HCD_DRIVE_RESET 0x0001
+#define HCD_SEND_SETUP 0x0002
+
+#define HC_MAX_PKT_COUNT 511
+#define HC_MAX_TRANSFER_SIZE 65535
+#define MAXP_SIZE_64BYTE 64
+#define MAXP_SIZE_512BYTE 512
+#define MAXP_SIZE_1024BYTE 1024
+
+typedef union _hcchar_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ // Maximum packet size in bytes
+ unsigned mps : 11;
+
+ // Endpoint number
+ unsigned epnum : 4;
+
+ // 0: OUT, 1: IN
+ unsigned epdir : 1;
+#define HCDIR_OUT 0
+#define HCDIR_IN 1
+
+ unsigned reserved : 1;
+
+ // 0: Full/high speed device, 1: Low speed device
+ unsigned lspddev : 1;
+
+ // 0: Control, 1: Isoc, 2: Bulk, 3: Intr
+ unsigned eptype : 2;
+#define OTG_EP_TYPE_CONTROL 0
+#define OTG_EP_TYPE_ISOC 1
+#define OTG_EP_TYPE_BULK 2
+#define OTG_EP_TYPE_INTR 3
+
+ // Packets per frame for periodic transfers. 0 is reserved.
+ unsigned multicnt : 2;
+
+ // Device address
+ unsigned devaddr : 7;
+
+ // Frame to transmit periodic transaction.
+ // 0: even, 1: odd
+ unsigned oddfrm : 1;
+
+ // Channel disable
+ unsigned chdis : 1;
+
+ // Channel enable
+ unsigned chen : 1;
+ } b;
+} hcchar_t;
+
+// sztupy: adding struct to handle HCSPLT register
+typedef union _hcsplt_t {
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ unsigned prtaddr : 7;
+ unsigned hubaddr : 7;
+ unsigned xactpos : 2;
+ unsigned compsplt : 1;
+ unsigned reserved : 14;
+ unsigned spltena : 1;
+ } b;
+
+} hcsplt_t;
+
+typedef union _hctsiz_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ // Total transfer size in bytes
+ unsigned xfersize : 19;
+
+ // Data packets to transfer
+ unsigned pktcnt : 10;
+
+ // Packet ID for next data packet
+ // 0: DATA0
+ // 1: DATA2
+ // 2: DATA1
+ // 3: MDATA (non-Control), SETUP (Control)
+ unsigned pid : 2;
+#define HCTSIZ_DATA0 0
+#define HCTSIZ_DATA1 2
+#define HCTSIZ_DATA2 1
+#define HCTSIZ_MDATA 3
+#define HCTSIZ_SETUP 3
+
+ // Do PING protocol when 1
+ unsigned dopng : 1;
+ } b;
+} hctsiz_t;
+
+
+
+typedef union _grxstsr_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ unsigned chnum : 4;
+ unsigned bcnt : 11;
+ unsigned dpid : 2;
+ unsigned pktsts : 4;
+ unsigned Reserved : 11;
+ } b;
+} grxstsr_t;
+
+typedef union _hfir_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ unsigned frint : 16;
+ unsigned Reserved : 16;
+ } b;
+} hfir_t;
+
+typedef union _hfnum_t
+{
+ // raw register data
+ u32 d32;
+
+ // register bits
+ struct
+ {
+ unsigned frnum : 16;
+#define HFNUM_MAX_FRNUM 0x3FFF
+ unsigned frrem : 16;
+ } b;
+} hfnum_t;
+
+typedef union grstctl_t
+{
+ /** raw register data */
+ u32 d32;
+ /** register bits */
+ struct
+ {
+ unsigned csftrst : 1;
+ unsigned hsftrst : 1;
+ unsigned hstfrm : 1;
+ unsigned intknqflsh : 1;
+ unsigned rxfflsh : 1;
+ unsigned txfflsh : 1;
+ unsigned txfnum : 5;
+ unsigned reserved11_29 : 19;
+ unsigned dmareq : 1;
+ unsigned ahbidle : 1;
+ } b;
+} grstctl_t;
+
+typedef struct hc_info
+{
+ hcintmsk_t hc_int_msk;
+ hcintn_t hc_int;
+ u32 dma_addr;
+ hcchar_t hc_char;
+ hctsiz_t hc_size;
+}hc_info_t;//, *hc_info_t *, **hc_info_t **;
+
+#ifndef USB_MAXCHILDREN
+ #define USB_MAXCHILDREN (31)
+#endif
+
+typedef struct _usb_hub_descriptor_t
+{
+ u8 desc_length;
+ u8 desc_type;
+ u8 port_number;
+ u16 hub_characteristics;
+ u8 power_on_to_power_good;
+ u8 hub_control_current;
+ /* add 1 bit for hub status change; round to bytes */
+ u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+ u8 port_pwr_ctrl_mask[(USB_MAXCHILDREN + 1 + 7) / 8];
+}usb_hub_descriptor_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h b/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h
new file mode 100644
index 0000000..b2b3bee
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : s3c-otg-common-errorcode.h
+ * [Description] : The Header file defines Error Codes to be used at sub-modules of S3C6400HCD.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file.
+ * (2) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com )
+ * - add HCD error code
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _ERROR_CODE_DEF_H
+#define _ERROR_CODE_DEF_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-common.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+typedef int USB_ERROR_CODE;
+
+//General USB Error Code.
+#define USB_ERR_SUCCESS 0
+#define USB_ERR_FAIL -1
+
+#define USB_ERR_NO 1
+
+#define USB_ERR_NO_ENTITY -2
+
+//S3CTransfer Error Code
+#define USB_ERR_NODEV -ENODEV
+#define USB_ERR_NOMEM -ENOMEM
+#define USB_ERR_NOSPACE -ENOSPC
+#define USB_ERR_NOIO -EIO
+
+//OTG-HCD error code
+#define USB_ERR_NOELEMENT -ENOENT
+#define USB_ERR_ESHUTDOWN -ESHUTDOWN /* unplug */
+#define USB_ERR_DEQUEUED -ECONNRESET /* unlink */
+
+
+//S3CScheduler Error Code
+#define USB_ERR_ALREADY_EXIST -1
+#define USB_ERR_NO_RESOURCE -2
+#define USB_ERR_NO_CHANNEL -3
+#define USB_ERR_NO_BANDWIDTH -4
+#define USB_ERR_ALL_RESROUCE -5
+
+
+
+
+/************************************************
+ *Defines the USB Error Status Code of USB Transfer.
+ ************************************************/
+
+//#ifdef LINUX
+
+#define USB_ERR_STATUS_COMPLETE 0
+#define USB_ERR_STATUS_INPROGRESS -EINPROGRESS
+#define USB_ERR_STATUS_CRC -EILSEQ
+#define USB_ERR_STATUS_XACTERR -EPROTO
+#define USB_ERR_STATUS_STALL -EPIPE
+#define USB_ERR_STATUS_BBLERR -EOVERFLOW
+#define USB_ERR_STATUS_AHBERR -EIO
+#define USB_ERR_STATUS_FRMOVRUN_OUT -ENOSR
+#define USB_ERR_STATUS_FRMOVRUN_IN -ECOMM
+#define USB_ERR_STATUS_SHORTREAD -EREMOTEIO
+
+//#else
+
+//#endif
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h b/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h
new file mode 100644
index 0000000..ed119d7
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h
@@ -0,0 +1,302 @@
+/****************************************************************************
+* (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+*
+* [File Name] : s3c-otg-common-regdef.h
+* [Description] :
+*
+* [Author] : Kyu Hyeok Jang { kyuhyeok.jang@samsung.com }
+* [Department] : System LSI Division/Embedded Software Center
+* [Created Date]: 2007/12/15
+* [Revision History]
+* (1) 2007/12/15 by Kyu Hyeok Jang { kyuhyeok.jang@samsung.com }
+* - Created
+*
+****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _OTG_REG_DEF_H
+#define _OTG_REG_DEF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct {
+ u32 OPHYPWR;
+ u32 OPHYCLK;
+ u32 ORSTCON;
+}OTG_PHY_REG, *PS_OTG_PHY_REG;
+
+#define GOTGCTL 0x000 // OTG Control & Status
+#define GOTGINT 0x004 // OTG Interrupt
+#define GAHBCFG 0x008 // Core AHB Configuration
+#define GUSBCFG 0x00C // Core USB Configuration
+#define GRSTCTL 0x010 // Core Reset
+#define GINTSTS 0x014 // Core Interrupt
+#define GINTMSK 0x018 // Core Interrupt Mask
+#define GRXSTSR 0x01C // Receive Status Debug Read/Status Read
+#define GRXSTSP 0x020 // Receive Status Debug Pop/Status Pop
+#define GRXFSIZ 0x024 // Receive FIFO Size
+#define GNPTXFSIZ 0x028 // Non-Periodic Transmit FIFO Size
+#define GNPTXSTS 0x02C // Non-Periodic Transmit FIFO/Queue Status
+#define GPVNDCTL 0x034 // PHY Vendor Control
+#define GGPIO 0x038 // General Purpose I/O
+#define GUID 0x03C // User ID
+#define GSNPSID 0x040 // Synopsys ID
+#define GHWCFG1 0x044 // User HW Config1
+#define GHWCFG2 0x048 // User HW Config2
+#define GHWCFG3 0x04C // User HW Config3
+#define GHWCFG4 0x050 // User HW Config4
+
+#define HPTXFSIZ 0x100 // Host Periodic Transmit FIFO Size
+#define DPTXFSIZ1 0x104 // Device Periodic Transmit FIFO-1 Size
+#define DPTXFSIZ2 0x108 // Device Periodic Transmit FIFO-2 Size
+#define DPTXFSIZ3 0x10C // Device Periodic Transmit FIFO-3 Size
+#define DPTXFSIZ4 0x110 // Device Periodic Transmit FIFO-4 Size
+#define DPTXFSIZ5 0x114 // Device Periodic Transmit FIFO-5 Size
+#define DPTXFSIZ6 0x118 // Device Periodic Transmit FIFO-6 Size
+#define DPTXFSIZ7 0x11C // Device Periodic Transmit FIFO-7 Size
+#define DPTXFSIZ8 0x120 // Device Periodic Transmit FIFO-8 Size
+#define DPTXFSIZ9 0x124 // Device Periodic Transmit FIFO-9 Size
+#define DPTXFSIZ10 0x128 // Device Periodic Transmit FIFO-10 Size
+#define DPTXFSIZ11 0x12C // Device Periodic Transmit FIFO-11 Size
+#define DPTXFSIZ12 0x130 // Device Periodic Transmit FIFO-12 Size
+#define DPTXFSIZ13 0x134 // Device Periodic Transmit FIFO-13 Size
+#define DPTXFSIZ14 0x138 // Device Periodic Transmit FIFO-14 Size
+#define DPTXFSIZ15 0x13C // Device Periodic Transmit FIFO-15 Size
+
+//*********************************************************************
+// Host Mode Registers
+//*********************************************************************
+// Host Global Registers
+
+// Channel specific registers
+#define HCCHAR_ADDR 0x500
+#define HCCHAR(n) 0x500 + ((n)*0x20)
+#define HCSPLT(n) 0x504 + ((n)*0x20)
+#define HCINT(n) 0x508 + ((n)*0x20)
+#define HCINTMSK(n) 0x50C + ((n)*0x20)
+#define HCTSIZ(n) 0x510 + ((n)*0x20)
+#define HCDMA(n) 0x514 + ((n)*0x20)
+
+#define HCFG 0x400 // Host Configuration
+#define HFIR 0x404 // Host Frame Interval
+#define HFNUM 0x408 // Host Frame Number/Frame Time Remaining
+#define HPTXSTS 0x410 // Host Periodic Transmit FIFO/Queue Status
+#define HAINT 0x414 // Host All Channels Interrupt
+#define HAINTMSK 0x418 // Host All Channels Interrupt Mask
+
+// Host Port Control & Status Registers
+
+#define HPRT 0x440 // Host Port Control & Status
+
+// Device Logical Endpoints-Specific Registers
+
+#define DIEPCTL 0x900 // Device IN Endpoint 0 Control
+#define DOEPCTL(n) 0xB00 + ((n)*0x20)) // Device OUT Endpoint 0 Control
+#define DIEPINT(n) 0x908 + ((n)*0x20)) // Device IN Endpoint 0 Interrupt
+#define DOEPINT(n) 0xB08 + ((n)*0x20)) // Device OUT Endpoint 0 Interrupt
+#define DIEPTSIZ(n) 0x910 + ((n)*0x20)) // Device IN Endpoint 0 Transfer Size
+#define DOEPTSIZ(n) 0xB10 + ((n)*0x20)) // Device OUT Endpoint 0 Transfer Size
+#define DIEPDMA(n) 0x914 + ((n)*0x20)) // Device IN Endpoint 0 DMA Address
+#define DOEPDMA(n) 0xB14 + ((n)*0x20)) // Device OUT Endpoint 0 DMA Address
+
+#define EP_FIFO(n) 0x1000 + ((n)*0x1000))
+
+#define PCGCCTL 0x0E00
+
+//
+#define BASE_REGISTER_OFFSET 0x0
+#define REGISTER_SET_SIZE 0x200
+
+// Power Reg Bits
+#define USB_RESET 0x8
+#define MCU_RESUME 0x4
+#define SUSPEND_MODE 0x2
+#define SUSPEND_MODE_ENABLE_CTRL 0x1
+
+// EP0 CSR
+#define EP0_OUT_PACKET_RDY 0x1
+#define EP0_IN_PACKET_RDY 0x2
+#define EP0_SENT_STALL 0x4
+#define DATA_END 0x8
+#define SETUP_END 0x10
+#define EP0_SEND_STALL 0x20
+#define SERVICED_OUT_PKY_RDY 0x40
+#define SERVICED_SETUP_END 0x80
+
+// IN_CSR1_REG Bit definitions
+#define IN_PACKET_READY 0x1
+#define UNDER_RUN 0x4 // Iso Mode Only
+#define FLUSH_IN_FIFO 0x8
+#define IN_SEND_STALL 0x10
+#define IN_SENT_STALL 0x20
+#define IN_CLR_DATA_TOGGLE 0x40
+
+// OUT_CSR1_REG Bit definitions
+#define OUT_PACKET_READY 0x1
+#define FLUSH_OUT_FIFO 0x10
+#define OUT_SEND_STALL 0x20
+#define OUT_SENT_STALL 0x40
+#define OUT_CLR_DATA_TOGGLE 0x80
+
+// IN_CSR2_REG Bit definitions
+#define IN_DMA_INT_DISABLE 0x10
+#define SET_MODE_IN 0x20
+
+#define EPTYPE (0x3<<18)
+#define SET_TYPE_CONTROL (0x0<<18)
+#define SET_TYPE_ISO (0x1<<18)
+#define SET_TYPE_BULK (0x2<<18)
+#define SET_TYPE_INTERRUPT (0x3<<18)
+
+#define AUTO_MODE 0x80
+
+// OUT_CSR2_REG Bit definitions
+#define AUTO_CLR 0x40
+#define OUT_DMA_INT_DISABLE 0x20
+
+// Can be used for Interrupt and Interrupt Enable Reg - common bit def
+#define EP0_IN_INT (0x1<<0)
+#define EP1_IN_INT (0x1<<1)
+#define EP2_IN_INT (0x1<<2)
+#define EP3_IN_INT (0x1<<3)
+#define EP4_IN_INT (0x1<<4)
+#define EP5_IN_INT (0x1<<5)
+#define EP6_IN_INT (0x1<<6)
+#define EP7_IN_INT (0x1<<7)
+#define EP8_IN_INT (0x1<<8)
+#define EP9_IN_INT (0x1<<9)
+#define EP10_IN_INT (0x1<<10)
+#define EP11_IN_INT (0x1<<11)
+#define EP12_IN_INT (0x1<<12)
+#define EP13_IN_INT (0x1<<13)
+#define EP14_IN_INT (0x1<<14)
+#define EP15_IN_INT (0x1<<15)
+#define EP0_OUT_INT (0x1<<16)
+#define EP1_OUT_INT (0x1<<17)
+#define EP2_OUT_INT (0x1<<18)
+#define EP3_OUT_INT (0x1<<19)
+#define EP4_OUT_INT (0x1<<20)
+#define EP5_OUT_INT (0x1<<21)
+#define EP6_OUT_INT (0x1<<22)
+#define EP7_OUT_INT (0x1<<23)
+#define EP8_OUT_INT (0x1<<24)
+#define EP9_OUT_INT (0x1<<25)
+#define EP10_OUT_INT (0x1<<26)
+#define EP11_OUT_INT (0x1<<27)
+#define EP12_OUT_INT (0x1<<28)
+#define EP13_OUT_INT (0x1<<29)
+#define EP14_OUT_INT (0x1<<30)
+#define EP15_OUT_INT (0x1<<31)
+
+// GOTGINT
+#define SesEndDet (0x1<<2)
+
+// GRSTCTL
+#define TxFFlsh (0x1<<5)
+#define RxFFlsh (0x1<<4)
+#define INTknQFlsh (0x1<<3)
+#define FrmCntrRst (0x1<<2)
+#define HSftRst (0x1<<1)
+#define CSftRst (0x1<<0)
+
+#define CLEAR_ALL_EP_INTRS 0xffffffff
+
+#define EP_INTERRUPT_DISABLE_ALL 0x0 // Bits to write to EP_INT_EN_REG - Use CLEAR
+
+// DMA control register bit definitions
+#define RUN_OB 0x80
+#define STATE 0x70
+#define DEMAND_MODE 0x8
+#define OUT_DMA_RUN 0x4
+#define IN_DMA_RUN 0x2
+#define DMA_MODE_EN 0x1
+
+
+#define REAL_PHYSICAL_ADDR_EP0_FIFO (0x520001c0) //Endpoint 0 FIFO
+#define REAL_PHYSICAL_ADDR_EP1_FIFO (0x520001c4) //Endpoint 1 FIFO
+#define REAL_PHYSICAL_ADDR_EP2_FIFO (0x520001c8) //Endpoint 2 FIFO
+#define REAL_PHYSICAL_ADDR_EP3_FIFO (0x520001cc) //Endpoint 3 FIFO
+#define REAL_PHYSICAL_ADDR_EP4_FIFO (0x520001d0) //Endpoint 4 FIFO
+
+// GAHBCFG
+#define MODE_DMA (1<<5)
+#define MODE_SLAVE (0<<5)
+#define BURST_SINGLE (0<<1)
+#define BURST_INCR (1<<1)
+#define BURST_INCR4 (3<<1)
+#define BURST_INCR8 (5<<1)
+#define BURST_INCR16 (7<<1)
+#define GBL_INT_MASK (0<<0)
+#define GBL_INT_UNMASK (1<<0)
+
+// For USB DMA
+//BOOL InitUsbdDriverGlobals(void); //:-)
+//void UsbdDeallocateVm(void); //:-)
+//BOOL UsbdAllocateVm(void); //:-)
+//void UsbdInitDma(int epnum, int bufIndex,int bufOffset); //:-)
+
+
+//by Kevin
+
+//////////////////////////////////////////////////////////////////////////
+
+/*
+inline u32 ReadReg(
+ u32 uiOffset
+ )
+{
+ volatile u32 *pbReg = ctrlr_base_reg_addr(uiOffset);
+ u32 uiValue = (u32) *pbReg;
+ return uiValue;
+}
+
+inline void WriteReg(
+ u32 uiOffset,
+ u32 bValue
+ )
+{
+ volatile ULONG *pbReg = ctrlr_base_reg_addr(uiOffset);
+ *pbReg = (ULONG) bValue;
+}
+
+inline void UpdateReg(
+ u32 uiOffset,
+ u32 bValue
+ )
+{
+ WriteReg(uiOffset, (ReadReg(uiOffset) | bValue));
+};
+
+inline void ClearReg(
+ u32 uiOffeset,
+ u32 bValue
+ )
+{
+ WriteReg(uiOffeset, (ReadReg(uiOffeset) & ~bValue));
+};
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h
new file mode 100644
index 0000000..20416b1
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-debug.c
+ * @brief It provides debug functions for display message \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_DEBUG_H_
+#define _S3C_OTG_HCDI_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define OTG_DEBUG
+
+#ifdef OTG_DEBUG
+#if 0
+#include <linux/stddef.h>
+#endif
+
+#define OTG_DBG_OTGHCDI_DRIVER true
+#define OTG_DBG_OTGHCDI_HCD false
+#define OTG_DBG_OTGHCDI_KAL false
+#define OTG_DBG_OTGHCDI_LIST false
+#define OTG_DBG_OTGHCDI_MEM false
+
+#define OTG_DBG_TRANSFER false
+#define OTG_DBG_SCHEDULE false
+#define OTG_DBG_OCI false
+#define OTG_DBG_DONETRASF false
+#define OTG_DBG_ISR false
+#define OTG_DBG_ROOTHUB false
+
+
+#include <linux/kernel.h> //for printk
+
+#define otg_err(is_active, msg...) \
+ do{ if (/*(is_active) == */true)\
+ {\
+ pr_err("otg_err: in %s()::%05d ", __func__ , __LINE__); \
+ pr_err("=> " msg); \
+ }\
+ }while(0)
+
+#define otg_dbg(is_active, msg...) \
+ do{ if ((is_active) == true)\
+ {\
+ pr_info("otg_dbg: in %s()::%05d ", __func__, __LINE__); \
+ pr_info("=> " msg); \
+ }\
+ }while(0)
+
+#else //OTG_DEBUG
+
+# define otg_err(is_active, msg...) do{}while(0)
+# define otg_dbg(is_active, msg...) do{}while(0)
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _S3C_OTG_HCDI_DEBUG_H_ */
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c
new file mode 100644
index 0000000..f99261f
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c
@@ -0,0 +1,386 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-driver.c
+ * @brief It provides functions related with module for OTGHCD driver. \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Modifying for successful rmmod & disconnecting \n
+ * @see None
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-hcdi-driver.h"
+#include "../../gadget/s3c_udc.h"
+extern void otg_phy_off(void);
+
+/**
+ * static int s5pc110_otg_drv_probe (struct platform_device *pdev)
+ *
+ * @brief probe function of OTG hcd platform_driver
+ *
+ * @param [in] pdev : pointer of platform_device of otg hcd platform_driver
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If fail \n
+ * @remark
+ * it allocates resources of it and call other modules' init function.
+ * then call usb_create_hcd, usb_add_hcd, s5pc110_otghcd_start functions
+ */
+
+struct usb_hcd* g_pUsbHcd = NULL;
+
+static void otg_power_work(struct work_struct *work)
+{
+ struct sec_otghost *otghost = container_of(work,
+ struct sec_otghost, work);
+ struct sec_otghost_data *hdata = otghost->otg_data;
+
+ if (hdata && hdata->set_pwr_cb) {
+ hdata->set_pwr_cb(0);
+#ifdef CONFIG_USB_HOST_NOTIFY
+ if (g_pUsbHcd)
+ host_state_notify(&g_pUsbHcd->ndev, NOTIFY_HOST_OVERCURRENT);
+#endif
+ } else {
+ otg_err(true, "invalid otghost data\n");
+ }
+}
+
+static int s5pc110_otg_drv_probe (struct platform_device *pdev)
+{
+ int ret_val = 0;
+ u32 reg_val = 0;
+ struct sec_otghost *otghost = NULL;
+ struct sec_otghost_data *otg_data = dev_get_platdata(&pdev->dev);
+
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s3c_otg_drv_probe\n");
+
+ reset_scheduler_numbers();
+ /*init for host mode*/
+ /**
+ Allocate memory for the base HCD & Initialize the base HCD.
+ */
+ g_pUsbHcd = usb_create_hcd(&s5pc110_otg_hc_driver, &pdev->dev,
+ "s3cotg");/*pdev->dev.bus_id*/
+ if (g_pUsbHcd == NULL) {
+ ret_val = -ENOMEM;
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "failed to usb_create_hcd\n");
+ goto err_out_clk;
+ }
+
+ /* mapping hcd resource & device resource*/
+
+ g_pUsbHcd->rsrc_start = pdev->resource[0].start;
+ g_pUsbHcd->rsrc_len = pdev->resource[0].end -
+ pdev->resource[0].start + 1;
+
+ if (!request_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len,
+ gHcdName)) {
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "failed to request_mem_region\n");
+ ret_val = -EBUSY;
+ goto err_out_create_hcd;
+ }
+
+
+ /* Physical address => Virtual address */
+ g_pUsbHcd->regs = S3C_VA_OTG;
+ g_pUsbHcd->self.otg_port = 1;
+
+ g_pUDCBase = (u8 *)g_pUsbHcd->regs;
+
+ otghost = hcd_to_sec_otghost(g_pUsbHcd);
+
+ if (otghost == NULL) {
+ otg_err(true, "failed to get otghost hcd\n");
+ ret_val = USB_ERR_FAIL;
+ goto err_out_create_hcd;
+ }
+ otghost->otg_data = otg_data;
+
+ if ((s3c_get_drivermode()) & USB_OTG_DRIVER_S3CFSLS) {
+ otghost->is_hs = 0; // force USB 1.x mode
+ } else {
+ otghost->is_hs = 1;
+ }
+
+ INIT_WORK(&otghost->work, otg_power_work);
+ otghost->wq = create_singlethread_workqueue("sec_otghostd");
+
+ /* call others' init() */
+ ret_val = otg_hcd_init_modules(otghost);
+ if (ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "failed to otg_hcd_init_modules\n");
+ ret_val = USB_ERR_FAIL;
+ goto err_out_create_hcd;
+ }
+
+ /**
+ * Attempt to ensure this device is really a s5pc110 USB-OTG Controller.
+ * Read and verify the SNPSID register contents. The value should be
+ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX".
+ */
+ reg_val = read_reg_32(0x40);
+ if ((reg_val & 0xFFFFF000) != 0x4F542000) {
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "Bad value for SNPSID: 0x%x\n", reg_val);
+ ret_val = -EINVAL;
+ goto err_out_create_hcd_init;
+ }
+#ifdef CONFIG_USB_HOST_NOTIFY
+ if (otg_data->host_notify) {
+ g_pUsbHcd->host_notify = otg_data->host_notify;
+ g_pUsbHcd->ndev.name = dev_name(&pdev->dev);
+ ret_val = host_notify_dev_register(&g_pUsbHcd->ndev);
+ if (ret_val) {
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "Failed to host_notify_dev_register\n");
+ goto err_out_create_hcd_init;
+ }
+ }
+#endif
+#ifdef CONFIG_USB_SEC_WHITELIST
+ if (otg_data->sec_whlist_table_num)
+ g_pUsbHcd->sec_whlist_table_num = otg_data->sec_whlist_table_num;
+#endif
+
+ /*
+ * Finish generic HCD initialization and start the HCD. This function
+ * allocates the DMA buffer pool, registers the USB bus, requests the
+ * IRQ line, and calls s5pc110_otghcd_start method.
+ */
+ ret_val = usb_add_hcd(g_pUsbHcd,
+ pdev->resource[1].start, IRQF_DISABLED);
+ if (ret_val < 0) {
+ goto err_out_host_notify_register;
+ }
+
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "OTG HCD Initialized HCD, bus=%s, usbbus=%d\n",
+ "C110 OTG Controller", g_pUsbHcd->self.busnum);
+
+ /* otg_print_registers(); */
+
+ wake_lock_init(&otghost->wake_lock, WAKE_LOCK_SUSPEND, "usb_otg");
+ wake_lock(&otghost->wake_lock);
+
+ return USB_ERR_SUCCESS;
+err_out_host_notify_register:
+#ifdef CONFIG_USB_HOST_NOTIFY
+ host_notify_dev_unregister(&g_pUsbHcd->ndev);
+#endif
+
+err_out_create_hcd_init:
+ otg_hcd_deinit_modules(otghost);
+ release_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len);
+
+err_out_create_hcd:
+ usb_put_hcd(g_pUsbHcd);
+
+err_out_clk:
+
+ return ret_val;
+}
+
+/**
+ * static int s5pc110_otg_drv_remove (struct platform_device *dev)
+ *
+ * @brief remove function of OTG hcd platform_driver
+ *
+ * @param [in] pdev : pointer of platform_device of otg hcd platform_driver
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If fail \n
+ * @remark
+ * This function is called when the otg device unregistered with the
+ * s5pc110_otg_driver. This happens, for example, when the rmmod command is
+ * executed. The device may or may not be electrically present. If it is
+ * present, the driver stops device processing. Any resources used on behalf
+ * of this device are freed.
+ */
+static int s5pc110_otg_drv_remove (struct platform_device *dev)
+{
+ struct sec_otghost *otghost = NULL;
+
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s5pc110_otg_drv_remove\n");
+
+ otghost = hcd_to_sec_otghost(g_pUsbHcd);
+
+#ifdef CONFIG_USB_HOST_NOTIFY
+ host_notify_dev_unregister(&g_pUsbHcd->ndev);
+#endif
+
+ otg_hcd_deinit_modules(otghost);
+
+ destroy_workqueue(otghost->wq);
+
+ wake_unlock(&otghost->wake_lock);
+ wake_lock_destroy(&otghost->wake_lock);
+
+ usb_remove_hcd(g_pUsbHcd);
+
+ release_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len);
+
+ usb_put_hcd(g_pUsbHcd);
+
+ otg_phy_off();
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * @struct s5pc110_otg_driver
+ *
+ * @brief
+ * This structure defines the methods to be called by a bus driver
+ * during the lifecycle of a device on that bus. Both drivers and
+ * devices are registered with a bus driver. The bus driver matches
+ * devices to drivers based on information in the device and driver
+ * structures.
+ *
+ * The probe function is called when the bus driver matches a device
+ * to this driver. The remove function is called when a device is
+ * unregistered with the bus driver.
+ */
+struct platform_driver s5pc110_otg_driver = {
+ .probe = s5pc110_otg_drv_probe,
+ .remove = s5pc110_otg_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "s3c_otghcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+/**
+ * static int __init s5pc110_otg_module_init(void)
+ *
+ * @brief module_init function
+ *
+ * @return it returns result of platform_driver_register
+ * @remark
+ * This function is called when the s5pc110_otg_driver is installed with the
+ * insmod command. It registers the s5pc110_otg_driver structure with the
+ * appropriate bus driver. This will cause the s5pc110_otg_driver_probe function
+ * to be called. In addition, the bus driver will automatically expose
+ * attributes defined for the device and driver in the special sysfs file
+ * system.
+ */
+ /*
+static int __init s5pc110_otg_module_init(void)
+{
+ int ret_val = 0;
+
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "s3c_otg_module_init \n");
+
+ ret_val = platform_driver_register(&s5pc110_otg_driver);
+ if (ret_val < 0)
+ {
+ otg_err(OTG_DBG_OTGHCDI_DRIVER,
+ "platform_driver_register \n");
+ }
+ return ret_val;
+} */
+
+/**
+ * static void __exit s5pc110_otg_module_exit(void)
+ *
+ * @brief module_exit function
+ *
+ * @remark
+ * This function is called when the driver is removed from the kernel
+ * with the rmmod command. The driver unregisters itself with its bus
+ * driver.
+ */
+static void __exit s5pc110_otg_module_exit(void)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "s3c_otg_module_exit \n");
+ platform_driver_unregister(&s5pc110_otg_driver);
+}
+
+/* for debug */
+void otg_print_registers(void)
+{
+ /* USB PHY Control Registers */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "USB_CONTROL = 0x%x.\n", readl(0xfb10e80c));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "UPHYPWR = 0x%x.\n", readl(S3C_USBOTG_PHYPWR));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "UPHYCLK = 0x%x.\n", readl(S3C_USBOTG_PHYCLK));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "URSTCON = 0x%x.\n", readl(S3C_USBOTG_RSTCON));
+
+ /* OTG LINK Core registers (Core Global Registers) */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GOTGCTL = 0x%x.\n", read_reg_32(GOTGCTL));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GOTGINT = 0x%x.\n", read_reg_32(GOTGINT));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GAHBCFG = 0x%x.\n", read_reg_32(GAHBCFG));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GUSBCFG = 0x%x.\n", read_reg_32(GUSBCFG));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GINTSTS = 0x%x.\n", read_reg_32(GINTSTS));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GINTMSK = 0x%x.\n", read_reg_32(GINTMSK));
+
+ /* Host Mode Registers */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "HCFG = 0x%x.\n", read_reg_32(HCFG));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "HPRT = 0x%x.\n", read_reg_32(HPRT));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "HFIR = 0x%x.\n", read_reg_32(HFIR));
+
+ /* Synopsys ID */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GSNPSID = 0x%x.\n", read_reg_32(GSNPSID));
+
+ /* HWCFG */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GHWCFG1 = 0x%x.\n", read_reg_32(GHWCFG1));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GHWCFG2 = 0x%x.\n", read_reg_32(GHWCFG2));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GHWCFG3 = 0x%x.\n", read_reg_32(GHWCFG3));
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "GHWCFG4 = 0x%x.\n", read_reg_32(GHWCFG4));
+
+ /* PCGCCTL */
+ otg_dbg(OTG_DBG_OTGHCDI_DRIVER,
+ "PCGCCTL = 0x%x.\n", read_reg_32(PCGCCTL));
+}
+
+/*
+module_init(s5pc110_otg_module_init);
+module_exit(s5pc110_otg_module_exit);
+*/
+
+MODULE_DESCRIPTION("OTG USB HOST controller driver");
+MODULE_AUTHOR("SAMSUNG / System LSI / EMSP");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h
new file mode 100644
index 0000000..4488df2
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-driver.h
+ * @brief header of s3c-otg-hcdi-driver \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Modifying for successful rmmod & disconnecting \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_DRIVER_H_
+#define _S3C_OTG_HCDI_DRIVER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+//#include <linux/clk.h> //for clk_get, clk_enable etc.
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h> //for SA_SHIRQ
+#include <mach/map.h> //address for smdk
+#include <linux/dma-mapping.h> //dma_alloc_coherent
+#include <linux/ioport.h> //request_mem_request ...
+#include <asm/irq.h> //for IRQ_OTG
+#include <linux/clk.h>
+
+
+#include "s3c-otg-common-common.h"
+#include "s3c-otg-common-regdef.h"
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-hcdi-hcd.h"
+#include "s3c-otg-hcdi-kal.h"
+
+
+volatile u8 * g_pUDCBase;
+
+static const char gHcdName[] = "EMSP_OTG_HCD";
+
+//extern int otg_hcd_init_modules(struct sec_otghost *otghost);
+//extern void otg_hcd_deinit_modules(struct sec_otghost *otghost);
+
+//void otg_print_registers();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _S3C_OTG_HCDI_DRIVER_H_ */
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c
new file mode 100644
index 0000000..2b440a3
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c
@@ -0,0 +1,708 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-hcd.c
+ * @brief implementation of structure hc_drive \n
+ * @version
+ * -# Jun 11,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Modifying for successful rmmod & disconnecting \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-hcdi-hcd.h"
+
+/**
+ * otg_hcd_init_modules(struct sec_otghost *otghost)
+ *
+ * @brief call other modules' init functions
+ *
+ * @return PASS : If success \n
+ * FAIL : If fail \n
+ */
+int otg_hcd_init_modules(struct sec_otghost *otghost)
+{
+ unsigned long spin_lock_flag = 0;
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_init_modules\n");
+
+ spin_lock_init(&otghost->lock);
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ init_transfer();
+ init_scheduler();
+ oci_init(otghost);
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ return USB_ERR_SUCCESS;
+};
+
+/**
+ * void otg_hcd_deinit_modules(struct sec_otghost *otghost)
+ *
+ * @brief call other modules' de-init functions
+ *
+ * @return PASS : If success \n
+ * FAIL : If fail \n
+ */
+void otg_hcd_deinit_modules(struct sec_otghost *otghost)
+{
+ unsigned long spin_lock_flag = 0;
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_deinit_modules \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ deinit_transfer(otghost);
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+}
+
+/**
+ * irqreturn_t (*s5pc110_otghcd_irq) (struct usb_hcd *hcd)
+ *
+ * @brief interrupt handler of otg irq
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ *
+ * @return IRQ_HANDLED \n
+ */
+irqreturn_t s5pc110_otghcd_irq(struct usb_hcd *hcd)
+{
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_irq \n");
+
+ spin_lock_otg(&otghost->lock);
+ otg_handle_interrupt(hcd);
+ spin_unlock_otg(&otghost->lock);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * int s5pc110_otghcd_start(struct usb_hcd *hcd)
+ *
+ * @brief initialize and start otg hcd
+ *
+ * @param [in] usb_hcd_p : pointer of usb_hcd
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+int s5pc110_otghcd_start(struct usb_hcd *usb_hcd_p)
+{
+ struct usb_bus *usb_bus_p;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(usb_hcd_p);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start \n");
+
+ usb_bus_p = hcd_to_bus(usb_hcd_p);
+
+ /* Initialize and connect root hub if one is not already attached */
+ if (usb_bus_p->root_hub) {
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "OTG HCD Has Root Hub\n");
+
+ /* Inform the HUB driver to resume. */
+ otg_usbcore_resume_roothub();
+ } else {
+ otg_err(OTG_DBG_OTGHCDI_HCD,
+ "OTG HCD Does Not Have Root Hub\n");
+ return USB_ERR_FAIL;
+ }
+
+ set_bit(HCD_FLAG_POLL_RH,&usb_hcd_p->flags);
+ usb_hcd_p->uses_new_polling = 1;
+
+ /* init bus state before enable irq */
+ usb_hcd_p->state = HC_STATE_RUNNING;
+
+ oci_start(otghost); /* enable irq */
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * void s5pc110_otghcd_stop(struct usb_hcd *hcd)
+ *
+ * @brief deinitialize and stop otg hcd
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ *
+ */
+void s5pc110_otghcd_stop(struct usb_hcd *hcd)
+{
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_stop \n");
+
+ otg_hcd_deinit_modules(otghost);
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ oci_stop(otghost);
+ root_hub_feature(hcd, 0, ClearPortFeature, USB_PORT_FEAT_POWER, NULL);
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+}
+
+/**
+ * void s5pc110_otghcd_shutdown(struct usb_hcd *hcd)
+ *
+ * @brief shutdown otg hcd
+ *
+ * @param [in] usb_hcd_p : pointer of usb_hcd
+ *
+ */
+void s5pc110_otghcd_shutdown(struct usb_hcd *usb_hcd_p)
+{
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(usb_hcd_p);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_shutdown \n");
+ otg_hcd_deinit_modules(otghost);
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ oci_stop(otghost);
+ root_hub_feature(usb_hcd_p, 0, ClearPortFeature, USB_PORT_FEAT_POWER, NULL);
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ free_irq(IRQ_OTG, usb_hcd_p);
+ usb_hcd_p->state = HC_STATE_HALT;
+ otg_usbcore_hc_died();
+}
+
+
+/**
+ * int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd)
+ *
+ * @brief get currnet frame number
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ *
+ * @return ret : frame number \n
+ */
+int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd)
+{
+ int ret = 0;
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_get_frame_number \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ ret = oci_get_frame_num();
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ return ret;
+}
+
+
+/**
+ * int s5pc110_otghcd_urb_enqueue()
+ *
+ * @brief enqueue a urb to otg hcd
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ * [in] ep : pointer of usb_host_endpoint
+ * [in] urb : pointer of urb
+ * [in] mem_flags : type of gfp_t
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+int s5pc110_otghcd_urb_enqueue (struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ int ret_val = 0;
+ u32 trans_flag = 0;
+ u32 return_td_addr = 0;
+ u8 dev_speed, ed_type = 0, additional_multi_count;
+ u16 max_packet_size;
+
+ u8 dev_addr = 0;
+ u8 ep_num = 0;
+ bool f_is_ep_in = true;
+ u8 interval = 0;
+ u32 sched_frame = 0;
+ u8 hub_addr = 0;
+ u8 hub_port = 0;
+ bool f_is_do_split = false;
+ ed_t *target_ed = NULL;
+ isoch_packet_desc_t *new_isoch_packet_desc = NULL;
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ if (!otghost->port_flag.b.port_connect_status) {
+ printk(KERN_ERR"%s %d\n", __func__, __LINE__);
+ return USB_ERR_NOIO;
+ }
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_urb_enqueue \n");
+
+ /* check ep has ed_t or not */
+ if(!(urb->ep->hcpriv)) {
+ /* for getting dev_speed */
+ switch (urb->dev->speed) {
+ case USB_SPEED_HIGH :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "HS_OTG \n");
+ dev_speed = HIGH_SPEED_OTG;
+ break;
+
+ case USB_SPEED_FULL :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "FS_OTG \n");
+ dev_speed = FULL_SPEED_OTG;
+ break;
+
+ case USB_SPEED_LOW :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "LS_OTG \n");
+ dev_speed = LOW_SPEED_OTG;
+ break;
+
+ default:
+ otg_err(OTG_DBG_OTGHCDI_HCD,
+ "unKnown Device Speed \n");
+ spin_unlock_irq_save_otg(&otghost->lock,
+ spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+
+ /* for getting ed_type */
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_BULK :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "bulk transfer \n");
+ ed_type = BULK_TRANSFER;
+ break;
+
+ case PIPE_INTERRUPT :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "interrupt transfer \n");
+ ed_type = INT_TRANSFER;
+ break;
+
+ case PIPE_CONTROL :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "control transfer \n");
+ ed_type = CONTROL_TRANSFER;
+ break;
+
+ case PIPE_ISOCHRONOUS :
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "isochronous transfer \n");
+ ed_type = ISOCH_TRANSFER;
+ break;
+ default:
+ otg_err(OTG_DBG_OTGHCDI_HCD, "unKnown ep type \n");
+ spin_unlock_irq_save_otg(&otghost->lock,
+ spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+
+ max_packet_size = usb_maxpacket(urb->dev, urb->pipe,
+ !(usb_pipein(urb->pipe)));
+ additional_multi_count = ((max_packet_size) >> 11) & 0x03;
+ dev_addr = usb_pipedevice(urb->pipe);
+ ep_num = usb_pipeendpoint(urb->pipe);
+ f_is_ep_in = usb_pipein(urb->pipe) ? true : false;
+ interval = (u8)(urb->interval);
+ sched_frame = (u8)(urb->start_frame);
+
+ /* check */
+ if(urb->dev->tt == NULL) {
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "urb->dev->tt == NULL\n");
+ hub_port = 0; /* u8 hub_port */
+ hub_addr = 0; /* u8 hub_addr */
+ }
+ else {
+ hub_port = (u8)(urb->dev->ttport);
+ if (urb->dev->tt->hub) {
+ if (((dev_speed == FULL_SPEED_OTG) ||
+ (dev_speed == LOW_SPEED_OTG)) &&
+ (urb->dev->tt) && (urb->dev->tt->hub->devnum != 1)) {
+ if (otghost->is_hs) // only allow split transactions in HS mode
+ f_is_do_split = true;
+ }
+ hub_addr = (u8)(urb->dev->tt->hub->devnum);
+ }
+ if (urb->dev->tt->multi) {
+ hub_addr = 0x80;
+ }
+ }
+ otg_dbg(OTG_DBG_OTGHCDI_HCD,
+ "dev_spped =%d, hub_port=%d, hub_addr=%d\n",
+ dev_speed, hub_port, hub_addr);
+
+ ret_val = create_ed(&target_ed);
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD,
+ "fail to create_ed() \n");
+ spin_unlock_irq_save_otg(&otghost->lock,
+ spin_lock_flag);
+ return ret_val;
+ }
+
+ ret_val = init_ed( target_ed,
+ dev_addr,
+ ep_num,
+ f_is_ep_in,
+ dev_speed,
+ ed_type,
+ max_packet_size,
+ additional_multi_count,
+ interval,
+ sched_frame,
+ hub_addr,
+ hub_port,
+ f_is_do_split,
+ (void *)urb->ep);
+
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD,
+ "fail to init_ed() :err = %d \n",(int)ret_val);
+ otg_mem_free(target_ed);
+ spin_unlock_irq_save_otg(&otghost->lock,
+ spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+
+ urb->ep->hcpriv = (void *)(target_ed);
+ } /* if(!(ep->hcpriv)) */
+ else {
+ dev_addr = usb_pipedevice(urb->pipe);
+ if(((ed_t *)(urb->ep->hcpriv))->ed_desc.device_addr != dev_addr) {
+ ((ed_t *)urb->ep->hcpriv)->ed_desc.device_addr = dev_addr;
+ }
+ }
+
+ target_ed = (ed_t *)urb->ep->hcpriv;
+
+ if(urb->transfer_flags & URB_SHORT_NOT_OK)
+ trans_flag += USB_TRANS_FLAG_NOT_SHORT;
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ trans_flag += USB_TRANS_FLAG_ISO_ASYNCH;
+
+ if(ed_type == ISOCH_TRANSFER) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "ISO not yet supported \n");
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+
+ if (!HC_IS_RUNNING(hcd->state)) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state) \n");
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return -USB_ERR_NODEV;
+ }
+
+ /* in case of unlink-during-submit */
+ if (urb->status != -EINPROGRESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "urb->status is -EINPROGRESS\n");
+ urb->hcpriv = NULL;
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ usb_hcd_giveback_urb(hcd, urb, urb->status);
+ return USB_ERR_SUCCESS;
+ }
+
+ ret_val = issue_transfer(otghost, target_ed, (void *)NULL, (void *)NULL,
+ trans_flag,
+ (usb_pipetype(urb->pipe) == PIPE_CONTROL)?true:false,
+ (u32)urb->setup_packet, (u32)urb->setup_dma,
+ (u32)urb->transfer_buffer, (u32)urb->transfer_dma,
+ (u32)urb->transfer_buffer_length,
+ (u32)urb->start_frame,(u32)urb->number_of_packets,
+ new_isoch_packet_desc, (void *)urb, &return_td_addr);
+
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "fail to issue_transfer() \n");
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+ urb->hcpriv = (void *)return_td_addr;
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int s5pc110_otghcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb )
+ *
+ * @brief dequeue a urb to otg
+ *
+ * @param [in] _hcd : pointer of usb_hcd
+ * [in] _urb : pointer of urb
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+int s5pc110_otghcd_urb_dequeue(
+ struct usb_hcd *_hcd, struct urb *_urb, int status)
+{
+ int ret_val = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd);
+
+ unsigned long spin_lock_flag = 0;
+ td_t *cancel_td;
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_urb_dequeue \n");
+
+ /* Dequeue should be performed only if endpoint is enabled */
+ if (_urb->ep->enabled == 0) {
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ usb_hcd_giveback_urb(_hcd, _urb, status);
+ return USB_ERR_SUCCESS;
+ }
+
+ //kevinh read this from inside the spinlock
+ cancel_td = (td_t *)_urb->hcpriv;
+
+ if (cancel_td == NULL) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "cancel_td is NULL\n");
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+ otg_dbg(OTG_DBG_OTGHCDI_HCD,
+ "s5pc110_otghcd_urb_dequeue, status = %d\n", status);
+
+
+ ret_val = usb_hcd_check_unlink_urb(_hcd, _urb, status);
+ if( (ret_val) && (ret_val != -EIDRM) ) {
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "ret_val = %d\n", ret_val);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ usb_hcd_giveback_urb(_hcd, _urb, status);
+ return ret_val;
+ }
+
+ if (!HC_IS_RUNNING(_hcd->state)) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state) \n");
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ otg_usbcore_giveback(cancel_td);
+ return USB_ERR_SUCCESS;
+ }
+
+ ret_val = cancel_transfer(otghost, cancel_td->parent_ed_p, cancel_td);
+ if(ret_val != USB_ERR_DEQUEUED && ret_val != USB_ERR_NOELEMENT) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "fail to cancel_transfer() \n");
+ otg_usbcore_giveback(cancel_td);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_FAIL;
+ }
+ otg_usbcore_giveback(cancel_td);
+ delete_td(otghost, cancel_td);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * void s5pc110_otghcd_endpoint_disable(
+ * struct usb_hcd *hcd,
+ * struct usb_host_endpoint *ep)
+ *
+ * @brief disable a endpoint
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ * [in] ep : pointer of usb_host_endpoint
+ */
+void s5pc110_otghcd_endpoint_disable(
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ int ret_val = 0;
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_endpoint_disable \n");
+
+ if(!((ed_t *)ep->hcpriv))
+ return;
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ ret_val = delete_ed(otghost, (ed_t *)ep->hcpriv);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "fail to delete_ed() \n");
+ return ;
+ }
+
+ /* ep->hcpriv = NULL; delete_ed coveres it */
+}
+
+/**
+ * int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf)
+ *
+ * @brief get status of root hub
+ *
+ * @param [in] _hcd : pointer of usb_hcd
+ * [inout] _buf : pointer of buffer for write a status data
+ *
+ * @return ret_val : return port status \n
+ */
+int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf)
+{
+ int ret_val = 0;
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd);
+
+ /* otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_status_data \n"); */
+
+ /* if !USB_SUSPEND, root hub timers won't get shut down ... */
+ if (!HC_IS_RUNNING(_hcd->state)) {
+ otg_dbg(OTG_DBG_OTGHCDI_HCD,
+ "_hcd->state is NOT HC_IS_RUNNING \n");
+ return 0;
+ }
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ ret_val = get_otg_port_status(_hcd, OTG_PORT_NUMBER, _buf);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ return (int)ret_val;
+}
+
+/**
+ * int s5pc110_otghcd_hub_control()
+ *
+ * @brief control root hub
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ * [in] typeReq : type of control request
+ * [in] value : value
+ * [in] index : index
+ * [in] buf_p : pointer of urb
+ * [in] length : type of gfp_t
+ *
+ * @return ret_val : return root_hub_feature \n
+ */
+int
+s5pc110_otghcd_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 value,
+ u16 index,
+ char* buf_p,
+ u16 length
+)
+{
+ int ret_val = 0;
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_control \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ ret_val = root_hub_feature(hcd, OTG_PORT_NUMBER,
+ typeReq, value, (void *)buf_p);
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD, "fail to root_hub_feature() \n");
+ return ret_val;
+ }
+ return (int)ret_val;
+}
+
+/**
+ * int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd)
+ *
+ * @brief suspend otg hcd
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ *
+ * @return USB_ERR_SUCCESS \n
+ */
+int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd)
+{
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_suspend \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ bus_suspend();
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd)
+ *
+ * @brief resume otg hcd
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ *
+ * @return USB_ERR_SUCCESS \n
+ */
+int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd)
+{
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_resume \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ if(bus_resume(otghost) != USB_ERR_SUCCESS) {
+ return USB_ERR_FAIL;
+ }
+
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port)
+ *
+ * @brief reset otg port
+ *
+ * @param [in] hcd : pointer of usb_hcd
+ * [in] port : number of port
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * ret_val : If call fail \n
+ */
+int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port)
+{
+ int ret_val = 0;
+
+ unsigned long spin_lock_flag = 0;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start_port_reset \n");
+
+ spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag);
+ ret_val = reset_and_enable_port(hcd, OTG_PORT_NUMBER);
+ spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag);
+
+ if(ret_val != USB_ERR_SUCCESS) {
+ otg_err(OTG_DBG_OTGHCDI_HCD,
+ "fail to reset_and_enable_port() \n");
+ return ret_val;
+ }
+ return USB_ERR_SUCCESS;
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h
new file mode 100644
index 0000000..95764d8
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-hcd.h
+ * @brief header of s3c-otg-hcdi-hcd \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Modifying for successful rmmod & disconnecting \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_HCD_H_
+#define _S3C_OTG_HCDI_HCD_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//for IRQ_NONE (0) IRQ_HANDLED (1) IRQ_RETVAL(x) ((x) != 0)
+#include <linux/interrupt.h>
+
+#include <linux/usb.h>
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-hcdi-kal.h"
+
+#include "s3c-otg-common-common.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-const.h"
+
+#include "s3c-otg-transfer-transfer.h"
+#include "s3c-otg-oci.h"
+#include "s3c-otg-roothub.h"
+
+//placed in ISR
+//void otg_handle_interrupt(struct usb_hcd *hcd);
+
+irqreturn_t s5pc110_otghcd_irq(struct usb_hcd *hcd);
+
+int s5pc110_otghcd_start(struct usb_hcd *hcd);
+void s5pc110_otghcd_stop(struct usb_hcd *hcd);
+void s5pc110_otghcd_shutdown(struct usb_hcd *hcd);
+
+int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd);
+
+int s5pc110_otghcd_urb_enqueue(
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags);
+
+int s5pc110_otghcd_urb_dequeue(
+ struct usb_hcd *_hcd,
+ struct urb *_urb,
+ int status);
+
+void s5pc110_otghcd_endpoint_disable(
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+
+int s5pc110_otghcd_hub_status_data(
+ struct usb_hcd *_hcd,
+ char *_buf);
+
+int s5pc110_otghcd_hub_control(
+ struct usb_hcd *hcd,
+ u16 type_req,
+ u16 value,
+ u16 index,
+ char * buf,
+ u16 length);
+
+int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd);
+int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd);
+int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port);
+
+/**
+ * @struct hc_driver s5pc110_otg_hc_driver
+ *
+ * @brief implementation of hc_driver for OTG HCD
+ *
+ * describe in detail
+ */
+static const struct hc_driver s5pc110_otg_hc_driver = {
+ .description = "EMSP_OTGHCD",
+ .product_desc = "S3C OTGHCD",
+ .hcd_priv_size = sizeof(struct sec_otghost),
+
+ .irq = s5pc110_otghcd_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /** basic lifecycle operations */
+ //.reset =
+ .start = s5pc110_otghcd_start,
+ //.suspend = ,
+ //.resume = ,
+ .stop = s5pc110_otghcd_stop,
+ .shutdown = s5pc110_otghcd_shutdown,
+
+ /** managing i/o requests and associated device resources */
+ .urb_enqueue = s5pc110_otghcd_urb_enqueue,
+ .urb_dequeue = s5pc110_otghcd_urb_dequeue,
+ .endpoint_disable = s5pc110_otghcd_endpoint_disable,
+
+ /** scheduling support */
+ .get_frame_number = s5pc110_otghcd_get_frame_number,
+
+ /** root hub support */
+ .hub_status_data = s5pc110_otghcd_hub_status_data,
+ .hub_control = s5pc110_otghcd_hub_control,
+ //.hub_irq_enable =
+ .bus_suspend = s5pc110_otghcd_bus_suspend,
+ .bus_resume = s5pc110_otghcd_bus_resume,
+ .start_port_reset = s5pc110_otghcd_start_port_reset,
+};
+
+static inline struct sec_otghost *hcd_to_sec_otghost (struct usb_hcd *hcd)
+{
+ return (struct sec_otghost *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *sec_otghost_to_hcd (struct sec_otghost *otghost)
+{
+ return container_of ((void *) otghost, struct usb_hcd, hcd_priv);
+}
+
+int otg_hcd_init_modules(struct sec_otghost *otghost);
+void otg_hcd_deinit_modules(struct sec_otghost *otghost);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _S3C_OTG_HCDI_HCD_H_ */
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h
new file mode 100644
index 0000000..23bded6
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h
@@ -0,0 +1,420 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-kal.h
+ * @brief header of s3c-otg-hcdi-kal \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Modifying for successful rmmod & disconnecting \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_KAL_H_
+#define _S3C_OTG_HCDI_KAL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-common-common.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-const.h"
+
+#include <asm/io.h> //for readl, writel
+#include <linux/usb/ch9.h> //for usb_device_driver, enum usb_device_speed
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <mach/map.h>
+#include <plat/regs-otg.h>
+
+extern volatile u8 * g_pUDCBase;
+extern struct usb_hcd* g_pUsbHcd;
+
+#include <linux/spinlock.h>
+#define SPINLOCK_t spinlock_t
+#define SPIN_LOCK_INIT SPIN_LOCK_UNLOCKED
+
+#define spin_lock_otg(lock) spin_lock(lock)
+#define spin_lock_irg_otg(lock) spin_lock_irq(lock)
+#define spin_lock_irq_save_otg(lock, flags) spin_lock_irqsave(lock, flags)
+
+#define spin_unlock_otg(lock) spin_unlock(lock)
+#define spin_unlock_irq_otg(lock) spin_unlock_irq(lock)
+#define spin_unlock_irq_save_otg(lock, flags) spin_unlock_irqrestore(lock, flags)
+
+#define ctrlr_base_reg_addr(offset) \
+ ((volatile unsigned int *)((g_pUDCBase) + (offset)))
+/**
+ * otg_kal_make_ep_null
+ *
+ * @brief make ep->hcpriv NULL
+ *
+ * @param [in] pdelete_ed : pointer of ed
+ *
+ * @return void \n
+ */
+static inline void
+otg_kal_make_ep_null
+(
+ ed_t *pdelete_ed
+)
+{
+ ((struct usb_host_endpoint *)(pdelete_ed->ed_private))->hcpriv = NULL;
+}
+//---------------------------------------------------------------------------------------
+
+/**
+ * otg_kal_is_ep_null
+ *
+ * @brief check ep->hcpriv is NULL or not
+ *
+ * @param [in] pdelete_ed : pointer of ed
+ *
+ * @return bool \n
+ */
+static inline bool
+otg_kal_is_ep_null
+(
+ ed_t *pdelete_ed
+)
+{
+ if (((struct usb_host_endpoint *)(pdelete_ed->ed_private))->hcpriv == NULL)
+ return true;
+ else
+ return false;
+}
+//---------------------------------------------------------------------------------------
+
+
+/**
+ * int otg_usbcore_get_calc_bustime()
+ *
+ * @brief get bus time of usbcore
+ *
+ * @param [in] speed : usb speed
+ * [in] is_input : input or not
+ * [in] is_isoch : isochronous or not
+ * [in] byte_count : bytes
+ *
+ * @return bus time of usbcore \n
+ */
+static inline int
+otg_usbcore_get_calc_bustime
+(
+ u8 speed,
+ bool is_input,
+ bool is_isoch,
+ unsigned int byte_count
+)
+{
+ unsigned int convert_speed = 0;
+
+ otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_get_calc_bustime \n");
+/* enum usb_device_speed {
+ USB_SPEED_UNKNOWN = 0,
+ USB_SPEED_LOW, USB_SPEED_FULL,
+ USB_SPEED_HIGH,
+ USB_SPEED_VARIABLE, };*/
+ switch(speed) {
+ case HIGH_SPEED_OTG :
+ convert_speed = USB_SPEED_HIGH;
+ break;
+
+ case FULL_SPEED_OTG :
+ convert_speed = USB_SPEED_FULL;
+ break;
+
+ case LOW_SPEED_OTG :
+ convert_speed = USB_SPEED_LOW;
+ break;
+
+ default:
+ convert_speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ return usb_calc_bus_time(convert_speed, is_input, (unsigned int)is_isoch, byte_count);
+}
+
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_usbcore_giveback(td_t td_p)
+ *
+ * @brief give-back a td as urb
+ *
+ * @param [in] td_p : pointer of td_t to give back
+ *
+ * @return void \n
+ */
+static inline void
+otg_usbcore_giveback(td_t * td_p)
+{
+ struct urb *urb_p = NULL;
+
+ otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_giveback \n");
+
+ if (td_p->td_private == NULL)
+ {
+ otg_err(OTG_DBG_OTGHCDI_KAL,
+ "td_p->td_private == NULL \n");
+ return;
+ }
+
+ urb_p = (struct urb *)td_p->td_private;
+
+ urb_p->actual_length = (int)(td_p->transferred_szie);
+ urb_p->status = (int)(td_p->error_code);
+ urb_p->error_count = (int)(td_p->err_cnt);
+ urb_p->hcpriv = NULL;
+
+ usb_hcd_giveback_urb(g_pUsbHcd, urb_p, urb_p->status);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_usbcore_hc_died(void)
+ *
+ * @brief inform usbcore of hc die
+ *
+ * @return void \n
+ */
+static inline void
+otg_usbcore_hc_died(void)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_hc_died \n");
+ usb_hc_died(g_pUsbHcd);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_usbcore_poll_rh_status(void)
+ *
+ * @brief invoke usbcore's usb_hcd_poll_rh_status
+ *
+ * @param void
+ *
+ * @return void \n
+ */
+static inline void
+otg_usbcore_poll_rh_status(void)
+{
+ usb_hcd_poll_rh_status(g_pUsbHcd);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_usbcore_resume_roothub(void)
+ *
+ * @brief invoke usbcore's usb_hcd_resume_root_hub
+ *
+ * @param void
+ *
+ * @return void \n
+ */
+static inline void
+otg_usbcore_resume_roothub(void)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL,
+ "otg_usbcore_resume_roothub \n");
+ usb_hcd_resume_root_hub(g_pUsbHcd);
+};
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_usbcore_inc_usb_bandwidth(u32 band_width)
+ *
+ * @brief increase bandwidth of usb bus
+ *
+ * @param [in] band_width : bandwidth to be increased
+ *
+ * @return USB_ERR_SUCCESS \n
+ */
+static inline int
+otg_usbcore_inc_usb_bandwidth(u32 band_width)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL,
+ "otg_usbcore_inc_usb_bandwidth \n");
+ hcd_to_bus(g_pUsbHcd)->bandwidth_allocated += band_width;
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_usbcore_des_usb_bandwidth(u32 uiBandwidth)
+ *
+ * @brief decrease bandwidth of usb bus
+ *
+ * @param [in] band_width : bandwidth to be decreased
+ *
+ * @return USB_ERR_SUCCESS \n
+ */
+static inline int
+otg_usbcore_des_usb_bandwidth(u32 band_width)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL,
+ "otg_usbcore_des_usb_bandwidth \n");
+ hcd_to_bus(g_pUsbHcd)->bandwidth_allocated -= band_width;
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_usbcore_inc_periodic_transfer_cnt(u8 transfer_type)
+ *
+ * @brief increase count of periodic transfer
+ *
+ * @param [in] transfer_type : type of transfer
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_usbcore_inc_periodic_transfer_cnt(u8 transfer_type)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL,
+ "otg_usbcore_inc_periodic_transfer_cnt \n");
+
+ switch(transfer_type) {
+ case INT_TRANSFER :
+ hcd_to_bus(g_pUsbHcd)->bandwidth_int_reqs++;
+ break;
+ case ISOCH_TRANSFER :
+ hcd_to_bus(g_pUsbHcd)->bandwidth_isoc_reqs++;
+ break;
+ default:
+ otg_err(OTG_DBG_OTGHCDI_KAL,
+ "not proper TransferType for otg_usbcore_inc_periodic_transfer_cnt()\n");
+ return USB_ERR_FAIL;
+ }
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_usbcore_des_periodic_transfer_cnt(u8 transfer_type)
+ *
+ * @brief decrease count of periodic transfer
+ *
+ * @param [in] transfer_type : type of transfer
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_usbcore_des_periodic_transfer_cnt(u8 transfer_type)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_KAL,
+ "otg_usbcore_des_periodic_transfer_cnt \n");
+
+ switch(transfer_type) {
+ case INT_TRANSFER :
+ hcd_to_bus(g_pUsbHcd)->bandwidth_int_reqs--;
+ break;
+ case ISOCH_TRANSFER :
+ hcd_to_bus(g_pUsbHcd)->bandwidth_isoc_reqs--;
+ break;
+ default:
+ otg_err(OTG_DBG_OTGHCDI_KAL,
+ "not proper TransferType for otg_usbcore_des_periodic_transfer_cnt()\n");
+ return USB_ERR_FAIL;
+ }
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * u32 read_reg_32(u32 offset)
+ *
+ * @brief Reads the content of a register.
+ *
+ * @param [in] offset : offset of address of register to read.
+ *
+ * @return contents of the register. \n
+ * @remark call readl()
+ */
+static inline u32 read_reg_32(u32 offset)
+{
+ volatile unsigned int * reg_addr_p = ctrlr_base_reg_addr(offset);
+
+ return *reg_addr_p;
+ //return readl(reg_addr_p);
+};
+//-------------------------------------------------------------------------------
+
+/**
+ * void write_reg_32( u32 offset, const u32 value)
+ *
+ * @brief Writes a register with a 32 bit value.
+ *
+ * @param [in] offset : offset of address of register to write.
+ * @param [in] value : value to write
+ *
+ * @remark call writel()
+ */
+static inline void write_reg_32( u32 offset, const u32 value)
+{
+ volatile unsigned int * reg_addr_p = ctrlr_base_reg_addr(offset);
+
+ *reg_addr_p = value;
+ //writel( value, reg_addr_p );
+};
+//-------------------------------------------------------------------------------
+
+/**
+ * void update_reg_32(u32 offset, u32 value)
+ *
+ * @brief logic or operation
+ *
+ * @param [in] offset : offset of address of register to write.
+ * @param [in] value : value to or
+ *
+ */
+static inline void update_reg_32(u32 offset, u32 value)
+{
+ write_reg_32(offset, (read_reg_32(offset) | value));
+}
+//---------------------------------------------------------------------------------------
+
+/**
+ * void clear_reg_32(u32 offset, u32 value)
+ *
+ * @brief logic not operation
+ *
+ * @param [in] offset : offset of address of register to write.
+ * @param [in] value : value to not
+ *
+ */
+static inline void clear_reg_32(u32 offset, u32 value)
+{
+ write_reg_32(offset, (read_reg_32(offset) & ~value));
+}
+//---------------------------------------------------------------------------------------
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _S3C_OTG_HCDI_KAL_H_ */
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h
new file mode 100644
index 0000000..03ae3d8
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-list.h
+ * @brief list functions for otg \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_LIST_H_
+#define _S3C_OTG_HCDI_LIST_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "s3c-otg-common-common.h"
+#include "s3c-otg-hcdi-debug.h"
+
+#include <linux/list.h>
+
+typedef struct list_head otg_list_head;
+
+#define otg_list_get_node(ptr, type, member) container_of(ptr, type, member)
+
+
+#define otg_list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+
+/**
+ * void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p)
+ *
+ * @brief push a list node into the next of head
+ *
+ * @param [in] new_node_p : node to be pushed
+ * @param [in] otg_list_head : target list head
+ *
+ * @return void \n
+ */
+
+static inline
+void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_push_next \n");
+ list_add(new_node_p, list_head_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p)
+ *
+ * @brief push a list node into the previous of head
+ *
+ * @param [in] new_node_p : node to be pushed
+ * @param [in] otg_list_head : target list head
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_push_prev \n");
+ list_add_tail(new_node_p, list_head_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_pop(otg_list_head *list_entity_p)
+ *
+ * @brief pop a list node
+ *
+ * @param [in] new_node_p : node to be poped
+ * @param [in] otg_list_head : target list head
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_pop(otg_list_head *list_entity_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_pop \n");
+ list_del(list_entity_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p)
+ *
+ * @brief move a list to next of head
+ *
+ * @param [in] new_node_p : node to be moved
+ * @param [in] otg_list_head : target list head
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_move_next \n");
+ list_move(node_p, list_head_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p)
+ *
+ * @brief move a list to previous of head
+ *
+ * @param [in] new_node_p : node to be moved
+ * @param [in] otg_list_head : target list head
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_move_prev \n");
+ list_move_tail(node_p, list_head_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * bool otg_list_empty(otg_list_head *list_head_p)
+ *
+ * @brief check a list empty or not
+ *
+ * @param [in] list_head_p : node to check
+ *
+ * @return true : empty list \n
+ * false : not empty list
+ */
+static inline
+bool otg_list_empty(otg_list_head *list_head_p)
+{
+
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_empty \n");
+ if(list_empty(list_head_p))
+ return true;
+ return false;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p)
+ *
+ * @brief merge two list
+ *
+ * @param [in] list_p : a head
+ * @param [in] head_p : target list head
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_merge \n");
+ list_splice(list_p, head_p);
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * void otg_list_init(otg_list_head *list_p)
+ *
+ * @brief initialize a list
+ *
+ * @param [in] list_p : node to be initialized
+ *
+ * @return void \n
+ */
+static inline
+void otg_list_init(otg_list_head *list_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_init \n");
+ list_p->next = list_p;
+ list_p->prev = list_p;
+}
+//-------------------------------------------------------------------------------
+
+/*
+void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p );
+void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p);
+void otg_list_pop(otg_list_head *list_entity_p);
+
+void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p);
+void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p);
+
+bool otg_list_empty(otg_list_head *list_head_p);
+void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p);
+void otg_list_init(otg_list_head *list_p);
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _S3C_OTG_HCDI_LIST_H_ */
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h
new file mode 100644
index 0000000..c87f15e
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * @file s3c-otg-hcdi-memory.h
+ * @brief header of s3c-otg-hcdi-memory \n
+ * @version
+ * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Creating the initial version of this code \n
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ * @see None
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _S3C_OTG_HCDI_MEMORY_H_
+#define _S3C_OTG_HCDI_MEMORY_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "s3c-otg-common-common.h"
+#include "s3c-otg-hcdi-debug.h"
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+/**
+ * @enum otg_mem_alloc_flag
+ *
+ * @brief enumeration for flag of memory allocation
+ */
+typedef
+enum otg_mem_alloc_flag
+{
+ USB_MEM_SYNC, USB_MEM_ASYNC, USB_MEM_DMA
+}otg_mem_alloc_flag_t;
+//---------------------------------------------------------------------------------------
+
+/*
+inline int otg_mem_alloc(void ** addr_pp, u16 byte_size, otg_mem_alloc_flag_t type);
+inline int otg_mem_copy(void * to_addr_p, void * from_addr_p, u16 byte_size);
+//inline int otg_mem_free(void * addr_p);
+inline int otg_mem_set(void * addr_p, char value, u16 byte_size);
+*/
+
+/**
+ * int otg_mem_alloc(void ** addr_pp, u16 byte_size, u8 ubType);
+ *
+ * @brief allocating momory specified
+ *
+ * @param [inout] addr_pp : address to be assigned
+ * [in] byte_size : size of memory
+ * [in] type : otg_mem_alloc_flag_t type
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_mem_alloc (
+ void ** addr_pp,
+ u16 byte_size,
+ otg_mem_alloc_flag_t type
+)
+{
+ gfp_t flags;
+ otg_dbg(OTG_DBG_OTGHCDI_MEM, "otg_mem_alloc \n");
+
+ switch(type) {
+ case USB_MEM_SYNC:
+ flags = GFP_KERNEL;
+ break;
+ case USB_MEM_ASYNC:
+ flags = GFP_ATOMIC;
+ break;
+ case USB_MEM_DMA:
+ flags = GFP_DMA;
+ break;
+ default:
+ otg_err(OTG_DBG_OTGHCDI_MEM,
+ "not proper otg_mem_alloc_flag_t in otg_mem_alloc \n");
+ return USB_ERR_FAIL;
+ }
+
+ *addr_pp = kmalloc((size_t)byte_size, flags);
+ if(*addr_pp == 0) {
+ otg_err(OTG_DBG_OTGHCDI_MEM,
+ "kmalloc failed\n");
+ return USB_ERR_FAIL;
+ }
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_mem_copy(void * to_addr_p, void * from_addr_p, u16 byte_size);
+ *
+ * @brief memory copy
+ *
+ * @param [in] to_addr_p : target address
+ * [in] from_addr_p : source address
+ * [in] byte_size : size
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_mem_copy
+(
+ void * to_addr_p,
+ void * from_addr_p,
+ u16 byte_size
+)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_MEM,
+ "otg_mem_copy \n");
+
+ memcpy(to_addr_p, from_addr_p, (size_t)byte_size);
+
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_mem_free(void * addr_p);
+ *
+ * @brief de-allocating memory
+ *
+ * @param [in] addr_p : target address to be de-allocated
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_mem_free(void * addr_p)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_MEM,
+ "otg_mem_free \n");
+ kfree(addr_p);
+ return USB_ERR_SUCCESS;
+}
+
+//-------------------------------------------------------------------------------
+
+/**
+ * int otg_mem_set(void * addr_p, char value, u16 byte_size)
+ *
+ * @brief writing a value to memory
+ *
+ * @param [in] addr_p : target address
+ * [in] value : value to be written
+ * [in] byte_size : size
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ */
+static inline int
+otg_mem_set
+(
+ void * addr_p,
+ char value,
+ u16 byte_size
+)
+{
+ otg_dbg(OTG_DBG_OTGHCDI_MEM,
+ "otg_mem_set \n");
+ memset(addr_p, value, (size_t)byte_size);
+ return USB_ERR_SUCCESS;
+}
+//-------------------------------------------------------------------------------
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _S3C_OTG_HCDI_MEMORY_H_ */
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-isr.c b/drivers/usb/host/s3c-otg/s3c-otg-isr.c
new file mode 100644
index 0000000..6bb790d
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-isr.c
@@ -0,0 +1,294 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Isr.c
+ * [Description] : The file implement the external and internal functions of ISR
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2009/02/10
+ * [Revision History]
+ * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and implements functions of ISR
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-isr.h"
+
+/**
+ * void otg_handle_interrupt(void)
+ *
+ * @brief Main interrupt processing routine
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+
+__inline__ void otg_handle_interrupt(struct usb_hcd *hcd)
+{
+ gintsts_t clearIntr = {.d32 = 0};
+ gintsts_t gintsts = {.d32 = 0};
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ gintsts.d32 = read_reg_32(GINTSTS) & read_reg_32(GINTMSK);
+
+ otg_dbg(OTG_DBG_ISR, "otg_handle_interrupt - GINTSTS=0x%8x\n",
+ gintsts.d32);
+
+ if (gintsts.b.wkupintr) {
+ otg_dbg(true, "Wakeup Interrupt\n");
+ clearIntr.b.wkupintr = 1;
+ }
+
+ if (gintsts.b.disconnect) {
+ otg_dbg(true, "Disconnect Interrupt\n");
+ otghost->port_flag.b.port_connect_status_change = 1;
+ otghost->port_flag.b.port_connect_status = 0;
+ clearIntr.b.disconnect = 1;
+ /*
+ wake_unlock(&otghost->wake_lock);
+ */
+ }
+
+ if (gintsts.b.conidstschng) {
+ otg_dbg(OTG_DBG_ISR, "Connect ID Status Change Interrupt\n");
+ clearIntr.b.conidstschng = 1;
+ oci_init_mode(otghost);
+ }
+
+ if (gintsts.b.hcintr) {
+ /* Mask Channel Interrupt to prevent generating interrupt */
+ otg_dbg(OTG_DBG_ISR, "Channel Interrupt\n");
+ if(!otghost->ch_halt) {
+ do_transfer_checker(otghost);
+ }
+ }
+
+ if (gintsts.b.portintr) {
+ /* Read Only */
+ otg_dbg(true, "Port Interrupt\n");
+ process_port_intr(hcd);
+ }
+
+
+ if (gintsts.b.otgintr) {
+ /* Read Only */
+ otg_dbg(OTG_DBG_ISR, "OTG Interrupt\n");
+ }
+
+ if (gintsts.b.sofintr) {
+ /* otg_dbg(OTG_DBG_ISR, "SOF Interrupt\n"); */
+ do_schedule(otghost);
+ clearIntr.b.sofintr = 1;
+ }
+
+ if (gintsts.b.modemismatch) {
+ otg_dbg(OTG_DBG_ISR, "Mode Mismatch Interrupt\n");
+ clearIntr.b.modemismatch = 1;
+ }
+ update_reg_32(GINTSTS, clearIntr.d32);
+}
+
+/**
+ * void mask_channel_interrupt(u32 ch_num, u32 mask_info)
+ *
+ * @brief Mask specific channel interrupt
+ *
+ * @param [IN] chnum : channel number for masking
+ * [IN] mask_info : mask information to write register
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void mask_channel_interrupt(u32 ch_num, u32 mask_info)
+{
+ clear_reg_32(HCINTMSK(ch_num),mask_info);
+}
+
+/**
+ * void unmask_channel_interrupt(u32 ch_num, u32 mask_info)
+ *
+ * @brief Unmask specific channel interrupt
+ *
+ * @param [IN] chnum : channel number for unmasking
+ * [IN] mask_info : mask information to write register
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void unmask_channel_interrupt(u32 ch_num, u32 mask_info)
+{
+ update_reg_32(HCINTMSK(ch_num),mask_info);
+}
+
+/**
+ * int get_ch_info(hc_info_t * hc_reg, u8 ch_num)
+ *
+ * @brief Get current channel information about specific channel
+ *
+ * @param [OUT] hc_reg : structure to write channel inforamtion value
+ * [IN] ch_num : channel number for unmasking
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+int get_ch_info(hc_info_t *hc_reg, u8 ch_num)
+{
+ if(hc_reg !=NULL) {
+ hc_reg->hc_int_msk.d32 = read_reg_32(HCINTMSK(ch_num));
+ hc_reg->hc_int.d32 = read_reg_32(HCINT(ch_num));
+ hc_reg->dma_addr = read_reg_32(HCDMA(ch_num));
+ hc_reg->hc_char.d32 = read_reg_32(HCCHAR(ch_num));
+ hc_reg->hc_size.d32 = read_reg_32(HCTSIZ(ch_num));
+
+ return USB_ERR_SUCCESS;
+ }
+ return USB_ERR_FAIL;
+}
+
+/**
+ * void get_intr_ch(u32* haint, u32* haintmsk)
+ *
+ * @brief Get Channel Interrupt Information in HAINT, HAINTMSK register
+ *
+ * @param [OUT] haint : HAINT register value
+ * [OUT] haintmsk : HAINTMSK register value
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void get_intr_ch(u32 *haint, u32 *haintmsk)
+{
+ *haint = read_reg_32(HAINT);
+ *haintmsk = read_reg_32(HAINTMSK);
+}
+
+/**
+ * void clear_ch_intr(u8 ch_num, u32 clear_bit)
+ *
+ * @brief Get Channel Interrupt Information in HAINT, HAINTMSK register
+ *
+ * @param [IN] haint : HAINT register value
+ * [IN] haintmsk : HAINTMSK register value
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void clear_ch_intr(u8 ch_num, u32 clear_bit)
+{
+ update_reg_32(HCINT(ch_num),clear_bit);
+}
+
+/**
+ * void enable_sof(void)
+ *
+ * @brief Generate SOF Interrupt.
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void enable_sof(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ gintmsk.b.sofintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+}
+
+/**
+ * void disable_sof(void)
+ *
+ * @brief Stop to generage SOF interrupt
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void disable_sof(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ gintmsk.b.sofintr = 1;
+ clear_reg_32(GINTMSK, gintmsk.d32);
+}
+
+/*Internal function of isr */
+void process_port_intr(struct usb_hcd *hcd)
+{
+ hprt_t hprt; /* by ss1, clear_hprt; */
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ hprt.d32 = read_reg_32(HPRT);
+
+ otg_dbg(OTG_DBG_ISR, "Port Interrupt() : HPRT = 0x%x\n",hprt.d32);
+
+ if(hprt.b.prtconndet) {
+ otg_dbg(true, "detect connection");
+
+ otghost->port_flag.b.port_connect_status_change = 1;
+
+ if(hprt.b.prtconnsts)
+ otghost->port_flag.b.port_connect_status = 1;
+
+ /* wake_lock(&otghost->wake_lock); */
+ }
+
+
+ if(hprt.b.prtenchng) {
+ otg_dbg(true, "port enable/disable changed\n");
+ otghost->port_flag.b.port_enable_change = 1;
+ }
+
+ if(hprt.b.prtovrcurrchng) {
+ otg_dbg(true, "over current condition is changed\n");
+ if(hprt.b.prtovrcurract) {
+ otg_dbg(true, "port_over_current_change = 1\n");
+ otghost->port_flag.b.port_over_current_change = 1;
+ }
+ else {
+ otghost->port_flag.b.port_over_current_change = 0;
+ }
+ /* defer otg power control into a kernel thread */
+ queue_work(otghost->wq, &otghost->work);
+ }
+
+ hprt.b.prtena = 0; /* prtena¸¦ writeclear½ÃŰ¸é ¾ÈµÊ. */
+ /* hprt.b.prtpwr = 0; */
+ hprt.b.prtrst = 0;
+ hprt.b.prtconnsts = 0;
+
+ write_reg_32(HPRT, hprt.d32);
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-isr.h b/drivers/usb/host/s3c-otg/s3c-otg-isr.h
new file mode 100644
index 0000000..cad4cf5
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-isr.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] :s3c-otg-isr.h
+ * [Description] : The Header file defines the external and internal functions of ISR.
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2008/06/18
+ * [Revision History]
+ * (1) 2008/06/18 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and defines functions of Scheduler
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _ISR_H_
+#define _ISR_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-regdef.h"
+#include "s3c-otg-hcdi-kal.h"
+#include "s3c-otg-scheduler-scheduler.h"
+#include "s3c-otg-transferchecker-common.h"
+#include "s3c-otg-roothub.h"
+#include "s3c-otg-oci.h"
+
+__inline__ void otg_handle_interrupt(struct usb_hcd *hcd);
+
+void process_port_intr(struct usb_hcd *hcd);
+
+void mask_channel_interrupt(u32 ch_num, u32 mask_info);
+
+void unmask_channel_interrupt(u32 ch_num, u32 mask_info);
+
+extern int get_ch_info(hc_info_t *hc_reg, u8 ch_num);
+
+extern void get_intr_ch(u32 *haint, u32 *haintmsk);
+
+extern void clear_ch_intr(u8 ch_num, u32 clear_bit);
+
+extern void enable_sof(void);
+
+extern void disable_sof(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _ISR_H_ */
+
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-oci.c b/drivers/usb/host/s3c-otg/s3c-otg-oci.c
new file mode 100644
index 0000000..0744b9e
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-oci.c
@@ -0,0 +1,815 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : OCI.c
+ * [Description] : The file implement the external and internal functions of OCI
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2009/02/10
+ * [Revision History]
+ * (1) 2008/06/12 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and Implement functions of OCI
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-oci.h"
+
+static bool ch_enable[16];
+
+/**
+ * int oci_init(struct sec_otghost *otghost)
+ *
+ * @brief Initialize oci module.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_init(struct sec_otghost *otghost)
+{
+ otg_mem_set((void*)ch_enable, true, sizeof(bool)*16);
+ otghost->ch_halt = false;
+
+ if(oci_sys_init(otghost) == USB_ERR_SUCCESS) {
+ if(oci_core_reset() == USB_ERR_SUCCESS) {
+ oci_set_global_interrupt(false);
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ otg_dbg(OTG_DBG_OCI, "oci_core_reset() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+
+ return USB_ERR_FAIL;
+}
+
+/**
+ * int oci_core_init(struct sec_otghost *otghost)
+ *
+ * @brief process core initialize as s5pc110 otg spec
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_core_init(struct sec_otghost *otghost)
+{
+ gahbcfg_t ahbcfg = {.d32 = 0};
+ gusbcfg_t usbcfg = {.d32 = 0};
+ ghwcfg2_t hwcfg2 = {.d32 = 0};
+ gintmsk_t gintmsk = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_core_init \n");
+
+ usbcfg.d32 = read_reg_32(GUSBCFG);
+ otg_dbg(OTG_DBG_OCI, "before - GUSBCFG=0x%x, GOTGCTL=0x%x\n",
+ usbcfg.d32, read_reg_32(GOTGCTL));
+
+ /* PHY parameters */
+ usbcfg.b.physel = 0;
+ usbcfg.b.phyif = 1; /* 16 bit */
+ usbcfg.b.ulpi_utmi_sel = 0; /* UTMI */
+ /* usbcfg.b.ddrsel = 1; */ /* DDR */
+ usbcfg.b.usbtrdtim = 5; /* 16 bit UTMI */
+ usbcfg.b.toutcal = 7;
+ usbcfg.b.forcehstmode = 1;
+ write_reg_32 (GUSBCFG, usbcfg.d32);
+
+ // per
+ // http://forum.xda-developers.com/showthread.php?t=709135&page=21
+ // we need a 25msec delay after setting this -kevinh
+
+ mdelay(100);
+
+ otg_dbg(OTG_DBG_OCI,
+ "after - GUSBCFG=0x%x, GOTGCTL=0x%x\n",
+ read_reg_32(GUSBCFG),
+ read_reg_32(GOTGCTL));
+
+ /* Reset after setting the PHY parameters */
+ if(oci_core_reset() == USB_ERR_SUCCESS) {
+ /* Program the GAHBCFG Register.*/
+ hwcfg2.d32 = read_reg_32 (GHWCFG2);
+
+ switch (hwcfg2.b.architecture) {
+ case HWCFG2_ARCH_SLAVE_ONLY:
+ otg_dbg(OTG_DBG_OCI, "Slave Only Mode\n");
+ ahbcfg.b.nptxfemplvl = 0;
+ ahbcfg.b.ptxfemplvl = 0;
+ break;
+
+ case HWCFG2_ARCH_EXT_DMA:
+ otg_dbg(OTG_DBG_OCI, "External DMA Mode - TBD!\n");
+ break;
+
+ case HWCFG2_ARCH_INT_DMA:
+ otg_dbg(OTG_DBG_OCI, "Internal DMA Setting \n");
+ ahbcfg.b.dmaenable = true;
+ ahbcfg.b.hburstlen = INT_DMA_MODE_INCR4;
+ break;
+
+ default:
+ otg_dbg(OTG_DBG_OCI, "ERR> hwcfg2\n ");
+ break;
+ }
+ write_reg_32 (GAHBCFG, ahbcfg.d32);
+
+ /* Program the GUSBCFG register.*/
+ switch (hwcfg2.b.op_mode) {
+ case MODE_HNP_SRP_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_HNP_SRP_CAPABLE \n");
+ usbcfg.b.hnpcap = 1;
+ usbcfg.b.srpcap = 1;
+
+ otg_dbg(OTG_DBG_OCI,
+ "OTG_DBG_OCI : use HNP and SRP \n");
+ break;
+
+ case MODE_SRP_ONLY_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_ONLY_CAPABLE \n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_HNP_SRP_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_HNP_SRP_CAPABLE \n");
+ usbcfg.b.hnpcap = 0;
+ break;
+
+ case MODE_SRP_CAPABLE_DEVICE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_DEVICE \n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_SRP_CAPABLE_DEVICE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_DEVICE \n");
+ usbcfg.b.srpcap = 0;
+ break;
+
+ case MODE_SRP_CAPABLE_HOST:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_HOST \n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_SRP_CAPABLE_HOST:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_HOST \n");
+ usbcfg.b.srpcap = 0;
+ break;
+ default :
+ otg_err(OTG_DBG_OCI, "ERR> hwcfg2\n ");
+ break;
+ }
+ write_reg_32 (GUSBCFG, usbcfg.d32);
+
+ /* Program the GINTMSK register.*/
+ gintmsk.b.modemismatch = 1;
+ gintmsk.b.sofintr = 1;
+ /*gintmsk.b.otgintr = 1; */
+ gintmsk.b.conidstschng = 1;
+ /*gintmsk.b.wkupintr = 1;*/
+ gintmsk.b.disconnect = 1;
+ /*gintmsk.b.usbsuspend = 1;*/
+ /*gintmsk.b.sessreqintr = 1;*/
+ /*gintmsk.b.portintr = 1;*/
+ /*gintmsk.b.hcintr = 1; */
+ write_reg_32(GINTMSK, gintmsk.d32);
+
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ otg_err(OTG_DBG_OCI, "Core Reset FAIL\n");
+ return USB_ERR_FAIL;
+ }
+}
+
+/**
+ * int oci_host_init(struct sec_otghost *otghost)
+ *
+ * @brief Process host initialize as s5pc110 spec
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_host_init(struct sec_otghost *otghost)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ hcfg_t hcfg = {.d32 = 0};
+#if 0
+/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/
+ hprt_t hprt;
+ hprt.d32 = read_reg_32(HPRT);
+#endif
+
+ otg_dbg(OTG_DBG_OCI, "oci_host_init\n");
+
+ gintmsk.b.portintr = 1;
+ update_reg_32(GINTMSK,gintmsk.d32);
+
+ if (!otghost->is_hs) {
+ hcfg.b.fslssupp = 1; // force USB 1.x mode
+ } else {
+ hcfg.b.fslssupp = 0;
+ }
+ hcfg.b.fslspclksel = HCFG_30_60_MHZ;
+ update_reg_32(HCFG, hcfg.d32);
+
+#if 0
+/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/
+ /* turn on vbus */
+ if(!hprt.b.prtpwr) {
+ hprt.b.prtpwr = 1;
+ write_reg_32(HPRT, hprt.d32);
+
+ otg_dbg(true, "turn on Vbus\n");
+ }
+#endif
+
+ oci_config_flush_fifo(OTG_HOST_MODE);
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int oci_start(struct sec_otghost *otghost)
+ *
+ * @brief start to operate oci module by calling oci_core_init function
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_start(struct sec_otghost *otghost)
+{
+ otg_dbg(OTG_DBG_OCI, "oci_start \n");
+
+ if(oci_core_init(otghost) == USB_ERR_SUCCESS) {
+ mdelay(50);
+ if(oci_init_mode(otghost) == USB_ERR_SUCCESS) {
+ oci_set_global_interrupt(true);
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ otg_dbg(OTG_DBG_OCI, "oci_init_mode() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+ else {
+ otg_dbg(OTG_DBG_OCI, "oci_core_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+}
+
+/**
+ * int oci_stop(struct sec_otghost *otghost)
+ *
+ * @brief stop to opearte otg core
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_stop(struct sec_otghost *otghost)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_stop\n");
+
+ /* sometimes, port interrupt occured after removed
+ * otg host driver. so, we have to mask port interrupt. */
+ write_reg_32(GINTMSK, gintmsk.d32);
+
+ oci_set_global_interrupt(false);
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t)
+ *
+ * @brief start transfer by using transfer information to receive from scheduler
+ *
+ * @param [IN] *st_t - information about transfer to write register by calling oci_channel_init function
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+
+u8 oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t)
+{
+ hcchar_t hcchar = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_start_transfer \n");
+
+ if(st_t->alloc_chnum ==CH_NONE) {
+
+ if( oci_channel_alloc(&(st_t->alloc_chnum)) == USB_ERR_SUCCESS) {
+ oci_channel_init(st_t->alloc_chnum, st_t);
+
+ hcchar.b.chen = 1;
+ update_reg_32(HCCHAR(st_t->alloc_chnum), hcchar.d32);
+ return st_t->alloc_chnum;
+ }
+ else {
+ otg_dbg(OTG_DBG_OCI,
+ "oci_start_transfer Fail - Channel Allocation Error\n");
+ return CH_NONE;
+ }
+ }
+ else {
+ oci_channel_init(st_t->alloc_chnum, st_t);
+
+ hcchar.b.chen = 1;
+ update_reg_32(HCCHAR(st_t->alloc_chnum), hcchar.d32);
+
+ return st_t->alloc_chnum;
+ }
+}
+
+/**
+ * int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num)
+ *
+ * @brief stop to transfer even if transfering
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num)
+{
+ hcchar_t hcchar = {.d32 = 0};
+ hcintmsk_t hcintmsk = {.d32 = 0};
+ int count = 0, max_error_count = 10000;
+
+ otg_dbg(OTG_DBG_OCI,
+ "step1: oci_stop_transfer ch=%d, hcchar=0x%x\n",
+ ch_num, read_reg_32(HCCHAR(ch_num)));
+
+ if(ch_num>16)
+ return USB_ERR_FAIL;
+
+ otghost->ch_halt = true;
+
+ hcintmsk.b.chhltd = 1;
+ update_reg_32(HCINTMSK(ch_num),hcintmsk.d32);
+
+ hcchar.b.chdis = 1;
+ hcchar.b.chen = 1;
+ update_reg_32(HCCHAR(ch_num),hcchar.d32);
+
+ /* wait for Channel Disabled Interrupt */
+ do {
+ hcchar.d32 = read_reg_32(HCCHAR(ch_num));
+
+ if(count > max_error_count) {
+ otg_dbg(OTG_DBG_OCI,
+ "Warning!! oci_stop_transfer()"
+ "ChDis is not cleared! ch=%d, hcchar=0x%x\n",
+ ch_num, hcchar.d32);
+ break;
+ }
+ count++;
+
+ } while(hcchar.b.chdis);
+
+ //kevinh: wait for Channel Disabled Interrupt
+ count = 0;
+ do {
+ hcintmsk.d32 = read_reg_32(HCINT(ch_num));
+ if(count > max_error_count) {
+ printk("chhltd int never occurred! ch=%d, intreg=0x%x\n",
+ ch_num, hcintmsk.d32);
+ break;
+ }
+ count++;
+ } while(!hcintmsk.b.chhltd);
+
+ oci_channel_dealloc(ch_num);
+
+ clear_reg_32(HAINTMSK,ch_num);
+ write_reg_32(HCINT(ch_num),INT_ALL);
+ clear_reg_32(HCINTMSK(ch_num), INT_ALL);
+
+ otghost->ch_halt = false;
+ otg_dbg(OTG_DBG_OCI,
+ "step2 : oci_stop_transfer ch=%d, hcchar=0x%x\n",
+ ch_num, read_reg_32(HCCHAR(ch_num)));
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int oci_channel_init( u8 ch_num, stransfer_t *st_t)
+ *
+ * @brief Process channel initialize to prepare starting transfer
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_channel_init( u8 ch_num, stransfer_t *st_t)
+{
+ u32 intr_enable = 0;
+ gintmsk_t gintmsk = {.d32 = 0};
+ hcchar_t hcchar = {.d32 = 0};
+ hctsiz_t hctsiz = {.d32 = 0};
+ hcsplt_t hcsplt = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_channel_init \n");
+
+ /* Clear channel information */
+ write_reg_32(HCTSIZ(ch_num), 0);
+ write_reg_32(HCCHAR(ch_num), 0);
+ write_reg_32(HCINTMSK(ch_num), 0);
+ write_reg_32(HCINT(ch_num), INT_ALL);/*write clear*/
+ write_reg_32(HCDMA(ch_num), 0);
+
+ /* enable host channel interrupt in GINTSTS */
+ gintmsk.b.hcintr =1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+ /* Enable the top level host channel interrupt in HAINT */
+ intr_enable = (1 << ch_num);
+ update_reg_32(HAINTMSK, intr_enable);
+ /* unmask the down level host channel interrupt in HCINT */
+ write_reg_32(HCINTMSK(ch_num),st_t->hc_reg.hc_int_msk.d32);
+
+ /* Program the HCSIZn register with the endpoint characteristics for */
+ hctsiz.b.xfersize = st_t->buf_size;
+ hctsiz.b.pktcnt = st_t->packet_cnt;
+
+ /* Program the HCCHARn register with the endpoint characteristics for */
+ hcchar.b.mps = st_t->ed_desc_p->max_packet_size;
+ hcchar.b.epnum = st_t->ed_desc_p->endpoint_num;
+ hcchar.b.epdir = st_t->ed_desc_p->is_ep_in;
+ hcchar.b.lspddev = (st_t->ed_desc_p->dev_speed == LOW_SPEED_OTG);
+ hcchar.b.eptype = st_t->ed_desc_p->endpoint_type;
+ hcchar.b.multicnt = st_t->ed_desc_p->mc;
+ hcchar.b.devaddr = st_t->ed_desc_p->device_addr;
+
+ if(st_t->ed_desc_p->endpoint_type == INT_TRANSFER ||
+ st_t->ed_desc_p->endpoint_type == ISOCH_TRANSFER) {
+ u32 uiFrameNum = 0;
+ uiFrameNum = oci_get_frame_num();
+
+ hcchar.b.oddfrm = uiFrameNum%2?1:0;
+
+ /*
+ * if transfer type is periodic transfer,
+ * must support sof interrupt
+ */
+
+ /*
+ gintmsk.b.sofintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+ */
+ }
+
+ if(st_t->ed_desc_p->endpoint_type == CONTROL_TRANSFER) {
+ td_t *td_p;
+ td_p = (td_t *)st_t->parent_td;
+
+ switch(td_p->standard_dev_req_info.conrol_transfer_stage) {
+ case SETUP_STAGE:
+ hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.setup_tgl;
+ hcchar.b.epdir = EP_OUT;
+ break;
+ case DATA_STAGE:
+ hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.data_tgl;
+ hcchar.b.epdir = st_t->ed_desc_p->is_ep_in;
+ break;
+ case STATUS_STAGE:
+ hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.status_tgl;
+
+ if(td_p->standard_dev_req_info.is_data_stage) {
+ hcchar.b.epdir = ~(st_t->ed_desc_p->is_ep_in);
+ }
+ else {
+ hcchar.b.epdir = EP_IN;
+ }
+ break;
+ default:break;
+ }
+ }
+ else {
+ hctsiz.b.pid = st_t->ed_status_p->data_tgl;
+ }
+
+ hctsiz.b.dopng = st_t->ed_status_p->is_ping_enable;
+ write_reg_32(HCTSIZ(ch_num),hctsiz.d32);
+ st_t->ed_status_p->is_ping_enable = false;
+
+ /* Write DMA Address */
+ write_reg_32(HCDMA(ch_num),st_t->start_phy_buf_addr);
+
+ /* Wrote HCCHAR Register */
+ write_reg_32(HCCHAR(ch_num),hcchar.d32);
+
+ /* sztupy: Enable split transaction support if driver is in HS mode */
+ if (st_t->ed_desc_p->is_do_split) {
+ printk("splittrans");
+ hcsplt.b.spltena = 1;
+ hcsplt.b.hubaddr = st_t->ed_desc_p->hub_addr;
+ hcsplt.b.prtaddr = st_t->ed_desc_p->hub_port;
+ hcsplt.b.xactpos = st_t->ed_status_p->split_pos;
+ hcsplt.b.compsplt = st_t->ed_status_p->is_complete_split;
+ }
+ write_reg_32(HCSPLT(ch_num),hcsplt.d32);
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * u32 oci_get_frame_num(void)
+ *
+ * @brief Get current frame number by reading register.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+u32 oci_get_frame_num(void)
+{
+ hfnum_t hfnum;
+ hfnum.d32 = read_reg_32(HFNUM);
+
+ otg_dbg(OTG_DBG_OCI, " oci_get_frame_num=%d\n", hfnum.b.frnum);
+
+ return hfnum.b.frnum;
+}
+
+/**
+ * u16 oci_get_frame_interval(void)
+ *
+ * @brief Get current frame interval by reading register.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+u16 oci_get_frame_interval(void)
+{
+ hfir_t hfir;
+ hfir.d32 = read_reg_32(HFIR);
+ return hfir.b.frint;
+}
+
+void oci_set_frame_interval(u16 interval)
+{
+ hfir_t hfir = {.d32 = 0};
+ hfir.b.frint = interval;
+ write_reg_32(HFIR, hfir.d32);
+}
+
+/* OCI Internal Functions */
+
+int oci_channel_alloc(u8 *ch_num)
+{
+ u8 ch;
+
+ hcchar_t hcchar = {.d32 = 0};
+
+ for(ch = 0 ; ch<16 ; ch++) {
+ if(ch_enable[ch] == true) {
+ hcchar.d32 = read_reg_32(HCCHAR(ch));
+
+ if(hcchar.b.chdis == 0) {
+ *ch_num = ch;
+ ch_enable[ch] = false;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ }
+
+ return USB_ERR_FAIL;
+}
+
+int oci_channel_dealloc(u8 ch_num)
+{
+ if(ch_num < 16 && ch_enable[ch_num] == false) {
+ ch_enable[ch_num] = true;
+
+ write_reg_32(HCTSIZ(ch_num), 0);
+ write_reg_32(HCCHAR(ch_num), 0);
+ write_reg_32(HCINTMSK(ch_num), 0);
+ write_reg_32(HCINT(ch_num), INT_ALL);
+ write_reg_32(HCDMA(ch_num), 0);
+ return USB_ERR_SUCCESS;
+ }
+
+ return USB_ERR_FAIL;
+}
+
+int oci_sys_init(struct sec_otghost *otghost)
+{
+ otg_host_phy_init();
+
+ return USB_ERR_SUCCESS;
+}
+
+void oci_set_global_interrupt(bool set)
+{
+ gahbcfg_t ahbcfg;
+
+ otg_dbg(OTG_DBG_OCI, " oci_set_global_interrupt\n");
+
+ ahbcfg.d32 = 0;
+ ahbcfg.b.glblintrmsk = 1;
+
+ if(set) {
+ update_reg_32(GAHBCFG,ahbcfg.d32);
+ }
+ else {
+ clear_reg_32(GAHBCFG,ahbcfg.d32);
+ }
+}
+
+int oci_init_mode(struct sec_otghost *otghost)
+{
+ gintsts_t gintsts;
+
+ gintsts.d32 = read_reg_32(GINTSTS);
+
+ otg_dbg(OTG_DBG_OCI,
+ "GINSTS = 0x%x,GINMSK = 0x%x.\n",
+ (unsigned int)gintsts.d32,
+ (unsigned int)read_reg_32(GINTMSK));
+
+ if(gintsts.b.curmode == OTG_HOST_MODE) {
+ otg_dbg(OTG_DBG_OCI, "HOST Mode\n");
+
+ if(oci_host_init(otghost) == USB_ERR_SUCCESS) {
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ otg_dbg(OTG_DBG_OCI, "oci_host_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+ else { /* Device Mode */
+ otg_dbg(OTG_DBG_OCI, "DEVICE Mode\n");
+ if(oci_dev_init(otghost) == USB_ERR_SUCCESS) {
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ otg_err(OTG_DBG_OCI, "oci_dev_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+
+ return USB_ERR_SUCCESS;
+}
+
+void oci_config_flush_fifo(u32 mode)
+{
+ ghwcfg2_t hwcfg2 = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_config_flush_fifo\n");
+
+ hwcfg2.d32 = read_reg_32(GHWCFG2);
+
+ /* Configure data FIFO sizes */
+ if (hwcfg2.b.dynamic_fifo) {
+ /* Rx FIFO */
+ write_reg_32(GRXFSIZ, 0x0000010D);
+
+ /* Non-periodic Tx FIFO */
+ write_reg_32(GNPTXFSIZ, 0x0080010D);
+
+ if (mode == OTG_HOST_MODE) {
+ /* For Periodic transactions, */
+ /* program HPTXFSIZ */
+ }
+ }
+
+ /* Flush the FIFOs */
+ oci_flush_tx_fifo(0);
+
+ oci_flush_rx_fifo();
+}
+
+void oci_flush_tx_fifo(u32 num)
+{
+ grstctl_t greset = {.d32 = 0};
+ u32 count = 0;
+
+ otg_dbg(OTG_DBG_OCI, "oci_flush_tx_fifo\n");
+
+ greset.b.txfflsh = 1;
+ greset.b.txfnum = num;
+ write_reg_32(GRSTCTL, greset.d32);
+
+ /* wait for flush to end */
+ while (greset.b.txfflsh == 1) {
+ greset.d32 = read_reg_32(GRSTCTL);
+ if (++count > MAX_COUNT)
+ break;
+ };
+
+ /* Wait for 3 PHY Clocks*/
+ udelay(30);
+}
+
+void oci_flush_rx_fifo(void)
+{
+ grstctl_t greset = {.d32 = 0};
+ u32 count = 0;
+
+ otg_dbg(OTG_DBG_OCI, "oci_flush_rx_fifo\n");
+
+ greset.b.rxfflsh = 1;
+ write_reg_32(GRSTCTL, greset.d32 );
+
+ do {
+ greset.d32 = read_reg_32(GRSTCTL);
+ if (++count > MAX_COUNT)
+ break;
+ } while (greset.b.rxfflsh == 1);
+
+ /* Wait for 3 PHY Clocks*/
+ udelay(30);
+}
+
+int oci_core_reset(void)
+{
+ u32 count = 0;
+ grstctl_t greset = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_core_reset\n");
+
+ /* Wait for AHB master IDLE state. */
+ do {
+ greset.d32 = read_reg_32 (GRSTCTL);
+ mdelay (50);
+
+ if(++count>100) {
+ otg_dbg(OTG_DBG_OCI, "AHB status is not IDLE\n");
+ return USB_ERR_FAIL;
+ }
+ } while (greset.b.ahbidle != 1);
+
+ /* Core Soft Reset */
+ greset.b.csftrst = 1;
+ write_reg_32 (GRSTCTL, greset.d32);
+
+ /* Wait for 3 PHY Clocks*/
+ mdelay (50);
+ return USB_ERR_SUCCESS;
+}
+
+int oci_dev_init(struct sec_otghost *otghost)
+{
+ otg_dbg(OTG_DBG_OCI, "oci_dev_init - do nothing.\n");
+ /* return USB_ERR_FAIL; */
+ return USB_ERR_SUCCESS;
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-oci.h b/drivers/usb/host/s3c-otg/s3c-otg-oci.h
new file mode 100644
index 0000000..03c97e9
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-oci.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] :s3c-otg-oci.h
+ * [Description] : The Header file defines the external and internal functions of OCI.
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2008/06/18
+ * [Revision History]
+ * (1) 2008/06/25 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Added some functions and data structure of OCI
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _OCI_H_
+#define _OCI_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-regdef.h"
+#include "s3c-otg-hcdi-kal.h"
+#include "s3c-otg-hcdi-memory.h"
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-roothub.h"
+#include "s3c-otg-hcdi-hcd.h"
+
+#include <mach/map.h> //virtual address for smdk
+
+extern void otg_host_phy_init(void);
+#include <mach/regs-clock.h>
+
+//OCI interace
+int oci_init(struct sec_otghost *otghost);
+
+int oci_start(struct sec_otghost *otghost);
+int oci_stop(struct sec_otghost *otghost);
+
+u8 oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t);
+int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num);
+
+int oci_channel_init(u8 ch_num, stransfer_t *st_t);
+u32 oci_get_frame_num(void);
+u16 oci_get_frame_interval(void);
+void oci_set_frame_interval(u16 intervl);
+
+///OCI Internal Functions
+int oci_sys_init(struct sec_otghost *otghost);
+int oci_core_init(struct sec_otghost *otghost);
+int oci_init_mode(struct sec_otghost *otghost);
+int oci_host_init(struct sec_otghost *otghost);
+int oci_dev_init(struct sec_otghost *otghost);
+
+int oci_channel_alloc(u8 *ch_num);
+int oci_channel_dealloc(u8 ch_num);
+
+void oci_config_flush_fifo(u32 mode);
+void oci_flush_tx_fifo(u32 num);
+void oci_flush_rx_fifo(void);
+
+int oci_core_reset(void);
+void oci_set_global_interrupt(bool set);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _OCI_H_ */
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-roothub.c b/drivers/usb/host/s3c-otg/s3c-otg-roothub.c
new file mode 100644
index 0000000..7d9d289
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-roothub.c
@@ -0,0 +1,605 @@
+/* for* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : RootHub.c
+ * [Description] : The file implement the external and internal functions of RootHub
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2009/02/10
+ * [Revision History]
+ * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and implements functions of RootHub
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-roothub.h"
+
+static void setPortPower(bool on)
+{
+#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS
+ hprt_t hprt = {.d32 = 0};
+
+ if(on) {
+ hprt.d32 = read_reg_32(HPRT);
+ if(!hprt.b.prtpwr) {
+ /*hprt.d32 = 0;*/
+ hprt.b.prtpwr = 1;
+ write_reg_32(HPRT, hprt.d32);
+ }
+ }
+ else {
+ hprt.b.prtpwr = 1;
+ clear_reg_32(HPRT, hprt.d32);
+ }
+#else
+ otg_dbg(true, "turn %s Vbus\n", on ? "on" : "off");
+#endif
+}
+
+/**
+ * int get_otg_port_status(const u8 port, char* status)
+ *
+ * @brief Get port change bitmap information
+ *
+ * @param [IN] port : port number
+ * [OUT] status : buffer to store bitmap information
+ *
+ * @returnUSB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ *
+ * @remark
+ *
+ */
+__inline__ int get_otg_port_status(
+ struct usb_hcd *hcd, const u8 port, char *status)
+{
+
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+ /* return root_hub_feature(port, GetPortStatus, NULL, status); */
+#if 0
+ /* for debug */
+ hprt_t hprt;
+
+ hprt.d32 = read_reg_32(HPRT);
+
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "HPRT:spd=%d,pwr=%d,lnsts=%d,rst=%d,susp=%d,res=%d,ovrcurract=%d,ena=%d,connsts=%d\n",
+ hprt.b.prtspd,
+ hprt.b.prtpwr,
+ hprt.b.prtlnsts,
+ hprt.b.prtrst,
+ hprt.b.prtsusp,
+ hprt.b.prtres,
+ hprt.b.prtovrcurract,
+ hprt.b.prtena,
+ hprt.b.prtconnsts);
+
+#endif
+ status[port] = 0;
+ status[port] |= (otghost->port_flag.b.port_connect_status_change ||
+ otghost->port_flag.b.port_reset_change ||
+ otghost->port_flag.b.port_enable_change ||
+ otghost->port_flag.b.port_suspend_change ||
+ otghost->port_flag.b.port_over_current_change) << 1;
+
+#if 0
+ //* for debug */
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "connect:%d,reset:%d,enable:%d,suspend:%d,over_current:%d\n",
+ otghost->port_flag.b.port_connect_status_change,
+ otghost->port_flag.b.port_reset_change,
+ otghost->port_flag.b.port_enable_change,
+ otghost->port_flag.b.port_suspend_change,
+ otghost->port_flag.b.port_over_current_change);
+#endif
+
+ if (status[port]) {
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " Root port status changed\n");
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " port_connect_status_change: %d\n",
+ otghost->port_flag.b.port_connect_status_change);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " port_reset_change: %d\n",
+ otghost->port_flag.b.port_reset_change);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " port_enable_change: %d\n",
+ otghost->port_flag.b.port_enable_change);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " port_suspend_change: %d\n",
+ otghost->port_flag.b.port_suspend_change);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " port_over_current_change: %d\n",
+ otghost->port_flag.b.port_over_current_change);
+ }
+
+ return (status[port] !=0);
+}
+
+/**
+ * int reset_and_enable_port(struct usb_hcd *hcd, const u8 port)
+ *
+ * @brief Reset port and make enable status the specific port
+ *
+ * @param [IN] port : port number
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ *
+ * @remark
+ *
+ */
+int reset_and_enable_port(struct usb_hcd *hcd, const u8 port)
+{
+ hprt_t hprt;
+ u32 count = 0;
+ u32 max_error_count = 10000;
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ hprt.d32 = read_reg_32(HPRT);
+
+ otg_dbg(OTG_DBG_ROOTHUB,
+ " reset_and_enable_port\n");
+
+ if(hprt.b.prtconnsts==0) {
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "No Attached Device, HPRT = 0x%x\n", hprt.d32);
+
+ otghost->port_flag.b.port_connect_status_change = 1;
+ otghost->port_flag.b.port_connect_status = 0;
+
+ return USB_ERR_FAIL;
+ }
+
+ if(!hprt.b.prtena) {
+ hprt.b.prtrst = 1; /* drive reset */
+ write_reg_32(HPRT, hprt.d32);
+
+ mdelay(60);
+ hprt.b.prtrst = 0;
+ write_reg_32(HPRT, hprt.d32);
+
+ do {
+ hprt.d32 = read_reg_32(HPRT);
+
+ if(count > max_error_count) {
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "Port Reset Fail : HPRT : 0x%x\n", hprt.d32);
+ return USB_ERR_FAIL;
+ }
+ count++;
+
+ } while(!hprt.b.prtena);
+
+ }
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int root_hub_feature(
+ * struct usb_hcd *hcd,
+ * const u8 port,
+ * const u16 type_req,
+ * const u16 feature,
+ * void* buf)
+ *
+ * @brief Get port change bitmap information
+ *
+ * @param [IN] port : port number
+ * [IN] type_req : request type of hub feature as usb 2.0 spec
+ * [IN] feature : hub feature as usb 2.0 spec
+ * [OUT] status : buffer to store results
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ *
+ * @remark
+ *
+ */
+__inline__ int root_hub_feature(
+ struct usb_hcd *hcd,
+ const u8 port,
+ const u16 type_req,
+ const u16 feature,
+ void *buf)
+{
+ int retval = USB_ERR_SUCCESS;
+ usb_hub_descriptor_t *desc = NULL;
+ u32 port_status = 0;
+ hprt_t hprt = {.d32 = 0};
+ struct sec_otghost *otghost = hcd_to_sec_otghost(hcd);
+
+ otg_dbg(OTG_DBG_ROOTHUB, " root_hub_feature\n");
+
+ switch (type_req) {
+ case ClearHubFeature:
+ otg_dbg(OTG_DBG_ROOTHUB, "case ClearHubFeature\n");
+ switch (feature) {
+ case C_HUB_LOCAL_POWER:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearHubFeature -C_HUB_LOCAL_POWER\n");
+ break;
+ case C_HUB_OVER_CURRENT:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearHubFeature -C_HUB_OVER_CURRENT\n");
+ /* Nothing required here */
+ break;
+ default:
+ retval = USB_ERR_FAIL;
+ }
+ break;
+
+ case ClearPortFeature:
+ otg_dbg(OTG_DBG_ROOTHUB, "case ClearPortFeature\n");
+ switch (feature) {
+ case USB_PORT_FEAT_ENABLE:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_ENABLE\n");
+ hprt.b.prtena = 1;
+ update_reg_32(HPRT, hprt.d32);
+ break;
+
+ case USB_PORT_FEAT_SUSPEND:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_SUSPEND\n");
+ bus_resume(otghost);
+ break;
+
+ case USB_PORT_FEAT_POWER:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_POWER\n");
+ setPortPower(false);
+ break;
+
+ case USB_PORT_FEAT_INDICATOR:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_INDICATOR\n");
+ /* Port inidicator not supported */
+ break;
+
+ case USB_PORT_FEAT_C_CONNECTION:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_C_CONNECTION\n");
+ /* Clears drivers internal connect status change flag */
+ otghost->port_flag.b.port_connect_status_change = 0;
+ break;
+
+ case USB_PORT_FEAT_C_RESET:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_C_RESET\n");
+ /* Clears the driver's internal Port Reset Change flag*/
+ otghost->port_flag.b.port_reset_change = 0;
+ break;
+
+ case USB_PORT_FEAT_C_ENABLE:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_C_ENABLE\n");
+ /* Clears the driver's internal Port Enable/Disable Change flag */
+ otghost->port_flag.b.port_enable_change = 0;
+ break;
+
+ case USB_PORT_FEAT_C_SUSPEND:
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature -USB_PORT_FEAT_C_SUSPEND\n");
+ /* Clears the driver's internal Port Suspend
+ * Change flag, which is set when resume signaling on
+ * the host port is complete */
+ otghost->port_flag.b.port_suspend_change = 0;
+ break;
+
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ otg_dbg(OTG_DBG_ROOTHUB, "case ClearPortFeature - \
+ USB_PORT_FEAT_C_OVER_CURRENT\n");
+ otghost->port_flag.b.port_over_current_change = 0;
+ break;
+
+ default:
+ retval = USB_ERR_FAIL;
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case ClearPortFeature - FAIL\n");
+ }
+ break;
+
+ case GetHubDescriptor:
+ otg_dbg(OTG_DBG_ROOTHUB, "case GetHubDescriptor\n");
+ desc = (usb_hub_descriptor_t *)buf;
+ desc->desc_length = 9;
+ desc->desc_type = 0x29;
+ desc->port_number = 1;
+ desc->hub_characteristics = 0x08;
+ desc->power_on_to_power_good = 1;
+ desc->hub_control_current = 0;
+ desc->DeviceRemovable[0] = 0;
+ desc->DeviceRemovable[1] = 0xff;
+ break;
+
+ case GetHubStatus:
+ otg_dbg(OTG_DBG_ROOTHUB, "case GetHubStatus\n");
+ otg_mem_set(buf, 0, 4);
+ break;
+
+ case GetPortStatus:
+ otg_dbg(OTG_DBG_ROOTHUB, "case GetPortStatus\n");
+
+ if (otghost->port_flag.b.port_connect_status_change) {
+ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - USB_PORT_FEAT_C_CONNECTION\n");
+ }
+
+ if (otghost->port_flag.b.port_enable_change) {
+ port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - USB_PORT_FEAT_C_ENABLE\n");
+ }
+
+ if (otghost->port_flag.b.port_suspend_change) {
+ port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - USB_PORT_FEAT_C_SUSPEND\n");
+ }
+
+ if (otghost->port_flag.b.port_reset_change) {
+ port_status|= (1 << USB_PORT_FEAT_C_RESET);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - USB_PORT_FEAT_C_RESET\n");
+ }
+
+ if (otghost->port_flag.b.port_over_current_change) {
+ port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT);
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - USB_PORT_FEAT_C_OVER_CURRENT\n");
+ }
+
+ if (!otghost->port_flag.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return 0's for the remainder of the port status
+ * since the port register can't be read if the core
+ * is in device mode.
+ */
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case GetPortStatus - disconnected\n");
+
+ *((__le32*)buf) = cpu_to_le32(port_status);
+ /*break;*/
+ }
+
+ hprt.d32 = read_reg_32(HPRT);
+
+ if (hprt.b.prtconnsts)
+ port_status|= (1 << USB_PORT_FEAT_CONNECTION);
+
+ if (hprt.b.prtena)
+ port_status |= (1 << USB_PORT_FEAT_ENABLE);
+
+ if (hprt.b.prtsusp)
+ port_status |= (1 << USB_PORT_FEAT_SUSPEND);
+
+ if (hprt.b.prtovrcurract)
+ port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT);
+
+ if (hprt.b.prtrst)
+ port_status |= (1 << USB_PORT_FEAT_RESET);
+
+ if (hprt.b.prtpwr)
+ port_status |= (1 << USB_PORT_FEAT_POWER);
+
+ if (hprt.b.prtspd == 0) {
+ port_status |= USB_PORT_STAT_HIGH_SPEED;
+ }
+ else {
+ if (hprt.b.prtspd == 2)
+ port_status |= USB_PORT_STAT_LOW_SPEED;
+ }
+
+ if (hprt.b.prttstctl)
+ port_status |= (1 << USB_PORT_FEAT_TEST);
+
+ *((__le32*)buf) = cpu_to_le32(port_status);
+
+ otg_dbg(OTG_DBG_ROOTHUB, "port_status=0x%x.\n", port_status);
+ break;
+
+ case SetHubFeature:
+ otg_dbg(OTG_DBG_ROOTHUB, "case SetHubFeature\n");
+ /* No HUB features supported */
+ break;
+
+ case SetPortFeature:
+ otg_dbg(OTG_DBG_ROOTHUB, "case SetPortFeature\n");
+ if (!otghost->port_flag.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return without doing anything since the port
+ * register can't be written if the core is in device
+ * mode.
+ */
+ otg_dbg(OTG_DBG_ROOTHUB,
+ "case SetPortFeature - disconnected\n");
+
+ /*break;*/
+ }
+
+ switch (feature) {
+ case USB_PORT_FEAT_SUSPEND:
+ otg_dbg(true,
+ "case SetPortFeature -USB_PORT_FEAT_SUSPEND\n");
+ bus_suspend();
+ break;
+
+ case USB_PORT_FEAT_POWER:
+ otg_dbg(true,
+ "case SetPortFeature -USB_PORT_FEAT_POWER\n");
+ setPortPower(true);
+ break;
+
+ case USB_PORT_FEAT_RESET:
+ otg_dbg(true,
+ "case SetPortFeature -USB_PORT_FEAT_RESET\n");
+ retval = reset_and_enable_port(hcd, port);
+ /*
+ if(retval == USB_ERR_SUCCESS)
+ wake_lock(&otghost->wake_lock);
+ */
+ break;
+
+ case USB_PORT_FEAT_INDICATOR:
+ otg_dbg(true,
+ "case SetPortFeature -USB_PORT_FEAT_INDICATOR\n");
+ break;
+
+ default :
+ otg_dbg(true, "case SetPortFeature -USB_ERR_FAIL\n");
+ retval = USB_ERR_FAIL;
+ break;
+ }
+ break;
+
+ default:
+ retval = USB_ERR_FAIL;
+ otg_err(true, "root_hub_feature() Function Error\n");
+ break;
+ }
+
+ if(retval != USB_ERR_SUCCESS)
+ retval = USB_ERR_FAIL;
+
+ return retval;
+}
+
+/**
+ * void bus_suspend(void)
+ *
+ * @brief Make suspend status when this platform support PM Mode
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+void bus_suspend(void)
+{
+ hprt_t hprt;
+ pcgcctl_t pcgcctl;
+
+ otg_dbg(OTG_DBG_ROOTHUB, " bus_suspend\n");
+
+ hprt.d32 = 0;
+ pcgcctl.d32 = 0;
+
+ hprt.b.prtsusp = 1;
+ update_reg_32(HPRT, hprt.d32);
+
+ pcgcctl.b.pwrclmp = 1;
+ update_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+
+ pcgcctl.b.rstpdwnmodule = 1;
+ update_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+
+ pcgcctl.b.stoppclk = 1;
+ update_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+}
+
+/**
+ * int bus_resume(struct sec_otghost *otghost)
+ *
+ * @brief Make resume status when this platform support PM Mode
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ *
+ * @remark
+ *
+ */
+int bus_resume(struct sec_otghost *otghost)
+{
+ /*
+ hprt_t hprt;
+ pcgcctl_t pcgcctl;
+ hprt.d32 = 0;
+ pcgcctl.d32 = 0;
+
+ pcgcctl.b.stoppclk = 1;
+ clear_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+
+ pcgcctl.b.pwrclmp = 1;
+ clear_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+
+ pcgcctl.b.rstpdwnmodule = 1;
+ clear_reg_32(PCGCCTL,pcgcctl.d32);
+ udelay(1);
+
+ hprt.b.prtres = 1;
+ update_reg_32(HPRT, hprt.d32);
+ mdelay(20);
+
+ clear_reg_32(HPRT, hprt.d32);
+ */
+ otg_dbg(OTG_DBG_ROOTHUB, "bus_resume()......\n");
+
+ otg_dbg(OTG_DBG_ROOTHUB, "wait for 50 ms...\n");
+
+ mdelay(50);
+ if(oci_init(otghost) == USB_ERR_SUCCESS) {
+ if(oci_start(otghost) == USB_ERR_SUCCESS) {
+ otg_dbg(OTG_DBG_ROOTHUB, "OTG Init Success\n");
+ return USB_ERR_SUCCESS;
+ }
+ }
+
+ return USB_ERR_FAIL;
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-roothub.h b/drivers/usb/host/s3c-otg/s3c-otg-roothub.h
new file mode 100644
index 0000000..03883d1
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-roothub.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] :s3c-otg-roothub.h
+ * [Description] : The Header file defines the external and internal functions of RootHub.
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2008/06/13
+ * [Revision History]
+ * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and defines functions of RootHub
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _ROOTHUB_H_
+#define _ROOTHUB_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-regdef.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-hcdi-kal.h"
+#include "s3c-otg-hcdi-memory.h"
+#include "s3c-otg-oci.h"
+
+__inline__ int root_hub_feature(
+ struct usb_hcd *hcd,
+ const u8 port,
+ const u16 type_req,
+ const u16 feature,
+ void *buf);
+
+__inline__ int get_otg_port_status(
+ struct usb_hcd *hcd, const u8 port, char *status);
+
+int reset_and_enable_port(struct usb_hcd *hcd, const u8 port);
+void bus_suspend(void);
+
+int bus_resume(struct sec_otghost *otghost);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _ROOTHUB_H_ */
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c
new file mode 100644
index 0000000..d94fb1a
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c
@@ -0,0 +1,369 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Scheduler.c
+ * [Description] : The source file implements the internal functions of Scheduler.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2009/2/10
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of Scheduler
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-scheduler-scheduler.h"
+
+void init_scheduler(void)
+{
+ /*init_scheduling();*/
+ init_transfer_ready_q();
+}
+
+/******************************************************************************/
+/*!
+ * @name int reserve_used_resource_for_periodic(u32 usb_time)
+ *
+ * @brief this function reserves the necessary resource of USB Transfer for Periodic Transfer.
+ * So, this function firstly checks there ares some available USB Time
+ * and Channel resource for USB Transfer.
+ * if there exists necessary resources for Periodic Transfer, then reserves the resource.
+ *
+ * @param [IN] usb_time = indicates the USB Time for the USB Transfer.
+ *
+ * @return USB_ERR_SUCCESS - if success to insert pInsertED to S3CScheduler.
+ * USB_ERR_NO_BANDWIDTH - if fail to reserve the USB Bandwidth.
+ * USB_ERR_NO_CHANNEL - if fail to reserve the Channel.
+ */
+/******************************************************************************/
+
+int reserve_used_resource_for_periodic(u32 usb_time, u8 dev_speed, u8 trans_type)
+{
+ if(inc_perio_bus_time(usb_time,dev_speed)==USB_ERR_SUCCESS) {
+ if(inc_perio_chnum()==USB_ERR_SUCCESS) {
+ otg_usbcore_inc_usb_bandwidth(usb_time);
+ otg_usbcore_inc_periodic_transfer_cnt(trans_type);
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ dec_perio_bus_time(usb_time);
+ return USB_ERR_NO_CHANNEL;
+ }
+ }
+ else {
+ return USB_ERR_NO_BANDWIDTH;
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int free_usb_resource_for_periodic(ed_t * pFreeED)
+ *
+ * @brief this function frees the resources to be allocated to pFreeED at S3CScheduler.
+ * that is, this functions only releases the resources to be allocated by S3C6400Scheduler.
+ *
+ * @param [IN] pFreeED = indicates ed_t to have the information of the resource to be released.
+ *
+ * @return USB_ERR_SUCCESS - if success to free the USB Resource.
+ * USB_ERR_FAIL - if fail to free the USB Resrouce.
+ */
+/******************************************************************************/
+int free_usb_resource_for_periodic(
+ u32 free_usb_time, u8 free_chnum, u8 trans_type)
+{
+ if(dec_perio_bus_time(free_usb_time)==USB_ERR_SUCCESS) {
+ if(dec_perio_chnum()==USB_ERR_SUCCESS) {
+ if(free_chnum!=CH_NONE) {
+ oci_channel_dealloc(free_chnum);
+ set_transferring_td_array(free_chnum, 0);
+ }
+ otg_usbcore_des_usb_bandwidth(free_usb_time);
+ otg_usbcore_des_periodic_transfer_cnt(trans_type);
+ return USB_ERR_SUCCESS;
+ }
+ }
+ return USB_ERR_FAIL;
+}
+
+/******************************************************************************/
+/*!
+ * @name int remove_ed_from_scheduler(ed_t * remove_ed)
+ *
+ * @brief this function just remove the remove_ed from TransferReadyQ. So if you want to
+ * stop the USB Tranfer of remove_ed or release the releated resources.
+ * you should call another functions of S3CScheduler.
+ *
+ * @param [IN] remove_ed = indicates ed_t to be removed from TransferReadyQ.
+ *
+ * @return USB_ERR_SUCCESS - if success to remove the remove_ed from TransferReadyQ.
+ * USB_ERR_FAIL - if fail to remove the remove_ed from TransferReadyQ.
+ */
+ /******************************************************************************/
+int remove_ed_from_scheduler(ed_t *remove_ed)
+{
+ if(remove_ed->ed_status.is_in_transfer_ready_q) {
+ remove_ed_from_ready_q(remove_ed);
+ remove_ed->ed_status.is_in_transfer_ready_q = false;
+
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td)
+ *
+ * @brief this function stop to execute the USB Transfer of cancel_td and
+ * release the Channel Resources to be allocated the cancel_td ,if the Transfer Type of
+ * cancel_td is NonPeriodic Transfer.
+ * this function don't release any usb resources(Channel, USB Bandwidth) for Periodic Transfer.
+ * if you want to release some usb resources for a periodic Transfer, you should call
+ * the free_usb_resource_for_periodic()
+ *
+ * @param [IN] cancel_td = indicates the td_t to be canceled.
+ *
+ * @return USB_ERR_SUCCESS - if success to cancel the USB Transfer of cancel_td.
+ * USB_ERR_FAIL - if fail to cancel the USB Transfer of cancel_td.
+ */
+ /******************************************************************************/
+int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td)
+{
+ if(cancel_td->is_transfer_done) {
+ return USB_ERR_FAIL;
+ }
+
+ if(cancel_td->is_transferring) {
+ int err;
+
+ err = oci_stop_transfer(otghost, cancel_td->cur_stransfer.alloc_chnum);
+
+ if(err == USB_ERR_SUCCESS) {
+ set_transferring_td_array(cancel_td->cur_stransfer.alloc_chnum,0);
+
+ cancel_td->cur_stransfer.alloc_chnum = CH_NONE;
+ cancel_td->is_transferring = false;
+ cancel_td->parent_ed_p->ed_status.is_in_transferring = false;
+ cancel_td->parent_ed_p->ed_status.in_transferring_td = 0;
+ cancel_td->parent_ed_p->is_need_to_insert_scheduler = true;
+
+ if(cancel_td->cur_stransfer.ed_desc_p->endpoint_type == BULK_TRANSFER ||
+ cancel_td->cur_stransfer.ed_desc_p->endpoint_type == CONTROL_TRANSFER ) {
+ dec_nonperio_chnum();
+ }
+ return err;
+ }
+ else {
+ return err;
+ }
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+ return USB_ERR_SUCCESS;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int retransmit(struct sec_otghost *otghost, td_t *retrasmit_td)
+ *
+ * @brief this function retransmits the retrasmit_td immediately.
+ * So, the Channel of pRetransmitted is reused for retransmittion.
+ *
+ * @param [IN] retrasmit_td = indicates the pointer ot the td_t to be retransmitted.
+ *
+ * @return USB_ERR_SUCCESS - if success to retransmit the retrasmit_td.
+ * USB_ERR_FAIL - if fail to retransmit the retrasmit_td.
+ */
+ /******************************************************************************/
+int retransmit(struct sec_otghost *otghost, td_t *retrasmit_td)
+{
+ u32 td_addr=0;
+
+ if(get_transferring_td_array(retrasmit_td->cur_stransfer.alloc_chnum,&td_addr)==USB_ERR_SUCCESS) {
+ if(td_addr == (u32)retrasmit_td) {
+ if(oci_start_transfer(otghost, &retrasmit_td->cur_stransfer)
+ == retrasmit_td->cur_stransfer.alloc_chnum) {
+ retrasmit_td->is_transferring = true;
+ retrasmit_td->parent_ed_p->ed_status.in_transferring_td = (u32)retrasmit_td;
+ retrasmit_td->parent_ed_p->ed_status.is_in_transfer_ready_q = false;
+ retrasmit_td->parent_ed_p->ed_status.is_in_transferring = true;
+ }
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+
+ return USB_ERR_SUCCESS;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name int reschedule(td_t *reschedule_td)
+ *
+ * @brief this function re-schedules the reschedule_td.
+ * So, the Channel of pRescheuleTD is released and reschedule_td is inserted to TransferReadyQ.
+ *
+ * @param [IN] reschedule_td = indicates the pointer ot the td_t to be rescheduled.
+ *
+ * @return USB_ERR_SUCCESS - if success to re-schedule the reschedule_td.
+ * USB_ERR_FAIL - if fail to re-schedule the reschedule_td.
+ */
+ /******************************************************************************/
+int reschedule(td_t *reschedule_td)
+{
+ u32 td_addr;
+
+ if(get_transferring_td_array(reschedule_td->cur_stransfer.alloc_chnum, &td_addr)==USB_ERR_SUCCESS)
+ {
+ if((u32)reschedule_td == td_addr)
+ {
+ set_transferring_td_array(reschedule_td->cur_stransfer.alloc_chnum, 0);
+ oci_channel_dealloc(reschedule_td->cur_stransfer.alloc_chnum);
+
+ reschedule_td->cur_stransfer.alloc_chnum = CH_NONE;
+ reschedule_td->parent_ed_p->is_need_to_insert_scheduler = true;
+ reschedule_td->parent_ed_p->ed_status.in_transferring_td = 0;
+
+ if(reschedule_td->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER||
+ reschedule_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER )
+ {
+ /* Increase the available Channel */
+ dec_nonperio_chnum();
+
+ }
+
+ insert_ed_to_ready_q(reschedule_td->parent_ed_p, false);
+ reschedule_td->parent_ed_p->ed_status.is_in_transfer_ready_q =true;
+
+ }
+ else
+ {
+ /* this case is not support.... */
+ }
+ }
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int deallocate(td_t *deallocate_td)
+ *
+ * @brief this function frees resources to be allocated deallocate_td by S3CScheduler.
+ * this function just free the resource by S3CScheduler. that is, Channel Resource.
+ * if there are another td_t at ed_t, deallocate() insert the ed_t to TransferReadyQ.
+ *
+ * @param [IN] deallocate_td = indicates the pointer ot the td_t to be deallocated.
+ *
+ * @return USB_ERR_SUCCESS - if success to dealloate the resources for the deallocate_td.
+ * USB_ERR_FAIL - if fail to dealloate the resources for the deallocate_td.
+ */
+ /******************************************************************************/
+int deallocate(td_t *deallocate_td)
+{
+ u32 td_addr;
+
+ if(get_transferring_td_array(deallocate_td->cur_stransfer.alloc_chnum , &td_addr)==USB_ERR_SUCCESS)
+ {
+ if((u32)deallocate_td == td_addr)
+ {
+ set_transferring_td_array(deallocate_td->cur_stransfer.alloc_chnum, 0);
+ oci_channel_dealloc(deallocate_td->cur_stransfer.alloc_chnum);
+
+ deallocate_td->cur_stransfer.alloc_chnum = CH_NONE;
+
+ if(deallocate_td->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER||
+ deallocate_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER )
+ {
+ /* Increase the available Channel */
+ dec_nonperio_chnum();
+ }
+
+ deallocate_td->parent_ed_p->is_need_to_insert_scheduler = true;
+
+ if(deallocate_td->parent_ed_p->num_td)
+ {
+ /* insert ed_t to TransferReadyQ. */
+ insert_ed_to_ready_q(deallocate_td->parent_ed_p , false);
+ deallocate_td->parent_ed_p->ed_status.is_in_transfer_ready_q = true;
+ deallocate_td->parent_ed_p->is_need_to_insert_scheduler = false;
+ }
+ return USB_ERR_SUCCESS;
+ }
+ else
+ {
+ return USB_ERR_FAIL;
+ }
+ }
+ else
+ {
+ return USB_ERR_FAIL;
+ }
+
+}
+
+/* TBD.... */
+void do_schedule(struct sec_otghost *otghost)
+{
+ if(get_avail_chnum()) {
+ do_periodic_schedule(otghost);
+ do_nonperiodic_schedule(otghost);
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int get_td_info(u8 chnum,
+ * unsigned int *td_addr_p)
+ *
+ * @brief this function returns the pointer of td_t at TransferringTDArray[chnum]
+ *
+ * @param [IN] chnum = indicates the index of TransferringTDArray
+ * to include the address of td_t which we gets
+ * [OUT] td_addr_p= indicate pointer to store the address of td_t.
+ *
+ * @return USB_ERR_SUCCESS -if success to get the address of td_t.
+ * USB_ERR_FAIL -if fail to get the address of td_t.
+ */
+ /******************************************************************************/
+int get_td_info( u8 chnum,
+ unsigned int *td_addr_p)
+{
+ u32 td_addr;
+
+ if(get_transferring_td_array(chnum, &td_addr)==USB_ERR_SUCCESS)
+ {
+ *td_addr_p = td_addr;
+ return USB_ERR_SUCCESS;
+ }
+
+ return USB_ERR_FAIL;
+}
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c
new file mode 100644
index 0000000..7b41f0c
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c
@@ -0,0 +1,264 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : TransferReadyQ.c
+ * [Description] : The source file implements the internal functions of TransferReadyQ.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/04
+ * [Revision History]
+ * (1) 2008/06/04 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of TransferReadyQ.
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-scheduler-scheduler.h"
+
+static trans_ready_q_t periodic_trans_ready_q;
+static trans_ready_q_t nonperiodic_trans_ready_q;
+
+/******************************************************************************/
+/*!
+ * @name void init_transfer_ready_q(void)
+ *
+ * @brief this function initiates PeriodicTransferReadyQ and NonPeriodicTransferReadyQ.
+ *
+ *
+ * @param void
+ *
+ * @return void.
+ */
+/******************************************************************************/
+void init_transfer_ready_q(void)
+{
+ otg_dbg(OTG_DBG_SCHEDULE,"start init_transfer_ready_q\n");
+
+ otg_list_init(&periodic_trans_ready_q.trans_ready_q_list_head);
+ periodic_trans_ready_q.is_periodic_transfer = true;
+ periodic_trans_ready_q.trans_ready_entry_num = 0;
+ periodic_trans_ready_q.total_alloc_chnum = 0;
+ periodic_trans_ready_q.total_perio_bus_bandwidth = 0;
+
+ otg_list_init(&nonperiodic_trans_ready_q.trans_ready_q_list_head);
+ nonperiodic_trans_ready_q.is_periodic_transfer = false;
+ nonperiodic_trans_ready_q.trans_ready_entry_num = 0;
+ nonperiodic_trans_ready_q.total_alloc_chnum = 0;
+ nonperiodic_trans_ready_q.total_perio_bus_bandwidth = 0;
+
+
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int insert_ed_to_ready_q(ed_t *insert_ed,
+ * bool f_isfirst)
+ *
+ * @brief this function inserts ed_t * to TransferReadyQ.
+ *
+ *
+ * @param [IN] insert_ed = indicates the ed_t to be inserted to TransferReadyQ.
+ * [IN] f_isfirst = indicates whether the insert_ed is inserted as first entry of TransferReadyQ.
+ *
+ * @return USB_ERR_SUCCESS -if successes to insert the insert_ed to TransferReadyQ.
+ * USB_ERR_FAILl -if fails to insert the insert_ed to TransferReadyQ.
+ */
+/******************************************************************************/
+int insert_ed_to_ready_q(ed_t *insert_ed,
+ bool f_isfirst)
+{
+
+ if(insert_ed->ed_desc.endpoint_type == BULK_TRANSFER||
+ insert_ed->ed_desc.endpoint_type == CONTROL_TRANSFER)
+ {
+ if(f_isfirst)
+ {
+ otg_list_push_next(&insert_ed->trans_ready_q_list_entry,&nonperiodic_trans_ready_q.trans_ready_q_list_head);
+ }
+ else
+ {
+ otg_list_push_prev(&insert_ed->trans_ready_q_list_entry,&nonperiodic_trans_ready_q.trans_ready_q_list_head);
+ }
+ nonperiodic_trans_ready_q.trans_ready_entry_num++;
+ }
+ else
+ {
+ if(f_isfirst)
+ {
+ otg_list_push_next(&insert_ed->trans_ready_q_list_entry,&periodic_trans_ready_q.trans_ready_q_list_head);
+ }
+ else
+ {
+ otg_list_push_prev(&insert_ed->trans_ready_q_list_entry,&periodic_trans_ready_q.trans_ready_q_list_head);
+ }
+ periodic_trans_ready_q.trans_ready_entry_num++;
+ }
+
+ return USB_ERR_SUCCESS;
+
+}
+
+
+u32 get_periodic_ready_q_entity_num(void)
+{
+ return periodic_trans_ready_q.trans_ready_entry_num;
+}
+/******************************************************************************/
+/*!
+ * @name int remove_ed_from_ready_q(ed_t *remove_ed)
+ *
+ * @brief this function removes ed_t * from TransferReadyQ.
+ *
+ *
+ * @param [IN] remove_ed = indicate the ed_t to be removed from TransferReadyQ.
+ *
+ * @return USB_ERR_SUCCESS -if successes to remove the remove_ed from TransferReadyQ.
+ * USB_ERR_FAILl -if fails to remove the remove_ed from TransferReadyQ.
+ */
+/******************************************************************************/
+int remove_ed_from_ready_q(ed_t *remove_ed)
+{
+// SPINLOCK_t SLForRemoveED_t = SPIN_LOCK_INIT;
+// u32 uiSLFlag=0;
+
+ otg_list_pop(&remove_ed->trans_ready_q_list_entry);
+
+ if(remove_ed->ed_desc.endpoint_type == BULK_TRANSFER||
+ remove_ed->ed_desc.endpoint_type == CONTROL_TRANSFER)
+ {
+// spin_lock_irq_save_otg(&SLForRemoveED_t, uiSLFlag);
+// otg_list_pop(&remove_ed->trans_ready_q_list_entry);
+ nonperiodic_trans_ready_q.trans_ready_entry_num--;
+// spin_unlock_irq_save_otg(&SLForRemoveED_t, uiSLFlag);
+ }
+ else
+ {
+// spin_lock_irq_save_otg(&SLForRemoveED_t, uiSLFlag);
+// otg_list_pop(&remove_ed->trans_ready_q_list_entry);
+ periodic_trans_ready_q.trans_ready_entry_num--;
+// spin_unlock_irq_save_otg(&SLForRemoveED_t, uiSLFlag);
+ }
+
+ return USB_ERR_SUCCESS;
+
+}
+
+//by ss1 unused func
+/*
+bool check_ed_on_ready_q(ed_t *check_ed_p)
+{
+
+ if(check_ed_p->ed_status.is_in_transfer_ready_q)
+ return true;
+ else
+ return false;
+}*/
+
+/******************************************************************************/
+/*!
+ * @name int get_ed_from_ready_q(bool f_isperiodic,
+ * td_t **get_ed)
+ *
+ * @brief this function returns the first entity of TransferReadyQ.
+ * if there are some ed_t on TransferReadyQ, this function pops first ed_t from TransferReadyQ.
+ * So, the TransferReadyQ don's has the poped ed_t.
+ *
+ *
+ * @param [IN] f_isperiodic = indicate whether Periodic or not
+ * [OUT] get_ed = indicate the double pointer to store the address of first entity
+ * on TransferReadyQ.
+ *
+ * @return USB_ERR_SUCCESS -if successes to get frist ed_t from TransferReadyQ.
+ * USB_ERR_NO_ENTITY -if fails to get frist ed_t from TransferReadyQ
+ * because there is no entity on TransferReadyQ.
+ */
+/******************************************************************************/
+
+int get_ed_from_ready_q(bool f_isperiodic,
+ ed_t **get_ed)
+{
+ if(f_isperiodic)
+ {
+ otg_list_head *transreadyq_list_entity=NULL;
+
+ if(periodic_trans_ready_q.trans_ready_entry_num==0)
+ {
+ return USB_ERR_NO_ENTITY;
+ }
+
+ transreadyq_list_entity = periodic_trans_ready_q.trans_ready_q_list_head.next;
+
+ //if(transreadyq_list_entity!= &periodic_trans_ready_q.trans_ready_q_list_head)
+ if(!otg_list_empty(&periodic_trans_ready_q.trans_ready_q_list_head))
+ {
+ *get_ed = otg_list_get_node(transreadyq_list_entity,ed_t,trans_ready_q_list_entry);
+ if (transreadyq_list_entity->prev == LIST_POISON2 ||
+ transreadyq_list_entity->next == LIST_POISON1) {
+ printk(KERN_ERR "s3c-otg-scheduler get_ed_from_ready_q error\n");
+ periodic_trans_ready_q.trans_ready_entry_num =0;
+ }
+ else {
+ otg_list_pop(transreadyq_list_entity);
+ periodic_trans_ready_q.trans_ready_entry_num--;
+ }
+
+ return USB_ERR_SUCCESS;
+ }
+ else
+ {
+ return USB_ERR_NO_ENTITY;
+ }
+ }
+ else
+ {
+ otg_list_head *transreadyq_list_entity=NULL;
+
+ if(nonperiodic_trans_ready_q.trans_ready_entry_num==0)
+ {
+ return USB_ERR_NO_ENTITY;
+ }
+
+ transreadyq_list_entity = nonperiodic_trans_ready_q.trans_ready_q_list_head.next;
+
+ //if(transreadyq_list_entity!= &nonperiodic_trans_ready_q.trans_ready_q_list_head)
+ if(!otg_list_empty(&nonperiodic_trans_ready_q.trans_ready_q_list_head))
+ {
+ *get_ed = otg_list_get_node(transreadyq_list_entity,ed_t, trans_ready_q_list_entry);
+ if (transreadyq_list_entity->prev == LIST_POISON2 ||
+ transreadyq_list_entity->next == LIST_POISON1) {
+ printk(KERN_ERR "s3c-otg-scheduler get_ed_from_ready_q error\n");
+ nonperiodic_trans_ready_q.trans_ready_entry_num =0;
+ }
+ else {
+ otg_list_pop(transreadyq_list_entity);
+ nonperiodic_trans_ready_q.trans_ready_entry_num--;
+ }
+
+ return USB_ERR_SUCCESS;
+ }
+ else
+ {
+ return USB_ERR_NO_ENTITY;
+ }
+ }
+}
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c
new file mode 100644
index 0000000..034cec4
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c
@@ -0,0 +1,424 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Scheduler.c
+ * [Description] : The source file implements the internal functions of Scheduler.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/04
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of Scheduler
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-scheduler-scheduler.h"
+
+//Define constant variables
+
+//the max periodic bus time is 80%*125us on High Speed Mode
+static const u32 perio_highbustime_threshold = 100;
+
+//the max periodic bus time is 90%*1000us(1ms) on Full/Low Speed Mode .
+static const u32 perio_fullbustime_threshold = 900;
+
+static const u8 perio_chnum_threshold = 14;
+//static const u8 total_chnum_threshold = 16;
+static u8 total_chnum_threshold = 16;
+
+ //Define global variables
+// kevinh: add volatile
+static volatile u32 perio_used_bustime = 0;
+static volatile u8 perio_used_chnum = 0;
+static volatile u8 nonperio_used_chnum = 0;
+static volatile u8 total_used_chnum = 0;
+static volatile u32 transferring_td_array[16]={0};
+
+void reset_scheduler_numbers(void) {
+ total_chnum_threshold = 16;
+ perio_used_bustime = 0;
+ perio_used_chnum = 0;
+ nonperio_used_chnum = 0;
+ total_used_chnum = 0;
+ memset(transferring_td_array,0,sizeof(transferring_td_array));
+}
+
+int inc_perio_bus_time(u32 bus_time, u8 dev_speed)
+{
+ switch(dev_speed) {
+ case HIGH_SPEED_OTG:
+ if((bus_time+perio_used_bustime)<=perio_highbustime_threshold) {
+ perio_used_bustime=+bus_time;
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+
+ case LOW_SPEED_OTG:
+ case FULL_SPEED_OTG:
+ if((bus_time+perio_used_bustime)<=perio_fullbustime_threshold) {
+ perio_used_bustime=+bus_time;
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+ case SUPER_SPEED_OTG:
+ break;
+ default:
+ break;
+ }
+ return USB_ERR_FAIL;
+}
+
+int dec_perio_bus_time(u32 bus_time)
+{
+ if(perio_used_bustime >= bus_time ) {
+ perio_used_bustime =- bus_time;
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ return USB_ERR_FAIL;
+ }
+}
+
+int inc_perio_chnum(void)
+{
+ if(perio_used_chnum<perio_chnum_threshold)
+ {
+ if(total_used_chnum<total_chnum_threshold)
+ {
+ perio_used_chnum++;
+ total_used_chnum++;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ return USB_ERR_FAIL;
+}
+
+u8 get_avail_chnum(void)
+{
+ return total_chnum_threshold - total_used_chnum;
+}
+
+int dec_perio_chnum(void)
+{
+ if(perio_used_chnum>0)
+ {
+ if(total_used_chnum>0)
+ {
+ perio_used_chnum--;
+ total_used_chnum--;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ return USB_ERR_FAIL;
+}
+
+int inc_non_perio_chnum(void)
+{
+ if(nonperio_used_chnum<total_chnum_threshold)
+ {
+ if(total_used_chnum<total_chnum_threshold)
+ {
+ nonperio_used_chnum++;
+ total_used_chnum++;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ return USB_ERR_FAIL;
+}
+
+int dec_nonperio_chnum(void)
+{
+ if(nonperio_used_chnum>0)
+ {
+ if(total_used_chnum>0)
+ {
+ nonperio_used_chnum--;
+ total_used_chnum--;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ return USB_ERR_FAIL;
+}
+
+int get_transferring_td_array(u8 chnum, unsigned int *td_addr)
+{
+ if(transferring_td_array[chnum]!=0)
+ {
+ *td_addr = transferring_td_array[chnum];
+ return USB_ERR_SUCCESS;
+ }
+
+ return USB_ERR_FAIL;
+}
+
+int set_transferring_td_array(u8 chnum, u32 td_addr)
+{
+ if(td_addr ==0)
+ {
+ transferring_td_array[chnum] = td_addr;
+ return USB_ERR_SUCCESS;
+ }
+
+ if(transferring_td_array[chnum] == 0)
+ {
+ transferring_td_array[chnum] = td_addr;
+ return USB_ERR_SUCCESS;
+ }
+ else
+ {
+ return USB_ERR_FAIL;
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed)
+ *
+ * @brief this function transfers the insert_ed to S3C6400Scheduler, and
+ * after that, the insert_ed is inserted to TransferReadyQ and scheduled by Scheduler.
+ *
+ *
+ * @param [IN] insert_ed = indicates pointer of ed_t to be inserted to TransferReadyQ.
+ *
+ * @return USB_ERR_ALREADY_EXIST - if the insert_ed is already existed.
+ * USB_ERR_SUCCESS - if success to insert insert_ed to S3CScheduler.
+ */
+/******************************************************************************/
+int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed)
+{
+ if(!insert_ed->is_need_to_insert_scheduler)
+ return USB_ERR_ALREADY_EXIST;
+
+ insert_ed_to_ready_q(insert_ed, false);
+ insert_ed->is_need_to_insert_scheduler = false;
+ insert_ed->ed_status.is_in_transfer_ready_q = true;
+
+ do_periodic_schedule(otghost);
+ do_nonperiodic_schedule(otghost);
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int do_periodic_schedule(struct sec_otghost *otghost)
+ *
+ * @brief this function schedules PeriodicTransferReadyQ.
+ * this function checks whether PeriodicTransferReadyQ has some ed_t.
+ * if there are some ed_t on PeriodicTransferReadyQ
+ * , this function request to start USB Trasnfer to S3C6400OCI.
+ *
+ *
+ * @param void
+ *
+ * @return void
+ */
+/******************************************************************************/
+void do_periodic_schedule(struct sec_otghost *otghost)
+{
+ ed_t *scheduling_ed= NULL;
+ int err_sched = USB_ERR_SUCCESS;
+ u32 sched_cnt = 0;
+
+ otg_dbg(OTG_DBG_SCHEDULE,"*******Start to DoPeriodicSchedul*********\n");
+
+ sched_cnt = get_periodic_ready_q_entity_num();
+
+ while(sched_cnt) {
+ //in periodic transfser, the channel resource was already reserved.
+ //So, we don't need this routine...
+
+start_sched_perio_transfer:
+ if(!sched_cnt)
+ goto end_sched_perio_transfer;
+
+ err_sched = get_ed_from_ready_q(true, &scheduling_ed);
+
+ if(err_sched==USB_ERR_SUCCESS) {
+ otg_list_head *td_list_entry;
+ td_t *td;
+ u32 cur_frame_num = 0;
+
+ otg_dbg(OTG_DBG_SCHEDULE,"the ed_t to be scheduled :%d",(int)scheduling_ed);
+ sched_cnt--;
+ td_list_entry = scheduling_ed->td_list_entry.next;
+
+ if(td_list_entry == &scheduling_ed->td_list_entry) {
+ //scheduling_ed has no td_t. so we schedules another ed_t on PeriodicTransferReadyQ.
+ goto start_sched_perio_transfer;
+ }
+
+ if(scheduling_ed->ed_status.is_in_transferring) {
+ //scheduling_ed is already Scheduled. so we schedules another ed_t on PeriodicTransferReadyQ.
+ goto start_sched_perio_transfer;
+ }
+
+ cur_frame_num = oci_get_frame_num();
+
+ if(((cur_frame_num-scheduling_ed->ed_desc.sched_frame)&HFNUM_MAX_FRNUM)>(HFNUM_MAX_FRNUM>>1)) {
+ insert_ed_to_ready_q(scheduling_ed, false);
+ goto start_sched_perio_transfer;
+ }
+
+ td = otg_list_get_node(td_list_entry, td_t, td_list_entry);
+
+ if((!td->is_transferring) && (!td->is_transfer_done)) {
+ u8 alloc_ch;
+ otg_dbg(OTG_DBG_SCHEDULE,"the td_t to be scheduled :%d",(int)td);
+ alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer);
+ if(alloc_ch<total_chnum_threshold) {
+ td->cur_stransfer.alloc_chnum = alloc_ch;
+ set_transferring_td_array(alloc_ch, (u32)td);
+
+ scheduling_ed->ed_status.is_in_transferring = true;
+ scheduling_ed->ed_status.is_in_transfer_ready_q = false;
+ scheduling_ed->ed_status.in_transferring_td = (u32)td;
+
+ td->is_transferring = true;
+ }
+ else {
+ //we should insert the ed_t to TransferReadyQ, because the USB Transfer of the ed_t is failed.
+ scheduling_ed->ed_status.is_in_transferring = false;
+ scheduling_ed->ed_status.is_in_transfer_ready_q = true;
+ scheduling_ed->ed_status.in_transferring_td = 0;
+
+ insert_ed_to_ready_q(scheduling_ed,true);
+
+ scheduling_ed->is_need_to_insert_scheduler = false;
+ goto end_sched_perio_transfer;
+ }
+
+ }
+ else { // the selected td_t was already transferring or completed to transfer.
+ //we should decide how to control this case.
+ goto end_sched_perio_transfer;
+ }
+ }
+ else {
+ // there is no ED on PeriodicTransferQ. So we finish scheduling.
+ goto end_sched_perio_transfer;
+ }
+ }
+
+end_sched_perio_transfer:
+
+ return;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int do_nonperiodic_schedule(struct sec_otghost *otghost)
+ *
+ * @brief this function start to schedule thie NonPeriodicTransferReadyQ.
+ * this function checks whether NonPeriodicTransferReadyQ has some ed_t.
+ * if there are some ed_t on NonPeriodicTransferReadyQ
+ * , this function request to start USB Trasnfer to S3C6400OCI.
+ *
+ *
+ * @param void
+ *
+ * @return void
+ */
+/******************************************************************************/
+void do_nonperiodic_schedule(struct sec_otghost *otghost)
+{
+ if(total_used_chnum<total_chnum_threshold) {
+ ed_t *scheduling_ed;
+ int err_sched;
+
+ while(1) {
+
+start_sched_nonperio_transfer:
+
+ //check there is available channel resource for Non-Periodic Transfer.
+ if(total_used_chnum==total_chnum_threshold) {
+ goto end_sched_nonperio_transfer;
+ }
+
+ err_sched = get_ed_from_ready_q(false, &scheduling_ed);
+
+ if(err_sched ==USB_ERR_SUCCESS ) {
+ otg_list_head *td_list_entry;
+ td_t *td;
+
+ td_list_entry = scheduling_ed->td_list_entry.next;
+
+ //if(td_list_entry == &scheduling_ed->td_list_entry)
+ if(otg_list_empty(&scheduling_ed->td_list_entry)) {
+ //scheduling_ed has no td_t. so we schedules another ed_t on PeriodicTransferReadyQ.
+ goto start_sched_nonperio_transfer;
+ }
+
+ if(scheduling_ed->ed_status.is_in_transferring) {
+ //scheduling_ed is already Scheduled. so we schedules another ed_t on PeriodicTransferReadyQ.
+ goto start_sched_nonperio_transfer;
+ }
+
+ td = otg_list_get_node(td_list_entry, td_t, td_list_entry);
+
+ if((!td->is_transferring) && (!td->is_transfer_done)) {
+ u8 alloc_ch;
+
+ alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer);
+
+ if(alloc_ch<total_chnum_threshold) {
+ td->cur_stransfer.alloc_chnum = alloc_ch;
+ set_transferring_td_array(alloc_ch, (u32)td);
+
+ inc_non_perio_chnum();
+
+ scheduling_ed->ed_status.is_in_transferring = true;
+ scheduling_ed->ed_status.is_in_transfer_ready_q = false;
+ scheduling_ed->ed_status.in_transferring_td =(u32)td;
+ td->is_transferring = true;
+ }
+ else {
+ //we should insert the ed_t to TransferReadyQ, because the USB Transfer of the ed_t is failed.
+ scheduling_ed->ed_status.is_in_transferring = false;
+ scheduling_ed->ed_status.in_transferring_td =0;
+ insert_ed_to_ready_q(scheduling_ed,true);
+ scheduling_ed->ed_status.is_in_transfer_ready_q = true;
+
+ goto end_sched_nonperio_transfer;
+ }
+ }
+ else {
+ goto end_sched_nonperio_transfer;
+ }
+ }
+ else { //there is no ed_t on NonPeriodicTransferReadyQ.
+ //So, we finish do_nonperiodic_schedule().
+ goto end_sched_nonperio_transfer;
+ }
+ }
+ }
+
+end_sched_nonperio_transfer:
+
+ return;
+}
+
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h
new file mode 100644
index 0000000..f9905fa
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Scheduler.h
+ * [Description] : The Header file defines the external and internal functions of Scheduler.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of Scheduler
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _SCHEDULER_H
+#define _SCHEDULER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-hcdi-memory.h"
+#include "s3c-otg-hcdi-kal.h"
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-oci.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//Defines external functions of IScheduler.c
+extern void init_scheduler(void);
+extern int reserve_used_resource_for_periodic(u32 usb_time,u8 dev_speed, u8 trans_type);
+extern int free_usb_resource_for_periodic(u32 free_usb_time, u8 free_chnum, u8 trans_type);
+extern int remove_ed_from_scheduler(ed_t *remove_ed);
+extern int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td);
+extern int retransmit(struct sec_otghost *otghost, td_t *retransmit_td);
+extern int reschedule(td_t *resched_td);
+extern int deallocate(td_t *dealloc_td);
+extern void do_schedule(struct sec_otghost *otghost);
+extern int get_td_info(u8 chnum,unsigned int *td_addr);
+
+// Defines functiions of TranferReadyQ.
+void init_transfer_ready_q(void);
+int insert_ed_to_ready_q(ed_t *insert_ed, bool f_isfirst);
+int remove_ed_from_ready_q(ed_t *remove_ed);
+int get_ed_from_ready_q(bool f_isperiodic, ed_t **get_ed);
+
+//Define functions of Scheduler
+void do_periodic_schedule(struct sec_otghost *otghost);
+void do_nonperiodic_schedule(struct sec_otghost *otghost);
+int set_transferring_td_array(u8 chnum, u32 td_addr);
+int get_transferring_td_array(u8 chnum, unsigned int *td_addr);
+
+
+//Define fuctions to manage some static global variable.
+int inc_perio_bus_time(u32 uiBusTime, u8 dev_speed);
+int dec_perio_bus_time(u32 uiBusTime);
+
+u8 get_avail_chnum(void);
+int inc_perio_chnum(void);
+int dec_perio_chnum(void);
+int inc_non_perio_chnum(void);
+int dec_nonperio_chnum(void);
+u32 get_periodic_ready_q_entity_num(void);
+
+int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed);
+
+void reset_scheduler_numbers(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c
new file mode 100644
index 0000000..93b6349
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c
@@ -0,0 +1,810 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Commons3c-otg-transfer-transfer.h
+ * [Description] : This source file implements the functions to be defined at CommonTransfer Module.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements some functions of CommonTransfer.
+ * (2) 2008/07/15 by SeungSoo Yang ( ss1.yang@samsung.com )n
+ * - Optimizing for performance \n
+ * (3) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com )
+ * - Modifying for successful rmmod & disconnecting \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-transfer-transfer.h"
+
+// the header pointer to indicate the ED_list to manage the ed_t to be created and initiated.
+static otg_list_head ed_list_head;
+static u32 ref_periodic_transfer;
+
+/******************************************************************************/
+/*!
+ * @name void init_transfer(void)
+ *
+ * @brief this function initiates the S3CTranfer module. that is, this functions initiates
+ * the ED_list_head OTG List which manages the all ed_t to be existed.
+ *
+ * @param void
+ *
+ * @return void
+ */
+/******************************************************************************/
+
+void init_transfer(void)
+{
+ otg_dbg(OTG_DBG_TRANSFER,"start to init_transfer\n");
+ otg_list_init(&ed_list_head);
+ ref_periodic_transfer = 0;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name void DeInitTransfer(void)
+ *
+ * @brief this function Deinitiates the S3CTranfer module. this functions check which there are
+ * some ed_t on ED_list_head. if some ed_t exists, deinit_transfer() deletes the ed_t.
+ *
+ *
+ * @param void
+ *
+ * @return void
+ */
+/******************************************************************************/
+void deinit_transfer(struct sec_otghost *otghost)
+{
+ otg_list_head *ed_list_member;
+ ed_t *delete_ed_p;
+
+ while(otg_list_empty(&ed_list_head) != true) {
+ ed_list_member = ed_list_head.next;
+
+ /* otg_list_pop(ed_list_member); */
+
+ delete_ed_p= otg_list_get_node(ed_list_member,ed_t,ed_list_entry);
+
+ delete_ed(otghost, delete_ed_p);
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int delete_ed(ed_t *delete_ed)
+ *
+ * @brief this function delete the delete_ed.
+ * if there is some available TD_ts on delete_ed, then this function also deletes these td_t
+ *
+ *
+ * @param [IN] delete_ed = indicates the address of ed_t to be deleted.
+ *
+ * @return USB_ERR_SUCCESS -if successes to delete the ed_t.
+ * USB_ERR_FAILl -if fails to delete the ed_t.
+ */
+/******************************************************************************/
+int delete_ed(struct sec_otghost *otghost, ed_t *delete_ed)
+{
+ otg_kal_make_ep_null(delete_ed);
+
+ if(delete_ed->num_td) {
+ cancel_all_td(otghost, delete_ed);
+ /**
+ need to giveback of td's urb with considering life-cycle of
+ TD, ED, urb->hcpriv, td->private, ep->hcpriv, td->parentED
+ (commented by ss1.yang)
+ */
+ }
+
+ otg_list_pop(&delete_ed->ed_list_entry);
+
+ if(delete_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ delete_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+ ref_periodic_transfer--;
+ }
+
+ if(ref_periodic_transfer==0) {
+ disable_sof();
+ }
+ otg_mem_free(delete_ed);
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int delete_td(struct sec_otghost *otghost, td_t *delete_td)
+ *
+ * @brief this function frees memory resource for the delete_td.
+ * and if delete_td is transferring USB Transfer, then this function request to cancel
+ * the USB Transfer to S3CScheduler.
+ *
+ *
+ * @param [OUT] new_td_p = returns the address of the new td_t .
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new td_t.
+ * USB_ERR_FAILl -if fails to create to new td_t.
+ */
+/******************************************************************************/
+int delete_td(struct sec_otghost *otghost, td_t *delete_td)
+{
+ if(delete_td->is_transferring) {
+ //at this case, we should cancel the USB Transfer.
+ cancel_to_transfer_td(otghost, delete_td);
+ }
+
+ otg_mem_free(delete_td);
+ return USB_ERR_SUCCESS;
+}
+
+int create_isoch_packet_desc(isoch_packet_desc_t **new_isoch_packet_desc,
+ u32 isoch_packet_num)
+{
+ return otg_mem_alloc((void **)new_isoch_packet_desc,
+ (u16)sizeof(isoch_packet_desc_t)*isoch_packet_num,USB_MEM_SYNC);
+}
+
+int delete_isoch_packet_desc(isoch_packet_desc_t *del_isoch_packet_desc,
+ u32 isoch_packet_num)
+{
+ return otg_mem_free(del_isoch_packet_desc);
+}
+
+/******************************************************************************/
+/*!
+ * @name void init_isoch_packet_desc( isoch_packet_desc_t *init_isoch_packet_desc,
+ * u32 isoch_packet_start_addr,
+ * u32 isoch_packet_size,
+ * u32 index)
+ *
+ * @brief this function initiates the isoch_packet_desc_t[index].
+ *
+ *
+ * @param [OUT] init_isoch_packet_desc = indicates the pointer of IsochPackDesc_t to be initiated.
+ * [IN] isoch_packet_start_addr = indicates the start address of the buffer to be used
+ * at USB Isochronous Transfer.
+ * [IN] isoch_packet_size = indicates the size of Isochronous packet.
+ * [IN] index = indicates the index to be mapped with this init_isoch_packet_desc.
+ *
+ * @return void
+ */
+/******************************************************************************/
+void init_isoch_packet_desc(isoch_packet_desc_t *init_isoch_packet_desc,
+ u32 isoch_packet_start_addr,
+ u32 isoch_packet_size,
+ u32 index)
+{
+ init_isoch_packet_desc[index].buf_size = isoch_packet_size;
+ init_isoch_packet_desc[index].isoch_packiet_start_addr = isoch_packet_start_addr;
+ init_isoch_packet_desc[index].isoch_status = 0;
+ init_isoch_packet_desc[index].transferred_szie = 0;
+}
+
+/******************************************************************************/
+/*!
+ * @name int create_ed(ed_t **new_ed)
+ *
+ * @brief this function creates a new ed_t and returns the ed_t to Caller
+ *
+ *
+ * @param [OUT] new_ed = returns the address of the new ed_t .
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new ed_t.
+ * USB_ERR_FAILl -if fails to create to new ed_t.
+ */
+/******************************************************************************/
+int create_ed(ed_t **new_ed)
+{
+ int err_code = USB_ERR_SUCCESS;
+
+ err_code = otg_mem_alloc((void **)new_ed,(u16)sizeof(ed_t), USB_MEM_ASYNC);
+ otg_mem_set(*new_ed, 0, sizeof(ed_t));
+ return err_code;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int init_ed( ed_t *init_ed,
+ * u8 dev_addr,
+ * u8 ep_num,
+ * bool f_is_ep_in,
+ * u8 dev_speed,
+ * u8 ep_type,
+ * u32 max_packet_size,
+ * u8 multi_count,
+ * u8 interval,
+ * u32 sched_frame,
+ * u8 hub_addr,
+ * u8 hub_port,
+ * bool f_is_do_split)
+ *
+ * @brief this function initiates the init_ed by using the another parameters.
+ *
+ *
+ * @param [OUT] init_ed = returns the ed_t to be initiated.
+ * [IN] dev_addr = inidcates the address of USB Device.
+ * [IN] ep_num = inidcates the number of the specific endpoint on USB Device.
+ * [IN] f_is_ep_in = inidcates whether the endpoint is IN or not
+ * [IN] dev_speed = inidcates the speed of USB Device.
+ * [IN] max_packet_size = inidcates the maximum packet size of a specific endpoint on USB Device.
+ * [IN] multi_count = if the endpoint supports periodic transfer
+ * , this indicates the multiple packet to be transferred on a uframe
+ * [IN] interval= if the endpoint support periodic transfer, this indicates the polling rate.
+ * [IN] sched_frame= if the endpoint supports periodic transfer, this indicates the start frame number.
+ * [IN] hub_addr= indicate the address of hub which the USB device attachs to.
+ * [IN] hub_port= inidcates the port number of the hub which the USB device attachs to.
+ * [IN] f_is_do_split= inidcates whether this tranfer is split transaction or not.
+ *
+ * @return USB_ERR_SUCCESS -if successes to initiate the ed_t.
+ * USB_ERR_FAILl -if fails to initiate the ed_t.
+ * USB_ERR_NOSPACE -if fails to initiate the ed_t
+ * because there is no USB Resource for this init_ed.
+ */
+/******************************************************************************/
+int init_ed(ed_t *init_ed,
+ u8 dev_addr,
+ u8 ep_num,
+ bool f_is_ep_in,
+ u8 dev_speed,
+ u8 ep_type,
+ u16 max_packet_size,
+ u8 multi_count,
+ u8 interval,
+ u32 sched_frame,
+ u8 hub_addr,
+ u8 hub_port,
+ bool f_is_do_split,
+ void *ep)
+{
+ init_ed->is_halted = false;
+ init_ed->is_need_to_insert_scheduler= true;
+ init_ed->ed_id = (u32)init_ed;
+ init_ed->num_td = 0;
+ init_ed->ed_private = ep;
+
+ otg_list_init(&init_ed->td_list_entry);
+
+ //start to initiate struct ed_desc....
+ init_ed->ed_desc.is_do_split = f_is_do_split;
+ init_ed->ed_desc.is_ep_in = f_is_ep_in;
+ init_ed->ed_desc.dev_speed = dev_speed;
+ init_ed->ed_desc.hub_addr = hub_addr;
+ init_ed->ed_desc.hub_port = hub_port;
+ init_ed->ed_desc.mc = multi_count;
+ init_ed->ed_desc.device_addr = dev_addr;
+ init_ed->ed_desc.endpoint_num = ep_num;
+ init_ed->ed_desc.endpoint_type = ep_type;
+ init_ed->ed_desc.max_packet_size = max_packet_size;
+ init_ed->ed_desc.sched_frame = sched_frame;
+
+ if(init_ed->ed_desc.endpoint_type == INT_TRANSFER) {
+ if(init_ed->ed_desc.dev_speed == LOW_SPEED_OTG ||init_ed->ed_desc.dev_speed == FULL_SPEED_OTG) {
+ init_ed->ed_desc.interval =interval;
+ }
+ else if(init_ed->ed_desc.dev_speed == HIGH_SPEED_OTG) {
+ u8 count = 0;
+ u8 cal_interval = 1;
+
+ for(count = 0;count<(init_ed->ed_desc.interval-1);count++) {
+ cal_interval *=2;
+ }
+
+ init_ed->ed_desc.interval =cal_interval;
+ }
+ else {
+ otg_dbg(OTG_DBG_TRANSFER,"Super-Speed is not supported\n");
+ }
+ init_ed->ed_desc.sched_frame = (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM;
+ ref_periodic_transfer++;
+ }
+ if(init_ed->ed_desc.endpoint_type==ISOCH_TRANSFER) {
+ u8 count = 0;
+ u8 cal_interval = 1;
+
+ for(count = 0;count<(init_ed->ed_desc.interval-1);count++)
+ {
+ cal_interval *=2;
+ }
+
+ init_ed->ed_desc.interval = cal_interval;
+ init_ed->ed_desc.sched_frame = (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM;
+ ref_periodic_transfer++;
+ }
+
+ //start to initiate struct ed_status....
+
+ //initiates PID
+ switch(ep_type) {
+ case BULK_TRANSFER:
+ case INT_TRANSFER:
+ init_ed->ed_status.data_tgl = DATA0;
+ break;
+
+ case CONTROL_TRANSFER:
+ init_ed->ed_status.control_data_tgl.setup_tgl = SETUP;
+ init_ed->ed_status.control_data_tgl.data_tgl = DATA1;
+ init_ed->ed_status.control_data_tgl.status_tgl = DATA1;
+ break;
+
+ case ISOCH_TRANSFER:
+ if(f_is_ep_in) {
+ switch(multi_count) {
+ case MULTI_COUNT_ZERO :
+ init_ed->ed_status.data_tgl = DATA0;
+ break;
+ case MULTI_COUNT_ONE :
+ init_ed->ed_status.data_tgl = DATA1;
+ break;
+ case MULTI_COUNT_TWO :
+ init_ed->ed_status.data_tgl = DATA2;
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ switch(multi_count) {
+ case MULTI_COUNT_ZERO :
+ init_ed->ed_status.data_tgl = DATA0;
+ break;
+ case MULTI_COUNT_ONE :
+ init_ed->ed_status.data_tgl = MDATA;
+ break;
+ case MULTI_COUNT_TWO :
+ init_ed->ed_status.data_tgl = MDATA;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(init_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ init_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+ u32 usb_time = 0, byte_count = 0;
+
+ //calculates the bytes to be transferred at one (uframe)frame.
+ byte_count = (init_ed->ed_desc.mc+1)*init_ed->ed_desc.max_packet_size;
+
+ usb_time = (u32)otg_usbcore_get_calc_bustime(init_ed->ed_desc.dev_speed,
+ init_ed->ed_desc.is_ep_in,
+ (init_ed->ed_desc.endpoint_type==ISOCH_TRANSFER?true:false),
+ byte_count);
+ usb_time /= 1000; //convert nanosec unit to usec unit
+
+ if(reserve_used_resource_for_periodic(usb_time, init_ed->ed_desc.dev_speed, init_ed->ed_desc.endpoint_type) != USB_ERR_SUCCESS) {
+ return USB_ERR_NOSPACE;
+ }
+
+ init_ed->ed_status.is_alloc_resource_for_ed =true;
+ init_ed->ed_desc.used_bus_time =usb_time;
+ init_ed->ed_desc.mc =multi_count+1;
+ }
+
+ init_ed->ed_status.is_in_transfer_ready_q =false;
+ init_ed->ed_status.is_in_transferring =false;
+ init_ed->ed_status.is_ping_enable =false;
+ init_ed->ed_status.in_transferring_td =0;
+
+// sztupy: split transaction support
+ init_ed->ed_status.is_complete_split = false;
+ init_ed->ed_status.split_pos = ED_STATUS_SPLIT_POS_ALL;
+ init_ed->ed_status.split_offset = 0;
+
+ //push the ed_t to ED_list.
+ otg_list_push_prev(&init_ed->ed_list_entry,&ed_list_head);
+
+ if(ref_periodic_transfer) {
+ enable_sof();
+ }
+ return USB_ERR_SUCCESS;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int create_td(td_t **new_td)
+ *
+ * @brief this function creates a new td_t and returns the td_t to Caller
+ *
+ *
+ * @param [OUT] new_td = returns the address of the new td_t .
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new td_t.
+ * USB_ERR_FAILl -if fails to create to new td_t.
+ */
+/******************************************************************************/
+int create_td(td_t **new_td)
+{
+ int err_code = USB_ERR_SUCCESS;
+
+ err_code = otg_mem_alloc((void **)new_td,(u16)sizeof(td_t), USB_MEM_ASYNC);
+ otg_mem_set(*new_td, 0, sizeof(td_t));
+ return err_code;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int init_td( td_t *init_td,
+ * ed_t *parent_ed,
+ * void *call_back_fun,
+ * void *call_back_param,
+ * u32 transfer_flag,
+ * bool f_is_standard_dev_req,
+ * u32 phy_setup,
+ * u32 vir_setup,
+ * u32 vir_buf_addr,
+ * u32 phy_buf_addr,
+ * u32 buf_size,
+ * u32 isoch_start_frame,
+ * isoch_packet_desc_t *isoch_packet_desc,
+ * u32 isoch_packet_num,
+ * void *td_priv)
+ *
+ * @brief this function initiates the init_td by using another parameter.
+ *
+ *
+ * @param [IN] init_td - indicate the td_t to be initiated.
+ * [IN] parent_ed - indicate the ed_t to manage this init_td
+ * [IN] call_back_func - indicate the call-back function of application.
+ * [IN] call_back_param - indicate the parameter of the call-back function.
+ * [IN] transfer_flag - indicate the transfer flag.
+ * [IN] f_is_standard_dev_req - indicates the issue transfer request is USB Standard Request
+ * [IN] phy_setup - the physical address of buffer to store the USB Standard Request.
+ * [IN] vir_setup - the virtual address of buffer to store the USB Standard Request.
+ * [IN] vir_buf_addr - the virtual address of buffer to store the data to be transferred or received.
+ * [IN] phy_buf_addr - the physical address of buffer to store the data to be transferred or received.
+ * [IN] buf_size - indicates the buffer size.
+ * [IN] isoch_start_frame - if this usb transfer is isochronous transfer
+ * , this indicates the start frame to start the usb transfer.
+ * [IN] isoch_packet_desc - if the usb transfer is isochronous transfer
+ * , this indicates the structure to describe the isochronous transfer.
+ * [IN] isoch_packet_num - if the usb transfer is isochronous transfer
+ * , this indicates the number of packet to consist of the usb transfer.
+ * [IN] td_priv - indicate the private data to be delivered from usb core of linux.
+ * td_priv stores the urb of linux.
+ *
+ * @return USB_ERR_SUCCESS -if successes to initiate the new td_t.
+ * USB_ERR_FAILl -if fails to create to new td_t.
+ */
+/******************************************************************************/
+int init_td( td_t *init_td,
+ ed_t *parent_ed,
+ void *call_back_fun,
+ void *call_back_param,
+ u32 transfer_flag,
+ bool f_is_standard_dev_req,
+ u32 phy_setup,
+ u32 vir_setup,
+ u32 vir_buf_addr,
+ u32 phy_buf_addr,
+ u32 buf_size,
+ u32 isoch_start_frame,
+ isoch_packet_desc_t *isoch_packet_desc,
+ u32 isoch_packet_num,
+ void *td_priv)
+{
+ if(f_is_standard_dev_req) {
+ if((phy_buf_addr>0) && (buf_size>0)) {
+ init_td->standard_dev_req_info.is_data_stage = true;
+ }
+ else {
+ init_td->standard_dev_req_info.is_data_stage = false;
+ }
+ init_td->standard_dev_req_info.conrol_transfer_stage = SETUP_STAGE;
+ init_td->standard_dev_req_info.phy_standard_dev_req_addr = phy_setup;
+ init_td->standard_dev_req_info.vir_standard_dev_req_addr = vir_setup;
+ }
+
+ init_td->call_back_func_p = call_back_fun;
+ init_td->call_back_func_param_p = call_back_param;
+ init_td->error_code = USB_ERR_SUCCESS;
+ init_td->is_standard_dev_req = f_is_standard_dev_req;
+ init_td->is_transfer_done = false;
+ init_td->is_transferring = false;
+ init_td->td_private = td_priv;
+ init_td->err_cnt = 0;
+ init_td->parent_ed_p = parent_ed;
+ init_td->phy_buf_addr = phy_buf_addr;
+ init_td->vir_buf_addr = vir_buf_addr;
+ init_td->buf_size = buf_size;
+ init_td->isoch_packet_desc_p = isoch_packet_desc;
+ init_td->isoch_packet_num = isoch_packet_num;
+ init_td->isoch_packet_index = 0;
+ init_td->isoch_packet_position = 0;
+ init_td->sched_frame = isoch_start_frame;
+ init_td->used_total_bus_time = parent_ed->ed_desc.used_bus_time;
+ init_td->td_id = (u32)init_td;
+ init_td->transfer_flag = transfer_flag;
+ init_td->transferred_szie = 0;
+
+ switch(parent_ed->ed_desc.endpoint_type) {
+ case CONTROL_TRANSFER:
+ init_nonperio_stransfer(true, init_td);
+ break;
+
+ case BULK_TRANSFER:
+ init_nonperio_stransfer(false, init_td);
+ break;
+
+ case INT_TRANSFER:
+ init_perio_stransfer(false, init_td);
+ break;
+
+ case ISOCH_TRANSFER:
+ init_perio_stransfer(true, init_td);
+ break;
+
+ default:
+ return USB_ERR_FAIL;
+ }
+
+ //insert the td_t to parent_ed->td_list_entry.
+ otg_list_push_prev(&init_td->td_list_entry,&parent_ed->td_list_entry);
+ parent_ed->num_td++;
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int issue_transfer(struct sec_otghost *otghost,
+ * ed_t *parent_ed,
+ * void *call_back_func,
+ * void *call_back_param,
+ * u32 transfer_flag,
+ * bool f_is_standard_dev_req,
+ * u32 setup_vir_addr,
+ * u32 setup_phy_addr,
+ * u32 vir_buf_addr,
+ * u32 phy_buf_addr,
+ * u32 buf_size,
+ * u32 start_frame,
+ * u32 isoch_packet_num,
+ * isoch_packet_desc_t *isoch_packet_desc,
+ * void *td_priv,
+ * unsigned int *return_td_addr)
+ *
+ * @brief this function start USB Transfer
+ *
+ *
+ * @param [IN] parent_ed - indicate the ed_t to manage this issue transfer.
+ * [IN] call_back_func - indicate the call-back function of application.
+ * [IN] call_back_param - indicate the parameter of the call-back function.
+ * [IN] transfer_flag - indicate the transfer flag.
+ * [IN] f_is_standard_dev_req - indicates the issue transfer request is USB Standard Request
+ * [IN] setup_vir_addr - the virtual address of buffer to store the USB Standard Request.
+ * [IN] setup_phy_addr - the physical address of buffer to store the USB Standard Request.
+ * [IN] vir_buf_addr - the virtual address of buffer to store the data to be transferred or received.
+ * [IN] phy_buf_addr - the physical address of buffer to store the data to be transferred or received.
+ * [IN] buf_size - indicates the buffer size.
+ * [IN] start_frame - if this usb transfer is isochronous transfer
+ * , this indicates the start frame to start the usb transfer.
+ * [IN] isoch_packet_num - if the usb transfer is isochronous transfer
+ * , this indicates the number of packet to consist of the usb transfer.
+ * [IN] isoch_packet_desc - if the usb transfer is isochronous transfer
+ * , this indicates the structure to describe the isochronous transfer.
+ * [IN] td_priv - indicate the private data to be delivered from usb core of linux.
+ * td_priv stores the urb of linux.
+ * [OUT] return_td_addr - indicates the variable address to store the new td_t for this transfer
+ *
+ * @return USB_ERR_SUCCESS -if successes to initiate the new td_t.
+ * USB_ERR_FAILl -if fails to create to new td_t.
+ */
+/******************************************************************************/
+int issue_transfer(struct sec_otghost *otghost,
+ ed_t *parent_ed,
+ void *call_back_func,
+ void *call_back_param,
+ u32 transfer_flag,
+ bool f_is_standard_dev_req,
+ u32 setup_vir_addr,
+ u32 setup_phy_addr,
+ u32 vir_buf_addr,
+ u32 phy_buf_addr,
+ u32 buf_size,
+ u32 start_frame,
+ u32 isoch_packet_num,
+ isoch_packet_desc_t *isoch_packet_desc,
+ void *td_priv,
+ unsigned int *return_td_addr)
+{
+ td_t *new_td_p = NULL;
+
+ int err = USB_ERR_SUCCESS;
+ if(create_td(&new_td_p)==USB_ERR_SUCCESS) {
+ err = init_td( new_td_p,
+ parent_ed,
+ call_back_func,
+ call_back_param,
+ transfer_flag,
+ f_is_standard_dev_req,
+ setup_phy_addr,
+ setup_vir_addr,
+ vir_buf_addr,
+ phy_buf_addr,
+ buf_size,
+ start_frame,
+ isoch_packet_desc,
+ isoch_packet_num,
+ td_priv);
+
+ if(err !=USB_ERR_SUCCESS) {
+ return USB_ERR_NOMEM;
+ }
+
+ if(parent_ed->is_need_to_insert_scheduler) {
+ insert_ed_to_scheduler(otghost, parent_ed);
+ }
+
+ *return_td_addr = (u32)new_td_p;
+
+ return USB_ERR_SUCCESS;
+ }
+ else {
+ return USB_ERR_NOMEM;
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int cancel_transfer(struct sec_otghost *otghost,
+ * ed_t *parent_ed,
+ * td_t *cancel_td)
+ *
+ * @brief this function cancels to transfer USB Transfer of cancel_td.
+ * this function firstly check whether this cancel_td is transferring or not
+ * if the cancel_td is transferring, the this function requests to cancel the USB Transfer
+ * to S3CScheduler. if the parent_ed is for Periodic Transfer, and
+ * there is not any td_t at parent_ed, then this function requests to release
+ * some usb resources for the ed_t to S3CScheduler. finally this function deletes the cancel_td.
+ *
+ * @param [IN] pUpdateTD = indicates the pointer ot the td_t to have STransfer to be updated.
+ *
+ * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD.
+ */
+ /******************************************************************************/
+int cancel_transfer(struct sec_otghost *otghost,
+ ed_t *parent_ed,
+ td_t *cancel_td)
+{
+ int err = USB_ERR_DEQUEUED;
+ otg_list_head *tmp_list_p, *tmp_list2_p;
+ bool cond_found = false;
+
+ if(parent_ed == NULL || cancel_td == NULL) {
+ otg_dbg(OTG_DBG_TRANSFER, "parent_ed == NULL || cancel_td == NULL\n");
+ cancel_td->error_code = USB_ERR_NOELEMENT;
+ otg_usbcore_giveback(cancel_td);
+ return cancel_td->error_code;
+ }
+
+ otg_list_for_each_safe(tmp_list_p, tmp_list2_p, &parent_ed->td_list_entry) {
+ if(&cancel_td->td_list_entry == tmp_list_p) {
+ cond_found = true;
+ break;
+ }
+ }
+
+ if (cond_found != true) {
+ otg_dbg(OTG_DBG_TRANSFER, "cond_found != true \n");
+ cancel_td->error_code = USB_ERR_NOELEMENT;
+ otg_usbcore_giveback(cancel_td);
+ return cancel_td->error_code;
+ }
+
+
+ if(cancel_td->is_transferring) {
+ if(!parent_ed->ed_status.is_in_transfer_ready_q) {
+ err = cancel_to_transfer_td(otghost, cancel_td);
+
+ parent_ed->ed_status.in_transferring_td = 0;
+
+ if(err != USB_ERR_SUCCESS) {
+ otg_dbg(OTG_DBG_TRANSFER, "cancel_to_transfer_td \n");
+ cancel_td->error_code = err;
+ otg_usbcore_giveback(cancel_td);
+ goto ErrorStatus;
+ }
+
+ }
+ // kevinh - even if the record was in the ready queue it is important to delete it as well. We can also always remove the ed from the scheduler
+ // once all tds have been removed
+ otg_list_pop(&cancel_td->td_list_entry);
+ parent_ed->num_td--;
+ }
+ else {
+ otg_list_pop(&cancel_td->td_list_entry);
+ parent_ed->num_td--;
+
+ if(parent_ed->num_td==0) {
+ remove_ed_from_scheduler(parent_ed);
+ }
+ }
+
+ if(parent_ed->num_td) {
+ // kevinh - we do not want to force insert_scheduler, because if this endpoint _was_ already scheduled
+ // because the deleted td was not the active td then we will now put ed into the scheduler list twice, thus
+ // corrupting it.
+ // parent_ed->is_need_to_insert_scheduler = true;
+ insert_ed_to_scheduler(otghost, parent_ed);
+ }
+ else {
+ if(parent_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ parent_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+ //Release channel and usb bus resource for this ed_t.
+ //but, not release memory for this ed_t.
+ free_usb_resource_for_periodic(parent_ed->ed_desc.used_bus_time,
+ cancel_td->cur_stransfer.alloc_chnum,
+ cancel_td->parent_ed_p->ed_desc.endpoint_type);
+
+ parent_ed->ed_status.is_alloc_resource_for_ed =false;
+ }
+ }
+ // the caller of this functions should call otg_usbcore_giveback(cancel_td);
+ cancel_td->error_code = USB_ERR_DEQUEUED;
+ // kevinh - fixed bug, the caller should take care of calling delete_td because they might still want to do some
+ // operations on that memory
+ // delete_td(cancel_td);
+ // otg_usbcore_giveback(cancel_td);
+
+ErrorStatus:
+
+ return err;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed)
+ *
+ * @brief this function cancels all Transfer which parent_ed manages.
+ *
+ * @param [IN] parent_ed = indicates the pointer ot the ed_t to manage TD_ts to be canceled.
+ *
+ * @return USB_ERR_SUCCESS - if success to cancel all TD_ts of pParentsED.
+ * USB_ERR_FAIL - if fail to cancel all TD_ts of pParentsED.
+ */
+ /******************************************************************************/
+int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed)
+{
+ otg_list_head *cancel_td_list_entry;
+ td_t *cancel_td;
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "cancel_all_td \n");
+ do {
+ cancel_td_list_entry = parent_ed->td_list_entry.next;
+
+ cancel_td = otg_list_get_node(cancel_td_list_entry,td_t, td_list_entry);
+
+ if(cancel_transfer(otghost, parent_ed, cancel_td) == USB_ERR_DEQUEUED)
+ // kevinh FIXME - do we also need to giveback?
+ delete_td(otghost,cancel_td);
+ } while(parent_ed->num_td);
+
+ return USB_ERR_SUCCESS;
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c
new file mode 100644
index 0000000..013a2a2
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c
@@ -0,0 +1,140 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : NonPeriodicTransfer.c
+ * [Description] : This source file implements the functions to be defined at NonPeriodicTransfer Module.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/07
+ * [Revision History]
+ * (1) 2008/06/07 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements some functions of NonPeriodicTransfer.
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+
+#include "s3c-otg-transfer-transfer.h"
+
+/******************************************************************************/
+/*!
+ * @name int init_nonperio_stransfer( bool f_is_standard_dev_req,
+ * td_t *parent_td)
+ *
+ * @brief this function initiates the parent_td->cur_stransfer for NonPeriodic Transfer and
+ * inserts this init_td_p to init_td_p->parent_ed_p.
+ *
+ * @param [IN] f_is_standard_dev_req = indicates whether this transfer is Control or not.
+ * [IN] parent_td = indicates the address of td_t to be initiated.
+ *
+ * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD.
+ */
+ /******************************************************************************/
+int init_nonperio_stransfer(bool f_is_standard_dev_req,
+ td_t *parent_td)
+{
+
+
+ parent_td->cur_stransfer.ed_desc_p = &parent_td->parent_ed_p->ed_desc;
+ parent_td->cur_stransfer.ed_status_p = &parent_td->parent_ed_p->ed_status;
+ parent_td->cur_stransfer.alloc_chnum = CH_NONE;
+ parent_td->cur_stransfer.parent_td = (u32)parent_td;
+ parent_td->cur_stransfer.stransfer_id = (u32)&parent_td->cur_stransfer;
+
+ otg_mem_set(&(parent_td->cur_stransfer.hc_reg), 0, sizeof(hc_reg_t));
+
+ parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1;
+
+ if(f_is_standard_dev_req)
+ {
+ parent_td->cur_stransfer.buf_size = USB_20_STAND_DEV_REQUEST_SIZE;
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->standard_dev_req_info.phy_standard_dev_req_addr;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->standard_dev_req_info.vir_standard_dev_req_addr;
+ }
+ else
+ {
+ parent_td->cur_stransfer.buf_size = (parent_td->buf_size>MAX_CH_TRANSFER_SIZE)
+ ?MAX_CH_TRANSFER_SIZE
+ :parent_td->buf_size;
+
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr;
+ }
+
+ parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size
+ , parent_td->parent_ed_p->ed_desc.max_packet_size);
+
+ return USB_ERR_SUCCESS;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name void update_nonperio_stransfer(td_t *parent_td)
+ *
+ * @brief this function updates the parent_td->cur_stransfer to be used by S3COCI.
+ *
+ * @param [IN/OUT]parent_td = indicates the pointer of td_t to store the STranser to be updated.
+ *
+ * @return USB_ERR_SUCCESS -if success to update the parent_td->cur_stransfer.
+ * USB_ERR_FAIL -if fail to update the parent_td->cur_stransfer.
+ */
+ /******************************************************************************/
+void update_nonperio_stransfer(td_t *parent_td)
+{
+ switch(parent_td->parent_ed_p->ed_desc.endpoint_type) {
+ case BULK_TRANSFER:
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.buf_size = ((parent_td->buf_size - parent_td->transferred_szie)>MAX_CH_TRANSFER_SIZE)
+ ?MAX_CH_TRANSFER_SIZE
+ :parent_td->buf_size - parent_td->transferred_szie;
+ break;
+
+ case CONTROL_TRANSFER:
+ if(parent_td->standard_dev_req_info.conrol_transfer_stage == SETUP_STAGE)
+ {
+ // but, this case will not be occured......
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->standard_dev_req_info.phy_standard_dev_req_addr;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->standard_dev_req_info.vir_standard_dev_req_addr;
+ parent_td->cur_stransfer.buf_size = 8;
+ }
+ else if(parent_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.buf_size = ((parent_td->buf_size - parent_td->transferred_szie)>MAX_CH_TRANSFER_SIZE)
+ ?MAX_CH_TRANSFER_SIZE
+ :parent_td->buf_size - parent_td->transferred_szie;
+ }
+ else
+ {
+ parent_td->cur_stransfer.start_phy_buf_addr = 0;
+ parent_td->cur_stransfer.start_vir_buf_addr = 0;
+ parent_td->cur_stransfer.buf_size = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size, parent_td->parent_ed_p->ed_desc.max_packet_size);
+
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c
new file mode 100644
index 0000000..f254b78
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c
@@ -0,0 +1,128 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : NonPeriodicTransfer.c
+ * [Description] : This source file implements the functions to be defined at NonPeriodicTransfer Module.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/09
+ * [Revision History]
+ * (1) 2008/06/09 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements some functions of PeriodicTransfer.
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-transfer-transfer.h"
+
+
+/******************************************************************************/
+/*!
+ * @name int init_perio_stransfer( bool f_is_isoch_transfer,
+ * td_t *parent_td)
+ *
+ * @brief this function initiates the parent_td->cur_stransfer for Periodic Transfer and
+ * inserts this init_td_p to init_td_p->parent_ed_p.
+ *
+ * @param [IN] f_is_isoch_transfer = indicates whether this transfer is Isochronous or not.
+ * [IN] parent_td = indicates the address of td_t to be initiated.
+ *
+ * @return USB_ERR_SUCCESS -if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL -if fail to update the STranfer of pUpdateTD.
+ */
+ /******************************************************************************/
+int init_perio_stransfer( bool f_is_isoch_transfer,
+ td_t *parent_td)
+{
+ parent_td->cur_stransfer.ed_desc_p = &parent_td->parent_ed_p->ed_desc;
+ parent_td->cur_stransfer.ed_status_p = &parent_td->parent_ed_p->ed_status;
+ parent_td->cur_stransfer.alloc_chnum = CH_NONE;
+ parent_td->cur_stransfer.parent_td = (u32)parent_td;
+ parent_td->cur_stransfer.stransfer_id = (u32)&parent_td->cur_stransfer;
+
+ otg_mem_set(&parent_td->cur_stransfer.hc_reg, 0, sizeof(hc_reg_t));
+
+ parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1;
+
+ if(f_is_isoch_transfer)
+ {
+ // initiates the STransfer usinb the IsochPacketDesc[0].
+ parent_td->cur_stransfer.buf_size =parent_td->isoch_packet_desc_p[0].buf_size;
+ parent_td->cur_stransfer.start_phy_buf_addr =parent_td->phy_buf_addr + parent_td->isoch_packet_desc_p[0].isoch_packiet_start_addr;
+ parent_td->cur_stransfer.start_vir_buf_addr =parent_td->vir_buf_addr + parent_td->isoch_packet_desc_p[0].isoch_packiet_start_addr;
+ }
+ else
+ {
+ parent_td->cur_stransfer.buf_size =(parent_td->buf_size>MAX_CH_TRANSFER_SIZE)
+ ?MAX_CH_TRANSFER_SIZE
+ :parent_td->buf_size;
+
+ parent_td->cur_stransfer.start_phy_buf_addr =parent_td->phy_buf_addr;
+ parent_td->cur_stransfer.start_vir_buf_addr =parent_td->vir_buf_addr;
+ }
+
+ parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size, parent_td->parent_ed_p->ed_desc.max_packet_size);
+
+ return USB_ERR_SUCCESS;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name void update_perio_stransfer(td_t *parent_td)
+ *
+ * @brief this function updates the parent_td->cur_stransfer to be used by S3COCI.
+ * the STransfer of parent_td is for Periodic Transfer.
+ *
+ * @param [IN/OUT]parent_td = indicates the pointer of td_t to store the STranser to be updated.
+ *
+ * @return USB_ERR_SUCCESS -if success to update the parent_td->cur_stransfer.
+ * USB_ERR_FAIL -if fail to update the parent_td->cur_stransfer.
+ */
+ /******************************************************************************/
+void update_perio_stransfer(td_t *parent_td)
+{
+ switch(parent_td->parent_ed_p->ed_desc.endpoint_type) {
+ case INT_TRANSFER:
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie;
+ parent_td->cur_stransfer.buf_size = (parent_td->buf_size>MAX_CH_TRANSFER_SIZE)
+ ?MAX_CH_TRANSFER_SIZE :parent_td->buf_size;
+ break;
+
+ case ISOCH_TRANSFER:
+ parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr
+ +parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].isoch_packiet_start_addr
+ +parent_td->isoch_packet_position;
+
+ parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr
+ +parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].isoch_packiet_start_addr
+ +parent_td->isoch_packet_position;
+
+ parent_td->cur_stransfer.buf_size = (parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].buf_size - parent_td->isoch_packet_position)>MAX_CH_TRANSFER_SIZE
+ ?MAX_CH_TRANSFER_SIZE
+ :parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].buf_size - parent_td->isoch_packet_position;
+
+ break;
+
+ default:
+ break;
+ }
+}
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h b/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h
new file mode 100644
index 0000000..6e1532c
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : s3c-otg-transfer-transfer.h
+ * [Description] : The Header file defines the external and internal functions of Transfer.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of Transfer
+ * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
+ * : Optimizing for performance \n
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _TRANSFER_H
+#define _TRANSFER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+//#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-hcdi-memory.h"
+#include "s3c-otg-hcdi-kal.h"
+#include "s3c-otg-hcdi-debug.h"
+
+#include "s3c-otg-scheduler-scheduler.h"
+#include "s3c-otg-isr.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void init_transfer(void);
+void deinit_transfer(struct sec_otghost *otghost);
+
+int issue_transfer(struct sec_otghost *otghost,
+ ed_t *parent_ed,
+ void *call_back_func,
+ void *call_back_param,
+ u32 transfer_flag,
+ bool f_is_standard_dev_req,
+ u32 setup_vir_addr,
+ u32 setup_phy_addr,
+ u32 vir_buf_addr,
+ u32 phy_buf_addr,
+ u32 buf_size,
+ u32 start_frame,
+ u32 isoch_packet_num,
+ isoch_packet_desc_t *isoch_packet_desc,
+ void *td_priv,
+ unsigned int *return_td_addr);
+
+int cancel_transfer(struct sec_otghost *otghost,
+ ed_t *parent_ed,
+ td_t *cancel_td);
+
+int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed);
+
+int create_ed(ed_t ** new_ed);
+
+int init_ed(ed_t * init_ed,
+ u8 dev_addr,
+ u8 ep_num,
+ bool f_is_ep_in,
+ u8 dev_speed,
+ u8 ep_type,
+ u16 max_packet_size,
+ u8 multi_count,
+ u8 interval,
+ u32 sched_frame,
+ u8 hub_addr,
+ u8 hub_port,
+ bool f_is_do_split,
+ void *ep);
+
+int delete_ed(struct sec_otghost *otghost, ed_t *delete_ed);
+
+int delete_td(struct sec_otghost *otghost, td_t * delete_td);
+
+int create_isoch_packet_desc( isoch_packet_desc_t **new_isoch_packet_desc,
+ u32 isoch_packet_num);
+
+int delete_isoch_packet_desc( isoch_packet_desc_t *del_isoch_packet_desc,
+ u32 isoch_packet_num);
+
+
+void init_isoch_packet_desc( isoch_packet_desc_t *init_isoch_packet_desc,
+ u32 offset,
+ u32 isoch_packet_size,
+ u32 index);
+
+// NonPeriodicTransfer.c implements.
+
+int init_nonperio_stransfer(bool f_is_standard_dev_req,
+ td_t *parent_td);
+
+void update_nonperio_stransfer(td_t *parent_td);
+
+//PeriodicTransfer.c implements
+
+int init_perio_stransfer( bool f_is_isoch_transfer,
+ td_t *parent_td);
+
+void update_perio_stransfer(td_t *parent_td);
+
+static inline u32 calc_packet_cnt(u32 data_size, u16 max_packet_size)
+{
+ if(data_size != 0)
+ {
+ return (data_size%max_packet_size==0)?data_size/max_packet_size:data_size/max_packet_size+1;
+ }
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c
new file mode 100644
index 0000000..e544f5c
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c
@@ -0,0 +1,720 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : BulkTransferChecker.c
+ * [Description] : The Source file implements the external and internal functions of BulkTransferChecker.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/13
+ * [Revision History]
+ * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of BulkTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-transferchecker-bulk.h"
+#include "s3c-otg-isr.h"
+
+/******************************************************************************/
+/*!
+ * @name u8 process_bulk_transfer(td_t *result_td,
+ hc_info_t *hc_reg_data)
+
+ *
+ * @brief this function processes the result of the Bulk Transfer.
+ * firstly, this function checks the result of the Bulk Transfer.
+ * and according to the result, calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t whose channel is interruped.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ * NO_ACTION -if we don't need any action,
+ */
+/******************************************************************************/
+u8 process_bulk_transfer(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ hcintn_t hc_intr_info;
+ u8 return_val=0;
+
+ //we just deal with the interrupts to be unmasked.
+ hc_intr_info.d32 = hc_reg_data->hc_int.d32 & result_td->cur_stransfer.hc_reg.hc_int_msk.d32;
+
+ if(result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ if(hc_intr_info.b.chhltd)
+ {
+ return_val = process_chhltd_on_bulk(result_td, hc_reg_data);
+ }
+
+ else if (hc_intr_info.b.ack)
+ {
+ return_val =process_ack_on_bulk(result_td, hc_reg_data);
+ }
+
+ else if (hc_intr_info.b.nak)
+ {
+ return_val =process_nak_on_bulk(result_td, hc_reg_data);
+ }
+
+ else if (hc_intr_info.b.datatglerr)
+ {
+ return_val = process_datatgl_on_bulk(result_td,hc_reg_data);
+ }
+ }
+ else
+ {
+ if(hc_intr_info.b.chhltd)
+ {
+ return_val = process_chhltd_on_bulk(result_td, hc_reg_data);
+
+ }
+
+ else if(hc_intr_info.b.ack)
+ {
+ return_val =process_ack_on_bulk( result_td, hc_reg_data);
+ }
+ }
+
+ return return_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_chhltd_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function processes Channel Halt event according to Synopsys OTG Spec.
+ * firstly, this function checks the reason of the Channel Halt, and according to the reason,
+ * calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ */
+/******************************************************************************/
+u8 process_chhltd_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+#if 0
+ if(result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_bulk( result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.bblerr)
+ {
+ return process_bblerr_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_bulk(result_td, hc_reg_data);
+ }
+ else
+ {
+ //Occure Error State.....
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->err_cnt++;
+ if(result_td->err_cnt == 3)
+ {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+
+ return RE_TRANSMIT;
+ }
+ }
+ else
+ {
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_bulk(result_td, hc_reg_data);
+ }
+
+ else if(hc_reg_data->hc_int.b.nak)
+ {
+ return process_nak_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.nyet)
+ {
+ return process_nyet_on_bulk(result_td, hc_reg_data);
+ }
+ else
+ {
+ //occur error state...
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->err_cnt++;
+ if(result_td->err_cnt == 3)
+ {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+
+ return RE_TRANSMIT;
+ }
+
+
+
+ }
+#else
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_bulk( result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.bblerr)
+ {
+ return process_bblerr_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.nak)
+ {
+ return process_nak_on_bulk(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.nyet)
+ {
+ return process_nyet_on_bulk(result_td, hc_reg_data);
+ }
+ else
+ {
+ //occur error state...
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->err_cnt++;
+ if(result_td->err_cnt == 3)
+ {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+
+ return RE_TRANSMIT;
+ }
+#endif
+ return USB_ERR_SUCCESS;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xfercompl_on_bulk( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the xfercompl event according to Synopsys OTG Spec.
+ * the procedure of this function is as following
+ * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr.
+ * 2. masks some bit of HCINTMSK
+ * 3. updates the result_td fields
+ * err_cnt/u8/standard_dev_req_info.
+ * 4. updates the result_td->parent_ed_p->ed_status.
+ * BulkDataTgl.
+ * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return USB_ERR_SUCCESS
+ */
+/******************************************************************************/
+u8 process_xfercompl_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ u8 ret_val=0;
+
+ result_td->err_cnt = 0;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable =false;
+
+ result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data);
+
+ if(result_td->transferred_szie==result_td->buf_size)
+ {//at IN Transfer, short transfer is accepted.
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ ret_val = DE_ALLOCATE;
+ }
+ else
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize)
+ {
+ if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT)
+ {
+ result_td->error_code =USB_ERR_STATUS_SHORTREAD;
+ }
+ else
+ {
+ result_td->error_code =USB_ERR_STATUS_COMPLETE;
+ }
+ ret_val = DE_ALLOCATE;
+ }
+ else
+ {
+ ret_val = RE_SCHEDULE;
+ }
+ }
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ if(hc_reg_data->hc_int.b.nyet)
+ {
+ //at OUT Transfer, we must re-transmit.
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+ }
+ }
+
+ return ret_val;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ahb_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec.
+ * this function stop the channel to be executed
+ * TBD....
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return USB_ERR_SUCCESS
+ */
+/******************************************************************************/
+u8 process_ahb_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt =0;
+ result_td->error_code =USB_ERR_STATUS_AHBERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ // we just calculate the size of the transferred data on Data Stage of Bulk Transfer.
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+
+ return DE_ALLOCATE;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_stall_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with theStall event according to Synopsys OTG Spec.
+ * when Stall is occured at Bulk Transfer, we should reset the PID as DATA0
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_stall_on_bulk( td_t * result_td,
+ hc_info_t * hc_reg_data)
+{
+ result_td->err_cnt =0;
+ result_td->error_code =USB_ERR_STATUS_STALL;
+
+ //this channel is stalled, So we don't process another interrupts.
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(DATA0, result_td);
+
+
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nak_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the nak event according to Synopsys OTG Spec.
+ * nak is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage.
+ * If nak is occured at IN Transaction, this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/DaaTglErr bit of HCINTMSK.
+ * 3. clears the nak bit of HCINT
+ * 4. be careful, nak of IN Transaction don't require re-transmit.
+ * If nak is occured at OUT Transaction, this function processes this interrupt as following.
+ * 1. all procedures of IN Transaction are executed.
+ * 2. calculates the size of the transferred data.
+ * 3. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 4. update the Toggle
+ * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not.
+ * if USB Device is High-Speed, then
+ * this function sets the ping protocol.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE -if the direction of the Transfer is OUT
+ * NO_ACTION -if the direction of the Transfer is IN
+ */
+/******************************************************************************/
+u8 process_nak_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+
+ //at OUT Transfer, we must re-transmit.
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return RE_TRANSMIT;
+ }
+ return NO_ACTION;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ack_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the ack event according to Synopsys OTG Spec.
+ * ack of IN/OUT Transaction don't need any retransmit.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears ack bit of HCINT and ed_status.is_ping_enable.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return USB_ERR_SUCCESS
+ */
+/******************************************************************************/
+u8 process_ack_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+
+ return NO_ACTION;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nyet_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the nyet event according to Synopsys OTG Spec.
+ * nyet is only occured at OUT Transaction.
+ * If nyet is occured at OUT Transaction, this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/datatglerr bit of HCINTMSK.
+ * 3. clears the nyet bit of HCINT
+ * 4. calculates the size of the transferred data.
+ * 5. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 6. update the Data Toggle.
+ * 7. return RE_SCHEDULE to retransmit.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE
+ */
+/******************************************************************************/
+u8 process_nyet_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ if(result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ // Error State....
+ return NO_ACTION;
+ }
+
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NYET);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return RE_TRANSMIT;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xacterr_on_bulk( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the xacterr event according to Synopsys OTG Spec.
+ * xacterr is occured at OUT/IN Transaction and we should retransmit the USB Transfer
+ * if the Error Counter is less than the RETRANSMIT_THRESHOLD.
+ * the reasons of xacterr is Timeout/CRC error/false EOP.
+ * the procedure to process xacterr is as following.
+ * 1. increses the result_td->err_cnt
+ * 2. check whether the result_td->err_cnt is equal to 3.
+ * 2. unmasks ack/nak/datatglerr bit of HCINTMSK.
+ * 3. clears the xacterr bit of HCINT
+ * 4. calculates the size of the transferred data.
+ * 5. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 6. update the Data Toggle.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if the error count is less than 3
+ * DE_ALLOCATE -if the error count is equal to 3
+ */
+/******************************************************************************/
+u8 process_xacterr_on_bulk( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ u8 ret_val = 0;
+
+ if(result_td->err_cnt<RETRANSMIT_THRESHOLD)
+ {
+ result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |= (CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr);
+ ret_val = RE_TRANSMIT;
+ result_td->err_cnt++ ;
+ }
+ else
+ {
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ ret_val = DE_ALLOCATE;
+ result_td->err_cnt = 0 ;
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ }
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+ }
+
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return ret_val;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name void process_bblerr_on_bulk(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the Babble event according to Synopsys OTG Spec.
+ * babble error is occured when the buffer to receive data to be transmit is overflow.
+ * So, babble error can be just occured at IN Transaction.
+ * when Babble Error is occured, we should stop the USB Transfer, and return the fact
+ * to Application.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_bblerr_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+
+ if(!result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ return NO_ACTION;
+ }
+
+ result_td->err_cnt =0;
+ result_td->error_code =USB_ERR_STATUS_BBLERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable =false;
+ result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data);
+
+ return DE_ALLOCATE;
+
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_datatgl_on_bulk( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the datatglerr event according to Synopsys OTG Spec.
+ * the datatglerr event is occured at IN Transfer, and the channel is not halted.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears datatglerr bit of HCINT.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return NO_ACTION
+ */
+/******************************************************************************/
+u8 process_datatgl_on_bulk(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ return NO_ACTION;
+
+}
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h
new file mode 100644
index 0000000..e1f84ca
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : BulkTransferChecker.h
+ * [Description] : The Header file defines the external and internal functions of BulkTransferChecker
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/18
+ * [Revision History]
+ * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of BulkTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _BULK_TRANSFER_CHECKER_H
+#define _BULK_TRANSFER_CHECKER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+u8 process_bulk_transfer(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_xfercompl_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_chhltd_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_ahb_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_stall_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_nak_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_ack_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_nyet_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_xacterr_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_bblerr_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_datatgl_on_bulk(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h
new file mode 100644
index 0000000..cefbcaf
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : DoneTransferChecker.h
+ * [Description] : The Header file defines the external and internal functions of S3CDoneTransferChecker.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/12
+ * [Revision History]
+ * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of S3CDoneTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _DONE_TRANSFER_CHECKER_H
+#define _DONE_TRANSFER_CHECKER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+
+#include "s3c-otg-hcdi-debug.h"
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
+
+
+
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c
new file mode 100644
index 0000000..689d379
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c
@@ -0,0 +1,238 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : CommonTransferChecker.c
+ * [Description] : The Source file implements the external and internal functions of CommonTransferChecker.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2009/01/12
+ * [Revision History]
+ * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of CommonTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-transferchecker-common.h"
+
+/******************************************************************************/
+/*!
+ * @name int init_done_transfer_checker(void)
+ *
+ * @brief this function initiates S3CDoneTransferChecker Module.
+ *
+ *
+ * @param void
+ *
+ * @return void
+ */
+/******************************************************************************/
+//ss1
+/*void init_done_transfer_checker(void)
+{
+ return USB_ERR_SUCCESS;
+}*/
+
+/******************************************************************************/
+/*!
+ * @name void do_transfer_checker(struct sec_otghost *otghost)
+ *
+ * @brief this function processes the result of USB Transfer. So, do_transfer_checker fistly
+ * check which channel occurs OTG Interrupt and gets the status information of the channel.
+ * do_transfer_checker requests the information of td_t to S3CScheduler.
+ * To process the interrupt of the channel, do_transfer_checker calls the sub-modules of
+ * S3CDoneTransferChecker, for example, ControlTransferChecker, BulkTransferChecker.
+ * according to the process result of the channel interrupt, do_transfer_checker decides
+ * the USB Transfer will be done or retransmitted.
+ *
+ *
+ * @param void
+ *
+ * @return void
+ */
+/*******************************************************************************/
+void do_transfer_checker (struct sec_otghost *otghost)
+__releases(&otghost->lock)
+__acquires(&otghost->lock)
+{
+ u32 hc_intr = 0;
+ u32 hc_intr_msk = 0;
+ u8 do_try_cnt = 0;
+
+ hc_info_t ch_info;
+ u32 td_addr = 0;
+ td_t *done_td = {0};
+ u8 proc_result = 0;
+
+ //by ss1
+ otg_mem_set((void *)&ch_info, 0, sizeof(hc_info_t));
+
+ // Get value of HAINT...
+ get_intr_ch(&hc_intr,&hc_intr_msk);
+
+start_do_transfer_checker:
+
+ while(do_try_cnt<MAX_CH_NUMBER) {
+ //checks the channel number to be masked or not.
+ if(!(hc_intr & hc_intr_msk & (1<<do_try_cnt))) {
+ do_try_cnt++;
+ goto start_do_transfer_checker;
+ }
+
+ //Gets the address of the td_t to have the channel to be interrupted.
+ if(get_td_info(do_try_cnt, &td_addr) == USB_ERR_SUCCESS) {
+
+ done_td = (td_t *)td_addr;
+
+ if(do_try_cnt != done_td->cur_stransfer.alloc_chnum) {
+ do_try_cnt++;
+ goto start_do_transfer_checker;
+ }
+ }
+ else {
+ do_try_cnt++;
+ goto start_do_transfer_checker;
+ }
+
+ //Gets the informationof channel to be interrupted.
+ get_ch_info(&ch_info,do_try_cnt);
+
+ switch(done_td->parent_ed_p->ed_desc.endpoint_type) {
+ case CONTROL_TRANSFER:
+ proc_result = process_control_transfer(done_td, &ch_info);
+ break;
+ case BULK_TRANSFER:
+ proc_result = process_bulk_transfer(done_td, &ch_info);
+ break;
+ case INT_TRANSFER:
+ proc_result = process_intr_transfer(done_td, &ch_info);
+ break;
+ case ISOCH_TRANSFER:
+ /* proc_result = ProcessIsochTransfer(done_td, &ch_info); */
+ break;
+ default:break;
+ }
+
+ if((proc_result == RE_TRANSMIT) || (proc_result == RE_SCHEDULE)) {
+ done_td->parent_ed_p->ed_status.is_in_transferring = false;
+ done_td->is_transfer_done = false;
+ done_td->is_transferring = false;
+
+ if(done_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER ||
+ done_td->parent_ed_p->ed_desc.endpoint_type==BULK_TRANSFER) {
+ update_nonperio_stransfer(done_td);
+ }
+ else {
+ update_perio_stransfer(done_td);
+ }
+
+ if(proc_result == RE_TRANSMIT) {
+ retransmit(otghost, done_td);
+ }
+ else {
+ reschedule(done_td);
+ }
+ }
+
+ else if(proc_result==DE_ALLOCATE) {
+ done_td->parent_ed_p->ed_status.is_in_transferring = false;
+ done_td->parent_ed_p->ed_status.in_transferring_td = 0;
+ done_td->is_transfer_done = true;
+ done_td->is_transferring = false;
+
+ spin_unlock_otg(&otghost->lock);
+ otg_usbcore_giveback( done_td);
+ spin_lock_otg(&otghost->lock);
+ release_trans_resource(otghost, done_td);
+ }
+ else { //NO_ACTION....
+ done_td->parent_ed_p->ed_status.is_in_transferring = true;
+ done_td->parent_ed_p->ed_status.in_transferring_td = (u32)done_td;
+ done_td->is_transfer_done = false;
+ done_td->is_transferring = true;
+ }
+ do_try_cnt++;
+ }
+ // Complete to process the Channel Interrupt.
+ // So. we now start to scheduler of S3CScheduler.
+ do_schedule(otghost);
+}
+
+
+int release_trans_resource(struct sec_otghost *otghost, td_t *done_td)
+{
+ //remove the pDeallocateTD from parent_ed_p.
+ otg_list_pop(&done_td->td_list_entry);
+ done_td->parent_ed_p->num_td--;
+
+ //Call deallocate to release the channel and bandwidth resource of S3CScheduler.
+ deallocate(done_td);
+ delete_td(otghost, done_td);
+ return USB_ERR_SUCCESS;
+}
+
+u32 calc_transferred_size(bool f_is_complete, td_t *td, hc_info_t *hc_info)
+{
+ if(f_is_complete) {
+ if(td->parent_ed_p->ed_desc.is_ep_in) {
+ return td->cur_stransfer.buf_size - hc_info->hc_size.b.xfersize;
+ }
+ else {
+ return td->cur_stransfer.buf_size;
+ }
+ }
+ else {
+ return (td->cur_stransfer.packet_cnt - hc_info->hc_size.b.pktcnt)*td->parent_ed_p->ed_desc.max_packet_size;
+ }
+}
+
+void update_frame_number(td_t *pResultTD)
+{
+ u32 cur_frame_num=0;
+
+ if(pResultTD->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER ||
+ pResultTD->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER) {
+ return;
+ }
+
+ pResultTD->parent_ed_p->ed_desc.sched_frame+= pResultTD->parent_ed_p->ed_desc.interval;
+ pResultTD->parent_ed_p->ed_desc.sched_frame &= HFNUM_MAX_FRNUM;
+
+ cur_frame_num = oci_get_frame_num();
+ if(((cur_frame_num - pResultTD->parent_ed_p->ed_desc.sched_frame)&HFNUM_MAX_FRNUM) <= (HFNUM_MAX_FRNUM>>1)) {
+ pResultTD->parent_ed_p->ed_desc.sched_frame = cur_frame_num;
+ }
+}
+
+void update_datatgl(u8 ubCurDataTgl, td_t *td)
+{
+ switch(td->parent_ed_p->ed_desc.endpoint_type) {
+ case CONTROL_TRANSFER:
+ if(td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) {
+ td->parent_ed_p->ed_status.control_data_tgl.data_tgl = ubCurDataTgl;
+ }
+ break;
+ case BULK_TRANSFER:
+ case INT_TRANSFER:
+ td->parent_ed_p->ed_status.data_tgl = ubCurDataTgl;
+ break;
+
+ case ISOCH_TRANSFER:
+ break;
+ default:break;
+ }
+}
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h
new file mode 100644
index 0000000..2ea05b8
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : CommonTransferChecker.h
+ * [Description] : The Header file defines the external and internal functions of CommonTransferChecker.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/12
+ * [Revision History]
+ * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of CommonTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _COMMON_TRANSFER_CHECKER_H
+#define _COMMON_TRANSFER_CHECKER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+#include "s3c-otg-common-common.h"
+//#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-regdef.h"
+#include "s3c-otg-transfer-transfer.h"
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-hcdi-memory.h"
+#include "s3c-otg-scheduler-scheduler.h"
+#include "s3c-otg-isr.h"
+#include "s3c-otg-transferchecker-control.h"
+#include "s3c-otg-transferchecker-bulk.h"
+#include "s3c-otg-transferchecker-interrupt.h"
+//#include "s3c-otg-transferchecker-iso.h"
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//void init_done_transfer_checker (void);
+void do_transfer_checker (struct sec_otghost *otghost);
+int release_trans_resource(struct sec_otghost *otghost, td_t *done_td);
+u32 calc_transferred_size(bool f_is_complete,
+ td_t *td,
+ hc_info_t *hc_info);
+void update_frame_number(td_t *result_td);
+void update_datatgl(u8 cur_data_tgl,
+ td_t *td);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c
new file mode 100644
index 0000000..863f951
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c
@@ -0,0 +1,698 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : ControlTransferChecker.c
+ * [Description] : The Source file implements the external and internal functions of ControlTransferChecker.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2009/02/10
+ * [Revision History]
+ * (1) 2008/06/13 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of ControlTransferChecker
+ * (2) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Completed to implement ControlTransferChecker.c v1.0
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "s3c-otg-transferchecker-control.h"
+#include "s3c-otg-isr.h"
+
+
+
+/******************************************************************************/
+/*!
+ * @name u8 process_control_transfer(td_t *result_td,
+ hc_info_t *hc_reg_data)
+
+ *
+ * @brief this function processes the result of the Control Transfer.
+ * firstly, this function checks the result the Control Transfer.
+ * and according to the result, calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] ubChNum -indicates the number of the channel to be interrupted.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ */
+/******************************************************************************/
+u8 process_control_transfer(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ hcintn_t hcintr_info;
+ u8 ret_val=0;
+
+ //we just deal with the interrupts to be unmasked.
+ hcintr_info.d32 = hc_reg_data->hc_int.d32&result_td->cur_stransfer.hc_reg.hc_int_msk.d32;
+
+ if(result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ if(hcintr_info.b.chhltd)
+ {
+ ret_val = process_chhltd_on_control(result_td, hc_reg_data);
+ }
+
+ else if (hcintr_info.b.ack)
+ {
+ ret_val =process_ack_on_control(result_td, hc_reg_data);
+ }
+
+ else if (hcintr_info.b.nak)
+ {
+ ret_val =process_nak_on_control(result_td, hc_reg_data);
+ }
+
+ else if (hcintr_info.b.datatglerr)
+ {
+ ret_val = process_datatgl_on_control(result_td,hc_reg_data);
+ }
+ }
+ else
+ {
+ if(hcintr_info.b.chhltd)
+ {
+ ret_val = process_chhltd_on_control(result_td, hc_reg_data);
+ }
+
+ else if(hcintr_info.b.ack)
+ {
+ ret_val =process_ack_on_control( result_td, hc_reg_data);
+
+ }
+ }
+
+ return ret_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_chhltd_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function processes Channel Halt event according to Synopsys OTG Spec.
+ * firstly, this function checks the reason of the Channel Halt, and according to the reason,
+ * calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ */
+/******************************************************************************/
+u8 process_chhltd_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_control( result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_control(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.bblerr)
+ {
+ return process_bblerr_on_control(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_control(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.nak)
+ {
+ return process_nak_on_control(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.nyet)
+ {
+ return process_nyet_on_control(result_td, hc_reg_data);
+ }
+ else
+ {
+
+ //Occure Error State.....
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ result_td->err_cnt++;
+ if(result_td->err_cnt == 3)
+ {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+ return RE_TRANSMIT;
+ }
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xfercompl_on_control(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the xfercompl event according to Synopsys OTG Spec.
+ * the procedure of this function is as following
+ * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr.
+ * 2. masks some bit of HCINTMSK
+ * 3. updates the result_td fields
+ * err_cnt/u8/standard_dev_req_info.
+ * 4. updates the result_td->parent_ed_p->ed_status.
+ * control_data_tgl.
+ * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return USB_ERR_SUCCESS
+ */
+/******************************************************************************/
+u8 process_xfercompl_on_control(td_t *result_td, hc_info_t *hc_reg_data)
+{
+ u8 ret_val = 0;
+
+ result_td->err_cnt = 0;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,CH_STATUS_ALL);
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+
+ switch(result_td->standard_dev_req_info.conrol_transfer_stage) {
+ case SETUP_STAGE:
+ if(result_td->standard_dev_req_info.is_data_stage) {
+ result_td->standard_dev_req_info.conrol_transfer_stage = DATA_STAGE;
+ }
+ else {
+ result_td->standard_dev_req_info.conrol_transfer_stage = STATUS_STAGE;
+ }
+ ret_val = RE_TRANSMIT;
+
+ break;
+
+ case DATA_STAGE:
+
+ result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data);
+
+ /* at IN Transfer, short transfer is accepted. */
+ if(result_td->transferred_szie==result_td->buf_size) {
+ result_td->standard_dev_req_info.conrol_transfer_stage =STATUS_STAGE;
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ }
+ else {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize) {
+ if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT) {
+ result_td->error_code = USB_ERR_STATUS_SHORTREAD;
+ result_td->standard_dev_req_info.conrol_transfer_stage=STATUS_STAGE;
+ }
+ else {
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ result_td->standard_dev_req_info.conrol_transfer_stage =STATUS_STAGE;
+ }
+ }
+ else { // the Data Stage is not completed. So we need to continue Data Stage.
+ result_td->standard_dev_req_info.conrol_transfer_stage = DATA_STAGE;
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+ }
+ }
+
+ if(hc_reg_data->hc_int.b.nyet) {
+ /* at OUT Transfer, we must re-transmit. */
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false) {
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+ }
+ }
+ ret_val = RE_TRANSMIT;
+ break;
+
+ case STATUS_STAGE:
+ result_td->standard_dev_req_info.conrol_transfer_stage = COMPLETE_STAGE;
+
+ if(hc_reg_data->hc_int.b.nyet) {
+ //at OUT Transfer, we must re-transmit.
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false) {
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ else {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+ }
+ }
+
+ ret_val = DE_ALLOCATE;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ahb_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec.
+ * this function stop the channel to be executed
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_ahb_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt =0;
+ result_td->error_code =USB_ERR_STATUS_AHBERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ // we just calculate the size of the transferred data on Data Stage of Control Transfer.
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+ }
+
+ return DE_ALLOCATE;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_stall_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with theStall event according to Synopsys OTG Spec.
+ * but USB2.0 Spec don't permit the Stall on Setup Stage of Control Transfer.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_stall_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt =0;
+ result_td->error_code =USB_ERR_STATUS_STALL;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+
+ // we just calculate the size of the transferred data on Data Stage of Control Transfer.
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+ }
+
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nak_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the nak event according to Synopsys OTG Spec.
+ * nak is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage.
+ * If nak is occured at IN Transaction, this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/DaaTglErr bit of HCINTMSK.
+ * 3. clears the nak bit of HCINT
+ * 4. be careful, nak of IN Transaction don't require re-transmit.
+ * If nak is occured at OUT Transaction, this function processes this interrupt as following.
+ * 1. all procedures of IN Transaction are executed.
+ * 2. calculates the size of the transferred data.
+ * 3. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 4. update the Toggle
+ * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not.
+ * if USB Device is High-Speed, then
+ * this function sets the ping protocol.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE
+ */
+/******************************************************************************/
+u8 process_nak_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+
+ //at OUT Transfer, we must re-transmit.
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+ }
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==true)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+
+ }
+
+ return RE_SCHEDULE;
+
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ack_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the ack event according to Synopsys OTG Spec.
+ * ack of IN/OUT Transaction don't need any retransmit.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears ack bit of HCINT.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return NO_ACTION
+ */
+/******************************************************************************/
+u8 process_ack_on_control(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable =false;
+
+ return NO_ACTION;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nyet_on_control(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the nyet event according to Synopsys OTG Spec.
+ * nyet is occured at OUT Transaction of Data/Status Stage, and is not occured at Setup Stage.
+ * If nyet is occured at OUT Transaction, this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/datatglerr bit of HCINTMSK.
+ * 3. clears the nyet bit of HCINT
+ * 4. calculates the size of the transferred data.
+ * 5. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 6. update the Data Toggle.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE
+ */
+/******************************************************************************/
+u8 process_nyet_on_control(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NYET);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==true)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+
+ }
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return RE_SCHEDULE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xacterr_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the xacterr event according to Synopsys OTG Spec.
+ * xacterr is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage.
+ * if Timeout/CRC error/false EOP is occured, then xacterr is occured.
+ * the procedure to process xacterr is as following.
+ * 1. increses the result_td->err_cnt
+ * 2. check whether the result_td->err_cnt is equal to 3.
+ * 2. unmasks ack/nak/datatglerr bit of HCINTMSK.
+ * 3. clears the xacterr bit of HCINT
+ * 4. calculates the size of the transferred data.
+ * 5. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 6. update the Data Toggle.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if the Error Counter is less than RETRANSMIT_THRESHOLD
+ * DE_ALLOCATE -if the Error Counter is equal to RETRANSMIT_THRESHOLD
+ */
+/******************************************************************************/
+u8 process_xacterr_on_control(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ u8 ret_val=0;
+
+ if(result_td->err_cnt<RETRANSMIT_THRESHOLD)
+ {
+ result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |= (CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr);
+ ret_val = RE_TRANSMIT;
+ unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ result_td->err_cnt++ ;
+ }
+ else
+ {
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ ret_val = DE_ALLOCATE;
+ result_td->err_cnt = 0 ;
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ }
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ }
+
+ if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG)
+ {
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==false)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE)
+ {
+ if(result_td->parent_ed_p->ed_desc.is_ep_in==true)
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = true;
+ }
+ }
+ else
+ {
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+ }
+
+
+ }
+
+
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return ret_val;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name void process_bblerr_on_control( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the Babble event according to Synopsys OTG Spec.
+ * babble error can be just occured at IN Transaction. So if the direction of transfer is
+ * OUT, this function return Error Code.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ * NO_ACTION -if the direction is OUT
+ */
+/******************************************************************************/
+u8 process_bblerr_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+
+ if(!result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ return NO_ACTION;
+ }
+
+ result_td->err_cnt = 0;
+ result_td->error_code =USB_ERR_STATUS_BBLERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->parent_ed_p->ed_status.is_ping_enable =false;
+
+ // we just calculate the size of the transferred data on Data Stage of Control Transfer.
+ if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE)
+ {
+ result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data);
+ }
+
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_datatgl_on_control(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the datatglerr event according to Synopsys OTG Spec.
+ * the datatglerr event is occured at IN Transfer.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears datatglerr bit of HCINT.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return NO_ACTION
+ */
+/******************************************************************************/
+u8 process_datatgl_on_control( td_t * result_td,
+ hc_info_t * hc_reg_data)
+{
+ result_td->err_cnt = 0;
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ return NO_ACTION;
+
+}
+
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h
new file mode 100644
index 0000000..4d85367
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : ControlTransferChecker.h
+ * [Description] : The Header file defines the external and internal functions of ControlTransferChecker
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2009/01/12
+ * [Revision History]
+ * (1) 2008/06/13 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of ControlTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _CONTROL_TRANSFER_CHECKER_H
+#define _CONTROL_TRANSFER_CHECKER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+#include "s3c-otg-common-common.h"
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-regdef.h"
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-scheduler-scheduler.h"
+
+#include "s3c-otg-transferchecker-checker.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+u8 process_control_transfer( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_xfercompl_on_control(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_chhltd_on_control( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_ahb_on_control( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_stall_on_control( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_nak_on_control (td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_ack_on_control (td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_nyet_on_control( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_xacterr_on_control(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_bblerr_on_control( td_t *raw_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_datatgl_on_control(td_t *raw_td,
+ hc_info_t *hc_reg_data);
+u8 process_indirection_on_control( td_t *result_td,
+ hc_info_t *hc_reg_data);
+
+u8 process_outdirection_on_control(td_t *result_td,
+ hc_info_t *hc_reg_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c
new file mode 100644
index 0000000..653628e
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c
@@ -0,0 +1,574 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : IntTransferChecker.c
+ * [Description] : The Source file implements the external and internal functions of IntTransferChecker
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/19
+ * [Revision History]
+ * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of IntTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+
+#include "s3c-otg-transferchecker-interrupt.h"
+
+/******************************************************************************/
+/*!
+ * @name u8 process_intr_transfer(td_t *result_td,
+ * hc_info_t *HCRegData)
+ *
+ *
+ * @brief this function processes the result of the Interrupt Transfer.
+ * firstly, this function checks the result of the Interrupt Transfer.
+ * and according to the result, calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t whose channel is interruped.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ * NO_ACTION -if we don't need any action,
+ */
+/******************************************************************************/
+u8 process_intr_transfer(td_t *result_td, hc_info_t *hc_reg_data)
+{
+ hcintn_t hc_intr_info;
+ u8 ret_val=0;
+
+ //we just deal with the interrupts to be unmasked.
+ hc_intr_info.d32 = hc_reg_data->hc_int.d32&result_td->cur_stransfer.hc_reg.hc_int_msk.d32;
+
+ if(result_td->parent_ed_p->ed_desc.is_ep_in) {
+ if(hc_intr_info.b.chhltd) {
+ ret_val = process_chhltd_on_intr(result_td, hc_reg_data);
+ }
+
+ else if (hc_intr_info.b.ack) {
+ ret_val = process_ack_on_intr(result_td, hc_reg_data);
+ }
+ }
+ else {
+ if(hc_intr_info.b.chhltd) {
+ ret_val = process_chhltd_on_intr(result_td, hc_reg_data);
+ }
+
+ else if(hc_intr_info.b.ack) {
+ ret_val = process_ack_on_intr( result_td, hc_reg_data);
+ }
+ }
+
+ return ret_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_chhltd_on_intr(td_t *result_td,
+ * hc_info_t *HCRegData)
+ *
+ *
+ * @brief this function processes Channel Halt event according to Synopsys OTG Spec.
+ * firstly, this function checks the reason of the Channel Halt, and according to the reason,
+ * calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ */
+/******************************************************************************/
+u8 process_chhltd_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ if(result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_intr( result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_intr(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.bblerr)
+ {
+ return process_bblerr_on_intr(result_td, hc_reg_data);
+ }
+ else if (hc_reg_data->hc_int.b.nak)
+ {
+ return process_nak_on_intr(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.datatglerr)
+ {
+ return process_datatgl_on_intr(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.frmovrun)
+ {
+ return process_frmovrrun_on_intr(result_td,hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_intr(result_td, hc_reg_data);
+ }
+ else
+ {
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ return RE_TRANSMIT;
+ }
+ }
+ else
+ {
+ if(hc_reg_data->hc_int.b.xfercompl)
+ {
+ return process_xfercompl_on_intr( result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.stall)
+ {
+ return process_stall_on_intr(result_td, hc_reg_data);
+ }
+ else if (hc_reg_data->hc_int.b.nak)
+ {
+ return process_nak_on_intr(result_td, hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.frmovrun)
+ {
+ return process_frmovrrun_on_intr(result_td,hc_reg_data);
+ }
+ else if(hc_reg_data->hc_int.b.xacterr)
+ {
+ return process_xacterr_on_intr(result_td, hc_reg_data);
+ }
+ else
+ {
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->err_cnt++;
+ if(result_td->err_cnt == 3)
+ {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+
+ return RE_TRANSMIT;
+ }
+
+ }
+
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xfercompl_on_intr( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the xfercompl event according to Synopsys OTG Spec.
+ * the procedure of this function is as following
+ * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr.
+ * 2. masks ack/nak(?)/datatglerr(?) bit of HCINTMSK
+ * 3. Resets the err_cnt of result_td.
+ * 4. updates the result_td->parent_ed_p->ed_status.
+ * IntDataTgl.
+ * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE -if USB Transfer is completed.
+ * RE_TRANSMIT -if need to retransmit the result_td.
+ */
+/******************************************************************************/
+u8 process_xfercompl_on_intr( td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ u8 ret_val=0;
+
+ result_td->err_cnt =0;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data);
+
+ if(result_td->transferred_szie==result_td->buf_size)
+ {//at IN Transfer, short transfer is accepted.
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ ret_val = DE_ALLOCATE;
+ }
+ else
+ {
+ // this routine will not be executed on Interrupt Transfer.
+ // So, we should decide to remove this routine or not.
+ if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize)
+ {
+ if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT)
+ {
+ result_td->error_code = USB_ERR_STATUS_SHORTREAD;
+ }
+ else
+ {
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ }
+ ret_val = DE_ALLOCATE;
+ }
+ else
+ { // the Data Stage is not completed. So we need to continue Data Stage.
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+ ret_val = RE_TRANSMIT;
+ }
+ }
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ return ret_val;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ahb_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec.
+ * this function stop the channel to be executed
+ *
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_ahb_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+ result_td->error_code = USB_ERR_STATUS_AHBERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ // we just calculate the size of the transferred data on Data Stage of Int Transfer.
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+ result_td->parent_ed_p->ed_status.is_ping_enable =false;
+
+ return DE_ALLOCATE;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_stall_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the Stall event according to Synopsys OTG Spec.
+ * when Stall is occured at Int Transfer, we should reset the PID as DATA0
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_stall_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+ result_td->error_code = USB_ERR_STATUS_STALL;
+
+ //this channel is stalled, So we don't process another interrupts.
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(DATA0, result_td);
+
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nak_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the nak event according to Synopsys OTG Spec.
+ * nak is occured at OUT/IN Transaction of Interrupt Transfer.
+ * we can't use ping protocol on Interrupt Transfer. and Syonopsys OTG IP occures
+ * chhltd interrupt on nak of IN/OUT Transaction. So we should retransmit the transfer
+ * on IN Transfer.
+ * If nak is occured at IN Transaction, this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/DaaTglErr bit of HCINTMSK.
+ * 3. clears the nak bit of HCINT
+ * 4. calculates frame number to retransmit this Interrupt Transfer.
+ *
+ * If nak is occured at OUT Transaction, this function processes this interrupt as following.
+ * 1. all procedures of IN Transaction are executed.
+ * 2. calculates the size of the transferred data.
+ * 3. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 4. update the Toggle
+ * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not.
+ * if USB Device is High-Speed, then
+ * this function sets the ping protocol.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE -if the direction of the Transfer is OUT
+ * NO_ACTION -if the direction of the Transfer is IN
+ */
+/******************************************************************************/
+u8 process_nak_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ update_frame_number(result_td);
+
+ return RE_SCHEDULE;
+// return RE_TRANSMIT;
+
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ack_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the ack event according to Synopsys OTG Spec.
+ * ack of IN/OUT Transaction don't need any retransmit.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears ack bit of HCINT and ed_status.is_ping_enable.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return NO_ACTION
+ */
+/******************************************************************************/
+u8 process_ack_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+
+ return NO_ACTION;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xacterr_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ * @brief this function deals with the xacterr event according to Synopsys OTG Spec.
+ * xacterr is occured at OUT/IN Transaction and we should retransmit the USB Transfer
+ * if the Error Counter is less than the RETRANSMIT_THRESHOLD.
+ * the reasons of xacterr is Timeout/CRC error/false EOP.
+ * the procedure to process xacterr is as following.
+ * 1. increses the result_td->err_cnt
+ * 2. check whether the result_td->err_cnt is equal to 3.
+ * 2. unmasks ack/nak/datatglerr bit of HCINTMSK.
+ * 3. clears the xacterr bit of HCINT
+ * 4. calculates the size of the transferred data.
+ * 5. update the Data Toggle.
+ * 6. update the frame number to start retransmitting the Interrupt Transfer.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE -if the error count is less than 3
+ * DE_ALLOCATE -if the error count is equal to 3
+ */
+/******************************************************************************/
+u8 process_xacterr_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ u8 ret_val = 0;
+
+ if(result_td->err_cnt<RETRANSMIT_THRESHOLD)
+ {
+ result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |=(CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr);
+ ret_val = RE_SCHEDULE;
+ result_td->err_cnt++ ;
+ }
+ else
+ {
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+ ret_val = DE_ALLOCATE;
+ result_td->err_cnt = 0 ;
+ }
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ if(ret_val == RE_SCHEDULE)
+ { //Calculates the frame number
+ update_frame_number(result_td);
+ }
+
+ return ret_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name void process_bblerr_on_intr(td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ *
+ * @brief this function deals with the Babble event according to Synopsys OTG Spec.
+ * babble error is occured when the USB device continues to send packets
+ * althrough EOP is occured. So Babble error is only occured at IN Transfer.
+ * when Babble Error is occured, we should stop the USB Transfer, and return the fact
+ * to Application.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+u8 process_bblerr_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+
+ if(!result_td->parent_ed_p->ed_desc.is_ep_in)
+ {
+ return NO_ACTION;
+ }
+
+ result_td->err_cnt = 0;
+ result_td->error_code =USB_ERR_STATUS_BBLERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ //Mask ack Interrupt..
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data);
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_datatgl_on_intr( td_t *result_td,
+ * hc_info_t *hc_reg_data)
+ *
+ * @brief this function deals with the datatglerr event according to Synopsys OTG Spec.
+ * the datatglerr event is occured at IN Transfer, and the channel is not halted.
+ * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears datatglerr bit of HCINT.
+ *
+ * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum.
+ * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE
+ */
+/******************************************************************************/
+u8 process_datatgl_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+
+ result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data);
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ update_frame_number(result_td);
+
+ return RE_SCHEDULE;
+}
+
+u8 process_frmovrrun_on_intr(td_t *result_td,
+ hc_info_t *hc_reg_data)
+{
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK);
+
+ update_datatgl(hc_reg_data->hc_size.b.pid, result_td);
+
+ update_frame_number(result_td);
+
+ return RE_TRANSMIT;
+}
+
diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h
new file mode 100644
index 0000000..0a735a2
--- /dev/null
+++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : IntTransferChecker.h
+ * [Description] : The Header file defines the external and internal functions of IntTransferChecker
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/19
+ * [Revision History]
+ * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and defines functions of IntTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#ifndef _INT_TRANSFER_CHECKER_H
+#define _INT_TRANSFER_CHECKER_H
+
+/*
+// ----------------------------------------------------------------------------
+// Include files : None.
+// ----------------------------------------------------------------------------
+*/
+
+#include "s3c-otg-common-common.h"
+//#include "s3c-otg-common-typedef.h"
+#include "s3c-otg-common-const.h"
+#include "s3c-otg-common-errorcode.h"
+#include "s3c-otg-common-datastruct.h"
+#include "s3c-otg-common-regdef.h"
+
+#include "s3c-otg-hcdi-debug.h"
+#include "s3c-otg-scheduler-scheduler.h"
+#include "s3c-otg-isr.h"
+#include "s3c-otg-transferchecker-checker.h"
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+u8 process_intr_transfer(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_xfercompl_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_chhltd_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_ahb_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_stall_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_nak_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_frmovrrun_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_ack_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_xacterr_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_bblerr_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+u8 process_datatgl_on_intr(td_t *pRawTD,
+ hc_info_t *pHCRegData);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index a4468d9..a44f2d4 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1505,6 +1505,7 @@ void xhci_free_command(struct xhci_hcd *xhci,
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ struct xhci_cd *cur_cd, *next_cd;
int size;
int i;
@@ -1520,10 +1521,16 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->event_ring = NULL;
xhci_dbg(xhci, "Freed event ring\n");
+ xhci->cmd_ring_reserved_trbs = 0;
if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL;
xhci_dbg(xhci, "Freed command ring\n");
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ }
for (i = 1; i < MAX_HC_SLOTS; ++i)
xhci_free_virt_device(xhci, i);
@@ -2013,6 +2020,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags);
if (!xhci->cmd_ring)
goto fail;
+ INIT_LIST_HEAD(&xhci->cancel_cmd_list);
xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
xhci_dbg(xhci, "First segment DMA is 0x%llx\n",
(unsigned long long)xhci->cmd_ring->first_seg->dma);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 4509f69..73fea4b 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -123,6 +123,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u "
"has broken MSI implementation\n",
pdev->revision);
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
if (pdev->vendor == PCI_VENDOR_ID_NEC)
@@ -139,11 +140,22 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
xhci->limit_active_eps = 64;
+ /*
+ * PPT desktop boards DH77EB and DH77DF will power back on after
+ * a few seconds of being shutdown. The fix for this is to
+ * switch the ports from xHCI to EHCI on shutdown. We can't use
+ * DMI information to find those particular boards (since each
+ * vendor will change the board name), so we have to key off all
+ * PPT chipsets.
+ */
+ xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+ xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index c42fdff..1a38281 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -147,25 +147,34 @@ static void next_trb(struct xhci_hcd *xhci,
*/
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
{
- union xhci_trb *next = ++(ring->dequeue);
unsigned long long addr;
ring->deq_updates++;
- /* Update the dequeue pointer further if that was a link TRB or we're at
- * the end of an event ring segment (which doesn't have link TRBS)
- */
- while (last_trb(xhci, ring, ring->deq_seg, next)) {
- if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next)) {
- ring->cycle_state = (ring->cycle_state ? 0 : 1);
- if (!in_interrupt())
- xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
- ring,
- (unsigned int) ring->cycle_state);
+
+ do {
+ /*
+ * Update the dequeue pointer further if that was a link TRB or
+ * we're at the end of an event ring segment (which doesn't have
+ * link TRBS)
+ */
+ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
+ if (consumer && last_trb_on_last_seg(xhci, ring,
+ ring->deq_seg, ring->dequeue)) {
+ if (!in_interrupt())
+ xhci_dbg(xhci, "Toggle cycle state "
+ "for ring %p = %i\n",
+ ring,
+ (unsigned int)
+ ring->cycle_state);
+ ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ }
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ } else {
+ ring->dequeue++;
}
- ring->deq_seg = ring->deq_seg->next;
- ring->dequeue = ring->deq_seg->trbs;
- next = ring->dequeue;
- }
+ } while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue));
+
addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
}
@@ -302,12 +311,123 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
/* Ring the host controller doorbell after placing a command on the ring */
void xhci_ring_cmd_db(struct xhci_hcd *xhci)
{
+ if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING))
+ return;
+
xhci_dbg(xhci, "// Ding dong!\n");
xhci_writel(xhci, DB_VALUE_HOST, &xhci->dba->doorbell[0]);
/* Flush PCI posted writes */
xhci_readl(xhci, &xhci->dba->doorbell[0]);
}
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+{
+ u64 temp_64;
+ int ret;
+
+ xhci_dbg(xhci, "Abort command ring\n");
+
+ if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING)) {
+ xhci_dbg(xhci, "The command ring isn't running, "
+ "Have the command ring been stopped?\n");
+ return 0;
+ }
+
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ if (!(temp_64 & CMD_RING_RUNNING)) {
+ xhci_dbg(xhci, "Command ring had been stopped\n");
+ return 0;
+ }
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
+ &xhci->op_regs->cmd_ring);
+
+ /* Section 4.6.1.2 of xHCI 1.0 spec says software should
+ * time the completion od all xHCI commands, including
+ * the Command Abort operation. If software doesn't see
+ * CRR negated in a timely manner (e.g. longer than 5
+ * seconds), then it should assume that the there are
+ * larger problems with the xHC and assert HCRST.
+ */
+ ret = handshake(xhci, &xhci->op_regs->cmd_ring,
+ CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+
+ return 0;
+}
+
+static int xhci_queue_cd(struct xhci_hcd *xhci,
+ struct xhci_command *command,
+ union xhci_trb *cmd_trb)
+{
+ struct xhci_cd *cd;
+ cd = kzalloc(sizeof(struct xhci_cd), GFP_ATOMIC);
+ if (!cd)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&cd->cancel_cmd_list);
+
+ cd->command = command;
+ cd->cmd_trb = cmd_trb;
+ list_add_tail(&cd->cancel_cmd_list, &xhci->cancel_cmd_list);
+
+ return 0;
+}
+
+/*
+ * Cancel the command which has issue.
+ *
+ * Some commands may hang due to waiting for acknowledgement from
+ * usb device. It is outside of the xHC's ability to control and
+ * will cause the command ring is blocked. When it occurs software
+ * should intervene to recover the command ring.
+ * See Section 4.6.1.1 and 4.6.1.2
+ */
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+ union xhci_trb *cmd_trb)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_warn(xhci, "Abort the command ring,"
+ " but the xHCI is dead.\n");
+ retval = -ESHUTDOWN;
+ goto fail;
+ }
+
+ /* queue the cmd desriptor to cancel_cmd_list */
+ retval = xhci_queue_cd(xhci, command, cmd_trb);
+ if (retval) {
+ xhci_warn(xhci, "Queuing command descriptor failed.\n");
+ goto fail;
+ }
+
+ /* abort command ring */
+ retval = xhci_abort_cmd_ring(xhci);
+ if (retval) {
+ xhci_err(xhci, "Abort command ring failed\n");
+ if (unlikely(retval == -ESHUTDOWN)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ xhci_dbg(xhci, "xHCI host controller is dead.\n");
+ return retval;
+ }
+ }
+
+fail:
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return retval;
+}
+
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
unsigned int slot_id,
unsigned int ep_index,
@@ -1037,6 +1157,20 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
}
}
+/* Complete the command and detele it from the devcie's command queue.
+ */
+static void xhci_complete_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
+ struct xhci_command *command, u32 status)
+{
+ command->status = status;
+ list_del(&command->cmd_list);
+ if (command->completion)
+ complete(command->completion);
+ else
+ xhci_free_command(xhci, command);
+}
+
+
/* Check to see if a command in the device's command queue matches this one.
* Signal the completion or free the command, and return 1. Return 0 if the
* completed command isn't at the head of the command list.
@@ -1055,15 +1189,155 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
if (xhci->cmd_ring->dequeue != command->command_trb)
return 0;
- command->status = GET_COMP_CODE(le32_to_cpu(event->status));
- list_del(&command->cmd_list);
- if (command->completion)
- complete(command->completion);
- else
- xhci_free_command(xhci, command);
+ xhci_complete_cmd_in_cmd_wait_list(xhci, command,
+ GET_COMP_CODE(le32_to_cpu(event->status)));
return 1;
}
+/*
+ * Finding the command trb need to be cancelled and modifying it to
+ * NO OP command. And if the command is in device's command wait
+ * list, finishing and freeing it.
+ *
+ * If we can't find the command trb, we think it had already been
+ * executed.
+ */
+static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd)
+{
+ struct xhci_segment *cur_seg;
+ union xhci_trb *cmd_trb;
+ u32 cycle_state;
+
+ if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
+ return;
+
+ /* find the current segment of command ring */
+ cur_seg = find_trb_seg(xhci->cmd_ring->first_seg,
+ xhci->cmd_ring->dequeue, &cycle_state);
+
+ if (!cur_seg) {
+ xhci_warn(xhci, "Command ring mismatch, dequeue = %p %llx (dma)\n",
+ xhci->cmd_ring->dequeue,
+ (unsigned long long)
+ xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
+ xhci->cmd_ring->dequeue));
+ xhci_debug_ring(xhci, xhci->cmd_ring);
+ xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
+ return;
+ }
+
+ /* find the command trb matched by cd from command ring */
+ for (cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_trb != xhci->cmd_ring->enqueue;
+ next_trb(xhci, xhci->cmd_ring, &cur_seg, &cmd_trb)) {
+ /* If the trb is link trb, continue */
+ if (TRB_TYPE_LINK_LE32(cmd_trb->generic.field[3]))
+ continue;
+
+ if (cur_cd->cmd_trb == cmd_trb) {
+
+ /* If the command in device's command list, we should
+ * finish it and free the command structure.
+ */
+ if (cur_cd->command)
+ xhci_complete_cmd_in_cmd_wait_list(xhci,
+ cur_cd->command, COMP_CMD_STOP);
+
+ /* get cycle state from the origin command trb */
+ cycle_state = le32_to_cpu(cmd_trb->generic.field[3])
+ & TRB_CYCLE;
+
+ /* modify the command trb to NO OP command */
+ cmd_trb->generic.field[0] = 0;
+ cmd_trb->generic.field[1] = 0;
+ cmd_trb->generic.field[2] = 0;
+ cmd_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+ break;
+ }
+ }
+}
+
+static void xhci_cancel_cmd_in_cd_list(struct xhci_hcd *xhci)
+{
+ struct xhci_cd *cur_cd, *next_cd;
+
+ if (list_empty(&xhci->cancel_cmd_list))
+ return;
+
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ xhci_cmd_to_noop(xhci, cur_cd);
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ }
+}
+
+/*
+ * traversing the cancel_cmd_list. If the command descriptor according
+ * to cmd_trb is found, the function free it and return 1, otherwise
+ * return 0.
+ */
+static int xhci_search_cmd_trb_in_cd_list(struct xhci_hcd *xhci,
+ union xhci_trb *cmd_trb)
+{
+ struct xhci_cd *cur_cd, *next_cd;
+
+ if (list_empty(&xhci->cancel_cmd_list))
+ return 0;
+
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ if (cur_cd->cmd_trb == cmd_trb) {
+ if (cur_cd->command)
+ xhci_complete_cmd_in_cmd_wait_list(xhci,
+ cur_cd->command, COMP_CMD_STOP);
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * If the cmd_trb_comp_code is COMP_CMD_ABORT, we just check whether the
+ * trb pointed by the command ring dequeue pointer is the trb we want to
+ * cancel or not. And if the cmd_trb_comp_code is COMP_CMD_STOP, we will
+ * traverse the cancel_cmd_list to trun the all of the commands according
+ * to command descriptor to NO-OP trb.
+ */
+static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ int cmd_trb_comp_code)
+{
+ int cur_trb_is_good = 0;
+
+ /* Searching the cmd trb pointed by the command ring dequeue
+ * pointer in command descriptor list. If it is found, free it.
+ */
+ cur_trb_is_good = xhci_search_cmd_trb_in_cd_list(xhci,
+ xhci->cmd_ring->dequeue);
+
+ if (cmd_trb_comp_code == COMP_CMD_ABORT)
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ else if (cmd_trb_comp_code == COMP_CMD_STOP) {
+ /* traversing the cancel_cmd_list and canceling
+ * the command according to command descriptor
+ */
+ xhci_cancel_cmd_in_cd_list(xhci);
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+ /*
+ * ring command ring doorbell again to restart the
+ * command ring
+ */
+ if (xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue)
+ xhci_ring_cmd_db(xhci);
+ }
+ return cur_trb_is_good;
+}
+
static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
@@ -1089,6 +1363,22 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci->error_bitmask |= 1 << 5;
return;
}
+
+ if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
+ (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
+ /* If the return value is 0, we think the trb pointed by
+ * command ring dequeue pointer is a good trb. The good
+ * trb means we don't want to cancel the trb, but it have
+ * been stopped by host. So we should handle it normally.
+ * Otherwise, driver should invoke inc_deq() and return.
+ */
+ if (handle_stopped_cmd_ring(xhci,
+ GET_COMP_CODE(le32_to_cpu(event->status)))) {
+ inc_deq(xhci, xhci->cmd_ring, false);
+ return;
+ }
+ }
+
switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
& TRB_TYPE_BITMASK) {
case TRB_TYPE(TRB_ENABLE_SLOT):
@@ -1739,8 +2029,12 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* handle completion code */
switch (trb_comp_code) {
case COMP_SUCCESS:
- frame->status = 0;
- break;
+ if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
+ frame->status = 0;
+ break;
+ }
+ if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
+ trb_comp_code = COMP_SHORT_TX;
case COMP_SHORT_TX:
frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
-EREMOTEIO : 0;
@@ -1756,6 +2050,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
break;
case COMP_DEV_ERR:
case COMP_STALL:
+ case COMP_TX_ERR:
frame->status = -EPROTO;
skip_td = true;
break;
@@ -1838,13 +2133,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) {
case COMP_SUCCESS:
/* Double check that the HW transferred everything. */
- if (event_trb != td->last_trb) {
+ if (event_trb != td->last_trb ||
+ TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
xhci_warn(xhci, "WARN Successful completion "
"on short TX\n");
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
*status = -EREMOTEIO;
else
*status = 0;
+ if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
+ trb_comp_code = COMP_SHORT_TX;
} else {
*status = 0;
}
@@ -1985,6 +2283,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* transfer type
*/
case COMP_SUCCESS:
+ if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
+ break;
+ if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
+ trb_comp_code = COMP_SHORT_TX;
+ else
+ xhci_warn(xhci, "WARN Successful completion on short TX: "
+ "needs XHCI_TRUST_TX_LENGTH quirk?\n");
case COMP_SHORT_TX:
break;
case COMP_STOP:
@@ -3341,7 +3646,9 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
} else {
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
- if (xhci->hci_version == 0x100) {
+ if (xhci->hci_version == 0x100 &&
+ !(xhci->quirks &
+ XHCI_AVOID_BEI)) {
/* Set BEI bit except for the last td */
if (i < num_tds - 1)
field |= TRB_BEI;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index dbba936..4864b25 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -51,7 +51,7 @@ MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*/
-static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
u32 mask, u32 done, int usec)
{
u32 result;
@@ -104,8 +104,10 @@ int xhci_halt(struct xhci_hcd *xhci)
ret = handshake(xhci, &xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
- if (!ret)
+ if (!ret) {
xhci->xhc_state |= XHCI_STATE_HALTED;
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ }
return ret;
}
@@ -163,7 +165,7 @@ int xhci_reset(struct xhci_hcd *xhci)
xhci_writel(xhci, command, &xhci->op_regs->command);
ret = handshake(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, 250 * 1000);
+ CMD_RESET, 0, 10 * 1000 * 1000);
if (ret)
return ret;
@@ -172,7 +174,8 @@ int xhci_reset(struct xhci_hcd *xhci)
* xHCI cannot write to any doorbells or operational registers other
* than status until the "Controller Not Ready" flag is cleared.
*/
- return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
+ return handshake(xhci, &xhci->op_regs->status,
+ STS_CNR, 0, 10 * 1000 * 1000);
}
/*
@@ -389,6 +392,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
return -ENODEV;
}
xhci->shared_hcd->state = HC_STATE_RUNNING;
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
if (xhci->quirks & XHCI_NEC_HOST)
xhci_ring_cmd_db(xhci);
@@ -593,6 +597,9 @@ void xhci_shutdown(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
+ usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
+
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
spin_unlock_irq(&xhci->lock);
@@ -716,7 +723,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
command &= ~CMD_RUN;
xhci_writel(xhci, command, &xhci->op_regs->command);
if (handshake(xhci, &xhci->op_regs->status,
- STS_HALT, STS_HALT, 100*100)) {
+ STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC)) {
xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
@@ -730,8 +737,8 @@ int xhci_suspend(struct xhci_hcd *xhci)
command = xhci_readl(xhci, &xhci->op_regs->command);
command |= CMD_CSS;
xhci_writel(xhci, command, &xhci->op_regs->command);
- if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10*100)) {
- xhci_warn(xhci, "WARN: xHC CMD_CSS timeout\n");
+ if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10 * 1000)) {
+ xhci_warn(xhci, "WARN: xHC save state timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
@@ -786,8 +793,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
command |= CMD_CRS;
xhci_writel(xhci, command, &xhci->op_regs->command);
if (handshake(xhci, &xhci->op_regs->status,
- STS_RESTORE, 0, 10*100)) {
- xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n");
+ STS_RESTORE, 0, 10 * 1000)) {
+ xhci_warn(xhci, "WARN: xHC restore state timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
@@ -1771,6 +1778,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
struct completion *cmd_completion;
u32 *cmd_status;
struct xhci_virt_device *virt_dev;
+ union xhci_trb *cmd_trb;
spin_lock_irqsave(&xhci->lock, flags);
virt_dev = xhci->devs[udev->slot_id];
@@ -1813,6 +1821,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
}
init_completion(cmd_completion);
+ cmd_trb = xhci->cmd_ring->dequeue;
if (!ctx_change)
ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
udev->slot_id, must_succeed);
@@ -1834,14 +1843,17 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
/* Wait for the configure endpoint command to complete */
timeleft = wait_for_completion_interruptible_timeout(
cmd_completion,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for %s command\n",
timeleft == 0 ? "Timeout" : "Signal",
ctx_change == 0 ?
"configure endpoint" :
"evaluate context");
- /* FIXME cancel the configure endpoint command */
+ /* cancel the configure endpoint command */
+ ret = xhci_cancel_cmd(xhci, command, cmd_trb);
+ if (ret < 0)
+ return ret;
return -ETIME;
}
@@ -2774,8 +2786,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
unsigned long flags;
int timeleft;
int ret;
+ union xhci_trb *cmd_trb;
spin_lock_irqsave(&xhci->lock, flags);
+ cmd_trb = xhci->cmd_ring->dequeue;
ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -2787,12 +2801,12 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
/* XXX: how much time for xHC slot assignment? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for a slot\n",
timeleft == 0 ? "Timeout" : "Signal");
- /* FIXME cancel the enable slot request */
- return 0;
+ /* cancel the enable slot request */
+ return xhci_cancel_cmd(xhci, NULL, cmd_trb);
}
if (!xhci->slot_id) {
@@ -2853,6 +2867,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u64 temp_64;
+ union xhci_trb *cmd_trb;
if (!udev->slot_id) {
xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id);
@@ -2891,6 +2906,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
spin_lock_irqsave(&xhci->lock, flags);
+ cmd_trb = xhci->cmd_ring->dequeue;
ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
udev->slot_id);
if (ret) {
@@ -2903,7 +2919,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
/* FIXME: From section 4.3.4: "Software shall be responsible for timing
* the SetAddress() "recovery interval" required by USB and aborting the
* command on a timeout.
@@ -2911,7 +2927,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for a slot\n",
timeleft == 0 ? "Timeout" : "Signal");
- /* FIXME cancel the address device command */
+ /* cancel the address device command */
+ ret = xhci_cancel_cmd(xhci, NULL, cmd_trb);
+ if (ret < 0)
+ return ret;
return -ETIME;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index dfd260a..1d72895 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1070,6 +1070,9 @@ union xhci_trb {
#define TRB_MFINDEX_WRAP 39
/* TRB IDs 40-47 reserved, 48-63 is vendor-defined */
+#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_LINK)))
+
/* Nec vendor-specific command completion event. */
#define TRB_NEC_CMD_COMP 48
/* Get NEC firmware revision. */
@@ -1111,6 +1114,16 @@ struct xhci_td {
union xhci_trb *last_trb;
};
+/* xHCI command default timeout value */
+#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ)
+
+/* command descriptor */
+struct xhci_cd {
+ struct list_head cancel_cmd_list;
+ struct xhci_command *command;
+ union xhci_trb *cmd_trb;
+};
+
struct xhci_dequeue_state {
struct xhci_segment *new_deq_seg;
union xhci_trb *new_deq_ptr;
@@ -1252,6 +1265,11 @@ struct xhci_hcd {
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
+ unsigned int cmd_ring_state;
+#define CMD_RING_STATE_RUNNING (1 << 0)
+#define CMD_RING_STATE_ABORTED (1 << 1)
+#define CMD_RING_STATE_STOPPED (1 << 2)
+ struct list_head cancel_cmd_list;
unsigned int cmd_ring_reserved_trbs;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1315,6 +1333,9 @@ struct xhci_hcd {
#define XHCI_BROKEN_MSI (1 << 6)
#define XHCI_RESET_ON_RESUME (1 << 7)
#define XHCI_AMD_0x96_HOST (1 << 9)
+#define XHCI_TRUST_TX_LENGTH (1 << 10)
+#define XHCI_SPURIOUS_REBOOT (1 << 13)
+#define XHCI_AVOID_BEI (1 << 15)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1483,6 +1504,8 @@ void xhci_unregister_pci(void);
#endif
/* xHCI host controller glue */
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+ u32 mask, u32 done, int usec);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
@@ -1565,6 +1588,8 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+ union xhci_trb *cmd_trb);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
index fc15ad4..723e833 100644
--- a/drivers/usb/misc/emi62.c
+++ b/drivers/usb/misc/emi62.c
@@ -259,7 +259,7 @@ wraperr:
return err;
}
-static const struct usb_device_id id_table[] __devinitconst = {
+static const struct usb_device_id id_table[] = {
{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index bb10846..5707f56 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -1023,7 +1023,10 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
case 13: /* short read, resembling case 10 */
req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
/* last data packet "should" be DATA1, not DATA0 */
- len = 1024 - udev->descriptor.bMaxPacketSize0;
+ if (udev->speed == USB_SPEED_SUPER)
+ len = 1024 - 512;
+ else
+ len = 1024 - udev->descriptor.bMaxPacketSize0;
expected = -EREMOTEIO;
break;
case 14: /* short read; try to fill the last packet */
@@ -1382,11 +1385,15 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
static int halt_simple(struct usbtest_dev *dev)
{
- int ep;
- int retval = 0;
- struct urb *urb;
+ int ep;
+ int retval = 0;
+ struct urb *urb;
+ struct usb_device *udev = testdev_to_usbdev(dev);
- urb = simple_alloc_urb(testdev_to_usbdev(dev), 0, 512);
+ if (udev->speed == USB_SPEED_SUPER)
+ urb = simple_alloc_urb(udev, 0, 1024);
+ else
+ urb = simple_alloc_urb(udev, 0, 512);
if (urb == NULL)
return -ENOMEM;
diff --git a/drivers/usb/notify/Kconfig b/drivers/usb/notify/Kconfig
new file mode 100755
index 0000000..159affa
--- /dev/null
+++ b/drivers/usb/notify/Kconfig
@@ -0,0 +1,11 @@
+#
+# USB Host notify configuration
+#
+
+config USB_HOST_NOTIFY
+ boolean "USB Host notify Driver"
+ depends on USB
+ help
+ Android framework needs uevents for usb host operation.
+ Host notify Driver serves uevent format
+ that is used by usb host or otg. \ No newline at end of file
diff --git a/drivers/usb/notify/Makefile b/drivers/usb/notify/Makefile
new file mode 100755
index 0000000..996eaa3
--- /dev/null
+++ b/drivers/usb/notify/Makefile
@@ -0,0 +1,4 @@
+
+# host notify driver
+obj-y += host_notify_class.o
+
diff --git a/drivers/usb/notify/host_notify_class.c b/drivers/usb/notify/host_notify_class.c
new file mode 100755
index 0000000..09ad529
--- /dev/null
+++ b/drivers/usb/notify/host_notify_class.c
@@ -0,0 +1,262 @@
+/*
+ * drivers/usb/notify/host_notify_class.c
+ *
+ * Copyright (C) 2011 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/host_notify.h>
+
+struct notify_data {
+ struct class *host_notify_class;
+ atomic_t device_count;
+};
+
+static struct notify_data host_notify;
+
+static ssize_t mode_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct host_notify_dev *ndev = (struct host_notify_dev *)
+ dev_get_drvdata(dev);
+ char *mode;
+
+ switch (ndev->mode) {
+ case NOTIFY_HOST_MODE:
+ mode = "HOST";
+ break;
+ case NOTIFY_PERIPHERAL_MODE:
+ mode = "PERIPHERAL";
+ break;
+ case NOTIFY_TEST_MODE:
+ mode = "TEST";
+ break;
+ case NOTIFY_NONE_MODE:
+ default:
+ mode = "NONE";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t mode_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct host_notify_dev *ndev = (struct host_notify_dev *)
+ dev_get_drvdata(dev);
+
+ char *mode;
+ size_t ret = -ENOMEM;
+
+ mode = kzalloc(size+1, GFP_KERNEL);
+ if (!mode)
+ goto error;
+
+ sscanf(buf, "%s", mode);
+
+ if (ndev->set_mode) {
+ if (!strcmp(mode, "HOST"))
+ ndev->set_mode(NOTIFY_SET_ON);
+ else if (!strcmp(mode, "NONE"))
+ ndev->set_mode(NOTIFY_SET_OFF);
+ printk(KERN_INFO "host_notify: set mode %s\n", mode);
+ }
+ ret = size;
+ kfree(mode);
+error:
+ return ret;
+}
+
+static ssize_t booster_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct host_notify_dev *ndev = (struct host_notify_dev *)
+ dev_get_drvdata(dev);
+ char *booster;
+
+ switch (ndev->booster) {
+ case NOTIFY_POWER_ON:
+ booster = "ON";
+ break;
+ case NOTIFY_POWER_OFF:
+ default:
+ booster = "OFF";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", booster);
+}
+
+static ssize_t booster_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct host_notify_dev *ndev = (struct host_notify_dev *)
+ dev_get_drvdata(dev);
+
+ char *booster;
+ size_t ret = -ENOMEM;
+
+ booster = kzalloc(size+1, GFP_KERNEL);
+ if (!booster)
+ goto error;
+
+ sscanf(buf, "%s", booster);
+
+ if (ndev->set_booster) {
+ if (!strcmp(booster, "ON")) {
+ ndev->set_booster(NOTIFY_SET_ON);
+ ndev->mode = NOTIFY_TEST_MODE;
+ } else if (!strcmp(booster, "OFF")) {
+ ndev->set_booster(NOTIFY_SET_OFF);
+ ndev->mode = NOTIFY_NONE_MODE;
+ }
+ printk(KERN_INFO "host_notify: set booster %s\n", booster);
+ }
+ ret = size;
+ kfree(booster);
+error:
+ return ret;
+}
+
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR | S_IWGRP, mode_show, mode_store);
+static DEVICE_ATTR(booster, S_IRUGO | S_IWUSR | S_IWGRP,
+ booster_show, booster_store);
+
+static struct attribute *host_notify_attrs[] = {
+ &dev_attr_mode.attr,
+ &dev_attr_booster.attr,
+ NULL,
+};
+
+static struct attribute_group host_notify_attr_grp = {
+ .attrs = host_notify_attrs,
+};
+
+void host_state_notify(struct host_notify_dev *ndev, int state)
+{
+ printk(KERN_INFO "host_notify: ndev name=%s: from state=%d -> to state=%d\n",
+ ndev->name, ndev->state, state);
+ if (ndev->state != state) {
+ ndev->state = state;
+ kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
+ }
+}
+EXPORT_SYMBOL_GPL(host_state_notify);
+
+static int
+host_notify_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct host_notify_dev *ndev = (struct host_notify_dev *)
+ dev_get_drvdata(dev);
+ char *state;
+
+ if (!ndev) {
+ /* this happens when the device is first created */
+ return 0;
+ }
+ switch (ndev->state) {
+ case NOTIFY_HOST_ADD:
+ state = "ADD";
+ break;
+ case NOTIFY_HOST_REMOVE:
+ state = "REMOVE";
+ break;
+ case NOTIFY_HOST_OVERCURRENT:
+ state = "OVERCURRENT";
+ break;
+ case NOTIFY_HOST_LOWBATT:
+ state = "LOWBATT";
+ break;
+ case NOTIFY_HOST_UNKNOWN:
+ state = "UNKNOWN";
+ break;
+ case NOTIFY_HOST_NONE:
+ default:
+ return 0;
+ }
+ if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name))
+ return -ENOMEM;
+ if (add_uevent_var(env, "STATE=%s", state))
+ return -ENOMEM;
+ return 0;
+}
+
+static int create_notify_class(void)
+{
+ if (!host_notify.host_notify_class) {
+ host_notify.host_notify_class
+ = class_create(THIS_MODULE, "host_notify");
+ if (IS_ERR(host_notify.host_notify_class))
+ return PTR_ERR(host_notify.host_notify_class);
+ atomic_set(&host_notify.device_count, 0);
+ host_notify.host_notify_class->dev_uevent = host_notify_uevent;
+ }
+
+ return 0;
+}
+
+int host_notify_dev_register(struct host_notify_dev *ndev)
+{
+ int ret;
+
+ if (!host_notify.host_notify_class) {
+ ret = create_notify_class();
+ if (ret < 0)
+ return ret;
+ }
+
+ ndev->index = atomic_inc_return(&host_notify.device_count);
+ ndev->dev = device_create(host_notify.host_notify_class, NULL,
+ MKDEV(0, ndev->index), NULL, ndev->name);
+ if (IS_ERR(ndev->dev))
+ return PTR_ERR(ndev->dev);
+
+ ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp);
+ if (ret < 0) {
+ device_destroy(host_notify.host_notify_class,
+ MKDEV(0, ndev->index));
+ return ret;
+ }
+
+ dev_set_drvdata(ndev->dev, ndev);
+ ndev->state = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(host_notify_dev_register);
+
+void host_notify_dev_unregister(struct host_notify_dev *ndev)
+{
+ ndev->state = NOTIFY_HOST_NONE;
+ sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp);
+ device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index));
+ dev_set_drvdata(ndev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(host_notify_dev_unregister);
+
+static int __init notify_class_init(void)
+{
+ return create_notify_class();
+}
+
+static void __exit notify_class_exit(void)
+{
+ class_destroy(host_notify.host_notify_class);
+}
+
+module_init(notify_class_init);
+module_exit(notify_class_exit);
+
+MODULE_AUTHOR("Dongrak Shin <dongrak.shin@samsung.com>");
+MODULE_DESCRIPTION("Usb host notify driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index f2c57e0..35e6b5f 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -82,6 +82,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+ { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
{ USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
@@ -92,6 +93,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
{ USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */
{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
+ { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */
{ USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */
{ USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */
{ USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */
@@ -133,7 +135,13 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+ { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
+ { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
+ { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */
+ { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */
+ { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */
+ { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
{ USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
{ USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
@@ -145,7 +153,11 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
+ { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */
+ { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
+ { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
+ { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 60d7b1e..00f1bf5 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -582,6 +582,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
/*
* ELV devices:
*/
@@ -702,6 +704,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) },
@@ -735,6 +738,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_SERIAL_VX7_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_CT29B_PID) },
+ { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_RTS01_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) },
{ USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) },
@@ -801,12 +805,33 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
+ USB_CLASS_VENDOR_SPEC,
+ USB_SUBCLASS_VENDOR_SPEC, 0x00) },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
+ { USB_DEVICE(FTDI_VID, PI_C865_PID) },
+ { USB_DEVICE(FTDI_VID, PI_C857_PID) },
+ { USB_DEVICE(PI_VID, PI_C866_PID) },
+ { USB_DEVICE(PI_VID, PI_C663_PID) },
+ { USB_DEVICE(PI_VID, PI_C725_PID) },
+ { USB_DEVICE(PI_VID, PI_E517_PID) },
+ { USB_DEVICE(PI_VID, PI_C863_PID) },
+ { USB_DEVICE(PI_VID, PI_E861_PID) },
+ { USB_DEVICE(PI_VID, PI_C867_PID) },
+ { USB_DEVICE(PI_VID, PI_E609_PID) },
+ { USB_DEVICE(PI_VID, PI_E709_PID) },
+ { USB_DEVICE(PI_VID, PI_100F_PID) },
+ { USB_DEVICE(PI_VID, PI_1011_PID) },
+ { USB_DEVICE(PI_VID, PI_1012_PID) },
+ { USB_DEVICE(PI_VID, PI_1013_PID) },
+ { USB_DEVICE(PI_VID, PI_1014_PID) },
+ { USB_DEVICE(PI_VID, PI_1015_PID) },
+ { USB_DEVICE(PI_VID, PI_1016_PID) },
+ { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
{ USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index c6dd18e..7b5eb74 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -75,6 +75,9 @@
#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB
#define FTDI_OPENDCC_GBM_PID 0xBFDC
+/* NZR SEM 16+ USB (http://www.nzr.de) */
+#define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */
+
/*
* RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
*/
@@ -514,6 +517,11 @@
*/
#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */
+/*
+ * TIAO product ids (FTDI_VID)
+ * http://www.tiaowiki.com/w/Main_Page
+ */
+#define FTDI_TIAO_UMPA_PID 0x8a98 /* TIAO/DIYGADGET USB Multi-Protocol Adapter */
/********************************/
@@ -539,7 +547,10 @@
/*
* Microchip Technology, Inc.
*
- * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are also used by:
+ * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
+ * used by single function CDC ACM class based firmware demo
+ * applications. The VID/PID has also been used in firmware
+ * emulating FTDI serial chips by:
* Hornby Elite - Digital Command Control Console
* http://www.hornby.com/hornby-dcc/controllers/
*/
@@ -784,6 +795,41 @@
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
#define RTSYSTEMS_SERIAL_VX7_PID 0x9e52 /* Serial converter for VX-7 Radios using FT232RL */
#define RTSYSTEMS_CT29B_PID 0x9e54 /* CT29B Radio Cable */
+#define RTSYSTEMS_RTS01_PID 0x9e57 /* USB-RTS01 Radio Cable */
+
+
+/*
+ * Physik Instrumente
+ * http://www.physikinstrumente.com/en/products/
+ */
+/* These two devices use the VID of FTDI */
+#define PI_C865_PID 0xe0a0 /* PI C-865 Piezomotor Controller */
+#define PI_C857_PID 0xe0a1 /* PI Encoder Trigger Box */
+
+#define PI_VID 0x1a72 /* Vendor ID */
+#define PI_C866_PID 0x1000 /* PI C-866 Piezomotor Controller */
+#define PI_C663_PID 0x1001 /* PI C-663 Mercury-Step */
+#define PI_C725_PID 0x1002 /* PI C-725 Piezomotor Controller */
+#define PI_E517_PID 0x1005 /* PI E-517 Digital Piezo Controller Operation Module */
+#define PI_C863_PID 0x1007 /* PI C-863 */
+#define PI_E861_PID 0x1008 /* PI E-861 Piezomotor Controller */
+#define PI_C867_PID 0x1009 /* PI C-867 Piezomotor Controller */
+#define PI_E609_PID 0x100D /* PI E-609 Digital Piezo Controller */
+#define PI_E709_PID 0x100E /* PI E-709 Digital Piezo Controller */
+#define PI_100F_PID 0x100F /* PI Digital Piezo Controller */
+#define PI_1011_PID 0x1011 /* PI Digital Piezo Controller */
+#define PI_1012_PID 0x1012 /* PI Motion Controller */
+#define PI_1013_PID 0x1013 /* PI Motion Controller */
+#define PI_1014_PID 0x1014 /* PI Device */
+#define PI_1015_PID 0x1015 /* PI Device */
+#define PI_1016_PID 0x1016 /* PI Digital Servo Module */
+
+/*
+ * Kondo Kagaku Co.Ltd.
+ * http://www.kondo-robot.com/EN
+ */
+#define KONDO_VID 0x165c
+#define KONDO_USB_SERIAL_PID 0x0002
/*
* Bayer Ascensia Contour blood glucose meter USB-converter cable.
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index ba0d287..42de17b 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -359,13 +359,16 @@ static int mct_u232_set_modem_ctrl(struct usb_serial *serial,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
WDR_TIMEOUT);
- if (rc < 0)
- dev_err(&serial->dev->dev,
- "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
+ kfree(buf);
+
dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr);
- kfree(buf);
- return rc;
+ if (rc < 0) {
+ dev_err(&serial->dev->dev,
+ "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
+ return rc;
+ }
+ return 0;
} /* mct_u232_set_modem_ctrl */
static int mct_u232_get_modem_stat(struct usb_serial *serial,
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 3257519..2d34dfd 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -206,7 +206,7 @@ static const struct usb_device_id moschip_port_id_table[] = {
{} /* terminating entry */
};
-static const struct usb_device_id moschip_id_table_combined[] __devinitconst = {
+static const struct usb_device_id moschip_id_table_combined[] = {
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
@@ -235,12 +235,10 @@ struct moschip_port {
int port_num; /*Actual port number in the device(1,2,etc) */
struct urb *write_urb; /* write URB for this port */
struct urb *read_urb; /* read URB for this port */
- struct urb *int_urb;
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
char open;
char open_ports;
- char zombie;
wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */
int delta_msr_cond;
@@ -505,7 +503,6 @@ static void mos7840_control_callback(struct urb *urb)
unsigned char *data;
struct moschip_port *mos7840_port;
__u8 regval = 0x0;
- int result = 0;
int status = urb->status;
mos7840_port = urb->context;
@@ -524,7 +521,7 @@ static void mos7840_control_callback(struct urb *urb)
default:
dbg("%s - nonzero urb status received: %d", __func__,
status);
- goto exit;
+ return;
}
dbg("%s urb buffer size is %d", __func__, urb->actual_length);
@@ -537,17 +534,6 @@ static void mos7840_control_callback(struct urb *urb)
mos7840_handle_new_msr(mos7840_port, regval);
else if (mos7840_port->MsrLsr == 1)
mos7840_handle_new_lsr(mos7840_port, regval);
-
-exit:
- spin_lock(&mos7840_port->pool_lock);
- if (!mos7840_port->zombie)
- result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC);
- spin_unlock(&mos7840_port->pool_lock);
- if (result) {
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting interrupt urb\n",
- __func__, result);
- }
}
static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
@@ -655,14 +641,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
wreg = MODEM_STATUS_REGISTER;
break;
}
- spin_lock(&mos7840_port->pool_lock);
- if (!mos7840_port->zombie) {
- rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
- } else {
- spin_unlock(&mos7840_port->pool_lock);
- return;
- }
- spin_unlock(&mos7840_port->pool_lock);
+ rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
}
}
}
@@ -1191,9 +1170,12 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
}
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
- for (i = 0; i < NUM_URBS; ++i)
- if (mos7840_port->busy[i])
- chars += URB_TRANSFER_BUFFER_SIZE;
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->busy[i]) {
+ struct urb *urb = mos7840_port->write_urb_pool[i];
+ chars += urb->transfer_buffer_length;
+ }
+ }
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
dbg("%s - returns %d", __func__, chars);
return chars;
@@ -2592,7 +2574,6 @@ error:
kfree(mos7840_port->ctrl_buf);
usb_free_urb(mos7840_port->control_urb);
kfree(mos7840_port);
- serial->port[i] = NULL;
}
return status;
}
@@ -2623,9 +2604,6 @@ static void mos7840_disconnect(struct usb_serial *serial)
mos7840_port = mos7840_get_port_private(serial->port[i]);
dbg ("mos7840_port %d = %p", i, mos7840_port);
if (mos7840_port) {
- spin_lock_irqsave(&mos7840_port->pool_lock, flags);
- mos7840_port->zombie = 1;
- spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
usb_kill_urb(mos7840_port->control_urb);
}
}
@@ -2659,6 +2637,7 @@ static void mos7840_release(struct usb_serial *serial)
mos7840_port = mos7840_get_port_private(serial->port[i]);
dbg("mos7840_port %d = %p", i, mos7840_port);
if (mos7840_port) {
+ usb_free_urb(mos7840_port->control_urb);
kfree(mos7840_port->ctrl_buf);
kfree(mos7840_port->dr);
kfree(mos7840_port);
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 96423f3..5d274b3 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -160,7 +160,11 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype,
{
struct usb_serial *serial = port->serial;
int retval;
- u8 buffer[2];
+ u8 *buffer;
+
+ buffer = kzalloc(1, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
buffer[0] = val;
/* Send the message to the vendor control endpoint
@@ -169,6 +173,7 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype,
requesttype,
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
0, 0, buffer, 1, 0);
+ kfree(buffer);
return retval;
}
@@ -292,7 +297,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
if (!dr) {
dev_err(&port->dev, "out of memory\n");
count = -ENOMEM;
- goto error;
+ goto error_no_dr;
}
dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
@@ -322,6 +327,8 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
return count;
error:
+ kfree(dr);
+error_no_dr:
usb_free_urb(urb);
error_no_urb:
kfree(buffer);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index cbe3451..c334670 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -47,6 +47,7 @@
/* Function prototypes */
static int option_probe(struct usb_serial *serial,
const struct usb_device_id *id);
+static void option_release(struct usb_serial *serial);
static int option_send_setup(struct usb_serial_port *port);
static void option_instat_callback(struct urb *urb);
@@ -79,84 +80,9 @@ static void option_instat_callback(struct urb *urb);
#define OPTION_PRODUCT_GTM380_MODEM 0x7201
#define HUAWEI_VENDOR_ID 0x12D1
-#define HUAWEI_PRODUCT_E600 0x1001
-#define HUAWEI_PRODUCT_E220 0x1003
-#define HUAWEI_PRODUCT_E220BIS 0x1004
-#define HUAWEI_PRODUCT_E1401 0x1401
-#define HUAWEI_PRODUCT_E1402 0x1402
-#define HUAWEI_PRODUCT_E1403 0x1403
-#define HUAWEI_PRODUCT_E1404 0x1404
-#define HUAWEI_PRODUCT_E1405 0x1405
-#define HUAWEI_PRODUCT_E1406 0x1406
-#define HUAWEI_PRODUCT_E1407 0x1407
-#define HUAWEI_PRODUCT_E1408 0x1408
-#define HUAWEI_PRODUCT_E1409 0x1409
-#define HUAWEI_PRODUCT_E140A 0x140A
-#define HUAWEI_PRODUCT_E140B 0x140B
-#define HUAWEI_PRODUCT_E140C 0x140C
-#define HUAWEI_PRODUCT_E140D 0x140D
-#define HUAWEI_PRODUCT_E140E 0x140E
-#define HUAWEI_PRODUCT_E140F 0x140F
-#define HUAWEI_PRODUCT_E1410 0x1410
-#define HUAWEI_PRODUCT_E1411 0x1411
-#define HUAWEI_PRODUCT_E1412 0x1412
-#define HUAWEI_PRODUCT_E1413 0x1413
-#define HUAWEI_PRODUCT_E1414 0x1414
-#define HUAWEI_PRODUCT_E1415 0x1415
-#define HUAWEI_PRODUCT_E1416 0x1416
-#define HUAWEI_PRODUCT_E1417 0x1417
-#define HUAWEI_PRODUCT_E1418 0x1418
-#define HUAWEI_PRODUCT_E1419 0x1419
-#define HUAWEI_PRODUCT_E141A 0x141A
-#define HUAWEI_PRODUCT_E141B 0x141B
-#define HUAWEI_PRODUCT_E141C 0x141C
-#define HUAWEI_PRODUCT_E141D 0x141D
-#define HUAWEI_PRODUCT_E141E 0x141E
-#define HUAWEI_PRODUCT_E141F 0x141F
-#define HUAWEI_PRODUCT_E1420 0x1420
-#define HUAWEI_PRODUCT_E1421 0x1421
-#define HUAWEI_PRODUCT_E1422 0x1422
-#define HUAWEI_PRODUCT_E1423 0x1423
-#define HUAWEI_PRODUCT_E1424 0x1424
-#define HUAWEI_PRODUCT_E1425 0x1425
-#define HUAWEI_PRODUCT_E1426 0x1426
-#define HUAWEI_PRODUCT_E1427 0x1427
-#define HUAWEI_PRODUCT_E1428 0x1428
-#define HUAWEI_PRODUCT_E1429 0x1429
-#define HUAWEI_PRODUCT_E142A 0x142A
-#define HUAWEI_PRODUCT_E142B 0x142B
-#define HUAWEI_PRODUCT_E142C 0x142C
-#define HUAWEI_PRODUCT_E142D 0x142D
-#define HUAWEI_PRODUCT_E142E 0x142E
-#define HUAWEI_PRODUCT_E142F 0x142F
-#define HUAWEI_PRODUCT_E1430 0x1430
-#define HUAWEI_PRODUCT_E1431 0x1431
-#define HUAWEI_PRODUCT_E1432 0x1432
-#define HUAWEI_PRODUCT_E1433 0x1433
-#define HUAWEI_PRODUCT_E1434 0x1434
-#define HUAWEI_PRODUCT_E1435 0x1435
-#define HUAWEI_PRODUCT_E1436 0x1436
-#define HUAWEI_PRODUCT_E1437 0x1437
-#define HUAWEI_PRODUCT_E1438 0x1438
-#define HUAWEI_PRODUCT_E1439 0x1439
-#define HUAWEI_PRODUCT_E143A 0x143A
-#define HUAWEI_PRODUCT_E143B 0x143B
-#define HUAWEI_PRODUCT_E143C 0x143C
-#define HUAWEI_PRODUCT_E143D 0x143D
-#define HUAWEI_PRODUCT_E143E 0x143E
-#define HUAWEI_PRODUCT_E143F 0x143F
#define HUAWEI_PRODUCT_K4505 0x1464
#define HUAWEI_PRODUCT_K3765 0x1465
-#define HUAWEI_PRODUCT_E14AC 0x14AC
-#define HUAWEI_PRODUCT_K3806 0x14AE
#define HUAWEI_PRODUCT_K4605 0x14C6
-#define HUAWEI_PRODUCT_K3770 0x14C9
-#define HUAWEI_PRODUCT_K3771 0x14CA
-#define HUAWEI_PRODUCT_K4510 0x14CB
-#define HUAWEI_PRODUCT_K4511 0x14CC
-#define HUAWEI_PRODUCT_ETS1220 0x1803
-#define HUAWEI_PRODUCT_E353 0x1506
-#define HUAWEI_PRODUCT_E173S 0x1C05
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
@@ -234,6 +160,7 @@ static void option_instat_callback(struct urb *urb);
#define NOVATELWIRELESS_PRODUCT_G1 0xA001
#define NOVATELWIRELESS_PRODUCT_G1_M 0xA002
#define NOVATELWIRELESS_PRODUCT_G2 0xA010
+#define NOVATELWIRELESS_PRODUCT_MC551 0xB001
/* AMOI PRODUCTS */
#define AMOI_VENDOR_ID 0x1614
@@ -425,7 +352,7 @@ static void option_instat_callback(struct urb *urb);
#define SAMSUNG_VENDOR_ID 0x04e8
#define SAMSUNG_PRODUCT_GT_B3730 0x6889
-/* YUGA products www.yuga-info.com*/
+/* YUGA products www.yuga-info.com gavin.kx@qq.com */
#define YUGA_VENDOR_ID 0x257A
#define YUGA_PRODUCT_CEM600 0x1601
#define YUGA_PRODUCT_CEM610 0x1602
@@ -442,6 +369,8 @@ static void option_instat_callback(struct urb *urb);
#define YUGA_PRODUCT_CEU516 0x160C
#define YUGA_PRODUCT_CEU528 0x160D
#define YUGA_PRODUCT_CEU526 0x160F
+#define YUGA_PRODUCT_CEU881 0x161F
+#define YUGA_PRODUCT_CEU882 0x162F
#define YUGA_PRODUCT_CWM600 0x2601
#define YUGA_PRODUCT_CWM610 0x2602
@@ -457,23 +386,26 @@ static void option_instat_callback(struct urb *urb);
#define YUGA_PRODUCT_CWU518 0x260B
#define YUGA_PRODUCT_CWU516 0x260C
#define YUGA_PRODUCT_CWU528 0x260D
+#define YUGA_PRODUCT_CWU581 0x260E
#define YUGA_PRODUCT_CWU526 0x260F
-
-#define YUGA_PRODUCT_CLM600 0x2601
-#define YUGA_PRODUCT_CLM610 0x2602
-#define YUGA_PRODUCT_CLM500 0x2603
-#define YUGA_PRODUCT_CLM510 0x2604
-#define YUGA_PRODUCT_CLM800 0x2605
-#define YUGA_PRODUCT_CLM900 0x2606
-
-#define YUGA_PRODUCT_CLU718 0x2607
-#define YUGA_PRODUCT_CLU716 0x2608
-#define YUGA_PRODUCT_CLU728 0x2609
-#define YUGA_PRODUCT_CLU726 0x260A
-#define YUGA_PRODUCT_CLU518 0x260B
-#define YUGA_PRODUCT_CLU516 0x260C
-#define YUGA_PRODUCT_CLU528 0x260D
-#define YUGA_PRODUCT_CLU526 0x260F
+#define YUGA_PRODUCT_CWU582 0x261F
+#define YUGA_PRODUCT_CWU583 0x262F
+
+#define YUGA_PRODUCT_CLM600 0x3601
+#define YUGA_PRODUCT_CLM610 0x3602
+#define YUGA_PRODUCT_CLM500 0x3603
+#define YUGA_PRODUCT_CLM510 0x3604
+#define YUGA_PRODUCT_CLM800 0x3605
+#define YUGA_PRODUCT_CLM900 0x3606
+
+#define YUGA_PRODUCT_CLU718 0x3607
+#define YUGA_PRODUCT_CLU716 0x3608
+#define YUGA_PRODUCT_CLU728 0x3609
+#define YUGA_PRODUCT_CLU726 0x360A
+#define YUGA_PRODUCT_CLU518 0x360B
+#define YUGA_PRODUCT_CLU516 0x360C
+#define YUGA_PRODUCT_CLU528 0x360D
+#define YUGA_PRODUCT_CLU526 0x360F
/* Viettel products */
#define VIETTEL_VENDOR_ID 0x2262
@@ -489,6 +421,19 @@ static void option_instat_callback(struct urb *urb);
/* MediaTek products */
#define MEDIATEK_VENDOR_ID 0x0e8d
+#define MEDIATEK_PRODUCT_DC_1COM 0x00a0
+#define MEDIATEK_PRODUCT_DC_4COM 0x00a5
+#define MEDIATEK_PRODUCT_DC_5COM 0x00a4
+#define MEDIATEK_PRODUCT_7208_1COM 0x7101
+#define MEDIATEK_PRODUCT_7208_2COM 0x7102
+#define MEDIATEK_PRODUCT_FP_1COM 0x0003
+#define MEDIATEK_PRODUCT_FP_2COM 0x0023
+#define MEDIATEK_PRODUCT_FPDC_1COM 0x0043
+#define MEDIATEK_PRODUCT_FPDC_2COM 0x0033
+
+/* Cellient products */
+#define CELLIENT_VENDOR_ID 0x2692
+#define CELLIENT_PRODUCT_MEN200 0x9005
/* some devices interfaces need special handling due to a number of reasons */
enum option_blacklist_reason {
@@ -542,6 +487,10 @@ static const struct option_blacklist_info net_intf1_blacklist = {
.reserved = BIT(1),
};
+static const struct option_blacklist_info net_intf2_blacklist = {
+ .reserved = BIT(2),
+};
+
static const struct option_blacklist_info net_intf3_blacklist = {
.reserved = BIT(3),
};
@@ -554,11 +503,19 @@ static const struct option_blacklist_info net_intf5_blacklist = {
.reserved = BIT(5),
};
+static const struct option_blacklist_info net_intf6_blacklist = {
+ .reserved = BIT(6),
+};
+
static const struct option_blacklist_info zte_mf626_blacklist = {
.sendsetup = BIT(0) | BIT(1),
.reserved = BIT(4),
};
+static const struct option_blacklist_info zte_1255_blacklist = {
+ .reserved = BIT(3) | BIT(4),
+};
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -590,99 +547,123 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1402, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1404, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1407, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1413, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1414, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1415, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1416, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1420, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1421, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1422, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1423, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1424, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1425, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1426, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1427, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1428, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1429, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1430, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1431, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1432, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1433, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1434, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1435, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1436, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1437, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1438, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1439, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x02) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x03) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x10) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x12) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x13) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x01) }, /* E398 3G Modem */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x02) }, /* E398 3G PC UI Interface */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x03) }, /* E398 3G Application Interface */
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+
+
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) },
@@ -722,6 +703,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G1) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G1_M) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G2) },
+ /* Novatel Ovation MC551 a.k.a. Verizon USB551L */
+ { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) },
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
@@ -878,13 +861,19 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0142, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) },
@@ -895,8 +884,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) },
@@ -904,9 +895,22 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
@@ -1022,18 +1026,24 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1169, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&zte_1255_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) },
@@ -1078,6 +1088,16 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */
+ .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
@@ -1089,15 +1109,21 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) },
@@ -1109,6 +1135,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
+
{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
{ USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
{ USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */
@@ -1207,6 +1237,11 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU516) },
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU528) },
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) },
+ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU881) },
+ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEU882) },
+ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU581) },
+ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU582) },
+ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CWU583) },
{ USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) },
{ USB_DEVICE(LG_VENDOR_ID, LG_PRODUCT_L02C) }, /* docomo L-02C modem */
@@ -1214,6 +1249,18 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a1, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x02, 0x01) }, /* MediaTek MT6276M modem & app port */
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_1COM, 0x0a, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x02, 0x01) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x02, 0x01) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_1COM, 0x02, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_2COM, 0x02, 0x02, 0x01) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_1COM, 0x0a, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) },
+ { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
@@ -1257,7 +1304,7 @@ static struct usb_serial_driver option_1port_device = {
.ioctl = usb_wwan_ioctl,
.attach = usb_wwan_startup,
.disconnect = usb_wwan_disconnect,
- .release = usb_wwan_release,
+ .release = option_release,
.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
@@ -1267,35 +1314,6 @@ static struct usb_serial_driver option_1port_device = {
static int debug;
-/* per port private data */
-
-#define N_IN_URB 4
-#define N_OUT_URB 4
-#define IN_BUFLEN 4096
-#define OUT_BUFLEN 4096
-
-struct option_port_private {
- /* Input endpoints and buffer for this port */
- struct urb *in_urbs[N_IN_URB];
- u8 *in_buffer[N_IN_URB];
- /* Output endpoints and buffer for this port */
- struct urb *out_urbs[N_OUT_URB];
- u8 *out_buffer[N_OUT_URB];
- unsigned long out_busy; /* Bit vector of URBs in use */
- int opened;
- struct usb_anchor delayed;
-
- /* Settings for the port */
- int rts_state; /* Handshaking pins (outputs) */
- int dtr_state;
- int cts_state; /* Handshaking pins (inputs) */
- int dsr_state;
- int dcd_state;
- int ri_state;
-
- unsigned long tx_start_time[N_OUT_URB];
-};
-
/* Functions used by new usb-serial code. */
static int __init option_init(void)
{
@@ -1393,12 +1411,22 @@ static int option_probe(struct usb_serial *serial,
return 0;
}
+static void option_release(struct usb_serial *serial)
+{
+ struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+ usb_wwan_release(serial);
+
+ kfree(priv);
+}
+
static void option_instat_callback(struct urb *urb)
{
int err;
int status = urb->status;
struct usb_serial_port *port = urb->context;
- struct option_port_private *portdata = usb_get_serial_port_data(port);
+ struct usb_wwan_port_private *portdata =
+ usb_get_serial_port_data(port);
dbg("%s", __func__);
dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata);
@@ -1459,7 +1487,7 @@ static int option_send_setup(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
struct usb_wwan_intf_private *intfdata =
(struct usb_wwan_intf_private *) serial->private;
- struct option_port_private *portdata;
+ struct usb_wwan_port_private *portdata;
int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
int val = 0;
dbg("%s", __func__);
diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c
index a348198..87271e3 100644
--- a/drivers/usb/serial/qcaux.c
+++ b/drivers/usb/serial/qcaux.c
@@ -36,8 +36,6 @@
#define UTSTARCOM_PRODUCT_UM175_V1 0x3712
#define UTSTARCOM_PRODUCT_UM175_V2 0x3714
#define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715
-#define PANTECH_PRODUCT_UML190_VZW 0x3716
-#define PANTECH_PRODUCT_UML290_VZW 0x3718
/* CMOTECH devices */
#define CMOTECH_VENDOR_ID 0x16d8
@@ -68,11 +66,9 @@ static struct usb_device_id id_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xfe, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfd, 0xff) }, /* NMEA */
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfe, 0xff) }, /* WMC */
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, /* DIAG */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfd, 0xff) }, /* NMEA */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfe, 0xff) }, /* WMC */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xff, 0xff) }, /* DIAG */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 247c014..7cd2c26 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -104,7 +104,13 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
+ {USB_DEVICE(0x1199, 0x9010)}, /* Sierra Wireless Gobi 3000 QDL */
+ {USB_DEVICE(0x1199, 0x9012)}, /* Sierra Wireless Gobi 3000 QDL */
{USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+ {USB_DEVICE(0x1199, 0x9014)}, /* Sierra Wireless Gobi 3000 QDL */
+ {USB_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */
+ {USB_DEVICE(0x1199, 0x9018)}, /* Sierra Wireless Gobi 3000 QDL */
+ {USB_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */
{USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */
{USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */
{ } /* Terminating entry */
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ef71ba3..a159ad0 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -171,7 +171,6 @@ static int sierra_probe(struct usb_serial *serial,
{
int result = 0;
struct usb_device *udev;
- struct sierra_intf_private *data;
u8 ifnum;
udev = serial->dev;
@@ -199,11 +198,6 @@ static int sierra_probe(struct usb_serial *serial,
return -ENODEV;
}
- data = serial->private = kzalloc(sizeof(struct sierra_intf_private), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- spin_lock_init(&data->susp_lock);
-
return result;
}
@@ -304,6 +298,10 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
},
+ /* AT&T Direct IP LTE modems */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ },
{ USB_DEVICE(0x0f3d, 0x68A3), /* Airprime/Sierra Wireless Direct IP modems */
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
},
@@ -911,6 +909,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on)
static int sierra_startup(struct usb_serial *serial)
{
struct usb_serial_port *port;
+ struct sierra_intf_private *intfdata;
struct sierra_port_private *portdata;
struct sierra_iface_info *himemoryp = NULL;
int i;
@@ -918,6 +917,14 @@ static int sierra_startup(struct usb_serial *serial)
dev_dbg(&serial->dev->dev, "%s\n", __func__);
+ intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
+ if (!intfdata)
+ return -ENOMEM;
+
+ spin_lock_init(&intfdata->susp_lock);
+
+ usb_set_serial_data(serial, intfdata);
+
/* Set Device mode to D0 */
sierra_set_power_state(serial->dev, 0x0000);
@@ -933,7 +940,7 @@ static int sierra_startup(struct usb_serial *serial)
dev_dbg(&port->dev, "%s: kmalloc for "
"sierra_port_private (%d) failed!\n",
__func__, i);
- return -ENOMEM;
+ goto err;
}
spin_lock_init(&portdata->lock);
init_usb_anchor(&portdata->active);
@@ -970,6 +977,14 @@ static int sierra_startup(struct usb_serial *serial)
}
return 0;
+err:
+ for (--i; i >= 0; --i) {
+ portdata = usb_get_serial_port_data(serial->port[i]);
+ kfree(portdata);
+ }
+ kfree(intfdata);
+
+ return -ENOMEM;
}
static void sierra_release(struct usb_serial *serial)
@@ -989,6 +1004,7 @@ static void sierra_release(struct usb_serial *serial)
continue;
kfree(portdata);
}
+ kfree(serial->private);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 21c82b0..2856474 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -165,7 +165,7 @@ static unsigned int product_5052_count;
/* the array dimension is the number of default entries plus */
/* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */
/* null entry */
-static struct usb_device_id ti_id_table_3410[14+TI_EXTRA_VID_PID_COUNT+1] = {
+static struct usb_device_id ti_id_table_3410[15+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
@@ -180,6 +180,7 @@ static struct usb_device_id ti_id_table_3410[14+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
};
static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = {
@@ -189,7 +190,7 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
};
-static struct usb_device_id ti_id_table_combined[18+2*TI_EXTRA_VID_PID_COUNT+1] = {
+static struct usb_device_id ti_id_table_combined[19+2*TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
@@ -208,6 +209,7 @@ static struct usb_device_id ti_id_table_combined[18+2*TI_EXTRA_VID_PID_COUNT+1]
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
{ }
};
diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h
index f140f1b..b353e7e 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.h
+++ b/drivers/usb/serial/ti_usb_3410_5052.h
@@ -37,6 +37,7 @@
#define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */
#define TI_5052_EEPROM_PRODUCT_ID 0x505A /* EEPROM, no firmware */
#define TI_5052_FIRMWARE_PRODUCT_ID 0x505F /* firmware is running */
+#define FRI2_PRODUCT_ID 0x5053 /* Fish River Island II */
/* Multi-Tech vendor and product ids */
#define MTS_VENDOR_ID 0x06E0
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 5d7b71b..6f81aa5 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -669,12 +669,14 @@ exit:
static struct usb_serial_driver *search_serial_device(
struct usb_interface *iface)
{
- const struct usb_device_id *id;
+ const struct usb_device_id *id = NULL;
struct usb_serial_driver *drv;
+ struct usb_driver *driver = to_usb_driver(iface->dev.driver);
/* Check if the usb id matches a known device */
list_for_each_entry(drv, &usb_serial_driver_list, driver_list) {
- id = get_iface_id(drv, iface);
+ if (drv->usb_driver == driver)
+ id = get_iface_id(drv, iface);
if (id)
return drv;
}
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 5b073bc..59d646d 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -576,6 +576,7 @@ no_firmware:
"%s: please contact support@connecttech.com\n",
serial->type->description);
kfree(result);
+ kfree(command);
return -ENODEV;
no_command_private:
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 24caba7..fa8a1b2 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1004,6 +1004,12 @@ UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9999,
USB_SC_8070, USB_PR_CB, NULL,
US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ),
+/* Submitted by Oleksandr Chumachenko <ledest@gmail.com> */
+UNUSUAL_DEV( 0x07cf, 0x1167, 0x0100, 0x0100,
+ "Casio",
+ "EX-N1 DigitalCamera",
+ USB_SC_8070, USB_PR_DEVICE, NULL, 0),
+
/* Submitted by Hartmut Wahl <hwahl@hwahl.de>*/
UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,
"Samsung",
@@ -1885,6 +1891,13 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/* Reported by Jesse Feddema <jdfeddema@gmail.com> */
+UNUSUAL_DEV( 0x177f, 0x0400, 0x0000, 0x0000,
+ "Yarvik",
+ "PMP400",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ),
+
/* Reported by Hans de Goede <hdegoede@redhat.com>
* These Appotech controllers are found in Picture Frames, they provide a
* (buggy) emulation of a cdrom drive which contains the windows software